diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3605b4346..0ce9bb2f1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,6 +8,31 @@ jobs: secrets: IGNITE_REALTIME_MAVEN_USERNAME: ${{ secrets.IGNITE_REALTIME_MAVEN_USERNAME }} IGNITE_REALTIME_MAVEN_PASSWORD: ${{ secrets.IGNITE_REALTIME_MAVEN_PASSWORD }} + + readme-up-to-date: + name: Check that the readme documents the current endpoints and data types + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Java + uses: actions/setup-java@v4 + with: + java-version: 17 + distribution: temurin + cache: maven + + - name: Regenerate the documentation + run: mvn -B -DskipTests process-classes + + - name: Fail when the readme was not up to date + run: | + if ! git diff --exit-code readme.md; then + echo "::error file=readme.md::The documentation of endpoints and data types in readme.md does not match the OpenAPI annotations in the source code. Run 'mvn process-classes' (or any later build phase, like 'mvn package') and commit the updated readme.md." + exit 1 + fi + hurl-e2e-test: name: Hurl E2E Tests runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 797bbefa1..c81331225 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,6 @@ debian/ /AGENTS.md /CLAUDE.md /.claude/settings.local.json + +# Generated from readme.md during the build +/readme.html diff --git a/pom.xml b/pom.xml index 067c625f4..f7be766f7 100644 --- a/pom.xml +++ b/pom.xml @@ -46,6 +46,111 @@ maven-surefire-plugin 3.6.0 + + + + + io.swagger.core.v3 + swagger-maven-plugin + ${swagger.version} + + + generate-openapi-spec + process-classes + + resolve + + + + + ${project.build.directory}/openapi + openapi + JSON + true + + org.jivesoftware.openfire.plugin.rest.service + + + + + + org.codehaus.mojo + exec-maven-plugin + 3.6.4 + + + generate-readme-documentation + process-classes + + exec + + + ${java.home}/bin/java + + compile + + -Dslf4j.internal.verbosity=ERROR + -classpath + + ${project.basedir}/src/build/ReadmeGenerator.java + ${project.build.directory}/openapi/openapi.json + ${project.basedir}/readme.md + + + + + + + + + + maven-resources-plugin + + + copy-readme-markdown + prepare-package + + copy-resources + + + ${project.build.directory}/readme + + + ${project.basedir} + + readme.md + + + + + + + + + + com.ruleoftech + markdown-page-generator-plugin + 2.5.2 + + + generate-readme-html + prepare-package + + generate + + + + + ${project.build.directory}/readme + ${project.basedir} + ${project.basedir}/src/readme/header.html + ${project.basedir}/src/readme/footer.html + Openfire REST API Plugin Readme + true + TABLES,FENCED_CODE_BLOCKS,AUTOLINKS,STRIKETHROUGH,ANCHORLINKS + + diff --git a/readme.html b/readme.html deleted file mode 100644 index 054238b27..000000000 --- a/readme.html +++ /dev/null @@ -1,2851 +0,0 @@ - - - - - - - Openfire REST API Plugin Readme - - - - -
-
- - - -
-
-
-
-

REST API Plugin Readme

-

The REST API Plugin provides the ability to manage Openfire by sending an REST/HTTP request to the server. This plugin’s functionality is useful for applications that need to administer Openfire outside of the Openfire admin console.

-

CI Build Status

-

Build Status

-

Reporting Issues

-

Issues may be reported to the forums or via this repo’s Github Issues.

-

Feature list

- -

Available REST API clients

-

REST API clients are implementations of the REST API in a specific programming language.

-

Official

- -

Third party

- -

Installation

-

Copy restAPI.jar into the plugins directory of your Openfire server. The plugin will be automatically deployed. To upgrade to a newer version, overwrite the restAPI.jar file with the new one.

-

Important Step: To enable the plugin make sure to set the system property adminConsole.access.allow-wildcards-in-excludes to true

-

Without the above step the REST API plugin always redirects to login.
- This was done in response to a security issue.

-

Explanation of REST

-

To provide a standard way of accessing the data the plugin is using REST.

- - - - - - - - - - - - - - - - - - - - - - - - - - -
HTTP MethodUsage
GETReceive a read-only data
PUTOverwrite an existing resource
POSTCreates a new resource
DELETEDeletes the given resource

Authentication

-

All REST Endpoint are secured and must be authenticated. There are two ways to authenticate:

- -

The configuration can be done in Openfire Admin console under Server > Server Settings > REST API.

-

Basic HTTP Authentication

-

To access the endpoints is that required to send the Username and Password of a Openfire Admin account in your HTTP header request.

-

E.g., for username: admin and password: 12345:

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-
-

Shared secret key

-

To access the endpoints is that required to send the secret key in your header request.
- The secret key can be defined in Openfire Admin console under Server > Server Settings > REST API.

-

E.g.

-
-

Header: Authorization: s3cretKey

-
-

User related REST Endpoints

-

Retrieve users

-

Endpoint to get all or filtered users

-
-

GET /users

-
-

Payload: none

-

Return value: Users

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
search@QueryParamSearch/Filter by username.
This act like the wildcard search %String%
propertyKey@QueryParamFilter by user propertyKey.
propertyValue@QueryParamFilter by user propertyKey and propertyValue.
Note: It can only be used within propertyKey parameter

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-
-
-

GET http://example.org:9090/plugins/restapi/v1/users

-
-
-

GET http://example.org:9090/plugins/restapi/v1/users?search=testuser

-
-
-

GET http://example.org:9090/plugins/restapi/v1/users?propertyKey=keyname

-
-
-

GET http://example.org:9090/plugins/restapi/v1/users?propertyKey=keyname&propertyValue=keyvalue

-
-

If you want to get a JSON format result, please add “Accept: application/json” to the Header.

-

Retrieve a user

-

Endpoint to get information over a specific user

-
-

GET /users/{username}

-
-

Payload: none

-

Return value: User

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/users/testuser

-
-

Create a user

-

Endpoint to create a new user

-
-

POST /users

-
-

Payload: User
- Return value: HTTP status 201 (Created)

-

Examples

-

XML Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/users

-
-

Payload Example 1 (required parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<user>  
- <username>test3</username> <password>p4ssword</password></user>  
-
-

Payload Example 2 (available parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<user>  
- <username>testuser</username> <password>p4ssword</password> <name>Test User</name> <email>test@localhost.de</email> <properties> <property key="keyname" value="value"/> <property key="anotherkey" value="value"/> </properties></user>  
-
-

JSON Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/json

-

POST http://example.org:9090/plugins/restapi/v1/users

-
-

Payload Example 1 (required parameters):

-
{
- "username": "admin", "password": "p4ssword"}  
-
-

Payload Example 2 (available parameters):

-
{
- "username": "admin", "password": "p4ssword", "name": "Administrator", "email": "admin@example.com", "properties": { "property": [ { "@key": "console.rows_per_page", "@value": "user-summary=8" }, { "@key": "console.order", "@value": "session-summary=1" } ] }}  
-
-

REST API Version 1.3.0 and later - Payload Example 3 (available parameters):

-
{
- "users": [ { "username": "admin", "name": "Administrator", "email": "admin@example.com", "password": "p4ssword", "properties": [ { "key": "console.order", "value": "session-summary=0" } ] }, { "username": "test", "name": "Test", "password": "p4ssword" } ]}  
-
-

Delete a user

-

Endpoint to delete a user

-
-

DELETE /users/{username}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/users/testuser

-
-

Update a user

-

Endpoint to update / rename a user

-
-

PUT /users/{username}

-
-

Payload: User

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-

XML Example

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/users/testuser

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<user>  
- <username>testuser</username> <name>Test User edit</name> <email>test@edit.de</email> <properties> <property key="keyname" value="value"/> </properties></user>  
-
-

Rename Example

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/users/oldUsername

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<user>  
- <username>newUsername</username> <name>Test User edit</name> <email>test@edit.de</email> <properties> <property key="keyname" value="value"/> </properties></user>  
-
-

JSON Example

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/json

-

PUT http://example.org:9090/plugins/restapi/v1/users/testuser

-
-

Payload:

-
{
- "username": "testuser", "name": "Test User edit", "email": "test@edit.de", "properties": { "property": { "@key": "keyname", "@value": "value" } }}  
-
-

REST API Version 1.3.0 and later - Payload Example 2 (available parameters):

-
{
- "username": "testuser", "name": "Test User edit", "email": "test@edit.de", "properties": [ { "key": "keyname", "value": "value" } ]}  
-
-

Retrieve all user groups

-

Endpoint to get group names of a specific user

-
-

GET /users/{username}/groups

-
-

Payload: none

-

Return value: Groups

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/users/testuser/groups

-
-

Add user to groups

-

Endpoint to add user to a groups

-
-

POST /users/{username}/groups

-
-

Payload: Groups

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/users/testuser/groups

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<groups>  
- <groupname>Admins</groupname> <groupname>Support</groupname></groups>  
-
-

Add user to group

-

Endpoint to add user to a group

-
-

POST /users/{username}/groups/{groupName}

-
-

Payload: none

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username
groupName@PathExact group name

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/users/testuser/groups/testGroup

-
-

Delete a user from a groups

-

Endpoint to remove a user from a groups

-
-

DELETE /users/{username}/groups

-
-

Payload: Groups

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

DELETE http://example.org:9090/plugins/restapi/v1/users/testuser/groups

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<groups>  
- <groupname>Admins</groupname> <groupname>Support</groupname></groups>  
-
-

Delete a user from a group

-

Endpoint to remove a user from a group

-
-

DELETE /users/{username}/groups/{groupName}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username
groupName@PathExact group name

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

DELETE http://example.org:9090/plugins/restapi/v1/users/testuser/groups/testGroup

-
-

Lockout a user

-

Endpoint to lockout / ban the user from the chat server. The user will be kicked if the user is online.

-
-

POST /lockouts/{username}

-
-

Payload: none

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

POST http://example.org:9090/plugins/restapi/v1/lockouts/testuser

-
-

Unlock a user

-

Endpoint to unlock / unban the user

-
-

DELETE /lockouts/{username}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/lockouts/testuser

-
-

Retrieve user roster

-

Endpoint to get roster entries (buddies) from a specific user

-
-

GET /users/{username}/roster

-
-

Payload: none

-

Return value: Roster

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/users/testuser/roster

-
-

Create a user roster entry

-

Endpoint to add a new roster entry to a user

-
-

POST /users/{username}/roster

-
-

Payload: RosterItem

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/users/testuser/roster

-
-

Payload:
- Payload Example 1 (required parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<rosterItem>  
- <jid>peter@pan.de</jid></rosterItem>  
-
-

Payload Example 2 (available parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<rosterItem>  
- <jid>peter@pan1.de</jid> <nickname>Peter1</nickname> <subscriptionType>3</subscriptionType> <groups> <group>Friends</group> </groups></rosterItem>  
-
-

Delete a user roster entry

-

Endpoint to remove a roster entry from a user

-
-

DELETE /users/{username}/roster/{jid}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username
jid@PathJID of the roster item

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/users/testuser/roster/peter@pan.de

-
-

Update a user roster entry

-

Endpoint to update a roster entry

-
-

PUT /users/{username}/roster/{jid}

-
-

Payload: RosterItem

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username
jid@PathJID of the roster item

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/users/testuser/roster/peter@pan.de

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<rosterItem>  
- <jid>peter@pan.de</jid> <nickname>Peter Pan</nickname> <subscriptionType>0</subscriptionType> <groups> <group>Support</group> </groups></rosterItem>  
-
-

Retrieve user’s vcard

-

Endpoint to get the vCard of a particular user

-
-

GET /users/{username}/vcard

-
-

Payload: none

-

Return value: vCard XML data

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/users/testuser/vcard

-
-

Add or update user’s vCard

-

Endpoint to add or replace a vCard of a particular user.

-
-

PUT /users/{username}/vcard

-
-

Payload: vCard XML data

-

Return value: HTTP status 200 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/users/testuser/vcard

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<vCard xmlns="vcard-temp">  
- <N> <FAMILY>Doe</FAMILY> <GIVEN>Janice</GIVEN> <MIDDLE>Francis</MIDDLE> </N> <ORG> <ORGNAME/> <ORGUNIT/> </ORG> <NICKNAME>Jane</NICKNAME> <FN>Janice Francis Doe</FN> <TITLE/> <URL/> <EMAIL> <HOME/> <INTERNET/> <PREF/> <USERID>j.doe@example.org</USERID> </EMAIL> <TEL> <WORK/> <VOICE/> <NUMBER/> </TEL> <TEL> <WORK/> <PAGER/> <NUMBER/> </TEL> <TEL> <WORK/> <FAX/> <NUMBER/> </TEL> <TEL> <WORK/> <CELL/> <NUMBER/> </TEL> <TEL> <HOME/> <VOICE/> <NUMBER/> </TEL> <TEL> <HOME/> <PAGER/> <NUMBER/> </TEL> <TEL> <HOME/> <FAX/> <NUMBER/> </TEL> <TEL> <HOME/> <CELL/> <NUMBER/> </TEL> <ADR> <WORK/> <LOCALITY/> <CTRY/> <STREET/> <PCODE/> <REGION/> </ADR> <ADR> <HOME/> <LOCALITY/> <CTRY/> <STREET/> <PCODE/> <REGION/> </ADR></vCard>  
-
-

Delete user’s vcard

-

Endpoint to remove the vCard of a particular user

-
-

DELETE /users/{username}/vcard

-
-

Payload: none

-

Return value: none

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathExact username

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/users/testuser/vcard

-
-

Chat room related REST Endpoints

-

Retrieve all chat services

-

Endpoint to get all chat services

-
-

GET /chatservices

-
-

Payload: none

-

Return value: Chat services

-

Possible parameters: none

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/chatservices

-
-

Create a chat service

-

Endpoint to create a new chat service.

-
-

POST /chatservices

-
-

Payload: Chatservice

-

Return value: HTTP status 201 (Created)

-

Possible parameters: none

-

XML Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatservices

-
-

Payload Example (available parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<chatService>  
- <serviceName>new-chat-service-name</serviceName> <description>A mightily fine service</description> <hidden>false</hidden></chatService>  
-
-

JSON Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/json

-

POST http://example.org:9090/plugins/restapi/v1/chatservices

-
-

Payload Example (available parameters):

-
{
- "serviceName": "new-chat-service-name", "description": "A mightily fine service", "hidden": false}  
-
-

Retrieve all chat rooms

-

Endpoint to get all chat rooms

-
-

GET /chatrooms

-
-

Payload: none

-

Return value: Chatrooms

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
servicename@QueryParamThe name of the Group Chat Serviceconference
type@QueryParampublic: Only as List Room in Directory set rooms
all: All rooms.
public
search@QueryParamSearch/Filter by room name.
This act like the wildcard search %String%

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms?type=all

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms?type=all&servicename=privateconf

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms?search=test

-
-

Retrieve a chat room

-

Endpoint to get information over specific chat room

-
-

GET /chatrooms/{roomName}

-
-

Payload: none

-

Return value: Chatroom

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms/test

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms/test?servicename=privateconf

-
-

Retrieve chat room participants

-

Endpoint to get all participants with a role of specified room.

-
-

GET /chatrooms/{roomName}/participants

-
-

Payload: none

-

Return value: Participants

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms/room1/participants

-
-

Retrieve chat room occupants

-

Endpoint to get all occupants (all roles / affiliations) of a specified room.

-
-

GET /chatrooms/{roomName}/occupants

-
-

Payload: none

-

Return value: Occupants

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms/room1/occupants

-
-

Retrieve chat room message history

-

Endpoint to get the chat message history of a specified room.

-
-

GET /chatrooms/{roomName}/chathistory

-
-

Payload: none

-

Return value: Chat History

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference

Create a chat room

-

Endpoint to create a new chat room.

-
-

POST /chatrooms

-
-

Payload: Chatroom

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitations to affiliated usersfalse

XML Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms

-
-

Payload Example 1 (required parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<chatRoom>  
- <naturalName>global-1</naturalName> <roomName>global</roomName> <description>Global Chat Room</description></chatRoom>  
-
-

Payload Example 2 (available parameters):

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<chatRoom>  
- <roomName>global</roomName> <naturalName>global-2</naturalName> <description>Global Chat Room</description> <subject>global-2 Subject</subject> <creationDate>2014-02-12T15:52:37.592+01:00</creationDate> <modificationDate>2014-09-12T15:35:54.702+02:00</modificationDate> <maxUsers>0</maxUsers> <persistent>true</persistent> <publicRoom>true</publicRoom> <registrationEnabled>false</registrationEnabled> <canAnyoneDiscoverJID>false</canAnyoneDiscoverJID> <canOccupantsChangeSubject>false</canOccupantsChangeSubject> <canOccupantsInvite>false</canOccupantsInvite> <canChangeNickname>false</canChangeNickname> <logEnabled>true</logEnabled> <loginRestrictedToNickname>false</loginRestrictedToNickname> <membersOnly>false</membersOnly> <moderated>false</moderated> <allowPM>anyone</allowPM> <broadcastPresenceRoles> <broadcastPresenceRole>moderator</broadcastPresenceRole> <broadcastPresenceRole>participant</broadcastPresenceRole> <broadcastPresenceRole>visitor</broadcastPresenceRole> </broadcastPresenceRoles> <owners> <owner>owner@localhost</owner> </owners> <admins> <admin>admin@localhost</admin> </admins> <members> <member>member2@localhost</member> <member>member1@localhost</member> </members> <outcasts> <outcast>outcast1@localhost</outcast> </outcasts></chatRoom>  
-
-

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):

-
{
- "roomName": "global", "naturalName": "global-2", "description": "Global chat room"}  
-
-

Payload Example 2 (available parameters):

-
{
- "roomName": "global-1", "naturalName": "global-1_test_hello", "description": "Global chat room", "subject": "Global chat room subject", "creationDate": "2012-10-18T16:55:12.803+02:00", "modificationDate": "2014-07-10T09:49:12.411+02:00", "maxUsers": "0", "persistent": "true", "publicRoom": "true", "registrationEnabled": "false", "canAnyoneDiscoverJID": "true", "canOccupantsChangeSubject": "false", "canOccupantsInvite": "false", "canChangeNickname": "false", "logEnabled": "true", "loginRestrictedToNickname": "true", "membersOnly": "false", "moderated": "false", "allowPM": "anyone", "broadcastPresenceRoles": { "broadcastPresenceRole": [ "moderator", "participant", "visitor" ] }, "owners": { "owner": "owner@localhost" }, "admins": { "admin": [ "admin@localhost", "admin2@localhost" ] }, "members": { "member": [ "member@localhost", "member2@localhost" ] }, "outcasts": { "outcast": [ "outcast@localhost", "outcast2@localhost" ] }}  
-
-

REST API Version 1.3.0 and later - Payload Example 2 (available parameters):

-
{
- "roomName": "global-1", "naturalName": "global-1_test_hello", "description": "Global chat room", "subject": "Global chat room subject", "creationDate": "2012-10-18T16:55:12.803+02:00", "modificationDate": "2014-07-10T09:49:12.411+02:00", "maxUsers": "0", "persistent": "true", "publicRoom": "true", "registrationEnabled": "false", "canAnyoneDiscoverJID": "true", "canOccupantsChangeSubject": "false", "canOccupantsInvite": "false", "canChangeNickname": "false", "logEnabled": "true", "loginRestrictedToNickname": "true", "membersOnly": "false", "moderated": "false", "allowPM": "anyone", "broadcastPresenceRoles": [ "moderator", "participant", "visitor" ], "owners": [ "owner@localhost" ], "admins": [ "admin@localhost" ], "members": [ "member@localhost" ], "outcasts": [ "outcast@localhost" ]}  
-
-

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 version="1.0" encoding="UTF-8" standalone="yes"?>
-<results>  
- <success> <result> <roomName>room1</roomName> <resultType>Success</resultType> <message>Room was successfully created</message> </result> <result> <roomName>room2</roomName> <resultType>Success</resultType> <message>Room was successfully created</message> </result> </success> <failure/> <other/></results>  
-
-
{
- "success": [ { "roomName": "room1", "resultType": "Success", "message": "Room was successfully created" }, { "roomName": "room2", "resultType": "Success", "message": "Room was successfully created" } ], "failure": [], "other": []}  
-
-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitations to newly affiliated usersfalse

XML Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/bulk

-
-

Payload Example:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<chatRooms>  
- <chatRoom> <roomName>room1</roomName> <description>description1</description> </chatRoom> <chatRoom> <roomName>room2</roomName> <description>description1</description> </chatRoom></chatRooms>  
-
-

For more examples, with more parameters, see the 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):

-
{
- "chatRooms": [ { "roomName": "room1", "description": "description1" }, { "roomName": "room2", "description": "description2" } ]}  
-
-

For more examples, with more parameters, see the create a chat room endpoint.

-

Delete a chat room

-

Endpoint to delete a chat room.

-
-

DELETE /chatrooms/{roomName}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/testroom

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/testroom?servicename=privateconf

-
-

Update a chat room

-

Endpoint to update a chat room.

-
-

PUT /chatrooms/{roomName}

-
-

Payload: Chatroom

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitations to newly affiliated usersfalse

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/chatrooms/global

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<chatRoom>  
- <roomName>global</roomName> <naturalName>global-2</naturalName> <description>Global Chat Room edit</description> <subject>New subject</subject> <password>test</password> <creationDate>2014-02-12T15:52:37.592+01:00</creationDate> <modificationDate>2014-09-12T14:20:56.286+02:00</modificationDate> <maxUsers>0</maxUsers> <persistent>true</persistent> <publicRoom>true</publicRoom> <registrationEnabled>false</registrationEnabled> <canAnyoneDiscoverJID>false</canAnyoneDiscoverJID> <canOccupantsChangeSubject>false</canOccupantsChangeSubject> <canOccupantsInvite>false</canOccupantsInvite> <canChangeNickname>false</canChangeNickname> <logEnabled>true</logEnabled> <loginRestrictedToNickname>false</loginRestrictedToNickname> <membersOnly>false</membersOnly> <moderated>false</moderated> <allowPM>anyone</allowPM> <broadcastPresenceRoles/> <owners> <owner>owner@localhost</owner> </owners> <admins> <admin>admin@localhost</admin> </admins> <members> <member>member2@localhost</member> <member>member1@localhost</member> </members> <outcasts> <outcast>outcast1@localhost</outcast> </outcasts></chatRoom>  
-
-

Invite user or user group to a chat Room

-

Endpoint to invite a user or a user group to a room.

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://localhost:9090/plugins/restapi/v1/chatrooms/{roomName}/invite/{name}

-
-

Payload Example:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<mucInvitation>  
- <reason>Hello, come to this room, it is nice</reason></mucInvitation>  
-
-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
name@PathThe local username or group name or the user JID or group JID

Invite multiple users and/or user groups to a chat Room

-

Endpoint to invite multiple users and/or user groups to a room. Works both with JIDs and (user/group) names.

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://localhost:9090/plugins/restapi/v1/chatrooms/{roomName}/invite

-
-

Payload Example:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<mucInvitation>  
- <reason>Hello, come to this room, it is nice</reason> <jidsToInvite> <jid>jane@example.org</jid> <jid>ADNMQP8=@example.org/695c6ae413c00446733d926ccadefd8b</jid> <jid>john</jid> <jid>SomeGroupName</jid> </jidsToInvite></mucInvitation>  
-
-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name

Get all users with a particular affiliation in a chat room

-

Retrieves a list of JIDs for all users with the specified affiliation in a multi-user chat room.

-
-

GET /chatrooms/{roomName}/{affiliation}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
affiliation@PathAvailable affiliations:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

GET http://example.org:9090/plugins/restapi/v1/chatrooms/global/member

-
-

Return payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<members>  
- <member>member2@localhost</member> <member>member1@localhost</member></members>  
-
-

Add user with affiliation to chat room

-

Endpoint to add a new user with affiliation to a room.

-
-

POST /chatrooms/{roomName}/{affiliation}/{name}

-
-

Payload: none

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
name@PathThe local username or the user JID
affiliation@PathAvailable affiliations:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitation to the newly affiliated userfalse

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser@openfire.com

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/testUser

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/testUser?sendInvitations=true

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/testUser

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser?servicename=privateconf

-
-

Replace all users with a affiliation in a chat room

-

Endpoint to replace all users with a particular affiliation in a multi-user chat room. Note that a user can only have one type of affiliation with a room. By adding a user using a particular affiliation, any other pre-existing affiliation is removed.

-
-

PUT /chatrooms/{roomName}/{affiliation}

-
-

Payload: list of affiliations

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
affiliation@PathAvailable affiliations:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitation to newly affiliated usersfalse

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/chatrooms/global/members

-
-

Request Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<members>  
- <member>member2@localhost</member> <member>member1@localhost</member></members>  
-
-

Add multiple users with a affiliation to a chat room

-

Endpoint to add multiple users with an affiliation to a multi-user chat room. Note that a user can only have one type of affiliation with a room. By adding a user using a particular affiliation, any other pre-existing affiliation is removed.

-
-

PUT /chatrooms/{roomName}/{affiliation}

-
-

Payload: list of affiliations

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
affiliation@PathAvailable affiliation:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitations to newly affiliated usersfalse

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/members

-
-

Request Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<members>  
- <member>member2@localhost</member> <member>member1@localhost</member></members>  
-
-

Add group with affiliation to chat room

-

Endpoint to add a new group with affiliation to a room.

-
-

POST /chatrooms/{roomName}/{affiliation}/group/{name}

-
-

Payload: none

-

Return value: HTTP status 201 (Created)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
name@PathThe group name
affiliation@PathAvailable affiliations:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference
sendInvitations@QueryParamWhether to send invitations to the users in the newly affiliated groupsfalse

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/group/testGroup

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/group/testGroup

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/group/testGroup

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/group/testGroup

-

POST http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/group/testUser?servicename=privateconf

-
-

Delete a user from a chat room

-

Endpoint to remove a room user affiliation.

-
-

DELETE /chatrooms/{roomName}/{affiliations}/{name}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
roomname@PathExact room name
name@PathThe local username or the user JID
affiliations@PathAvailable affiliations:
owners
admins
members
outcasts
servicename@QueryParamThe name of the Group Chat Serviceconference

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser@openfire.com

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/testUser

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/testUser

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/testUser

-

DELETE http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser?servicename=privateconf

-
-

System related REST Endpoints

-

Retrieve all system properties

-

Endpoint to get all system properties

-
-

GET /system/properties

-
-

Payload: none

-

Return value: System properties

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/system/properties

-
-

Retrieve system property

-

Endpoint to get information over specific system property

-
-

GET /system/properties/{propertyName}

-
-

Payload: none

-

Return value: System property

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
propertyName@PathThe name of system property

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/system/properties/xmpp.domain

-
-

Create a system property

-

Endpoint to create a system property. Note that the name of the property must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens.

-
-

POST system/properties

-
-

Payload: System Property

-

Return value: HTTP status 201 (Created)

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/system/properties

-
-

Payload Example:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<property key="propertyName" value="propertyValue"/>  
-
-

Delete a system property

-

Endpoint to delete a system property, together with all of its child properties (properties of which the name starts with the name of this property, followed by a dot). Note that the name of the property must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens. A deletion that could also delete other properties (for example, because the name contains an underscore, which can match any character) is rejected.

-
-

DELETE /system/properties/{propertyName}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
propertyName@PathThe name of system property

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/system/properties/propertyName

-
-

Update a system property

-

Endpoint to update / overwrite a system property

-
-

PUT /system/properties/{propertyName}

-
-

Payload: System property

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
propertyName@PathThe name of system property

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/system/properties/propertyName

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<property key="propertyName" value="anotherValue"/>  
-
-

Retrieve concurrent sessions

-

Endpoint to get count of concurrent sessions

-
-

GET /system/statistics/sessions

-
-

Payload: none

-

Return value: Sessions count

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/system/statistics/sessions

-
-

Check the ‘liveness’ state (using all checks)

-

Detects if Openfire has reached a state that it cannot recover from, except for with a restart, based on every liveness check that it has implemented.

-
-

GET /system/liveness

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘deadlock’ liveness check

-

Detects if Openfire has reached a state that it cannot recover from because of a deadlock.

-
-

GET /system/liveness/deadlock

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘properties’ liveness check

-

Detects if Openfire has reached a state that it cannot recover from because a system property change requires a restart to take effect.

-
-

GET /system/liveness/properties

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Check the ‘readiness’ state (using all checks)

-

Detects if Openfire is in a state where it is ready to process traffic, based on every readiness check that it has implemented.

-
-

GET /system/readiness

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘server’ readiness check

-

Detects if Openfire’s core service has been started.

-
-

GET /system/readiness/server

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘cluster’ readiness check

-

Detects if the cluster functionality has finished starting (or is disabled).

-
-

GET /system/readiness/cluster

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘plugins’ readiness check

-

Detects if Openfire has finished starting its plugins.

-
-

GET /system/readiness/plugins

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Perform ‘connections’ readiness check

-

Detects if Openfire is ready to accept connection requests.

-
-

GET /system/readiness/connections

-
-

Payload: none

-

Return value: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure.

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
connectionType@PathOptional. Use to limit the check to one particular connection type. One of: SOCKET_S2S, SOCKET_C2S, BOSH_C2S, WEBADMIN, COMPONENT, CONNECTION_MANAGER
encypted@PathCheck the encrypted (true) or unencrypted (false) variant of the connection type. Only used in combination with ‘connectionType’, as without it, all types and both encrypted and unencrypted are checked.

Group related REST Endpoints

-

Retrieve all groups

-

Endpoint to get all groups

-
-

GET /groups

-
-

Payload: none

-

Return value: Groups

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/groups

-
-

Retrieve a group

-

Endpoint to get information over specific group

-
-

GET /groups/{groupName}

-
-

Payload: none

-

Return value: Group

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
groupName@PathThe name of the group

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/groups/moderators

-
-

Create a group

-

Endpoint to create a new group

-
-

POST /groups

-
-

Payload: Group

-

Return value: HTTP status 201 (Created)

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type: application/xml

-

POST http://example.org:9090/plugins/restapi/v1/groups

-
-

Payload Example:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<group>  
- <name>GroupName</name> <description>Some description</description> <isshared>false</isshared></group>  
-
-

Delete a group

-

Endpoint to delete a group

-
-

DELETE /groups/{groupName}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
groupName@PathThe name of the group

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/groups/groupToDelete

-
-

Update a group

-

Endpoint to update / overwrite a group

-
-

PUT /groups/{groupName}

-
-

Payload: Group

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
groupName@PathThe name of the group

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

Header: Content-Type application/xml

-

PUT http://example.org:9090/plugins/restapi/v1/groups/groupNameToUpdate

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<group>  
- <name>groupNameToUpdate</name> <description>New description</description> <isshared>false</isshared></group>  
-
-

Session related REST Endpoints

-

Retrieve all user session

-

Endpoint to get all user sessions

-
-

GET /sessions

-
-

Payload: none

-

Return value: Sessions

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/sessions

-
-

Retrieve the user sessions

-

Endpoint to get sessions from a user

-
-

GET /sessions/{username}

-
-

Payload: none

-

Return value: Sessions

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathThe username of the user

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/sessions/testuser

-
-

Close all user sessions

-

Endpoint to close/kick sessions from a user

-
-

DELETE /sessions/{username}

-
-

Payload: none

-

Return value: HTTP status 200 (OK)

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@PathThe username of the user

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

DELETE http://example.org:9090/plugins/restapi/v1/sessions/testuser

-
-

Message related REST Endpoints

-

Send a broadcast message

-

Endpoint to send a broadcast/server message to all online users

-
-

POST /messages/users

-
-

Payload: Message

-

Return value: HTTP status 201 (Created)

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

POST http://example.org:9090/plugins/restapi/v1/messages/users

-
-

Payload:

-
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<message>  
- <body>Your message</body></message>  
-
-

Security Audit related REST Endpoints

-

Retrieve the Security audit logs

-

Endpoint to get security audit logs

-
-

GET /logs/security

-
-

Payload: none

-

Return value: Security Audit Logs

-

Possible parameters

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
username@QueryParamUsername of user to look up
startTime@QueryParamOldest timestamp of range of logs to retrieve
endTime@QueryParamMost recent timestamp of range of logs to retrieve0 (until now)
offset@QueryParamNumber of logs to skip
limit@QueryParamNumber of logs to retrieve

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/logs/security

-
-

Clustering related REST Endpoints

-

Retrieve information for all cluster nodes.

-

Endpoint to get information for all nodes in the cluster. Note that this endpoint can only return data for remote nodes
- when the instance of Openfire that processes this query has successfully joined the cluster.

-
-

GET http://example.org:9090/plugins/restapi/v1/clustering/nodes

-
-

Payload: none

-

Return value: ClusterNodes

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/clustering/nodes

-
-

Retrieve information for a specific cluster node.

-

Endpoint to get information for a specific cluster node. Note that this endpoint can only return data for remote nodes
- when the instance of Openfire that processes this query has successfully joined the cluster.

-
-

GET http://example.org:9090/plugins/restapi/v1/clustering/nodes/{nodeId}

-
-

Payload: none

-

Return value: ClusterNode

-

Possible parameters

- - - - - - - - - - - - - - - - - - -
ParameterParameter TypeDescriptionDefault value
nodeId@PathExact NodeID

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/clustering/nodes/52a89928-66f7-45fd-9bb8-096de07400ac

-
-

Retrieve the Clustering status

-

Endpoint to get description of clustering status

-
-

GET /clustering/status

-
-

Payload: none

-

Return value: String describing the clustering status of this Openfire instance

-

Examples

-
-

Header: Authorization: Basic YWRtaW46MTIzNDU=

-

GET http://example.org:9090/plugins/restapi/v1/clustering/status

-
-

Possible Responses

- -

Data format

-

Openfire REST API provides XML and JSON as data format. The default data format is XML.
- To get a JSON result, please add “Accept: application/json” to the request header.
- If you want to create a resource with JSON data format, please add “Content-Type: application/json”.

-

Data types

-

ClusterNode

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
hostNameNoThe hostname and IP address of the server on which this cluster node is running.
nodeIDNoA unique identifier of this cluster node.
joinedTimeNoTimestamp when the node joined the cluster.
seniorMemberNoBoolean value indicating if the node is currently the senior member of the cluster.

User

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
usernameNoThe username of the user
nameYesThe name of the user
emailYesThe email of the user
passwordNoThe password of the user
propertiesYesList of properties. Property is a key / value object. The key must to be per user unique

RosterItem

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
jidNoThe JID of the roster item
nicknameYesThe nickname for the user when used in this roster
subscriptionTypeYesThe subscription type
Possible numeric values are: -1 (remove), 0 (none), 1 (to), 2 (from), 3 (both)
groupsNoA list of groups to organize roster entries under (e.g. friends, co-workers, etc.)

Chatroom

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
roomNameNoThe name/id of the room. Can only contains lowercase and alphanumeric characters.
naturalNameNoAlso the name of the room, but can contains non alphanumeric characters. It’s mainly used for users while discovering rooms hosted by the Multi-User Chat service.
descriptionNoDescription text of the room.
subjectYesSubject of the room.
passwordYesThe password that the user must provide to enter the room
creationDateYesThe date when the room was created. Will be automatically set by creation. Example: 2014-07-10T09:49:12.411+02:00
modificationDateYesThe last date when the room’s configuration was modified. If the room’s configuration was never modified then the initial value will be the same as the creation date. Will be automatically set by update. Example: 2014-07-10T09:49:12.411+02:00
maxUsersYesthe maximum number of occupants that can be simultaneously in the room. 0 means unlimited number of occupants.
persistentYesCan be “true” or “false”. Persistent rooms are saved to the database to make their configurations persistent together with the affiliation of the users. Otherwise the room will be destroyed if the last occupant leave the room.
publicRoomYesCan be “true” or “false”. True if the room is searchable and visible through service discovery.
registrationEnabledYesCan be “true” or “false”. True if users are allowed to register with the room. By default, room registration is enabled.
canAnyoneDiscoverJIDYesCan be “true” or “false”. True if every presence packet will include the JID of every occupant.
canOccupantsChangeSubjectYesCan be “true” or “false”. True if participants are allowed to change the room’s subject.
canOccupantsInviteYesCan be “true” or “false”. True if occupants can invite other users to the room. If the room does not require an invitation to enter (i.e. is not members-only) then any occupant can send invitations. On the other hand, if the room is members-only and occupants cannot send invitation then only the room owners and admins are allowed to send invitations.
canChangeNicknameYesCan be “true” or “false”. True if room occupants are allowed to change their nicknames in the room. By default, occupants are allowed to change their nicknames.
logEnabledYesCan be “true” or “false”. True if the room’s conversation is being logged. If logging is activated the room conversation will be saved to the database every couple of minutes. The saving frequency is the same for all the rooms and can be configured by changing the property “xmpp.muc.tasks.log.timeout”.
loginRestrictedToNicknameYesCan be “true” or “false”. True if registered users can only join the room using their registered nickname. By default, registered users can join the room using any nickname.
membersOnlyYesCan be “true” or “false”. True if the room requires an invitation to enter. That is if the room is members-only.
moderatedYesCan be “true” or “false”. True if the room in which only those with “voice” may send messages to all occupants.
allowPMYesOne of “anyone”, “participants”, “moderators” or “none”. Controls who is allowed to send private messages to other occupants in the room.
broadcastPresenceRolesYesThe list of roles of which presence will be broadcasted to the rest of the occupants.
ownersYesA collection with the current list of owners. The collection contains the bareJID of the users with owner affiliation.
adminsYesA collection with the current list of admins. The collection contains the bareJID of the users with admin affiliation.
membersYesA collection with the current list of room members. The collection contains the bareJID of the users with member affiliation. If the room is not members-only then the list will contain the users that registered with the room and therefore they may have reserved a nickname.
outcastsYesA collection with the current list of outcast users. An outcast user is not allowed to join the room again. The collection contains the bareJID of the users with outcast affiliation.
ownerGroupsYesA collection with the current list of groups with owner affiliation. The collection contains the name only.
adminGroupsYesA collection with the current list of groups with admin affiliation. The collection contains the name only.
memberGroupsYesA collection with the current list of groups with member affiliation. The collection contains the name only.
outcastGroupsYesA collection with the current list of groups with outcast affiliation. The collection contains the name only.

Group

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
nameNoThe name of the group
descriptionNoThe description of the group
adminsYesA collection with current admins of the group
membersYesA collection with current members of the group

System Property

- - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
keyNoThe name of the system property
valueNoThe value of the system property

Session

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
sessionIdNoFull JID of a user e.g. (testUser@testserver.de/SomeRessource)
usernameNoThe username associated with this session. Can be also “Anonymous”.
resourceYesResource name
nodeNoCan be “Local” or “Remote”
sessionStatusNoThe current status of this session. Can be “Closed”, “Connected”, “Authenticated” or “Unknown”.
presenceStatusNoThe status of this presence packet, a natural-language description of availability status.
priorityNoThe priority of the session. The valid priority range is -128 through 128.
hostAddressNoThe IP address string in textual presentation.
hostNameNoThe host name for this IP address.
creationDateNoThe date the session was created.
lastActionDateNoThe time the session last had activity.
secureNoIs “true” if this connection is secure.

Sessions count

- - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
clusterSessionsNoNumber of client sessions that are authenticated with the server. This includes anonymous and non-anoymous users from the whole cluster.
localSessionsNoNumber of client sessions that are authenticated with the server. This includes anonymous and non-anoymous users.

Security Audit Logs

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
logIdNoUnique ID of this log
usernameNoThe username of the user who performed this event
timestampNoThe time stamp of when this event occurred
summaryNoThe summary, or short description of what transpired in the event
nodeNoThe node that triggered the event, usually a hostname or IP address
detailsNoDetailed information about what occurred in the event

Occupants

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ParameterOptionalDescription
jidNoThe JID of the MUC room
userAddressNoThe JID of the user
roleNoRole of the user
affiliationNoAffiliation of the user
-
-
- - - diff --git a/readme.md b/readme.md index bacf218d7..0a8f8928b 100644 --- a/readme.md +++ b/readme.md @@ -132,2101 +132,2508 @@ Be aware of the following: client's address. - Changes to the `adminConsole.forwarded.*` properties take effect only after the admin console has been restarted. -# User related REST Endpoints + -## Retrieve users -Endpoint to get all or filtered users -> **GET** /users +# REST Endpoints -**Payload:** none +The paths of all endpoints below are relative to the root of the Openfire admin console, for example `http://example.org:9090`. The data types that are used by these endpoints are described in [Data types](#data-types). -**Return value:** Users +In addition to the responses that are documented for each endpoint, every endpoint can respond with: -### Possible parameters +- `401`: Web service authentication failed. +- `500`: Unexpected, generic error condition. -| Parameter | Parameter Type | Description | Default value | -|---------------|----------------|--------------------------------------------------------------------------------------------------------------|---------------| -| search | @QueryParam | Search/Filter by username.
This act like the wildcard search %String% | | -| propertyKey | @QueryParam | Filter by user propertyKey. | | -| propertyValue | @QueryParam | Filter by user propertyKey and propertyValue.
**Note:** It can only be used within propertyKey parameter | | +Interactive documentation of these endpoints is available in the Openfire admin console, via the link on the REST API settings page (Server > Server Settings > REST API). -### Examples +# Users ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= +Managing Openfire users. ->**GET** http://example.org:9090/plugins/restapi/v1/users +## Get users ->**GET** http://example.org:9090/plugins/restapi/v1/users?search=testuser +> **GET** /plugins/restapi/v1/users ->**GET** http://example.org:9090/plugins/restapi/v1/users?propertyKey=keyname +Retrieve all users defined in Openfire (with optional filtering). ->**GET** http://example.org:9090/plugins/restapi/v1/users?propertyKey=keyname&propertyValue=keyvalue +**Parameters** -If you want to get a JSON format result, please add "**Accept: application/json**" to the **Header**. +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| search | query | no | Search/Filter by username. This acts like the wildcard search %String%. | | +| propertyKey | query | no | Filter by a user property name. | | +| propertyValue | query | no | Filter by user property value. Note: This can only be used in combination with a property name parameter. | | -## Retrieve a user -Endpoint to get information over a specific user -> **GET** /users/{username} +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | A list of Openfire users. | [UserEntities](#userentities) (XML or JSON) | -**Return value:** User +## Create user -### Possible parameters +> **POST** /plugins/restapi/v1/users -| Parameter | Parameter Type | Description | Default value | -|-----------|----------------|------------------|---------------| -| username | @Path | Exact username | | +Add a new user to Openfire. -### Examples +**Request body** (required): [UserEntity](#userentity) (XML or JSON) - The definition of the user to create. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/users/testuser +
+Example request bodies -## Create a user -Endpoint to create a new user -> **POST** /users +XML (`Content-Type: application/xml`): -**Payload:** User -**Return value:** HTTP status 201 (Created) +```xml + + john + John Doe + john@example.org + s3cr3t + + + + +``` -### Examples -#### XML Examples +JSON (`Content-Type: application/json`): +```json +{ + "username" : "john", + "name" : "John Doe", + "email" : "john@example.org", + "password" : "s3cr3t", + "properties" : [ { + "key" : "department", + "value" : "Sales" + } ] +} +``` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/**xml** -> ->**POST** http://example.org:9090/plugins/restapi/v1/users +
-**Payload Example 1 (required parameters):** +**Responses** -```xml - - - test3 - p4ssword - -``` +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The user was created. | | +| 400 | No user definition, username or password was provided. | [ErrorResponse](#errorresponse) | +| 409 | A user with this username already exists. | [ErrorResponse](#errorresponse) | + +## Get user + +> **GET** /plugins/restapi/v1/users/{username} + +Retrieve a user that is defined in Openfire. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user to return. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The Openfire user. | [UserEntity](#userentity) (XML or JSON) | +| 404 | No user with that username was found. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Update user + +> **PUT** /plugins/restapi/v1/users/{username} + +Update an existing user in Openfire. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user to update. | | + +**Request body** (required): [UserEntity](#userentity) (XML or JSON) - The updated definition of the user. + +
+Example request bodies + +XML (`Content-Type: application/xml`): -**Payload Example 2 (available parameters):** ```xml - - testuser - p4ssword - Test User - test@localhost.de + john + John Doe + john@example.org + s3cr3t - - + ``` -#### JSON Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/**json** -> ->**POST** http://example.org:9090/plugins/restapi/v1/users -**Payload Example 1 (required parameters):** +JSON (`Content-Type: application/json`): + ```json { - "username": "admin", - "password": "p4ssword" + "username" : "john", + "name" : "John Doe", + "email" : "john@example.org", + "password" : "s3cr3t", + "properties" : [ { + "key" : "department", + "value" : "Sales" + } ] } ``` -**Payload Example 2 (available parameters):** +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The user was updated. | | +| 404 | No user with that username was found. | [ErrorResponse](#errorresponse) | +| 409 | The user is to be renamed, but a user with the new username already exists. | [ErrorResponse](#errorresponse) | + +## Delete user + +> **DELETE** /plugins/restapi/v1/users/{username} + +Remove an existing user from Openfire. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user to remove. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The user was removed. | | +| 404 | No user with that username was found. | [ErrorResponse](#errorresponse) | + +## Get user's groups + +> **GET** /plugins/restapi/v1/users/{username}/groups + +Retrieve names of all groups that a particular user is in. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to return group names. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The names of the groups that the user is in. | [UserGroupsEntity](#usergroupsentity) (XML or JSON) | +| 404 | No user with that username was found. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Add user to groups + +> **POST** /plugins/restapi/v1/users/{username}/groups + +Add a particular user to a collection of groups. When a group that is provided does not exist, it will be automatically created if possible. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user that is to be added to groups. | | + +**Request body** (required): [UserGroupsEntity](#usergroupsentity) (XML or JSON) - A collection of names for groups that the user is to be added to. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + Sales + +``` + +JSON (`Content-Type: application/json`): + ```json { - "username": "admin", - "password": "p4ssword", - "name": "Administrator", - "email": "admin@example.com", - "properties": { - "property": [ - { - "@key": "console.rows_per_page", - "@value": "user-summary=8" - }, - { - "@key": "console.order", - "@value": "session-summary=1" - } - ] - } + "groupnames" : [ "Sales" ] } ``` -**REST API Version 1.3.0 and later - Payload Example 3 (available parameters):** +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The user was added to all groups. | | +| 400 | The username cannot be parsed into a JID. | [ErrorResponse](#errorresponse) | + +## Delete user from groups + +> **DELETE** /plugins/restapi/v1/users/{username}/groups + +Removes a user from a collection of groups. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user that is to be removed from groups. | | + +**Request body** (required): [UserGroupsEntity](#usergroupsentity) (XML or JSON) - A collection of names for groups from which the user is to be removed. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + Sales + +``` + +JSON (`Content-Type: application/json`): + ```json { - "users": [ - { - "username": "admin", - "name": "Administrator", - "email": "admin@example.com", - "password": "p4ssword", - "properties": [ - { - "key": "console.order", - "value": "session-summary=0" - } - ] - }, - { - "username": "test", - "name": "Test", - "password": "p4ssword" - } - ] + "groupnames" : [ "Sales" ] } ``` -## Delete a user -Endpoint to delete a user -> **DELETE** /users/{username} +
-**Payload:** none +**Responses** -**Return value:** HTTP status 200 (OK) +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The user was taken out of the groups. | | +| 404 | One or more groups could not be found. | [ErrorResponse](#errorresponse) | -### Possible parameters +## Add user to group -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +> **POST** /plugins/restapi/v1/users/{username}/groups/{groupName} -### Examples +Add a particular user to a particular group. When the group does not exist, it will be automatically created if possible. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/users/testuser +**Parameters** -## Update a user -Endpoint to update / rename a user -> **PUT** /users/{username} +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user that is to be added to a group. | | +| groupName | path | yes | The name of the group that the user is to be added to. | | -**Payload:** User +**Responses** -**Return value:** HTTP status 200 (OK) +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The user was added to the group. | | +| 400 | The username cannot be parsed into a JID. | [ErrorResponse](#errorresponse) | -### Possible parameters +## Delete user from group -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +> **DELETE** /plugins/restapi/v1/users/{username}/groups/{groupName} -### Examples -#### XML Example ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/users/testuser +Removes a user from a group. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user that is to be removed from a group. | | +| groupName | path | yes | The name of the group that the user is to be removed from. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The user was taken out of the group. | | +| 404 | The group could not be found. | [ErrorResponse](#errorresponse) | + +## Retrieve user roster + +> **GET** /plugins/restapi/v1/users/{username}/roster + +Get a list of all roster entries (buddies / contact list) of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to retrieve the roster entries. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | All roster entries. | [RosterEntities](#rosterentities) (XML or JSON) | +| 404 | No user with this username exists. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Create roster entry + +> **POST** /plugins/restapi/v1/users/{username}/roster + +Add a roster entry to the roster (buddies / contact list) of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to add a roster entry. | | + +**Request body** (required): [RosterItemEntity](#rosteritementity) (XML or JSON) - The definition of the roster entry that is to be added. + +
+Example request bodies + +XML (`Content-Type: application/xml`): -**Payload:** ```xml - - - testuser - Test User edit - test@edit.de - - - - + + jane@example.org + Jane + 3 + + Friends + + ``` -#### Rename Example ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/users/oldUsername +JSON (`Content-Type: application/json`): + +```json +{ + "jid" : "jane@example.org", + "nickname" : "Jane", + "subscriptionType" : 3, + "groups" : [ "Friends" ] +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The entry was added to the roster. | | +| 400 | A roster entry cannot be added to a 'shared group' (try removing group names from the roster entry and try again). | [ErrorResponse](#errorresponse) | +| 404 | No user with this username exists. | [ErrorResponse](#errorresponse) | +| 409 | A roster entry already exists for the provided contact JID. | [ErrorResponse](#errorresponse) | + +## Update roster entry + +> **PUT** /plugins/restapi/v1/users/{username}/roster/{rosterJid} + +Changes a roster entry on the roster (buddies / contact list) of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to update a roster entry. | | +| rosterJid | path | yes | The JID of the entry/contact to update. | | + +**Request body** (required): [RosterItemEntity](#rosteritementity) (XML or JSON) - The updated definition of the roster entry. + +
+Example request bodies + +XML (`Content-Type: application/xml`): -**Payload:** ```xml - - - newUsername - Test User edit - test@edit.de - - - - + + jane@example.org + Jane + 3 + + Friends + + ``` -#### JSON Example ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/json -> ->**PUT** http://example.org:9090/plugins/restapi/v1/users/testuser +JSON (`Content-Type: application/json`): -**Payload:** ```json { - "username": "testuser", - "name": "Test User edit", - "email": "test@edit.de", - "properties": { - "property": { - "@key": "keyname", - "@value": "value" - } - } + "jid" : "jane@example.org", + "nickname" : "Jane", + "subscriptionType" : 3, + "groups" : [ "Friends" ] } ``` -**REST API Version 1.3.0 and later - Payload Example 2 (available parameters):** +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The roster entry was updated. | | +| 400 | A roster entry cannot be added with a 'shared group'. | [ErrorResponse](#errorresponse) | +| 404 | No user with this username exists. | [ErrorResponse](#errorresponse) | +| 409 | A roster entry already exists for the provided contact JID. | [ErrorResponse](#errorresponse) | + +## Remove roster entry + +> **DELETE** /plugins/restapi/v1/users/{username}/roster/{rosterJid} + +Removes one of the roster entries (contacts) of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to remove a roster entry. | | +| rosterJid | path | yes | The JID of the entry/contact to remove. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The entry was removed from the roster. | | +| 400 | A roster entry cannot be removed from a 'shared group'. | [ErrorResponse](#errorresponse) | +| 404 | No user with this username exists, or its roster did not contain this entry. | [ErrorResponse](#errorresponse) | + +## Get user's vCard + +> **GET** /plugins/restapi/v1/users/{username}/vcard + +Retrieves the vCard for a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to return the vCard. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The vCard of the user. | | +| 204 | No vCard found. | | + +## Update vCard + +> **PUT** /plugins/restapi/v1/users/{username}/vcard + +Creates or changes a vCard of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to update the vCard. | | + +**Request body** (required): string (XML) - The updated definition of the vCard, in the vcard-temp format of XEP-0054. + +
+Example request body + +`Content-Type: application/xml`: + +```xml + + Janice Francis Doe + + Doe + Janice + Francis + + Jane + + + + j.doe@example.org + + +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The vCard was updated/created. | | +| 400 | Provided data could not be parsed. | [ErrorResponse](#errorresponse) | +| 409 | Cannot change vCard, as Openfire is configured to have read-only vCards. | [ErrorResponse](#errorresponse) | + +## Delete vCard + +> **DELETE** /plugins/restapi/v1/users/{username}/vcard + +Removes a vCard of a particular user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which to delete the vCard. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The vCard was deleted. | | +| 409 | Cannot delete vCard, as Openfire is configured to have read-only vCards. | [ErrorResponse](#errorresponse) | + +## Lock user out + +> **POST** /plugins/restapi/v1/lockouts/{username} + +Lockout / ban the user from the chat server. The user will be kicked if the user is online. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user that is to be locked out. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The user was locked out. | | +| 404 | No user with this username exists. | [ErrorResponse](#errorresponse) | + +## Unlock user + +> **DELETE** /plugins/restapi/v1/lockouts/{username} + +Removes a previously applied lockout / ban of a user. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The username of the user for which the lockout is to be undone. | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | User is unlocked. | | +| 404 | No user with this username exists. | [ErrorResponse](#errorresponse) | + +# User Group + +Managing Openfire user groups. + +## Get groups + +> **GET** /plugins/restapi/v1/groups + +Get a list of all user groups. + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | All groups. | [GroupEntities](#groupentities) (XML or JSON) | + +## Create group + +> **POST** /plugins/restapi/v1/groups + +Create a new user group. + +**Request body** (required): [GroupEntity](#groupentity) (XML or JSON) - The group that needs to be created. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + UserGroup1 + My group of users + + jane.smith + + + john.jones + + false + +``` + +JSON (`Content-Type: application/json`): + ```json { - "username": "testuser", - "name": "Test User edit", - "email": "test@edit.de", - "properties": [ - { - "key": "keyname", - "value": "value" - } - ] + "name" : "UserGroup1", + "description" : "My group of users", + "admins" : [ "jane.smith" ], + "members" : [ "john.jones" ], + "shared" : false } ``` -## Retrieve all user groups -Endpoint to get group names of a specific user -> **GET** /users/{username}/groups +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Group created. | | +| 400 | Group or group name missing, or invalid syntax for a property. | [ErrorResponse](#errorresponse) | +| 409 | Group already exists. | [ErrorResponse](#errorresponse) | + +## Get group + +> **GET** /plugins/restapi/v1/groups/{groupName} + +Get one specific user group by name. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| groupName | path | yes | The name of the group that needs to be fetched. Example: `Colleagues` | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The group. | [GroupEntity](#groupentity) (XML or JSON) | +| 404 | Group with this name not found. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Update group + +> **PUT** /plugins/restapi/v1/groups/{groupName} + +Updates / overwrites an existing user group. Note that the name of the group cannot be changed. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| groupName | path | yes | The name of the group that needs to be updated. Example: `Colleagues` | | + +**Request body** (required): [GroupEntity](#groupentity) (XML or JSON) - The new group definition that needs to overwrite the old definition. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + UserGroup1 + My group of users + + jane.smith + + + john.jones + + false + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "name" : "UserGroup1", + "description" : "My group of users", + "admins" : [ "jane.smith" ], + "members" : [ "john.jones" ], + "shared" : false +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Group updated. | | +| 400 | Group or group name missing, or name does not match existing group, or invalid syntax for a property. | [ErrorResponse](#errorresponse) | +| 404 | Group with this name not found. | [ErrorResponse](#errorresponse) | + +## Delete group + +> **DELETE** /plugins/restapi/v1/groups/{groupName} + +Removes an existing user group. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| groupName | path | yes | The name of the group that needs to be removed. Example: `Colleagues` | | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Group deleted. | | +| 404 | Group with this name not found. | [ErrorResponse](#errorresponse) | + +# Chat service + +Managing multi-user chat services. + +## Get chat services + +> **GET** /plugins/restapi/v1/chatservices + +Get a list of all multi-user chat services. + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | All chat services. | [MUCServiceEntities](#mucserviceentities) (XML or JSON) | + +## Create chat service + +> **POST** /plugins/restapi/v1/chatservices + +Create a new multi-user chat service. + +**Request body** (required): [MUCServiceEntity](#mucserviceentity) (XML or JSON) - The MUC service that needs to be created. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + conference + A public service + false + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "serviceName" : "conference", + "description" : "A public service", + "hidden" : false +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Service created. | | +| 403 | Service creation is not permitted. | [ErrorResponse](#errorresponse) | +| 409 | Service already exists, or another conflict occurred while creating the service. | [ErrorResponse](#errorresponse) | + +# Chat room + +Managing multi-user chat rooms. + +## Get chat rooms + +> **GET** /plugins/restapi/v1/chatrooms + +Get a list of all multi-user chat rooms of a particular chat room service. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service for which to return all chat rooms. Example: `conference` | `conference` | +| type | query | no | Room type-based filter: 'all' or 'public'. | `public` | +| search | query | no | Search/Filter by room name.
This acts like the wildcard search %String% Example: `conference` | | +| expandGroups | query | no | For all groups defined in owners, admins, members and outcasts, list individual members instead of the group name. | `false` | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | All chat rooms. | [MUCRoomEntities](#mucroomentities) (XML or JSON) | +| 404 | MUC service does not exist or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Create chat room + +> **POST** /plugins/restapi/v1/chatrooms + +Create a new multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service in which to create a chat room. Example: `conference` | `conference` | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | + +**Request body** (required): [MUCRoomEntity](#mucroomentity) (XML or JSON) - The MUC room that needs to be created. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + global + Global Chat + A room for everyone + s3cr3t + Welcome! + 2026-01-31T12:34:56.789Z + 2026-01-31T12:34:56.789Z + 30 + true + true + false + false + false + false + true + true + false + false + false + + moderator + + + admin@example.org + + + jane@example.org + + + john@example.org + + + spammer@example.org + + + Management + + + Moderators + + + Sales + + + Banned + + anyone + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "roomName" : "global", + "naturalName" : "Global Chat", + "description" : "A room for everyone", + "password" : "s3cr3t", + "subject" : "Welcome!", + "creationDate" : 1769862896789, + "modificationDate" : 1769862896789, + "maxUsers" : 30, + "persistent" : true, + "publicRoom" : true, + "registrationEnabled" : false, + "canAnyoneDiscoverJID" : false, + "canOccupantsChangeSubject" : false, + "canOccupantsInvite" : false, + "canChangeNickname" : true, + "logEnabled" : true, + "loginRestrictedToNickname" : false, + "membersOnly" : false, + "moderated" : false, + "broadcastPresenceRoles" : [ "moderator" ], + "owners" : [ "admin@example.org" ], + "admins" : [ "jane@example.org" ], + "members" : [ "john@example.org" ], + "outcasts" : [ "spammer@example.org" ], + "ownerGroups" : [ "Management" ], + "adminGroups" : [ "Moderators" ], + "memberGroups" : [ "Sales" ], + "outcastGroups" : [ "Banned" ], + "allowPM" : "anyone" +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Room created. | | +| 403 | Room creation is not permitted. | [ErrorResponse](#errorresponse) | +| 404 | MUC service does not exist or is not accessible. | [ErrorResponse](#errorresponse) | +| 409 | Room already exists, or another conflict occurred while creating the room. | [ErrorResponse](#errorresponse) | + +## Create multiple chat rooms + +> **POST** /plugins/restapi/v1/chatrooms/bulk + +Create a number of new multi-user chat rooms. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service in which to create the chat rooms. Example: `conference` | `conference` | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | + +**Request body** (required): [MUCRoomEntities](#mucroomentities) (XML or JSON) - The MUC rooms that need to be created. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + + global + Global Chat + A room for everyone + s3cr3t + Welcome! + 2026-01-31T12:34:56.789Z + 2026-01-31T12:34:56.789Z + 30 + true + true + false + false + false + false + true + true + false + false + false + + moderator + + + admin@example.org + + + jane@example.org + + + john@example.org + + + spammer@example.org + + + Management + + + Moderators + + + Sales + + + Banned + + anyone + + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "chatRooms" : [ { + "roomName" : "global", + "naturalName" : "Global Chat", + "description" : "A room for everyone", + "password" : "s3cr3t", + "subject" : "Welcome!", + "creationDate" : 1769862896789, + "modificationDate" : 1769862896789, + "maxUsers" : 30, + "persistent" : true, + "publicRoom" : true, + "registrationEnabled" : false, + "canAnyoneDiscoverJID" : false, + "canOccupantsChangeSubject" : false, + "canOccupantsInvite" : false, + "canChangeNickname" : true, + "logEnabled" : true, + "loginRestrictedToNickname" : false, + "membersOnly" : false, + "moderated" : false, + "broadcastPresenceRoles" : [ "moderator" ], + "owners" : [ "admin@example.org" ], + "admins" : [ "jane@example.org" ], + "members" : [ "john@example.org" ], + "outcasts" : [ "spammer@example.org" ], + "ownerGroups" : [ "Management" ], + "adminGroups" : [ "Moderators" ], + "memberGroups" : [ "Sales" ], + "outcastGroups" : [ "Banned" ], + "allowPM" : "anyone" + } ] +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Request has been processed. Results are reported in the response. | [RoomCreationResultEntities](#roomcreationresultentities) (XML or JSON) | +| 404 | MUC service does not exist or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Get chat room + +> **GET** /plugins/restapi/v1/chatrooms/{roomName} + +Get information of a specific multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the MUC room to return. Example: `lobby` | | +| servicename | query | no | The name of the MUC service for which to return a chat room. Example: `conference` | `conference` | +| expandGroups | query | no | For all groups defined in owners, admins, members and outcasts, list individual members instead of the group name. | `false` | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The chat room. | [MUCRoomEntity](#mucroomentity) (XML or JSON) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Update chat room + +> **PUT** /plugins/restapi/v1/chatrooms/{roomName} + +Updates an existing multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room that needs to be updated. Example: `lobby` | | +| servicename | query | no | The name of the MUC service in which to update a chat room. Example: `conference` | `conference` | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | + +**Request body** (required): [MUCRoomEntity](#mucroomentity) (XML or JSON) - The new MUC room definition that needs to overwrite the old definition. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + global + Global Chat + A room for everyone + s3cr3t + Welcome! + 2026-01-31T12:34:56.789Z + 2026-01-31T12:34:56.789Z + 30 + true + true + false + false + false + false + true + true + false + false + false + + moderator + + + admin@example.org + + + jane@example.org + + + john@example.org + + + spammer@example.org + + + Management + + + Moderators + + + Sales + + + Banned + + anyone + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "roomName" : "global", + "naturalName" : "Global Chat", + "description" : "A room for everyone", + "password" : "s3cr3t", + "subject" : "Welcome!", + "creationDate" : 1769862896789, + "modificationDate" : 1769862896789, + "maxUsers" : 30, + "persistent" : true, + "publicRoom" : true, + "registrationEnabled" : false, + "canAnyoneDiscoverJID" : false, + "canOccupantsChangeSubject" : false, + "canOccupantsInvite" : false, + "canChangeNickname" : true, + "logEnabled" : true, + "loginRestrictedToNickname" : false, + "membersOnly" : false, + "moderated" : false, + "broadcastPresenceRoles" : [ "moderator" ], + "owners" : [ "admin@example.org" ], + "admins" : [ "jane@example.org" ], + "members" : [ "john@example.org" ], + "outcasts" : [ "spammer@example.org" ], + "ownerGroups" : [ "Management" ], + "adminGroups" : [ "Moderators" ], + "memberGroups" : [ "Sales" ], + "outcastGroups" : [ "Banned" ], + "allowPM" : "anyone" +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Room updated. | | +| 403 | Room update/create is not permitted. | [ErrorResponse](#errorresponse) | +| 404 | MUC service does not exist or is not accessible. | [ErrorResponse](#errorresponse) | +| 409 | This update causes a conflict, possibly with another existing room. | [ErrorResponse](#errorresponse) | + +## Delete chat room + +> **DELETE** /plugins/restapi/v1/chatrooms/{roomName} + +Removes an existing multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the MUC room to delete. Example: `lobby` | | +| servicename | query | no | The name of the MUC service from which to delete a chat room. Example: `conference` | `conference` | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Room deleted. | | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | + +## Get room history + +> **GET** /plugins/restapi/v1/chatrooms/{roomName}/chathistory + +Get messages that have been exchanged in a specific multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room for which to return message history. Example: `lobby` | | +| servicename | query | no | The name of the chat room's MUC service. Example: `conference` | `conference` | + +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The chat room message history. | [MUCRoomMessageEntities](#mucroommessageentities) (XML or JSON) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | + +## Invite a collection of users and/or groups + +> **POST** /plugins/restapi/v1/chatrooms/{roomName}/invite + +Invites a collection of users and/or groups to join a specific multi-user chat room. Each entity can be identified by the JID of a user or group, or by the name of a local user or group. When a group is invited, all of its members are invited. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room to which to invite users and/or groups. Example: `lobby` | | +| servicename | query | no | The name of the chat room's MUC service. Example: `conference` | `conference` | + +**Request body** (required): [MUCInvitationsEntity](#mucinvitationsentity) (XML or JSON) - The invitation message to send and whom to send it to. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + Come join this cool room please! + + john@example.org + + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "reason" : "Come join this cool room please!", + "jidsToInvite" : [ "john@example.org" ] +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Invitation sent. | | +| 403 | Not allowed to invite a user or group to this room. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | + +## Invite user or group + +> **POST** /plugins/restapi/v1/chatrooms/{roomName}/invite/{jid} + +Invites a user or group to join a specific multi-user chat room. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room to which to invite a user or group. Example: `lobby` | | +| jid | path | yes | The entity to invite into the room: the JID of a user or group, or the name of a local user or group. When a group is invited, all of its members are invited. Example: `john@example.org` | | +| servicename | query | no | The name of the chat room's MUC service. Example: `conference` | `conference` | + +**Request body** (required): [MUCInvitationEntity](#mucinvitationentity) (XML or JSON) - The invitation message to send and whom to send it to. + +
+Example request bodies + +XML (`Content-Type: application/xml`): + +```xml + + Come join this cool room please! + +``` + +JSON (`Content-Type: application/json`): + +```json +{ + "reason" : "Come join this cool room please!" +} +``` + +
+ +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Invitation sent. | | +| 403 | Not allowed to invite a user to this room. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | -**Payload:** none +## Get room occupants -**Return value:** Groups +> **GET** /plugins/restapi/v1/chatrooms/{roomName}/occupants -### Possible parameters +Get all occupants of a specific multi-user chat room. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +**Parameters** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/users/testuser/groups +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room for which to return occupants. Example: `lobby` | | +| servicename | query | no | The name of the chat room's MUC service. Example: `conference` | `conference` | -## Add user to groups -Endpoint to add user to a groups -> **POST** /users/{username}/groups +**Responses** -**Payload:** Groups +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The chat room occupants. | [OccupantEntities](#occupantentities) (XML or JSON) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | -**Return value:** HTTP status 201 (Created) +## Get room participants -### Possible parameters +> **GET** /plugins/restapi/v1/chatrooms/{roomName}/participants +Get all participants of a specific multi-user chat room. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +**Parameters** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/users/testuser/groups +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| roomName | path | yes | The name of the chat room for which to return participants. Example: `lobby` | | +| servicename | query | no | The name of the chat room's MUC service. Example: `conference` | `conference` | -**Payload:** -```xml - - - Admins - Support - -``` +**Responses** -## Add user to group -Endpoint to add user to a group -> **POST** /users/{username}/groups/{groupName} +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The chat room participants. | [ParticipantEntities](#participantentities) (XML or JSON) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | -**Payload:** none +## Get room affiliations -**Return value:** HTTP status 201 (Created) +> **GET** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation} -### Possible parameters +Retrieves a list of JIDs for all users that have a particular affiliation with a multi-user chat room. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|------------------|---------------| -| username | @Path | Exact username | | -| groupName | @Path | Exact group name | | +**Parameters** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/users/testuser/groups/testGroup +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| roomName | path | yes | The name of the MUC room for which to return affiliations. Example: `lobby` | | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | -## Delete a user from a groups -Endpoint to remove a user from a groups ->**DELETE** /users/{username}/groups +**Responses** -**Payload:** Groups +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Affiliated user list retrieved. | unspecified (XML or JSON) | +| 400 | Provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) (XML or JSON) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) (XML or JSON) | -**Return value:** HTTP status 200 (OK) +## Add room affiliations -### Possible parameters +> **POST** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation} -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +Affiliates multiple users to a particular multi-user chat room (without removing existing affiliated users of that type). Note that a user can only have one type of affiliation with a room. By affiliating a user to a room, any other pre-existing affiliation for that user is removed. -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/users/testuser/groups +**Parameters** -**Payload:** -```xml - - - Admins - Support - -``` +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| roomName | path | yes | The name of the MUC room to which users are to be affiliated. Example: `lobby` | | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | -## Delete a user from a group -Endpoint to remove a user from a group ->**DELETE** /users/{username}/groups/{groupName} +**Request body** (required): [AffiliatedEntities](#affiliatedentities) (XML or JSON) - The list of users to affiliate to the room. -**Payload:** none +**Responses** -**Return value:** HTTP status 200 (OK) +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Users have been affiliated to the room. | | +| 400 | Provided values cannot be parsed as JIDs, or provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to perform this affiliation change. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | -### Possible parameters +## Replace room affiliations +> **PUT** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation} -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|------------------|---------------| -| username | @Path | Exact username | | -| groupName | @Path | Exact group name | | +Replaces the list of users in a multi-user chat room with a specific affiliation with a new list of users. Note that a user can only have one type of affiliation with a room. By affiliating a user to a room, any other pre-existing affiliation for that user is removed. -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/users/testuser/groups/testGroup +**Parameters** -## Lockout a user -Endpoint to lockout / ban the user from the chat server. The user will be kicked if the user is online. ->**POST** /lockouts/{username} +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| roomName | path | yes | The name of the MUC room of which affiliations are to be replaced. Example: `lobby` | | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | -**Payload:** none +**Request body** (required): [AffiliatedEntities](#affiliatedentities) (XML or JSON) - The new list of users with this particular affiliation. -**Return value:** HTTP status 201 (Created) +**Responses** -### Possible parameters +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Affiliations of the room have been replaced. | | +| 400 | Provided values cannot be parsed as JIDs, or provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to perform this affiliation change. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +## Add group room affiliations -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**POST** http://example.org:9090/plugins/restapi/v1/lockouts/testuser +> **POST** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation}/group/{groupname} -## Unlock a user -Endpoint to unlock / unban the user ->**DELETE** /lockouts/{username} +Affiliate all members of an Openfire user group to a multi-user chat room. Note that a user can only have one type of affiliation with a room. By affiliating a user to a room, any other pre-existing affiliation for that user is removed. -**Payload:** none +**Parameters** -**Return value:** HTTP status 200 (OK) +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| groupname | path | yes | The name of the user group from which all members will be affiliated to the room. Example: `Operators` | | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| roomName | path | yes | The name of the MUC room to which affiliations are to be added. Example: `lobby` | | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | -### Possible parameters +**Responses** -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Affiliations added to the room. | | +| 400 | Provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to perform this affiliation change. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/lockouts/testuser +## Remove group room affiliations -## Retrieve user roster -Endpoint to get roster entries (buddies) from a specific user ->**GET** /users/{username}/roster +> **DELETE** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation}/group/{groupname} -**Payload:** none +Removes affiliation for all members of an Openfire user group from a multi-user chat room. -**Return value:** Roster +**Parameters** -### Possible parameters +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| groupname | path | yes | The name of the user group from which all members will get their room affiliation removed. Example: `Operators` | | +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| roomName | path | yes | The name of the MUC room from which affiliations are to be removed. Example: `lobby` | | -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +**Responses** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/users/testuser/roster +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Affiliations removed from the room. | | +| 400 | Provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to remove this affiliation. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | +| 409 | Applying this affiliation change would cause a room conflict. | [ErrorResponse](#errorresponse) | -## Create a user roster entry -Endpoint to add a new roster entry to a user ->**POST** /users/{username}/roster +## Add room affiliation -**Payload:** RosterItem +> **POST** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation}/{jid} -**Return value:** HTTP status 201 (Created) +Affiliates a single user to a multi-user chat room. Note that a user can only have one type of affiliation with a room. By affiliating a user to a room, any other pre-existing affiliation for that user is removed. -### Possible parameters +**Parameters** -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| jid | path | yes | The entity that is to be affiliated: a (bare) JID, or the name of a local user. Example: `john@example.org` | | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| roomName | path | yes | The name of the MUC room to which an affiliation is to be added. Example: `lobby` | | +| sendInvitations | query | no | Whether to send invitations to newly affiliated users. Example: `true` | `false` | -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/users/testuser/roster +**Responses** -**Payload:** -Payload Example 1 (required parameters): -```xml - - - peter@pan.de - -``` -Payload Example 2 (available parameters): -```xml - - - peter@pan1.de - Peter1 - 3 - - Friends - - -``` +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | User has been affiliated to the room. | | +| 400 | Provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to perform this affiliation change. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | -## Delete a user roster entry -Endpoint to remove a roster entry from a user ->**DELETE** /users/{username}/roster/{jid} +## Remove room affiliation -**Payload:** none +> **DELETE** /plugins/restapi/v1/chatrooms/{roomName}/{affiliation}/{jid} -**Return value:** HTTP status 200 (OK) +Removes an affiliation of a user to a multi-user chat room. -### Possible parameters +**Parameters** -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|------------------------|---------------| -| username | @Path | Exact username | | -| jid | @Path | JID of the roster item | | +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| jid | path | yes | The entity for which the room affiliation is to be removed: a (bare) JID, or the name of a local user. Example: `john@example.org` | | +| servicename | query | no | The name of the MUC service that the MUC room is part of. Example: `conference` | `conference` | +| affiliation | path | yes | The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'. Example: `members` | | +| roomName | path | yes | The name of the MUC room from which an affiliation is to be removed. Example: `lobby` | | -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/users/testuser/roster/peter@pan.de +**Responses** -## Update a user roster entry -Endpoint to update a roster entry ->**PUT** /users/{username}/roster/{jid} +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Affiliation removed from the room. | | +| 400 | Provided 'affiliations' value is invalid. | [ErrorResponse](#errorresponse) | +| 403 | Not allowed to remove this affiliation. | [ErrorResponse](#errorresponse) | +| 404 | The chat room (or its service) can not be found or is not accessible. | [ErrorResponse](#errorresponse) | +| 409 | Applying this affiliation change would cause a room conflict. | [ErrorResponse](#errorresponse) | -**Payload:** RosterItem +# Client Sessions -**Return value:** HTTP status 200 (OK) +Managing live client sessions. -### Possible parameters +## Get all sessions -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|------------------------|---------------| -| username | @Path | Exact username | | -| jid | @Path | JID of the roster item | | +> **GET** /plugins/restapi/v1/sessions -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/users/testuser/roster/peter@pan.de +Retrieve all live client sessions. -**Payload:** -```xml - - - peter@pan.de - Peter Pan - 0 - - Support - - -``` +**Responses** -## Retrieve user's vcard -Endpoint to get the vCard of a particular user -> **GET** /users/{username}/vcard +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The client sessions currently active in Openfire. | [SessionEntities](#sessionentities) (XML or JSON) | -**Payload:** none +## Get user sessions -**Return value:** vCard XML data +> **GET** /plugins/restapi/v1/sessions/{username} -### Possible parameters +Retrieve all live client sessions for a particular user. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +**Parameters** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/users/testuser/vcard +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The name of a user for which to return client sessions. Example: `johndoe` | | -## Add or update user's vCard -Endpoint to add or replace a vCard of a particular user. -> **PUT** /users/{username}/vcard +**Responses** -**Payload:** vCard XML data +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The client sessions for one particular user that are currently active in Openfire. | [SessionEntities](#sessionentities) (XML or JSON) | -**Return value:** HTTP status 200 (Created) +## Kick user sessions -### Possible parameters +> **DELETE** /plugins/restapi/v1/sessions/{username} +Close/disconnect all live client sessions for a particular user. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +**Parameters** -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/users/testuser/vcard +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | path | yes | The name of a user for which to drop all client sessions. Example: `johndoe` | | -**Payload:** -```xml - - - - Doe - Janice - Francis - - - - - - Jane - Janice Francis Doe - - <URL/> - <EMAIL> - <HOME/> - <INTERNET/> - <PREF/> - <USERID>j.doe@example.org</USERID> - </EMAIL> - <TEL> - <WORK/> - <VOICE/> - <NUMBER/> - </TEL> - <TEL> - <WORK/> - <PAGER/> - <NUMBER/> - </TEL> - <TEL> - <WORK/> - <FAX/> - <NUMBER/> - </TEL> - <TEL> - <WORK/> - <CELL/> - <NUMBER/> - </TEL> - <TEL> - <HOME/> - <VOICE/> - <NUMBER/> - </TEL> - <TEL> - <HOME/> - <PAGER/> - <NUMBER/> - </TEL> - <TEL> - <HOME/> - <FAX/> - <NUMBER/> - </TEL> - <TEL> - <HOME/> - <CELL/> - <NUMBER/> - </TEL> - <ADR> - <WORK/> - <LOCALITY/> - <CTRY/> - <STREET/> - <PCODE/> - <REGION/> - </ADR> - <ADR> - <HOME/> - <LOCALITY/> - <CTRY/> - <STREET/> - <PCODE/> - <REGION/> - </ADR> -</vCard> -``` +**Responses** -## Delete user's vcard -Endpoint to remove the vCard of a particular user -> **DELETE** /users/{username}/vcard +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The client sessions for one particular user have been closed. | | -**Payload:** none +# Message -**Return value:** none +Sending (chat) messages to users. -### Possible parameters +## Broadcast -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|----------------|---------------| -| username | @Path | Exact username | | +> **POST** /plugins/restapi/v1/messages/users -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/users/testuser/vcard +Sends a message to all users that are currently online. -# Chat room related REST Endpoints +**Request body** (required): [MessageEntity](#messageentity) (XML or JSON) - The message that is to be broadcast. -## Retrieve all chat services +<details> +<summary>Example request bodies</summary> -Endpoint to get all chat services ->**GET** /chatservices +XML (`Content-Type: application/xml`): -**Payload:** none +```xml +<message> + <body>The server will be restarted in 5 minutes.</body> +</message> +``` -**Return value:** Chat services +JSON (`Content-Type: application/json`): -**Possible parameters:** none +```json +{ + "body" : "The server will be restarted in 5 minutes." +} +``` -### Examples +</details> ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatservices +**Responses** -## Create a chat service -Endpoint to create a new chat service. ->**POST** /chatservices +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | Message is sent. | | +| 400 | The message content is empty or missing. | | -**Payload:** Chatservice +# Message Archive -**Return value:** HTTP status 201 (Created) +Server-sided storage of chat messages. -**Possible parameters:** none +## Unread message count -### XML Examples +> **GET** /plugins/restapi/v1/archive/messages/unread/{jid} ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatservices +Gets a count of messages that haven't been delivered to the user yet. -**Payload Example (available parameters):** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<chatService> - <serviceName>new-chat-service-name</serviceName> - <description>A mightily fine service</description> - <hidden>false</hidden> -</chatService> -``` +**Parameters** -### JSON Examples +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| jid | path | yes | The (bare) JID of the user for which the unread message count needs to be fetched. Example: `john@example.org` | | ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/json -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatservices +**Responses** -**Payload Example (available parameters):** -```json -{ - "serviceName": "new-chat-service-name", - "description": "A mightily fine service", - "hidden": false -} -``` +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | A message count. | [MsgArchiveEntity](#msgarchiveentity) (XML or JSON) | -## Retrieve all chat rooms -Endpoint to get all chat rooms ->**GET** /chatrooms +# Security Audit Log -**Payload:** none +Inspecting the security audit log. -**Return value:** Chatrooms +## Get log entries -### Possible parameters +> **GET** /plugins/restapi/v1/logs/security -| Parameter | Parameter Type | Description | Default value | -|--------------|----------------|-------------------------------------------------------------------------------|---------------| -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| type | @QueryParam | **public:** Only as List Room in Directory set rooms <br> **all:** All rooms. | public | -| search | @QueryParam | Search/Filter by room name. <br> This act like the wildcard search %String% | | +Retrieve entries from the security audit log. -### Examples +**Parameters** ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms?type=all -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms?type=all&servicename=privateconf -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms?search=test +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| username | query | no | The name of a user for which to filter events. Example: `admin` | | +| offset | query | no | Number of log entries to skip. Example: `0` | | +| limit | query | no | Number of log entries to retrieve. Example: `100` | `100` | +| startTime | query | no | Oldest timestamp of range of logs to retrieve. 0 for 'forever'. | | +| endTime | query | no | Most recent timestamp of range of logs to retrieve. 0 for 'now'. | | -## Retrieve a chat room -Endpoint to get information over specific chat room ->**GET** /chatrooms<span>/{roomName} +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The requested log entries. | [SecurityAuditLogs](#securityauditlogs) (XML or JSON) | +| 403 | The audit log is not readable (configured to be write-only). | [ErrorResponse](#errorresponse) (XML or JSON) | -**Return value:** Chatroom +# Statistics -### Possible parameters +Inspecting Openfire statistics. -| Parameter | Parameter Type | Description | Default value | -|-------------|----------------|------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +## Get client session counts -### Examples +> **GET** /plugins/restapi/v1/system/statistics/sessions ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms/test -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms/test?servicename=privateconf +Retrieve statistics on the number of client sessions. -## Retrieve chat room participants -Endpoint to get all participants with a role of specified room. ->**GET** /chatrooms/{roomName}/participants +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The requested statistics. | [SessionsCount](#sessionscount) (XML or JSON) | -**Return value:** Participants +# System -### Possible parameters +Managing Openfire system configuration. -| Parameter | Parameter Type | Description | Default value | -|-------------|-----------------|------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +## Perform all liveness checks -### Examples +> **GET** /plugins/restapi/v1/system/liveness ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms/room1/participants +Detects if Openfire has reached a state that it cannot recover from, except for with a restart, based on every liveness check that it has implemented. -## Retrieve chat room occupants -Endpoint to get all occupants (all roles / affiliations) of a specified room. ->**GET** /chatrooms/{roomName}/occupants +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is live. | | +| 503 | At least one liveness check failed: the system is determined to not be alive. | | -**Return value:** Occupants +## Perform 'deadlock' liveness check -### Possible parameters +> **GET** /plugins/restapi/v1/system/liveness/deadlock -| Parameter | Parameter Type | Description | Default value | -|-------------|-----------------|------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +Detects if Openfire has reached a state that it cannot recover from because of a deadlock. -### Examples +**Responses** ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms/room1/occupants +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is live. | | +| 503 | A deadlock is detected. | | -## Retrieve chat room message history -Endpoint to get the chat message history of a specified room. +## Perform 'properties' liveness check ->**GET** /chatrooms/{roomName}/chathistory +> **GET** /plugins/restapi/v1/system/liveness/properties -**Payload:** none +Detects if Openfire has reached a state that it cannot recover from because a system property change requires a restart to take effect. -**Return value:** Chat History +**Responses** -### Possible parameters +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is live. | | +| 503 | One or more system property changes that require a server restart have been detected. | | -| Parameter | Parameter Type | Description | Default value | -|-------------|-----------------|------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +## Get system properties -## Create a chat room -Endpoint to create a new chat room. ->**POST** /chatrooms +> **GET** /plugins/restapi/v1/system/properties -**Payload:** Chatroom +Get all Openfire system properties. -**Return value:** HTTP status 201 (Created) +**Responses** -### Possible parameters +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system properties. | [SystemProperties](#systemproperties) (XML or JSON) | -| Parameter | Parameter Type | Description | Default value | -|-----------------|-----------------|-------------------------------------------------|---------------| -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitations to affiliated users | false | +## Create system property -### XML Examples +> **POST** /plugins/restapi/v1/system/properties ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms +Create a new Openfire system property. Will overwrite a pre-existing system property that uses the same name. -**Payload Example 1 (required parameters):** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<chatRoom> - <naturalName>global-1</naturalName> - <roomName>global</roomName> - <description>Global Chat Room</description> -</chatRoom> -``` +**Request body** (required): [SystemProperty](#systemproperty) (XML or JSON) - The system property to create. + +<details> +<summary>Example request bodies</summary> + +XML (`Content-Type: application/xml`): -**Payload Example 2 (available parameters):** ```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<chatRoom> - <roomName>global</roomName> - <naturalName>global-2</naturalName> - <description>Global Chat Room</description> - <subject>global-2 Subject</subject> - <creationDate>2014-02-12T15:52:37.592+01:00</creationDate> - <modificationDate>2014-09-12T15:35:54.702+02:00</modificationDate> - <maxUsers>0</maxUsers> - <persistent>true</persistent> - <publicRoom>true</publicRoom> - <registrationEnabled>false</registrationEnabled> - <canAnyoneDiscoverJID>false</canAnyoneDiscoverJID> - <canOccupantsChangeSubject>false</canOccupantsChangeSubject> - <canOccupantsInvite>false</canOccupantsInvite> - <canChangeNickname>false</canChangeNickname> - <logEnabled>true</logEnabled> - <loginRestrictedToNickname>false</loginRestrictedToNickname> - <membersOnly>false</membersOnly> - <moderated>false</moderated> - <allowPM>anyone</allowPM> - <broadcastPresenceRoles> - <broadcastPresenceRole>moderator</broadcastPresenceRole> - <broadcastPresenceRole>participant</broadcastPresenceRole> - <broadcastPresenceRole>visitor</broadcastPresenceRole> - </broadcastPresenceRoles> - <owners> - <owner>owner@localhost</owner> - </owners> - <admins> - <admin>admin@localhost</admin> - </admins> - <members> - <member>member2@localhost</member> - <member>member1@localhost</member> - </members> - <outcasts> - <outcast>outcast1@localhost</outcast> - </outcasts> -</chatRoom> +<property key="xmpp.domain" value="example.org"/> ``` -### JSON Examples +JSON (`Content-Type: application/json`): ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/json -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms - -**Payload Example 1 (required parameters):** ```json { - "roomName": "global", - "naturalName": "global-2", - "description": "Global chat room" + "key" : "xmpp.domain", + "value" : "example.org" } ``` -**Payload Example 2 (available parameters):** -```json -{ - "roomName": "global-1", - "naturalName": "global-1_test_hello", - "description": "Global chat room", - "subject": "Global chat room subject", - "creationDate": "2012-10-18T16:55:12.803+02:00", - "modificationDate": "2014-07-10T09:49:12.411+02:00", - "maxUsers": "0", - "persistent": "true", - "publicRoom": "true", - "registrationEnabled": "false", - "canAnyoneDiscoverJID": "true", - "canOccupantsChangeSubject": "false", - "canOccupantsInvite": "false", - "canChangeNickname": "false", - "logEnabled": "true", - "loginRestrictedToNickname": "true", - "membersOnly": "false", - "moderated": "false", - "allowPM": "anyone", - "broadcastPresenceRoles": { - "broadcastPresenceRole": [ - "moderator", - "participant", - "visitor" - ] - }, - "owners": { - "owner": "owner@localhost" - }, - "admins": { - "admin": [ - "admin@localhost", - "admin2@localhost" - ] - }, - "members": { - "member": [ - "member@localhost", - "member2@localhost" - ] - }, - "outcasts": { - "outcast": [ - "outcast@localhost", - "outcast2@localhost" - ] - } -} -``` +</details> -**REST API Version 1.3.0 and later - Payload Example 2 (available parameters):** -```json -{ - "roomName": "global-1", - "naturalName": "global-1_test_hello", - "description": "Global chat room", - "subject": "Global chat room subject", - "creationDate": "2012-10-18T16:55:12.803+02:00", - "modificationDate": "2014-07-10T09:49:12.411+02:00", - "maxUsers": "0", - "persistent": "true", - "publicRoom": "true", - "registrationEnabled": "false", - "canAnyoneDiscoverJID": "true", - "canOccupantsChangeSubject": "false", - "canOccupantsInvite": "false", - "canChangeNickname": "false", - "logEnabled": "true", - "loginRestrictedToNickname": "true", - "membersOnly": "false", - "moderated": "false", - "allowPM": "anyone", - "broadcastPresenceRoles": [ - "moderator", - "participant", - "visitor" - ], - "owners": [ - "owner@localhost" - ], - "admins": [ - "admin@localhost" - ], - "members": [ - "member@localhost" - ], - "outcasts": [ - "outcast@localhost" - ] -} -``` +**Responses** + +| Status | Description | Response body | +|--------|-------------|---------------| +| 201 | The system property is created. | | +| 400 | No system property was provided, the system property has no value, or its name is not valid. The name must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens. | [ErrorResponse](#errorresponse) | +| 403 | Prohibited to create this system property. | [ErrorResponse](#errorresponse) | +| 409 | The name of the system property differs only in case from the name of an existing system property. | [ErrorResponse](#errorresponse) | + +## Get system property + +> **GET** /plugins/restapi/v1/system/properties/{propertyKey} + +Get a specific Openfire system property. + +**Parameters** + +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| propertyKey | path | yes | The name of the system property to return. Example: `foo.bar.xyz` | | +**Responses** +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The requested system property. | [SystemProperty](#systemproperty) (XML or JSON) | +| 403 | Reading this system property is prohibited. | [ErrorResponse](#errorresponse) (XML or JSON) | +| 404 | The system property could not be found. | [ErrorResponse](#errorresponse) (XML or JSON) | +## Update system property +> **PUT** /plugins/restapi/v1/system/properties/{propertyKey} +Updates an existing Openfire system property. +**Parameters** -## Create multiple chat room -Endpoint to create multiple new chat rooms at once. ->**POST** /chatrooms/bulk +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| propertyKey | path | yes | The name of the system property to update. Example: `foo.bar.xyz` | | -**Payload:** Chatrooms +**Request body** (required): [SystemProperty](#systemproperty) (XML or JSON) - The new system property definition that replaces an existing definition. + +<details> +<summary>Example request bodies</summary> + +XML (`Content-Type: application/xml`): -**Return value:** Result list, ordered by successes and failures ```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<results> - <success> - <result> - <roomName>room1</roomName> - <resultType>Success</resultType> - <message>Room was successfully created</message> - </result> - <result> - <roomName>room2</roomName> - <resultType>Success</resultType> - <message>Room was successfully created</message> - </result> - </success> - <failure/> - <other/> -</results> +<property key="xmpp.domain" value="example.org"/> ``` +JSON (`Content-Type: application/json`): + ```json { - "success": [ - { - "roomName": "room1", - "resultType": "Success", - "message": "Room was successfully created" - }, - { - "roomName": "room2", - "resultType": "Success", - "message": "Room was successfully created" - } - ], - "failure": [], - "other": [] + "key" : "xmpp.domain", + "value" : "example.org" } ``` -### Possible parameters -| Parameter | Parameter Type | Description | Default value | -|-----------------|-----------------|-------------------------------------------------------|---------------| -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitations to newly affiliated users | false | +</details> -### XML Examples +**Responses** ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/bulk +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system property is updated. | | +| 400 | No system property was provided, the system property has no value, or it does not match the name in the URL. | [ErrorResponse](#errorresponse) | +| 403 | Prohibited to update this system property. | [ErrorResponse](#errorresponse) | +| 404 | The system property could not be found. | [ErrorResponse](#errorresponse) | +| 409 | The name of the system property differs only in case from the name of another existing system property. | [ErrorResponse](#errorresponse) | -**Payload Example:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<chatRooms> - <chatRoom> - <roomName>room1</roomName> - <description>description1</description> - </chatRoom> - <chatRoom> - <roomName>room2</roomName> - <description>description1</description> - </chatRoom> -</chatRooms> -``` +## Remove system property -For more examples, with more parameters, see the [create a chat room](#create-a-chat-room) endpoint. +> **DELETE** /plugins/restapi/v1/system/properties/{propertyKey} -### JSON Examples +Removes an existing Openfire system property, together with all of its child properties (properties of which the name starts with the name of this property, followed by a dot). ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/json -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms +**Parameters** -**Payload Example 1 (required parameters):** -```json -{ - "chatRooms": [ - { "roomName": "room1", "description": "description1" }, - { "roomName": "room2", "description": "description2" } - ] -} -``` +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| propertyKey | path | yes | The name of the system property to delete. Example: `foo.bar.xyz` | | -For more examples, with more parameters, see the [create a chat room](#create-a-chat-room) endpoint. +**Responses** +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system property and its child properties are deleted. | | +| 400 | The name of the system property is not valid. It must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens. | [ErrorResponse](#errorresponse) | +| 403 | Prohibited to delete this system property, or one of its child properties. | [ErrorResponse](#errorresponse) | +| 404 | The system property could not be found. | [ErrorResponse](#errorresponse) | +| 409 | Deleting this system property could also delete unintended properties (other than this property and its child properties). This can happen, for example, when its name contains an underscore, which can match any character. | [ErrorResponse](#errorresponse) | +## Perform all readiness checks +> **GET** /plugins/restapi/v1/system/readiness +Detects if Openfire is in a state where it is ready to process traffic, based on every readiness check that it has implemented. +**Responses** +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is ready. | | +| 503 | At least one readiness check failed: the system is determined to not be able to process traffic. | | +## Perform 'cluster' readiness check +> **GET** /plugins/restapi/v1/system/readiness/cluster -## Delete a chat room -Endpoint to delete a chat room. ->**DELETE** /chatrooms/{roomName} +Detects if the cluster functionality has finished starting (or is disabled). -**Payload:** none +**Responses** -**Return value:** HTTP status 200 (OK) +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is ready. | | +| 503 | Clustering functionality is enabled, but has not finished starting up yet. | | -### Possible parameters +## Perform 'connections' readiness check -| Parameter | Parameter Type | Description | Default value | -|-------------|-----------------|------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +> **GET** /plugins/restapi/v1/system/readiness/connections -### Examples +Detects if Openfire is ready to accept connection requests. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/testroom -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/testroom?servicename=privateconf +**Parameters** -## Update a chat room -Endpoint to update a chat room. ->**PUT** /chatrooms/{roomName} +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| connectionType | query | no | Optional. Use to limit the check to one particular connection type. One of: SOCKET_S2S, SOCKET_C2S, BOSH_C2S, WEBADMIN, COMPONENT, CONNECTION_MANAGER. Example: `SOCKET_C2S` | | +| encrypted | query | no | Check the encrypted (true) or unencrypted (false) variant of the connection type. Only used in combination with 'connectionType', as without it, all types and both encrypted and unencrypted are checked. | | -**Payload:** Chatroom +**Responses** -**Return value:** HTTP status 200 (OK) +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is ready. | | +| 400 | The provided connectionType value is invalid. | | +| 503 | Openfire currently does not accept (all) connections. | | -### Possible parameters +## Perform 'plugins' readiness check -| Parameter | Parameter Type | Description | Default value | -|-----------------|----------------|-------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitations to newly affiliated users | false | +> **GET** /plugins/restapi/v1/system/readiness/plugins -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/chatrooms/global +Detects if Openfire has finished starting its plugins. -**Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<chatRoom> - <roomName>global</roomName> - <naturalName>global-2</naturalName> - <description>Global Chat Room edit</description> - <subject>New subject</subject> - <password>test</password> - <creationDate>2014-02-12T15:52:37.592+01:00</creationDate> - <modificationDate>2014-09-12T14:20:56.286+02:00</modificationDate> - <maxUsers>0</maxUsers> - <persistent>true</persistent> - <publicRoom>true</publicRoom> - <registrationEnabled>false</registrationEnabled> - <canAnyoneDiscoverJID>false</canAnyoneDiscoverJID> - <canOccupantsChangeSubject>false</canOccupantsChangeSubject> - <canOccupantsInvite>false</canOccupantsInvite> - <canChangeNickname>false</canChangeNickname> - <logEnabled>true</logEnabled> - <loginRestrictedToNickname>false</loginRestrictedToNickname> - <membersOnly>false</membersOnly> - <moderated>false</moderated> - <allowPM>anyone</allowPM> - <broadcastPresenceRoles/> - <owners> - <owner>owner@localhost</owner> - </owners> - <admins> - <admin>admin@localhost</admin> - </admins> - <members> - <member>member2@localhost</member> - <member>member1@localhost</member> - </members> - <outcasts> - <outcast>outcast1@localhost</outcast> - </outcasts> -</chatRoom> -``` +**Responses** -## Invite user or user group to a chat Room +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is ready. | | +| 503 | Plugins have not all been started yet. | | -Endpoint to invite a user or a user group to a room. -> **Header:** Authorization: Basic YWRtaW46MTIzNDU= -> -> **Header:** Content-Type: application/xml -> -> **POST** http://localhost:9090/plugins/restapi/v1/chatrooms/{roomName}/invite/{name} +## Perform 'server started' readiness check -**Payload Example:** +> **GET** /plugins/restapi/v1/system/readiness/server -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<mucInvitation> - <reason>Hello, come to this room, it is nice</reason> -</mucInvitation> -``` -**Return value:** HTTP status 200 (OK) +Detects if Openfire's core service has been started. -### Possible parameters -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|---------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| name | @Path | The local username or group name or the user JID or group JID | | +**Responses** -## Invite multiple users and/or user groups to a chat Room +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The system is ready. | | +| 503 | The Openfire service has not finished starting up yet. | | -Endpoint to invite multiple users and/or user groups to a room. Works both with JIDs and (user/group) names. -> **Header:** Authorization: Basic YWRtaW46MTIzNDU= -> -> **Header:** Content-Type: application/xml -> -> **POST** http://localhost:9090/plugins/restapi/v1/chatrooms/{roomName}/invite +# Clustering -**Payload Example:** +Reporting the status of Openfire clustering. -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<mucInvitation> - <reason>Hello, come to this room, it is nice</reason> - <jidsToInvite> - <jid>jane@example.org</jid> - <jid>ADNMQP8=@example.org/695c6ae413c00446733d926ccadefd8b</jid> - <jid>john</jid> - <jid>SomeGroupName</jid> - </jidsToInvite> -</mucInvitation> -``` -**Return value:** HTTP status 200 (OK) +## Get all cluster nodes -### Possible parameters -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|---------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | +> **GET** /plugins/restapi/v1/clustering/nodes -## Get all users with a particular affiliation in a chat room -Retrieves a list of JIDs for all users with the specified affiliation in a multi-user chat room. +Get a list of all nodes of the cluster. Note that this endpoint can only return data for remote nodes when the instance of Openfire that processes this query has successfully joined the cluster. ->**GET** /chatrooms/{roomName}/{affiliation} +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | All cluster nodes. | [ClusterNodeEntities](#clusternodeentities) (XML or JSON) | -**Return value:** HTTP status 200 (OK) +## Get a specific cluster node -### Possible parameters +> **GET** /plugins/restapi/v1/clustering/nodes/{nodeId} -| Parameter | Parameter Type | Description | Default value | -|-------------|-----------------|--------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| affiliation | @Path | Available affiliations: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | +Get a specific node of the cluster. Note that this endpoint can only return data for remote nodes when the instance of Openfire that processes this query has successfully joined the cluster. -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**GET** http://example.org:9090/plugins/restapi/v1/chatrooms/global/member +**Parameters** -**Return payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<members> - <member>member2@localhost</member> - <member>member1@localhost</member> -</members> -``` +| Name | Located in | Required | Description | Default value | +|------|------------|----------|-------------|---------------| +| nodeId | path | yes | The nodeID value for a particular node. Example: `52a89928-66f7-45fd-9bb8-096de07400ac` | | -## Add user with affiliation to chat room -Endpoint to add a new user with affiliation to a room. ->**POST** /chatrooms/{roomName}/{affiliation}/{name} +**Responses** -**Payload:** none +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | The cluster node. | [ClusterNodeEntity](#clusternodeentity) (XML or JSON) | +| 404 | The provided NodeID does not identify an existing cluster node. | [ErrorResponse](#errorresponse) (XML or JSON) | -**Return value:** HTTP status 201 (Created) +## Get clustering status -### Possible parameters +> **GET** /plugins/restapi/v1/clustering/status -| Parameter | Parameter Type | Description | Default value | -|-----------------|-----------------|--------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| name | @Path | The local username or the user JID | | -| affiliation | @Path | Available affiliations: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitation to the newly affiliated user | false | +Describes the point-in-time state of Openfire's clustering with other servers. The status is one of: 'SENIOR AND ONLY MEMBER', 'Senior member', 'Junior member', 'Starting up' or 'Disabled'. -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser@openfire.com -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/testUser -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/testUser?sendInvitations=true -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/testUser -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser?servicename=privateconf - -## Replace all users with a affiliation in a chat room -Endpoint to replace all users with a particular affiliation in a multi-user chat room. Note that a user can only have one type of affiliation with a room. By adding a user using a particular affiliation, any other pre-existing affiliation is removed. ->**PUT** /chatrooms/{roomName}/{affiliation} - -**Payload:** list of affiliations - -**Return value:** HTTP status 201 (Created) - -### Possible parameters - -| Parameter | Parameter Type | Description | Default value | -|-----------------|-----------------|--------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| affiliation | @Path | Available affiliations: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitation to newly affiliated users | false | - -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/chatrooms/global/members -> -**Request Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<members> - <member>member2@localhost</member> - <member>member1@localhost</member> -</members> -``` +**Responses** -## Add multiple users with a affiliation to a chat room -Endpoint to add multiple users with an affiliation to a multi-user chat room. Note that a user can only have one type of affiliation with a room. By adding a user using a particular affiliation, any other pre-existing affiliation is removed. ->**PUT** /chatrooms/{roomName}/{affiliation} +| Status | Description | Response body | +|--------|-------------|---------------| +| 200 | Status returned. | [ClusteringEntity](#clusteringentity) (XML or JSON) | -**Payload:** list of affiliations +<!-- END GENERATED ENDPOINTS --> -**Return value:** HTTP status 201 (Created) +# Data format +Openfire REST API provides XML and JSON as data format. The default data format is XML. +To get a JSON result, please add "**Accept: application/json**" to the request header. +If you want to create a resource with JSON data format, please add "**Content-Type: application/json**". -### Possible parameters +<!-- BEGIN GENERATED DATA TYPES: do not edit this section by hand. It is generated from the OpenAPI annotations in the source code by the Maven build. --> -| Parameter | Parameter Type | Description | Default value | -|-----------------|-----------------|-------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| affiliation | @Path | Available affiliation: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitations to newly affiliated users | false | +## Data types -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/members -> -**Request Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<members> - <member>member2@localhost</member> - <member>member1@localhost</member> -</members> -``` +These are the data types that are used in the request and response bodies of the endpoints. The name of a field is the name that is used in JSON. When XML uses a different name, it is mentioned in the description of the field. -## Add group with affiliation to chat room -Endpoint to add a new group with affiliation to a room. ->**POST** /chatrooms/{roomName}/{affiliation}/group/{name} +Date/time values are represented as an ISO-8601 formatted text in XML (for example: `2026-01-31T12:34:56.789Z`), and as the number of milliseconds since the Unix epoch in JSON (for example: `1769862896789`). In JSON request bodies, the ISO-8601 format can also be used. -**Payload:** none +### AdminEntities -**Return value:** HTTP status 201 (Created) +A list of entities that have an admin affiliation with a multi-user chat room. -### Possible parameters +XML root element: `<admins>` -| Parameter | Parameter Type | Description | Default value | -|-----------------|----------------|--------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| name | @Path | The group name | | -| affiliation | @Path | Available affiliations: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | -| sendInvitations | @QueryParam | Whether to send invitations to the users in the newly affiliated groups | false | +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| admins | array of string | no | The JIDs (or names of local users) of the entities. In XML, items are represented as `<admin>` elements. Example: `jane@example.org` | -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/group/testGroup -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/group/testGroup -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/group/testGroup -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/group/testGroup -> ->**POST** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/group/testUser?servicename=privateconf - - -## Delete a user from a chat room -Endpoint to remove a room user affiliation. ->**DELETE** /chatrooms/{roomName}/{affiliations}/{name} - -**Payload:** none - -**Return value:** HTTP status 200 (OK) - -### Possible parameters - -| Parameter | Parameter Type | Description | Default value | -|--------------|-----------------|--------------------------------------------------------------------------------------------|---------------| -| roomname | @Path | Exact room name | | -| name | @Path | The local username or the user JID | | -| affiliations | @Path | Available affiliations: <br>**owners** <br> **admins** <br> **members** <br> **outcasts** | | -| servicename | @QueryParam | The name of the Group Chat Service | conference | - -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser@openfire.com -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/admins/testUser -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/members/testUser -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/outcasts/testUser -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/chatrooms/global/owners/testUser?servicename=privateconf +### AffiliatedEntities -# System related REST Endpoints +A list of entities that have a particular affiliation with a multi-user chat room. Depending on the affiliation, this is an AdminEntities, MemberEntities, OutcastEntities or OwnerEntities value. -## Retrieve all system properties -Endpoint to get all system properties ->**GET** /system/properties +### ClusterNodeEntities -**Payload:** none +A list of the nodes in an Openfire cluster. -**Return value:** System properties - -### Examples +XML root element: `<clusterNodes>` ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/system/properties +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| clusterNodes | array of [ClusterNodeEntity](#clusternodeentity) | no | The nodes of the cluster. In XML, items are represented as `<clusterNode>` elements. | -## Retrieve system property -Endpoint to get information over specific system property ->**GET** /system/properties/{propertyName} +### ClusterNodeEntity -**Payload:** none +A node in an Openfire cluster. -**Return value:** System property +XML root element: `<clusterNode>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| hostName | string | no | The host name and IP address of the server on which this cluster node is running. Example: `xmpp1.example.org (192.168.0.10)` | +| nodeID | string | no | The unique identifier of this cluster node. Example: `a3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d` | +| joinedTime | date-time | no | The moment at which this node joined the cluster. | +| seniorMember | boolean | no | Whether this node currently is the senior member of the cluster. Example: `true` | -| Parameter | Parameter Type | Description | Default value | -|--------------|-----------------|-----------------------------|---------------| -| propertyName | @Path | The name of system property | | +### ClusteringEntity -### Examples +The clustering status of an Openfire instance. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/system/properties/xmpp.domain +XML root element: `<clustering>` -## Create a system property -Endpoint to create a system property. Note that the name of the property must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens. ->**POST** system/properties +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| status | string | no | The clustering status of this Openfire instance. Allowed values: `SENIOR AND ONLY MEMBER`, `Senior member`, `Junior member`, `Starting up`, `Disabled`. Example: `Senior member` | -**Payload:** System Property +### ErrorResponse -**Return value:** HTTP status 201 (Created) +A description of an error that occurred while processing a request. -### Examples +XML root element: `<error>` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/system/properties +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| resource | string | no | The resource (for example, a username or room name) that the error relates to. Example: `john` | +| message | string | no | A description of the error. Example: `Could not get user` | +| exception | string | no | The type of the error. Example: `UserNotFoundException` | -**Payload Example:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<property key="propertyName" value="propertyValue"/> -``` +### GroupEntities -## Delete a system property -Endpoint to delete a system property, together with all of its child properties (properties of which the name starts with the name of this property, followed by a dot). Note that the name of the property must consist of one or more dot-separated parts, each consisting of ASCII letters, digits, underscores, apostrophes and hyphens. A deletion that could also delete other properties (for example, because the name contains an underscore, which can match any character) is rejected. ->**DELETE** /system/properties/{propertyName} +A list of Openfire user groups. -**Payload:** none +XML root element: `<groups>` -**Return value:** HTTP status 200 (OK) +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| groups | array of [GroupEntity](#groupentity) | no | The groups. In XML, items are represented as `<group>` elements. | -### Possible parameters +### GroupEntity -| Parameter | Parameter Type | Description | Default value | -|--------------|-----------------|-----------------------------|---------------| -| propertyName | @Path | The name of system property | | +An Openfire user group. -### Examples +XML root element: `<group>` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/system/properties/propertyName +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| name | string | yes | The name of the group. When updating a group, this must be equal to the group name in the path of the request. Example: `UserGroup1` | +| description | string | no | The description of the group. Example: `My group of users` | +| shared | boolean | no | Whether the group is shared: whether it automatically appears in the rosters of its members. Example: `false` | +| admins | array of string | no | The admins of the group. When creating or updating a group, each admin can be identified by a username or a JID. Responses contain (bare) JIDs. In XML, items are represented as `<admin>` elements, wrapped in the `<admins>` element. Example: `jane.smith` | +| members | array of string | no | The members of the group. When creating or updating a group, each member can be identified by a username or a JID. Responses contain (bare) JIDs. In XML, items are represented as `<member>` elements, wrapped in the `<members>` element. Example: `john.jones` | -## Update a system property -Endpoint to update / overwrite a system property ->**PUT** /system/properties/{propertyName} +### MUCInvitationEntity -**Payload:** System property +An invitation to join a multi-user chat room. -**Return value:** HTTP status 200 (OK) +XML root element: `<mucInvitation>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| reason | string | no | The reason that is included in the invitation message(s). Example: `Come join this cool room please!` | -| Parameter | Parameter Type | Description | Default value | -|--------------|-----------------|-----------------------------|---------------| -| propertyName | @Path | The name of system property | | +### MUCInvitationsEntity -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/system/properties/propertyName +An invitation for a collection of users and/or groups to join a multi-user chat room. -**Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<property key="propertyName" value="anotherValue"/> -``` +XML root element: `<mucInvitations>` -## Retrieve concurrent sessions -Endpoint to get count of concurrent sessions ->**GET** /system/statistics/sessions +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| reason | string | no | The reason that is included in the invitation message(s). Example: `Come join this cool room please!` | +| jidsToInvite | array of string | no | The users and/or groups to invite into the room, each identified by the JID of a user or group, or by the name of a local user or group. In XML, items are represented as `<jid>` elements, wrapped in the `<jidsToInvite>` element. Example: `john@example.org` | -**Payload:** none +### MUCRoomEntities -**Return value:** Sessions count +A list of multi-user chat rooms. -### Examples +XML root element: `<chatRooms>` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/system/statistics/sessions +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| chatRooms | array of [MUCRoomEntity](#mucroomentity) | no | The chat rooms. In XML, items are represented as `<chatRoom>` elements. | -## Check the 'liveness' state (using all checks) -Detects if Openfire has reached a state that it cannot recover from, except for with a restart, based on every liveness check that it has implemented. +### MUCRoomEntity ->**GET** /system/liveness +A multi-user chat room. When a room is created or updated, boolean values that are not provided are treated as 'false'. -**Payload:** none +XML root element: `<chatRoom>` -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| roomName | string | yes | The name of the room, which is used as the local part of the room's JID. It is converted to lowercase. When updating a room, this must be equal to the room name in the path of the request. Example: `global` | +| description | string | no | The description of the room. Example: `A room for everyone` | +| password | string | no | The password that users must provide to enter the room. Example: `s3cr3t` | +| subject | string | no | The subject (topic) of the room. Example: `Welcome!` | +| naturalName | string | no | The human-readable name of the room, as shown to users that discover rooms on the chat service. Example: `Global Chat` | +| maxUsers | integer | no | The maximum number of occupants that can be in the room at the same time. 0 means unlimited. Example: `30` | +| creationDate | date-time | no | The moment at which the room was created. When creating a room without this value, the current time is used. | +| modificationDate | date-time | no | The moment at which the configuration of the room was last modified. When creating or updating a room without this value, the current time is used. | +| persistent | boolean | no | Whether the room is persistent. Persistent rooms are saved to the database, and are not destroyed when the last occupant leaves. Example: `true` | +| publicRoom | boolean | no | Whether the room is public: searchable and visible through service discovery. Example: `true` | +| registrationEnabled | boolean | no | Whether users are allowed to register with the room. Example: `false` | +| canAnyoneDiscoverJID | boolean | no | Whether the real JID of every occupant is visible to every other occupant (a non-anonymous room). Example: `false` | +| canOccupantsChangeSubject | boolean | no | Whether participants are allowed to change the subject of the room. Example: `false` | +| canOccupantsInvite | boolean | no | Whether occupants can invite other users to the room. When the room is not members-only, anyone can send invitations regardless of this value. When the room is members-only and this is 'false', only owners and admins can send invitations. Example: `false` | +| canChangeNickname | boolean | no | Whether occupants are allowed to change their nickname in the room. Example: `true` | +| logEnabled | boolean | no | Whether the conversation in the room is logged (saved to the database). Example: `true` | +| loginRestrictedToNickname | boolean | no | Whether registered users can only join the room using their registered nickname. Example: `false` | +| membersOnly | boolean | no | Whether the room is members-only: users need to be a member (or be invited) to enter. Example: `false` | +| moderated | boolean | no | Whether the room is moderated: only occupants with 'voice' can send messages to all occupants. Example: `false` | +| allowPM | string | no | Defines who is allowed to send private messages to other occupants. Must be one of "anyone", "participants", "moderators" or "none". Example: `anyone` | +| broadcastPresenceRoles | array of string | no | The roles of occupants of which presence is broadcast to the other occupants. Each is one of: 'moderator', 'participant', 'visitor'. In XML, items are represented as `<broadcastPresenceRole>` elements, wrapped in the `<broadcastPresenceRoles>` element. Example: `moderator` | +| owners | array of string | no | The (bare) JIDs of the users that have an owner affiliation with the room. When creating a room without owners, the 'admin' user is made owner. In XML, items are represented as `<owner>` elements, wrapped in the `<owners>` element. Example: `admin@example.org` | +| ownerGroups | array of string | no | The names of the user groups that have an owner affiliation with the room. In XML, items are represented as `<ownerGroup>` elements, wrapped in the `<ownerGroups>` element. Example: `Management` | +| admins | array of string | no | The (bare) JIDs of the users that have an admin affiliation with the room. In XML, items are represented as `<admin>` elements, wrapped in the `<admins>` element. Example: `jane@example.org` | +| adminGroups | array of string | no | The names of the user groups that have an admin affiliation with the room. In XML, items are represented as `<adminGroup>` elements, wrapped in the `<adminGroups>` element. Example: `Moderators` | +| members | array of string | no | The (bare) JIDs of the users that have a member affiliation with the room. In XML, items are represented as `<member>` elements, wrapped in the `<members>` element. Example: `john@example.org` | +| memberGroups | array of string | no | The names of the user groups that have a member affiliation with the room. In XML, items are represented as `<memberGroup>` elements, wrapped in the `<memberGroups>` element. Example: `Sales` | +| outcasts | array of string | no | The (bare) JIDs of the users that have an outcast affiliation with the room: users that are banned from the room. In XML, items are represented as `<outcast>` elements, wrapped in the `<outcasts>` element. Example: `spammer@example.org` | +| outcastGroups | array of string | no | The names of the user groups that have an outcast affiliation with the room. In XML, items are represented as `<outcastGroup>` elements, wrapped in the `<outcastGroups>` element. Example: `Banned` | -## Perform 'deadlock' liveness check -Detects if Openfire has reached a state that it cannot recover from because of a deadlock. ->**GET** /system/liveness/deadlock +### MUCRoomMessageEntities -**Payload:** none +A list of messages from the history of a multi-user chat room. -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +XML root element: `<messages>` -## Perform 'properties' liveness check -Detects if Openfire has reached a state that it cannot recover from because a system property change requires a restart to take effect. ->**GET** /system/liveness/properties +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| message | array of [MUCRoomMessageEntity](#mucroommessageentity) | no | The messages. In XML, items are represented as `<message>` elements. | -**Payload:** none +### MUCRoomMessageEntity -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +A message from the history of a multi-user chat room. -## Check the 'readiness' state (using all checks) -Detects if Openfire is in a state where it is ready to process traffic, based on every readiness check that it has implemented. ->**GET** /system/readiness +XML root element: `<message>` -**Payload:** none +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| to | string | no | The JID of the addressee of the message. Example: `global@conference.example.org` | +| from | string | no | The JID of the sender of the message: the room JID, followed by the nickname of the occupant. Example: `global@conference.example.org/john` | +| type | string | no | The XMPP message type. Example: `groupchat` | +| body | string | no | The text of the message. Example: `Hello, everyone!` | +| delay_stamp | string | no | The moment at which the message was originally sent (XEP-0203 delayed delivery timestamp). Example: `2026-01-31T12:34:56.789Z` | +| delay_from | string | no | The JID of the entity that delayed the delivery of the message (XEP-0203). Example: `global@conference.example.org` | -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +### MUCServiceEntities -## Perform 'server' readiness check -Detects if Openfire's core service has been started. ->**GET** /system/readiness/server +A list of multi-user chat services. -**Payload:** none +XML root element: `<chatServices>` -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| chatService | array of [MUCServiceEntity](#mucserviceentity) | no | The chat services. In XML, items are represented as `<chatService>` elements. | -## Perform 'cluster' readiness check -Detects if the cluster functionality has finished starting (or is disabled). ->**GET** /system/readiness/cluster +### MUCServiceEntity -**Payload:** none +A multi-user chat service. -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +XML root element: `<chatService>` -## Perform 'plugins' readiness check -Detects if Openfire has finished starting its plugins. ->**GET** /system/readiness/plugins +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| serviceName | string | yes | The name of the chat service, which is used as the subdomain of the service. Example: `conference` | +| description | string | no | The description of the chat service. Example: `A public service` | +| hidden | boolean | no | Whether the service is hidden from service discovery. Example: `false` | -**Payload:** none +### MemberEntities -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +A list of entities that have a member affiliation with a multi-user chat room. -## Perform 'connections' readiness check -Detects if Openfire is ready to accept connection requests. ->**GET** /system/readiness/connections +XML root element: `<members>` -**Payload:** none +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| members | array of string | no | The JIDs (or names of local users) of the entities. In XML, items are represented as `<member>` elements. Example: `john@example.org` | -**Return value**: HTTP status 200 (OK). Any HTTP status outside the range 200-399 indicates failure. +### MessageEntity -### Possible parameters +A message. -| Parameter | Parameter Type | Description | Default value | -|----------------|----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| -| connectionType | @Path | Optional. Use to limit the check to one particular connection type. One of: SOCKET_S2S, SOCKET_C2S, BOSH_C2S, WEBADMIN, COMPONENT, CONNECTION_MANAGER | | -| encypted | @Path | Check the encrypted (true) or unencrypted (false) variant of the connection type. Only used in combination with 'connectionType', as without it, all types and both encrypted and unencrypted are checked. | | +XML root element: `<message>` -# Group related REST Endpoints +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| body | string | yes | The text of the message. Example: `The server will be restarted in 5 minutes.` | -## Retrieve all groups -Endpoint to get all groups ->**GET** /groups +### MsgArchiveEntity -**Payload:** none +The number of unread messages of a user. -**Return value:** Groups - -### Examples +XML root element: `<archive>` ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/groups +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| jid | string | no | The JID of the user. Example: `john@example.org` | +| count | integer | no | The number of unread messages. Example: `3` | -## Retrieve a group -Endpoint to get information over specific group ->**GET** /groups/{groupName} +### OccupantEntities -**Payload:** none +A list of occupants of a multi-user chat room. -**Return value:** Group +XML root element: `<occupants>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| occupants | array of [OccupantEntity](#occupantentity) | no | The occupants. In XML, items are represented as `<occupant>` elements. | -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|-----------------------|---------------| -| groupName | @Path | The name of the group | | +### OccupantEntity -### Examples +An occupant of a multi-user chat room. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/groups/moderators +XML root element: `<occupant>` -## Create a group -Endpoint to create a new group ->**POST** /groups +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| jid | string | no | The occupant JID: the room JID, followed by the nickname of the occupant. Example: `global@conference.example.org/john` | +| userAddress | string | no | The real (full) JID of the user. Example: `john@example.org/laptop` | +| role | string | no | The role of the occupant in the room. One of: 'moderator', 'participant', 'visitor', 'none'. Example: `participant` | +| affiliation | string | no | The affiliation of the occupant with the room. One of: 'owner', 'admin', 'member', 'outcast', 'none'. Example: `member` | -**Payload:** Group +### OutcastEntities -**Return value:** HTTP status 201 (Created) +A list of entities that have an outcast affiliation with a multi-user chat room: entities that are banned from the room. -### Examples +XML root element: `<outcasts>` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type: application/xml -> ->**POST** http://example.org:9090/plugins/restapi/v1/groups +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| outcasts | array of string | no | The JIDs (or names of local users) of the entities. In XML, items are represented as `<outcast>` elements. Example: `spammer@example.org` | -**Payload Example:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<group> - <name>GroupName</name> - <description>Some description</description> - <isshared>false</isshared> -</group> -``` +### OwnerEntities -## Delete a group -Endpoint to delete a group ->**DELETE** /groups/{groupName} +A list of entities that have an owner affiliation with a multi-user chat room. -**Payload:** none +XML root element: `<owners>` -**Return value:** HTTP status 200 (OK) +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| owners | array of string | no | The JIDs (or names of local users) of the entities. In XML, items are represented as `<owner>` elements. Example: `admin@example.org` | -### Possible parameters +### ParticipantEntities -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|-----------------------|---------------| -| groupName | @Path | The name of the group | | +A list of occupants of a multi-user chat room. -### Examples +XML root element: `<participants>` ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/groups/groupToDelete +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| participants | array of [ParticipantEntity](#participantentity) | no | The occupants. In XML, items are represented as `<participant>` elements. | -## Update a group -Endpoint to update / overwrite a group ->**PUT** /groups/{groupName} +### ParticipantEntity -**Payload:** Group +An occupant of a multi-user chat room. -**Return value:** HTTP status 200 (OK) +XML root element: `<participant>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| jid | string | no | The occupant JID: the room JID, followed by the nickname of the occupant. Example: `global@conference.example.org/john` | +| role | string | no | The role of the occupant in the room. One of: 'moderator', 'participant', 'visitor', 'none'. Example: `participant` | +| affiliation | string | no | The affiliation of the occupant with the room. One of: 'owner', 'admin', 'member', 'outcast', 'none'. Example: `member` | -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|-----------------------|---------------| -| groupName | @Path | The name of the group | | +### RoomCreationResultEntities -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**Header:** Content-Type application/xml -> ->**PUT** http://example.org:9090/plugins/restapi/v1/groups/groupNameToUpdate +The results of the creation of multiple multi-user chat rooms, grouped by result type. -**Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<group> - <name>groupNameToUpdate</name> - <description>New description</description> - <isshared>false</isshared> -</group> -``` +XML root element: `<results>` -# Session related REST Endpoints +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| success | array of [RoomCreationResultEntity](#roomcreationresultentity) | no | The results of the rooms that were created successfully. In XML, the items are wrapped in the `<success>` element. | +| failure | array of [RoomCreationResultEntity](#roomcreationresultentity) | no | The results of the rooms that could not be created. In XML, the items are wrapped in the `<failure>` element. | +| other | array of [RoomCreationResultEntity](#roomcreationresultentity) | no | The results of a type other than success or failure. In XML, the items are wrapped in the `<other>` element. | -## Retrieve all user session -Endpoint to get all user sessions ->**GET** /sessions +### RoomCreationResultEntity -**Payload:** none +The result of the creation of one multi-user chat room. -**Return value:** Sessions - -### Examples +XML root element: `<result>` ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/sessions +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| roomName | string | no | The name of the room that was to be created. Example: `open_chat` | +| resultType | string | no | The result of creating the room. Allowed values: `Success`, `Failure`. Example: `Failure` | +| message | string | no | A message that describes the result. Example: `Room already existed and therefore not created again` | -## Retrieve the user sessions -Endpoint to get sessions from a user ->**GET** /sessions/{username} +### RosterEntities -**Payload:** none +The roster (contact list) of a user. -**Return value:** Sessions +XML root element: `<roster>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| rosterItem | array of [RosterItemEntity](#rosteritementity) | no | The entries of the roster. In XML, items are represented as `<rosterItem>` elements. | -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|--------------------------|---------------| -| username | @Path | The username of the user | | +### RosterItemEntity -### Examples +An entry in the roster (contact list) of a user. ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/sessions/testuser +XML root element: `<rosterItem>` -## Close all user sessions -Endpoint to close/kick sessions from a user ->**DELETE** /sessions/{username} +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| jid | string | yes | The JID of the contact. Example: `jane@example.org` | +| nickname | string | no | The name of the contact, as shown in this roster. Example: `Jane` | +| subscriptionType | integer | no | The presence subscription state of the contact. One of: -1 (remove), 0 (none), 1 (to: the user receives presence updates of the contact), 2 (from: the contact receives presence updates of the user), 3 (both). Example: `3` | +| groups | array of string | no | The roster groups (for example 'Friends' or 'Co-workers') that this contact is organized under. In XML, items are represented as `<group>` elements, wrapped in the `<groups>` element. Example: `Friends` | -**Payload:** none +### SecurityAuditLog -**Return value:** HTTP status 200 (OK) +An entry of the security audit log. -### Possible parameters +XML root element: `<log>` -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|--------------------------|---------------| -| username | @Path | The username of the user | | +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| logId | integer | no | The unique identifier of the log entry. Example: `42` | +| username | string | no | The username of the user that performed the audited action. Example: `admin` | +| timestamp | integer | no | The moment at which the audited action occurred, in seconds since the Unix epoch. Example: `1769862896` | +| summary | string | no | A short description of the audited action. Example: `Created new user john` | +| node | string | no | The node that triggered the audited action, usually a host name or IP address. Example: `xmpp1.example.org` | +| details | string | no | Detailed information about the audited action. Example: `name = John Doe, email = john@example.org` | -### Examples +### SecurityAuditLogs ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**DELETE** http://example.org:9090/plugins/restapi/v1/sessions/testuser +A list of entries of the security audit log. -# Message related REST Endpoints +XML root element: `<logs>` -## Send a broadcast message -Endpoint to send a broadcast/server message to all online users ->**POST** /messages/users +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| logs | array of [SecurityAuditLog](#securityauditlog) | no | The log entries. In XML, items are represented as `<log>` elements. | -**Payload:** Message +### SessionEntities -**Return value:** HTTP status 201 (Created) - -### Examples +A list of client sessions. ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**POST** http://example.org:9090/plugins/restapi/v1/messages/users +XML root element: `<sessions>` -**Payload:** -```xml -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<message> - <body>Your message</body> -</message> -``` -# Security Audit related REST Endpoints +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| sessions | array of [SessionEntity](#sessionentity) | no | The sessions. In XML, items are represented as `<session>` elements. | -## Retrieve the Security audit logs -Endpoint to get security audit logs ->**GET** /logs/security +### SessionEntity -**Payload:** none +A client session. -**Return value:** Security Audit Logs +XML root element: `<session>` -### Possible parameters +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| sessionId | string | no | The (full) JID of the session. Example: `john@example.org/laptop` | +| username | string | no | The username of the user of the session, or 'Anonymous' for anonymous sessions. Example: `john` | +| resource | string | no | The resource part of the JID of the session. Example: `laptop` | +| node | string | no | Whether the session is connected to the cluster node that processes the request ('Local'), or to another cluster node ('Remote'). Example: `Local` | +| sessionStatus | string | no | The status of the session. One of: 'Closed', 'Connected', 'Authenticated', 'Unknown'. Example: `Authenticated` | +| presenceStatus | string | no | The availability of the user of the session. One of: 'Online', 'Away', 'Available to Chat', 'Do Not Disturb', 'Extended Away', 'Unknown/Not Recognized'. Example: `Online` | +| presenceMessage | string | no | The (optional) natural-language description of the availability of the user of the session. Example: `In a meeting` | +| priority | integer | no | The presence priority of the session, from -128 to 127. Example: `0` | +| hostAddress | string | no | The IP address of the client. Example: `192.168.0.20` | +| hostName | string | no | The host name of the client. Example: `laptop.example.org` | +| creationDate | date-time | no | The moment at which the session was created. | +| lastActionDate | date-time | no | The moment at which the session last had activity. | +| secure | boolean | no | Whether the connection of the session is encrypted. Example: `true` | -| Parameter | Parameter Type | Description | Default value | -|-----------|----------------|----------------------------------------------------|---------------| -| username | @QueryParam | Username of user to look up | | -| startTime | @QueryParam | Oldest timestamp of range of logs to retrieve | | -| endTime | @QueryParam | Most recent timestamp of range of logs to retrieve | 0 (until now) | -| offset | @QueryParam | Number of logs to skip | | -| limit | @QueryParam | Number of logs to retrieve | | +### SessionsCount -### Examples +The number of client sessions. ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/logs/security +XML root element: `<sessions>` -# Clustering related REST Endpoints +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| localSessions | integer | no | The number of authenticated client sessions (of both anonymous and non-anonymous users) on the cluster node that processes the request. Example: `12` | +| clusterSessions | integer | no | The number of authenticated client sessions (of both anonymous and non-anonymous users) in the entire cluster. Example: `30` | -## Retrieve information for all cluster nodes. -Endpoint to get information for all nodes in the cluster. Note that this endpoint can only return data for remote nodes -when the instance of Openfire that processes this query has successfully joined the cluster. +### SystemProperties ->**GET** http://example.org:9090/plugins/restapi/v1/clustering/nodes +A list of Openfire system properties. -**Payload:** none +XML root element: `<properties>` -**Return value:** ClusterNodes +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| property | array of [SystemProperty](#systemproperty) | no | The system properties. In XML, items are represented as `<property>` elements. | -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/clustering/nodes -> +### SystemProperty -## Retrieve information for a specific cluster node. -Endpoint to get information for a specific cluster node. Note that this endpoint can only return data for remote nodes -when the instance of Openfire that processes this query has successfully joined the cluster. +An Openfire system property. ->**GET** http://example.org:9090/plugins/restapi/v1/clustering/nodes/{nodeId} +XML root element: `<property>` -**Payload:** none +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| key | string | yes | The name of the system property. In XML, this is the `key` attribute. Example: `xmpp.domain` | +| value | string | yes | The value of the system property. In XML, this is the `value` attribute. Example: `example.org` | -**Return value:** ClusterNode +### UserEntities -### Possible parameters +A list of Openfire users. -| Parameter | Parameter Type | Description | Default value | -|-----------|-----------------|--------------|---------------| -| nodeId | @Path | Exact NodeID | | +XML root element: `<users>` -### Examples ->**Header:** Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/clustering/nodes/52a89928-66f7-45fd-9bb8-096de07400ac -> +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| users | array of [UserEntity](#userentity) | no | The users. In XML, items are represented as `<user>` elements. | -## Retrieve the Clustering status -Endpoint to get description of clustering status ->**GET** /clustering/status +### UserEntity -**Payload:** none +An Openfire user. -**Return value:** String describing the clustering status of this Openfire instance +XML root element: `<user>` -### Examples ->**Header**: Authorization: Basic YWRtaW46MTIzNDU= -> ->**GET** http://example.org:9090/plugins/restapi/v1/clustering/status +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| username | string | no | The username of the user. Required when creating a user. When updating a user, providing a different username renames the user. Example: `john` | +| name | string | no | The name of the user. Example: `John Doe` | +| email | string | no | The email address of the user. Example: `john@example.org` | +| password | string | no | The password of the user. Required when creating a user. Never included in responses. Example: `s3cr3t` | +| properties | array of [UserProperty](#userproperty) | no | Custom properties of the user. Property keys are unique per user. When updating a user, all existing properties of the user are replaced by the provided properties: omitting this removes all properties of the user. In XML, the items are wrapped in the `<properties>` element. | -### Possible Responses +### UserGroupsEntity -* SENIOR AND ONLY MEMBER -* Senior member -* Junior member -* Starting up -* Disabled +A list of names of Openfire user groups. -# Data format -Openfire REST API provides XML and JSON as data format. The default data format is XML. -To get a JSON result, please add "**Accept: application/json**" to the request header. -If you want to create a resource with JSON data format, please add "**Content-Type: application/json**". +XML root element: `<groups>` -## Data types +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| groupnames | array of string | no | The names of the groups. In XML, items are represented as `<groupname>` elements. Example: `Sales` | + +### UserProperty + +A custom property of an Openfire user. + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| key | string | yes | The key (name) of the property. Unique per user. In XML, this is the `key` attribute. Example: `department` | +| value | string | yes | The value of the property. In XML, this is the `value` attribute. Example: `Sales` | -### ClusterNode - -| Parameter | Optional | Description | -|--------------|----------|-------------------------------------------------------------------------------------| -| hostName | No | The hostname and IP address of the server on which this cluster node is running. | -| nodeID | No | A unique identifier of this cluster node. | -| joinedTime | No | Timestamp when the node joined the cluster. | -| seniorMember | No | Boolean value indicating if the node is currently the senior member of the cluster. | - -### User - -| Parameter | Optional | Description | -|------------|----------|------------------------------------------------------------------------------------------| -| username | No | The username of the user | -| name | Yes | The name of the user | -| email | Yes | The email of the user | -| password | No | The password of the user | -| properties | Yes | List of properties. Property is a key / value object. The key must to be per user unique | - -### RosterItem -| Parameter | Optional | Description | -|------------------|----------|-----------------------------------------------------------------------------------------------------------| -| jid | No | The JID of the roster item | -| nickname | Yes | The nickname for the user when used in this roster | -| subscriptionType | Yes | The subscription type <br> Possible numeric values are: -1 (remove), 0 (none), 1 (to), 2 (from), 3 (both) | -| groups | No | A list of groups to organize roster entries under (e.g. friends, co-workers, etc.) | - -### Chatroom - -| Parameter | Optional | Description | -|---------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| roomName | No | The name/id of the room. Can only contains lowercase and alphanumeric characters. | -| naturalName | No | Also the name of the room, but can contains non alphanumeric characters. It's mainly used for users while discovering rooms hosted by the Multi-User Chat service. | -| description | No | Description text of the room. | -| subject | Yes | Subject of the room. | -| password | Yes | The password that the user must provide to enter the room | -| creationDate | Yes | The date when the room was created. Will be automatically set by creation. Example: 2014-07-10T09:49:12.411+02:00 | -| modificationDate | Yes | The last date when the room's configuration was modified. If the room's configuration was never modified then the initial value will be the same as the creation date. Will be automatically set by update. Example: 2014-07-10T09:49:12.411+02:00 | -| maxUsers | Yes | the maximum number of occupants that can be simultaneously in the room. 0 means unlimited number of occupants. | -| persistent | Yes | Can be "true" or "false". Persistent rooms are saved to the database to make their configurations persistent together with the affiliation of the users. Otherwise the room will be destroyed if the last occupant leave the room. | -| publicRoom | Yes | Can be "true" or "false". True if the room is searchable and visible through service discovery. | -| registrationEnabled | Yes | Can be "true" or "false". True if users are allowed to register with the room. By default, room registration is enabled. | -| canAnyoneDiscoverJID | Yes | Can be "true" or "false". True if every presence packet will include the JID of every occupant. | -| canOccupantsChangeSubject | Yes | Can be "true" or "false". True if participants are allowed to change the room's subject. | -| canOccupantsInvite | Yes | Can be "true" or "false". True if occupants can invite other users to the room. If the room does not require an invitation to enter (i.e. is not members-only) then any occupant can send invitations. On the other hand, if the room is members-only and occupants cannot send invitation then only the room owners and admins are allowed to send invitations. | -| canChangeNickname | Yes | Can be "true" or "false". True if room occupants are allowed to change their nicknames in the room. By default, occupants are allowed to change their nicknames. | -| logEnabled | Yes | Can be "true" or "false". True if the room's conversation is being logged. If logging is activated the room conversation will be saved to the database every couple of minutes. The saving frequency is the same for all the rooms and can be configured by changing the property "xmpp.muc.tasks.log.timeout". | -| loginRestrictedToNickname | Yes | Can be "true" or "false". True if registered users can only join the room using their registered nickname. By default, registered users can join the room using any nickname. | -| membersOnly | Yes | Can be "true" or "false". True if the room requires an invitation to enter. That is if the room is members-only. | -| moderated | Yes | Can be "true" or "false". True if the room in which only those with "voice" may send messages to all occupants. | -| allowPM | Yes | One of "anyone", "participants", "moderators" or "none". Controls who is allowed to send private messages to other occupants in the room. | -| broadcastPresenceRoles | Yes | The list of roles of which presence will be broadcasted to the rest of the occupants. | -| owners | Yes | A collection with the current list of owners. The collection contains the bareJID of the users with owner affiliation. | -| admins | Yes | A collection with the current list of admins. The collection contains the bareJID of the users with admin affiliation. | -| members | Yes | A collection with the current list of room members. The collection contains the bareJID of the users with member affiliation. If the room is not members-only then the list will contain the users that registered with the room and therefore they may have reserved a nickname. | -| outcasts | Yes | A collection with the current list of outcast users. An outcast user is not allowed to join the room again. The collection contains the bareJID of the users with outcast affiliation. | -| ownerGroups | Yes | A collection with the current list of groups with owner affiliation. The collection contains the name only. | -| adminGroups | Yes | A collection with the current list of groups with admin affiliation. The collection contains the name only. | -| memberGroups | Yes | A collection with the current list of groups with member affiliation. The collection contains the name only. | -| outcastGroups | Yes | A collection with the current list of groups with outcast affiliation. The collection contains the name only. | - -### Group - -| Parameter | Optional | Description | -|-------------|----------|------------------------------------------------| -| name | No | The name of the group | -| description | No | The description of the group | -| admins | Yes | A collection with current admins of the group | -| members | Yes | A collection with current members of the group | - -### System Property - -| Parameter | Optional | Description | -|-----------|----------|----------------------------------| -| key | No | The name of the system property | -| value | No | The value of the system property | - -### Session -| Parameter | Optional | Description | -|----------------|----------|-------------------------------------------------------------------------------------------------| -| sessionId | No | Full JID of a user e.g. (testUser@testserver.de/SomeRessource) | -| username | No | The username associated with this session. Can be also "Anonymous". | -| resource | Yes | Resource name | -| node | No | Can be "Local" or "Remote" | -| sessionStatus | No | The current status of this session. Can be "Closed", "Connected", "Authenticated" or "Unknown". | -| presenceStatus | No | The status of this presence packet, a natural-language description of availability status. | -| priority | No | The priority of the session. The valid priority range is -128 through 128. | -| hostAddress | No | The IP address string in textual presentation. | -| hostName | No | The host name for this IP address. | -| creationDate | No | The date the session was created. | -| lastActionDate | No | The time the session last had activity. | -| secure | No | Is "true" if this connection is secure. | - -### Sessions count -| Parameter | Optional | Description | -|-----------------|----------|------------------------------------------------------------------------------------------------------------------------------------------| -| clusterSessions | No | Number of client sessions that are authenticated with the server. This includes anonymous and non-anoymous users from the whole cluster. | -| localSessions | No | Number of client sessions that are authenticated with the server. This includes anonymous and non-anoymous users. | - -### Security Audit Logs -| Parameter | Optional | Description | -|-----------|----------|---------------------------------------------------------------------| -| logId | No | Unique ID of this log | -| username | No | The username of the user who performed this event | -| timestamp | No | The time stamp of when this event occurred | -| summary | No | The summary, or short description of what transpired in the event | -| node | No | The node that triggered the event, usually a hostname or IP address | -| details | No | Detailed information about what occurred in the event | - -### Occupants -| Parameter | Optional | Description | -|-------------|----------|-------------------------| -| jid | No | The JID of the MUC room | -| userAddress | No | The JID of the user | -| role | No | Role of the user | -| affiliation | No | Affiliation of the user | +<!-- END GENERATED DATA TYPES --> diff --git a/src/build/ReadmeGenerator.java b/src/build/ReadmeGenerator.java new file mode 100644 index 000000000..9f72a6be9 --- /dev/null +++ b/src/build/ReadmeGenerator.java @@ -0,0 +1,571 @@ +/* + * Copyright (C) 2026 Ignite Realtime Foundation. All rights reserved. + * + * 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. + */ + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.core.util.Json; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; +import io.swagger.v3.oas.models.media.Content; +import io.swagger.v3.oas.models.media.MediaType; +import io.swagger.v3.oas.models.media.Schema; +import io.swagger.v3.oas.models.parameters.Parameter; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.tags.Tag; +import org.jivesoftware.openfire.plugin.rest.CustomJacksonMapperProvider; + +import java.io.StringWriter; +import java.lang.reflect.Modifier; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.TimeZone; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.stream.Collectors; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.Marshaller; + +/** + * Generates the documentation of the endpoints and data types in readme.md from the OpenAPI specification, which in + * turn is generated from the OpenAPI annotations in the source code. This keeps the documentation in sync with the + * implementation. + * + * The generated documentation replaces all text between pairs of marker lines in the readme, like + * {@code <!-- BEGIN GENERATED ENDPOINTS ... -->} and {@code <!-- END GENERATED ENDPOINTS -->}. Text outside of these + * markers is not modified. + * + * Example request bodies are generated from the examples in the specification, and are then converted into the entity + * classes of the plugin and back, using the same JSON and XML serialization as the plugin. This guarantees that the + * examples use the actual format of the REST API: when an example cannot be processed, generation fails. + * + * This is a single-file Java program that is executed by the Maven build. It is not part of the plugin. + * + * Usage: {@code java -cp <classpath> ReadmeGenerator.java <openapi.json> <readme.md>} + */ +public class ReadmeGenerator +{ + /** The prefix under which the REST API is exposed, relative to the root of the Openfire admin console. */ + static final String CONTEXT_ROOT = "/plugins"; + + /** Order in which tags are documented. Tags that are not listed here are documented after these, alphabetically. */ + static final List<String> TAG_ORDER = List.of("Users", "User Group", "Chat service", "Chat room", "Client Sessions", "Message", "Message Archive", "Security Audit Log", "Statistics", "System", "Clustering"); + + /** Responses that apply to (nearly) all endpoints. These are documented once, instead of for each endpoint. */ + static final Map<String, String> GENERIC_RESPONSES = Map.of( + "401", "Web service authentication failed.", + "500", "Unexpected, generic error condition." + ); + + static final List<PathItem.HttpMethod> METHOD_ORDER = List.of(PathItem.HttpMethod.GET, PathItem.HttpMethod.POST, PathItem.HttpMethod.PUT, PathItem.HttpMethod.PATCH, PathItem.HttpMethod.DELETE, PathItem.HttpMethod.HEAD, PathItem.HttpMethod.OPTIONS, PathItem.HttpMethod.TRACE); + + /** Packages that contain the classes that are used as request and response bodies. */ + static final List<String> ENTITY_PACKAGES = List.of("org.jivesoftware.openfire.plugin.rest.entity", "org.jivesoftware.openfire.plugin.rest.exceptions"); + + /** The value used in examples for date/time values, which do not have an example defined in the annotations. */ + static final String EXAMPLE_DATE_TIME = "2026-01-31T12:34:56.789Z"; + + record Endpoint(String path, PathItem.HttpMethod method, Operation operation) {} + + final OpenAPI openAPI; + final ObjectMapper mapper = new CustomJacksonMapperProvider().getContext(Object.class); + + ReadmeGenerator(final OpenAPI openAPI) + { + this.openAPI = openAPI; + } + + public static void main(String[] args) throws Exception + { + if (args.length != 2) { + System.err.println("Usage: java ReadmeGenerator.java <openapi.json> <readme.md>"); + System.exit(1); + } + final Path specFile = Path.of(args[0]); + final Path readmeFile = Path.of(args[1]); + + // Make the generated output independent of the environment that the build runs in. + TimeZone.setDefault(TimeZone.getTimeZone("UTC")); + Locale.setDefault(Locale.ROOT); + + final ReadmeGenerator generator = new ReadmeGenerator(Json.mapper().readValue(specFile.toFile(), OpenAPI.class)); + generator.verifyPropertyNames(); + + final String readme = Files.readString(readmeFile, StandardCharsets.UTF_8); + String updated = replaceSection(readme, "ENDPOINTS", generator.generateEndpoints()); + updated = replaceSection(updated, "DATA TYPES", generator.generateDataTypes()); + + if (updated.equals(readme)) { + System.out.println("Generated documentation in " + readmeFile + " is up to date."); + } else { + Files.writeString(readmeFile, updated, StandardCharsets.UTF_8); + System.out.println("Updated the generated documentation in " + readmeFile + "."); + } + } + + /** Replaces the text between the BEGIN and END markers of a section with new content. */ + static String replaceSection(final String readme, final String section, final String content) + { + final String beginMarker = "<!-- BEGIN GENERATED " + section; + final String endMarker = "<!-- END GENERATED " + section + " -->"; + final int begin = readme.indexOf(beginMarker); + final int end = readme.indexOf(endMarker); + if (begin < 0 || end < begin) { + throw new IllegalStateException("Unable to find the '" + beginMarker + "' and '" + endMarker + "' markers in the readme."); + } + final int beginLineEnd = readme.indexOf('\n', begin) + 1; + return readme.substring(0, beginLineEnd) + "\n" + content.strip().replaceAll("\n{3,}", "\n\n") + "\n\n" + readme.substring(end); + } + + // --------------------------------------------------------------------------------------------------------------- + // Endpoints + // --------------------------------------------------------------------------------------------------------------- + + String generateEndpoints() throws Exception + { + // Group all endpoints by their (first) tag. + final Map<String, List<Endpoint>> endpointsByTag = new TreeMap<>(Comparator.comparingInt((String tag) -> TAG_ORDER.contains(tag) ? TAG_ORDER.indexOf(tag) : TAG_ORDER.size()).thenComparing(Comparator.naturalOrder())); + openAPI.getPaths().forEach((path, pathItem) -> pathItem.readOperationsMap().forEach((method, operation) -> { + final String tag = operation.getTags() == null || operation.getTags().isEmpty() ? "Other" : operation.getTags().get(0); + endpointsByTag.computeIfAbsent(tag, t -> new ArrayList<>()).add(new Endpoint(path, method, operation)); + })); + + final Map<String, String> tagDescriptions = new LinkedHashMap<>(); + if (openAPI.getTags() != null) { + for (final Tag tag : openAPI.getTags()) { + tagDescriptions.put(tag.getName(), tag.getDescription()); + } + } + + final StringBuilder out = new StringBuilder(); + out.append("# REST Endpoints\n\n"); + out.append("The paths of all endpoints below are relative to the root of the Openfire admin console, for example `http://example.org:9090`. The data types that are used by these endpoints are described in [Data types](#data-types).\n\n"); + out.append("In addition to the responses that are documented for each endpoint, every endpoint can respond with:\n\n"); + GENERIC_RESPONSES.entrySet().stream().sorted(Map.Entry.comparingByKey()).forEach(e -> out.append("- `").append(e.getKey()).append("`: ").append(e.getValue()).append("\n")); + out.append("\n"); + out.append("Interactive documentation of these endpoints is available in the Openfire admin console, via the link on the REST API settings page (Server > Server Settings > REST API).\n"); + + for (final Map.Entry<String, List<Endpoint>> entry : endpointsByTag.entrySet()) { + final String tag = entry.getKey(); + final List<Endpoint> endpoints = entry.getValue(); + out.append("\n# ").append(tag).append("\n\n"); + final String tagDescription = tagDescriptions.get(tag); + if (tagDescription != null && !tagDescription.isBlank()) { + out.append(tagDescription.trim()).append("\n"); + } + + // Keep endpoints for the same resource (sharing the first path segment after the API prefix) together, and + // document resources that have the most general (shortest) paths first. + final Map<String, Integer> resourceDepth = new TreeMap<>(); + endpoints.forEach(e -> resourceDepth.merge(resource(e.path()), depth(e.path()), Math::min)); + endpoints.sort(Comparator.comparingInt((Endpoint e) -> resourceDepth.get(resource(e.path()))) + .thenComparing(e -> resource(e.path())) + .thenComparing(Endpoint::path) + .thenComparingInt(e -> METHOD_ORDER.indexOf(e.method()))); + for (final Endpoint endpoint : endpoints) { + appendEndpoint(out, endpoint); + } + } + return out.toString(); + } + + void appendEndpoint(final StringBuilder out, final Endpoint endpoint) throws Exception + { + final Operation operation = endpoint.operation(); + out.append("\n## ").append(Optional.ofNullable(operation.getSummary()).orElse(endpoint.method() + " " + endpoint.path())).append("\n\n"); + out.append("> **").append(endpoint.method()).append("** ").append(CONTEXT_ROOT).append(endpoint.path()).append("\n\n"); + if (Boolean.TRUE.equals(operation.getDeprecated())) { + out.append("**Deprecated:** this endpoint may be removed in a future version.\n\n"); + } + if (operation.getDescription() != null && !operation.getDescription().isBlank()) { + out.append(operation.getDescription().trim()).append("\n\n"); + } + + final List<Parameter> parameters = Optional.ofNullable(operation.getParameters()).orElse(List.of()); + if (!parameters.isEmpty()) { + out.append("**Parameters**\n\n"); + out.append("| Name | Located in | Required | Description | Default value |\n"); + out.append("|------|------------|----------|-------------|---------------|\n"); + for (final Parameter parameter : parameters) { + String description = Optional.ofNullable(parameter.getDescription()).orElse(""); + if (parameter.getExample() != null) { + description = (description.isBlank() ? "" : description.trim() + " ") + "Example: `" + parameter.getExample() + "`"; + } else if (parameter.getExamples() != null && !parameter.getExamples().isEmpty()) { + final List<String> examples = new ArrayList<>(); + parameter.getExamples().values().forEach(example -> examples.add("`" + example.getValue() + "`" + (example.getDescription() == null || example.getDescription().isBlank() ? "" : " (" + example.getDescription().trim() + ")"))); + description = (description.isBlank() ? "" : description.trim() + " ") + "Examples: " + String.join(", ", examples); + } + final Object defaultValue = parameter.getSchema() != null ? parameter.getSchema().getDefault() : null; + out.append("| ").append(parameter.getName()) + .append(" | ").append(parameter.getIn()) + .append(" | ").append(Boolean.TRUE.equals(parameter.getRequired()) ? "yes" : "no") + .append(" | ").append(cell(description)) + .append(" | ").append(defaultValue == null ? "" : "`" + cell(defaultValue.toString()) + "`") + .append(" |\n"); + } + out.append("\n"); + } + + final RequestBody requestBody = operation.getRequestBody(); + if (requestBody != null) { + out.append("**Request body**"); + out.append(Boolean.TRUE.equals(requestBody.getRequired()) ? " (required)" : " (optional)"); + out.append(": ").append(describeContent(requestBody.getContent())); + if (requestBody.getDescription() != null && !requestBody.getDescription().isBlank()) { + out.append(" - ").append(requestBody.getDescription().trim()); + } + out.append("\n\n"); + appendExamples(out, requestBody.getContent()); + } + + if (operation.getResponses() != null) { + final Map<String, ApiResponse> responses = new TreeMap<>(operation.getResponses()); + responses.entrySet().removeIf(e -> Objects.equals(GENERIC_RESPONSES.get(e.getKey()), e.getValue().getDescription())); + if (!responses.isEmpty()) { + out.append("**Responses**\n\n"); + out.append("| Status | Description | Response body |\n"); + out.append("|--------|-------------|---------------|\n"); + responses.forEach((status, response) -> out.append("| ").append(status) + .append(" | ").append(cell(Optional.ofNullable(response.getDescription()).orElse(""))) + .append(" | ").append(response.getContent() == null || response.getContent().isEmpty() ? "" : describeContent(response.getContent())) + .append(" |\n")); + out.append("\n"); + } + } + } + + /** Appends example XML and JSON representations of content, if the content is an entity of the plugin. */ + void appendExamples(final StringBuilder out, final Content content) throws Exception + { + if (content == null) { + return; + } + + // Use examples that are explicitly defined for the content, when available. + final Map<String, String> explicitExamples = new LinkedHashMap<>(); + content.forEach((mediaType, value) -> { + if (value.getExample() != null) { + explicitExamples.put(mediaType, value.getExample().toString()); + } else if (value.getExamples() != null && !value.getExamples().isEmpty()) { + explicitExamples.put(mediaType, String.valueOf(value.getExamples().values().iterator().next().getValue())); + } + }); + if (!explicitExamples.isEmpty()) { + out.append("<details>\n<summary>Example request body</summary>\n\n"); + explicitExamples.forEach((mediaType, example) -> { + final String language = mediaType.endsWith("xml") ? "xml" : mediaType.endsWith("json") ? "json" : ""; + out.append("`Content-Type: ").append(mediaType).append("`:\n\n```").append(language).append("\n").append(example.strip()).append("\n```\n\n"); + }); + out.append("</details>\n\n"); + return; + } + + final Schema<?> schema = content.values().stream().map(MediaType::getSchema).filter(Objects::nonNull).findFirst().orElse(null); + if (schema == null || schema.get$ref() == null) { + return; + } + final Class<?> entityClass = findEntityClass(refName(schema.get$ref())); + if (entityClass == null || Modifier.isAbstract(entityClass.getModifiers())) { + return; + } + + // Convert the example into an instance of the entity class, then serialize that instance like the plugin does. + final Object entity = mapper.treeToValue(example(schema, 0), entityClass); + + out.append("<details>\n<summary>Example request bodies</summary>\n\n"); + if (content.containsKey("application/xml")) { + final Marshaller marshaller = JAXBContext.newInstance(entityClass).createMarshaller(); + marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true); + final StringWriter xml = new StringWriter(); + marshaller.marshal(entity, xml); + out.append("XML (`Content-Type: application/xml`):\n\n```xml\n").append(xml.toString().strip()).append("\n```\n\n"); + } + if (content.containsKey("application/json")) { + out.append("JSON (`Content-Type: application/json`):\n\n```json\n").append(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(entity).strip()).append("\n```\n\n"); + } + out.append("</details>\n\n"); + } + + /** Builds an example JSON value for a schema, based on the examples that are defined in the specification. */ + JsonNode example(final Schema<?> schema, final int depth) + { + if (depth > 10) { + throw new IllegalStateException("Schema nesting is too deep (recursive schema?)"); + } + if (schema.get$ref() != null) { + return example(resolve(schema.get$ref()), depth + 1); + } + if (schema.getExample() != null) { + return mapper.valueToTree(schema.getExample()); + } + if ("array".equals(schema.getType()) && schema.getItems() != null) { + final ArrayNode array = JsonNodeFactory.instance.arrayNode(); + array.add(example(schema.getItems(), depth + 1)); + return array; + } + if (schema.getProperties() != null) { + final ObjectNode object = JsonNodeFactory.instance.objectNode(); + schema.getProperties().forEach((name, property) -> object.set(name, example(property, depth + 1))); + return object; + } + if ("date-time".equals(schema.getFormat())) { + return JsonNodeFactory.instance.textNode(EXAMPLE_DATE_TIME); + } + if (schema.getEnum() != null && !schema.getEnum().isEmpty()) { + return mapper.valueToTree(schema.getEnum().get(0)); + } + return switch (Optional.ofNullable(schema.getType()).orElse("")) { + case "integer", "number" -> JsonNodeFactory.instance.numberNode(0); + case "boolean" -> JsonNodeFactory.instance.booleanNode(false); + default -> JsonNodeFactory.instance.textNode("string"); + }; + } + + /** + * Verifies that the names of the properties in the specification are the names that are used in JSON. These can + * differ, as JSON serialization uses JAXB annotations when there are no Jackson annotations, which the generator of + * the specification does not do. The difference is fixed by adding a Jackson annotation (like + * {@code @JsonProperty}) that uses the name that is used in JSON. + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + void verifyPropertyNames() + { + final List<String> problems = new ArrayList<>(); + final Map<String, Schema> schemas = new TreeMap<>(Optional.ofNullable(openAPI.getComponents().getSchemas()).orElse(Map.of())); + schemas.forEach((name, schema) -> { + final Class<?> entityClass = findEntityClass(name); + if (entityClass == null || schema.getProperties() == null) { + return; + } + final Set<String> jsonNames = mapper.getSerializationConfig().introspect(mapper.constructType(entityClass)).findProperties().stream() + .filter(BeanPropertyDefinition::couldSerialize) + .map(BeanPropertyDefinition::getName) + .collect(Collectors.toSet()); + final Set<String> specNames = new TreeSet<String>(schema.getProperties().keySet()); + specNames.removeAll(jsonNames); + if (!specNames.isEmpty()) { + problems.add(name + ": " + specNames + " (names used in JSON: " + new TreeSet<>(jsonNames) + ")"); + } + }); + if (!problems.isEmpty()) { + throw new IllegalStateException("The OpenAPI specification uses property names that are not used in JSON. Add a @JsonProperty annotation with the name that is used in JSON to:\n" + String.join("\n", problems)); + } + } + + static Class<?> findEntityClass(final String name) + { + for (final String pkg : ENTITY_PACKAGES) { + try { + return Class.forName(pkg + "." + name); + } catch (ClassNotFoundException e) { + // Try the next package. + } + } + return null; + } + + // --------------------------------------------------------------------------------------------------------------- + // Data types + // --------------------------------------------------------------------------------------------------------------- + + @SuppressWarnings({"rawtypes", "unchecked"}) + String generateDataTypes() + { + final StringBuilder out = new StringBuilder(); + out.append("## Data types\n\n"); + out.append("These are the data types that are used in the request and response bodies of the endpoints. The name of a field is the name that is used in JSON. When XML uses a different name, it is mentioned in the description of the field.\n\n"); + out.append("Date/time values are represented as an ISO-8601 formatted text in XML (for example: `" + EXAMPLE_DATE_TIME + "`), and as the number of milliseconds since the Unix epoch in JSON (for example: `1769862896789`). In JSON request bodies, the ISO-8601 format can also be used.\n"); + + final Map<String, Schema> schemas = new TreeMap<>(Optional.ofNullable(openAPI.getComponents().getSchemas()).orElse(Map.of())); + schemas.forEach((name, schema) -> { + out.append("\n### ").append(name).append("\n\n"); + if (schema.getDescription() != null && !schema.getDescription().isBlank()) { + out.append(schema.getDescription().trim()).append("\n\n"); + } + if (schema.getXml() != null && schema.getXml().getName() != null) { + out.append("XML root element: `<").append(schema.getXml().getName()).append(">`\n\n"); + } + final Map<String, Schema> properties = schema.getProperties(); + if (properties == null || properties.isEmpty()) { + return; + } + final List<String> required = Optional.ofNullable((List<String>) schema.getRequired()).orElse(List.of()); + out.append("| Field | Type | Required | Description |\n"); + out.append("|-------|------|----------|-------------|\n"); + properties.forEach((propertyName, property) -> out.append("| ").append(propertyName) + .append(" | ").append(describeSchema(property)) + .append(" | ").append(required.contains(propertyName) ? "yes" : "no") + .append(" | ").append(cell(describeProperty(propertyName, property))) + .append(" |\n")); + }); + return out.toString(); + } + + /** Describes a property of a data type, including its XML representation, allowed values and example. */ + @SuppressWarnings("rawtypes") + static String describeProperty(final String propertyName, final Schema property) + { + final List<String> parts = new ArrayList<>(); + if (property.getDescription() != null && !property.getDescription().isBlank()) { + parts.add(sentence(property.getDescription())); + } + final List<?> allowed = property.getEnum() != null ? property.getEnum() : (property.getItems() != null ? property.getItems().getEnum() : null); + if (allowed != null && !allowed.isEmpty()) { + parts.add("Allowed values: " + allowed.stream().map(v -> "`" + v + "`").collect(Collectors.joining(", ")) + "."); + } + final String xml = describeXml(propertyName, property); + if (xml != null) { + parts.add(xml); + } + final Object example = property.getExample() != null ? property.getExample() : (property.getItems() != null ? property.getItems().getExample() : null); + if (example != null) { + parts.add("Example: `" + example + "`"); + } + return String.join(" ", parts); + } + + /** Describes how a property is represented in XML, when that differs from its (JSON) name. */ + @SuppressWarnings("rawtypes") + static String describeXml(final String propertyName, final Schema property) + { + final String xmlName = property.getXml() != null && property.getXml().getName() != null ? property.getXml().getName() : propertyName; + if (property.getXml() != null && Boolean.TRUE.equals(property.getXml().getAttribute())) { + return "In XML, this is the `" + xmlName + "` attribute."; + } + if ("array".equals(property.getType()) && property.getItems() != null) { + final Schema items = property.getItems(); + final String itemName = items.getXml() != null && items.getXml().getName() != null ? items.getXml().getName() : null; + if (property.getXml() != null && Boolean.TRUE.equals(property.getXml().getWrapped())) { + return itemName == null ? "In XML, the items are wrapped in the `<" + xmlName + ">` element." : "In XML, items are represented as `<" + itemName + ">` elements, wrapped in the `<" + xmlName + ">` element."; + } + return "In XML, items are represented as `<" + xmlName + ">` elements."; + } + return xmlName.equals(propertyName) ? null : "In XML, this is the `<" + xmlName + ">` element."; + } + + /** Returns text as a sentence that ends with a punctuation mark. */ + static String sentence(final String text) + { + final String trimmed = text.trim(); + return trimmed.matches(".*[.!?:]$") ? trimmed : trimmed + "."; + } + + // --------------------------------------------------------------------------------------------------------------- + // Utilities + // --------------------------------------------------------------------------------------------------------------- + + Schema<?> resolve(final String ref) + { + final Schema<?> schema = openAPI.getComponents().getSchemas().get(refName(ref)); + if (schema == null) { + throw new IllegalStateException("Unable to resolve schema reference: " + ref); + } + return schema; + } + + static String refName(final String ref) + { + return ref.substring(ref.lastIndexOf('/') + 1); + } + + /** Describes the data type of content, for example "`UserEntity` (XML or JSON)". */ + static String describeContent(final Content content) + { + if (content == null || content.isEmpty()) { + return "none"; + } + final List<String> formats = new ArrayList<>(); + String type = null; + for (final Map.Entry<String, MediaType> entry : content.entrySet()) { + final String format = switch (entry.getKey()) { + case "application/xml" -> "XML"; + case "application/json" -> "JSON"; + case "text/plain" -> "plain text"; + case "*/*" -> null; + default -> entry.getKey(); + }; + if (format != null) { + formats.add(format); + } + if (type == null && entry.getValue().getSchema() != null) { + type = describeSchema(entry.getValue().getSchema()); + } + } + final StringBuilder result = new StringBuilder(type == null ? "unspecified" : type); + if (!formats.isEmpty()) { + result.append(" (").append(String.join(" or ", formats)).append(")"); + } + return result.toString(); + } + + /** Describes the type of a schema, linking to the documentation of data types. */ + @SuppressWarnings("rawtypes") + static String describeSchema(final Schema schema) + { + if (schema.get$ref() != null) { + final String name = refName(schema.get$ref()); + return "[" + name + "](#" + name.toLowerCase(Locale.ROOT) + ")"; + } + if ("array".equals(schema.getType()) && schema.getItems() != null) { + return "array of " + describeSchema(schema.getItems()); + } + if (schema.getFormat() != null && "date-time".equals(schema.getFormat())) { + return "date-time"; + } + return schema.getType() == null ? "unspecified" : schema.getType(); + } + + /** Returns the path without the API prefix, for example "users/{username}" for "/restapi/v1/users/{username}". */ + static String relativePath(final String path) + { + return path.replaceFirst("^/restapi/v\\d+/", ""); + } + + /** Returns the name of the resource that is addressed by a path, for example "users" for "/restapi/v1/users/{username}". */ + static String resource(final String path) + { + return relativePath(path).split("/")[0]; + } + + /** Returns the number of path segments of a path, excluding the API prefix. */ + static int depth(final String path) + { + return relativePath(path).split("/").length; + } + + /** Makes text safe for use in a markdown table cell. */ + static String cell(final String text) + { + return text.trim().replace("|", "\\|").replaceAll("\\s*\\R\\s*", "<br>"); + } +} diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/AdminEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/AdminEntities.java index 44620a9aa..df9d880f0 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/AdminEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/AdminEntities.java @@ -17,12 +17,15 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "admins") +@Schema(description = "A list of entities that have an admin affiliation with a multi-user chat room.") public class AdminEntities extends AffiliatedEntities { List<String> admins; @@ -36,6 +39,7 @@ public AdminEntities(List<String> admins) { @XmlElement(name = "admin") @JsonProperty(value = "admins") + @ArraySchema(arraySchema = @Schema(description = "The JIDs (or names of local users) of the entities."), schema = @Schema(example = "jane@example.org")) public List<String> getAdmins() { return admins; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/AffiliatedEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/AffiliatedEntities.java index 282bad741..1d406554b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/AffiliatedEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/AffiliatedEntities.java @@ -15,6 +15,8 @@ */ package org.jivesoftware.openfire.plugin.rest.entity; +import io.swagger.v3.oas.annotations.media.Schema; + /** * A base class for pre-existing classes that each represent a collection of MUC-room affiliated users of a specific * type. @@ -24,6 +26,7 @@ * * @author Guus der Kinderen, guus@goodbytes.nl */ +@Schema(description = "A list of entities that have a particular affiliation with a multi-user chat room. Depending on the affiliation, this is an AdminEntities, MemberEntities, OutcastEntities or OwnerEntities value.") public abstract class AffiliatedEntities { /** diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntities.java index 60f9d7c15..f6a0309d2 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntities.java @@ -16,6 +16,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.annotation.Nonnull; import javax.xml.bind.annotation.XmlElement; @@ -23,6 +25,7 @@ import java.util.List; @XmlRootElement(name = "clusterNodes") +@Schema(description = "A list of the nodes in an Openfire cluster.") public class ClusterNodeEntities { private List<ClusterNodeEntity> clusterNodeEntities; @@ -35,6 +38,7 @@ public ClusterNodeEntities(@Nonnull final List<ClusterNodeEntity> clusterNodeEnt @XmlElement(name = "clusterNode") @JsonProperty(value = "clusterNodes") + @ArraySchema(arraySchema = @Schema(description = "The nodes of the cluster.")) public List<ClusterNodeEntity> getClusterNodeEntities() { return clusterNodeEntities; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntity.java index 8bb3ccccf..c149ba8f9 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusterNodeEntity.java @@ -15,6 +15,7 @@ */ package org.jivesoftware.openfire.plugin.rest.entity; +import io.swagger.v3.oas.annotations.media.Schema; import org.jivesoftware.openfire.cluster.ClusterNodeInfo; import org.jivesoftware.openfire.cluster.NodeID; @@ -24,6 +25,7 @@ import java.util.Date; @XmlRootElement(name = "clusterNode") +@Schema(description = "A node in an Openfire cluster.") public class ClusterNodeEntity { private String hostName; @@ -49,6 +51,7 @@ public ClusterNodeEntity(String hostName, NodeID nodeID, long joinedTime, boolea } @XmlElement + @Schema(description = "The host name and IP address of the server on which this cluster node is running.", example = "xmpp1.example.org (192.168.0.10)") public String getHostName() { return hostName; } @@ -58,6 +61,7 @@ public void setHostName(String hostName) { } @XmlElement + @Schema(description = "The unique identifier of this cluster node.", example = "a3f1c2d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d") public String getNodeID() { return nodeID; } @@ -67,6 +71,7 @@ public void setNodeID(String nodeID) { } @XmlElement + @Schema(description = "The moment at which this node joined the cluster.") public Date getJoinedTime() { return joinedTime; } @@ -76,6 +81,7 @@ public void setJoinedTime(Date joinedTime) { } @XmlElement + @Schema(description = "Whether this node currently is the senior member of the cluster.", example = "true") public boolean isSeniorMember() { return seniorMember; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusteringEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusteringEntity.java index 778d52019..91b9b6f39 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusteringEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ClusteringEntity.java @@ -15,10 +15,14 @@ */ 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; @XmlRootElement(name = "clustering") +@Schema(description = "The clustering status of an Openfire instance.") public class ClusteringEntity { String status; @@ -30,6 +34,7 @@ public ClusteringEntity(String status){ } @XmlElement() + @Schema(description = "The clustering status of this Openfire instance.", allowableValues = {"SENIOR AND ONLY MEMBER", "Senior member", "Junior member", "Starting up", "Disabled"}, example = "Senior member") public String getStatus(){ return status; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java index d7f86bc3c..356e01a1d 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntities.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -28,6 +30,7 @@ * The Class GroupEntities. */ @XmlRootElement(name = "groups") +@Schema(description = "A list of Openfire user groups.") public class GroupEntities { /** The groups. */ @@ -55,6 +58,7 @@ public GroupEntities(List<GroupEntity> groups) { */ @XmlElement(name = "group") @JsonProperty(value = "groups") + @ArraySchema(arraySchema = @Schema(description = "The groups.")) public List<GroupEntity> getGroups() { return groups; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java index 277e3448f..6fcf04c92 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/GroupEntity.java @@ -33,6 +33,7 @@ */ @XmlRootElement(name = "group") @XmlType(propOrder = { "name", "description", "admins", "members", "shared" }) +@Schema(description = "An Openfire user group.") public class GroupEntity { /** The name. */ @@ -75,7 +76,7 @@ public GroupEntity(String name, String description) { * @return the name */ @XmlElement - @Schema(description = "Name of the group", example = "UserGroup1") + @Schema(description = "The name of the group. When updating a group, this must be equal to the group name in the path of the request.", example = "UserGroup1", requiredMode = Schema.RequiredMode.REQUIRED) public String getName() { return name; } @@ -96,7 +97,7 @@ public void setName(String name) { * @return the description */ @XmlElement - @Schema(description = "Description of the group", example = "My group of users") + @Schema(description = "The description of the group.", example = "My group of users") public String getDescription() { return description; } @@ -119,7 +120,7 @@ public void setDescription(String description) { @XmlElementWrapper(name = "admins") @XmlElement(name = "admin") @JsonProperty(value = "admins") - @ArraySchema(schema = @Schema(example = "jane.smith"), arraySchema = @Schema(description = "List of admins of the group")) + @ArraySchema(schema = @Schema(example = "jane.smith"), arraySchema = @Schema(description = "The admins of the group. When creating or updating a group, each admin can be identified by a username or a JID. Responses contain (bare) JIDs.")) public List<String> getAdmins() { return admins; } @@ -132,7 +133,7 @@ public List<String> getAdmins() { @XmlElementWrapper(name = "members") @XmlElement(name = "member") @JsonProperty(value = "members") - @ArraySchema(schema = @Schema(example = "john.jones"), arraySchema = @Schema(description = "List of members of the group")) + @ArraySchema(schema = @Schema(example = "john.jones"), arraySchema = @Schema(description = "The members of the group. When creating or updating a group, each member can be identified by a username or a JID. Responses contain (bare) JIDs.")) public List<String> getMembers() { return members; } @@ -162,7 +163,7 @@ public void setMembers(List<String> members) { * @return whether it's a shared group */ @XmlElement(name = "shared") - @Schema(description = "Whether the group should automatically appear in the rosters of the users", example = "false") + @Schema(description = "Whether the group is shared: whether it automatically appears in the rosters of its members.", example = "false") public Boolean getShared(){ return shared; } /** diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java index 907daa31c..8dde69535 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationEntity.java @@ -16,12 +16,14 @@ 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; @XmlRootElement(name = "mucInvitation") +@Schema(description = "An invitation to join a multi-user chat room.") public class MUCInvitationEntity { String reason; @@ -30,7 +32,7 @@ public MUCInvitationEntity() { } @XmlElement - @Schema(description = "The reason that will be included in the invitation message(s)", example = "Come join this cool room please!") + @Schema(description = "The reason that is included in the invitation message(s).", example = "Come join this cool room please!") public String getReason() { return reason; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationsEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationsEntity.java index d0d3a7b9e..a92e6449b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationsEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCInvitationsEntity.java @@ -17,6 +17,7 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; @@ -26,6 +27,7 @@ import java.util.List; @XmlRootElement(name = "mucInvitations") +@Schema(description = "An invitation for a collection of users and/or groups to join a multi-user chat room.") public class MUCInvitationsEntity extends MUCInvitationEntity { public MUCInvitationsEntity() { @@ -37,7 +39,7 @@ public MUCInvitationsEntity() { @XmlElementWrapper(name = "jidsToInvite") @XmlElement(name = "jid") @JsonProperty(value = "jidsToInvite") - @Schema(description = "The JIDs and/or names of the users and groups to invite into the room") + @ArraySchema(arraySchema = @Schema(description = "The users and/or groups to invite into the room, each identified by the JID of a user or group, or by the name of a local user or group."), schema = @Schema(example = "john@example.org")) public List<String> getJidsToInvite() { if (jidsToInvite == null) { jidsToInvite = new ArrayList<>(); diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java index c3d00ed62..01ffa16a6 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntities.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -24,6 +26,7 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "chatRooms") +@Schema(description = "A list of multi-user chat rooms.") public class MUCRoomEntities { List<MUCRoomEntity> mucRooms; @@ -36,6 +39,7 @@ public MUCRoomEntities(List<MUCRoomEntity> mucRooms) { @XmlElement(name = "chatRoom") @JsonProperty(value = "chatRooms") + @ArraySchema(arraySchema = @Schema(description = "The chat rooms.")) public List<MUCRoomEntity> getMucRooms() { return mucRooms; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java index e846ac042..40cf8e1ff 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomEntity.java @@ -17,6 +17,7 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import org.xmpp.packet.JID; @@ -34,6 +35,7 @@ "canOccupantsChangeSubject", "canOccupantsInvite", "canChangeNickname", "logEnabled", "loginRestrictedToNickname", "membersOnly", "moderated", "broadcastPresenceRoles", "owners", "admins", "members", "outcasts", "ownerGroups", "adminGroups", "memberGroups", "outcastGroups", "allowPM" }) +@Schema(description = "A multi-user chat room. When a room is created or updated, boolean values that are not provided are treated as 'false'.") public class MUCRoomEntity { private String roomName; @@ -84,6 +86,7 @@ public MUCRoomEntity(String naturalName, String roomName, String description) { } @XmlElement + @Schema(description = "The human-readable name of the room, as shown to users that discover rooms on the chat service.", example = "Global Chat") public String getNaturalName() { return naturalName; } @@ -93,6 +96,7 @@ public void setNaturalName(String naturalName) { } @XmlElement + @Schema(description = "The name of the room, which is used as the local part of the room's JID. It is converted to lowercase. When updating a room, this must be equal to the room name in the path of the request.", example = "global", requiredMode = Schema.RequiredMode.REQUIRED) public String getRoomName() { return roomName; } @@ -102,6 +106,7 @@ public void setRoomName(String roomName) { } @XmlElement + @Schema(description = "The description of the room.", example = "A room for everyone") public String getDescription() { return description; } @@ -111,6 +116,7 @@ public void setDescription(String description) { } @XmlElement + @Schema(description = "The password that users must provide to enter the room.", example = "s3cr3t") public String getPassword() { return password; } @@ -120,6 +126,7 @@ public void setPassword(String password) { } @XmlElement + @Schema(description = "The subject (topic) of the room.", example = "Welcome!") public String getSubject() { return subject; } @@ -129,6 +136,7 @@ public void setSubject(String subject) { } @XmlElement + @Schema(description = "The maximum number of occupants that can be in the room at the same time. 0 means unlimited.", example = "30") public int getMaxUsers() { return maxUsers; } @@ -138,6 +146,7 @@ public void setMaxUsers(int maxUsers) { } @XmlElement + @Schema(description = "The moment at which the room was created. When creating a room without this value, the current time is used.") public Date getCreationDate() { return creationDate; } @@ -147,6 +156,7 @@ public void setCreationDate(Date creationDate) { } @XmlElement + @Schema(description = "The moment at which the configuration of the room was last modified. When creating or updating a room without this value, the current time is used.") public Date getModificationDate() { return modificationDate; } @@ -156,6 +166,7 @@ public void setModificationDate(Date modificationDate) { } @XmlElement + @Schema(description = "Whether the room is persistent. Persistent rooms are saved to the database, and are not destroyed when the last occupant leaves.", example = "true") public boolean isPersistent() { return persistent; } @@ -165,6 +176,7 @@ public void setPersistent(boolean persistent) { } @XmlElement + @Schema(description = "Whether the room is public: searchable and visible through service discovery.", example = "true") public boolean isPublicRoom() { return publicRoom; } @@ -174,6 +186,7 @@ public void setPublicRoom(boolean publicRoom) { } @XmlElement + @Schema(description = "Whether users are allowed to register with the room.", example = "false") public boolean isRegistrationEnabled() { return registrationEnabled; } @@ -183,6 +196,7 @@ public void setRegistrationEnabled(boolean registrationEnabled) { } @XmlElement + @Schema(description = "Whether the real JID of every occupant is visible to every other occupant (a non-anonymous room).", example = "false") public boolean isCanAnyoneDiscoverJID() { return canAnyoneDiscoverJID; } @@ -192,6 +206,7 @@ public void setCanAnyoneDiscoverJID(boolean canAnyoneDiscoverJID) { } @XmlElement + @Schema(description = "Whether participants are allowed to change the subject of the room.", example = "false") public boolean isCanOccupantsChangeSubject() { return canOccupantsChangeSubject; } @@ -201,6 +216,7 @@ public void setCanOccupantsChangeSubject(boolean canOccupantsChangeSubject) { } @XmlElement + @Schema(description = "Whether occupants can invite other users to the room. When the room is not members-only, anyone can send invitations regardless of this value. When the room is members-only and this is 'false', only owners and admins can send invitations.", example = "false") public boolean isCanOccupantsInvite() { return canOccupantsInvite; } @@ -214,6 +230,7 @@ public void setBroadcastPresenceRoles(List<String> broadcastPresenceRoles) { } @XmlElement + @Schema(description = "Whether occupants are allowed to change their nickname in the room.", example = "true") public boolean isCanChangeNickname() { return canChangeNickname; } @@ -223,6 +240,7 @@ public void setCanChangeNickname(boolean canChangeNickname) { } @XmlElement + @Schema(description = "Whether the conversation in the room is logged (saved to the database).", example = "true") public boolean isLogEnabled() { return logEnabled; } @@ -232,6 +250,7 @@ public void setLogEnabled(boolean logEnabled) { } @XmlElement + @Schema(description = "Whether registered users can only join the room using their registered nickname.", example = "false") public boolean isLoginRestrictedToNickname() { return loginRestrictedToNickname; } @@ -241,6 +260,7 @@ public void setLoginRestrictedToNickname(boolean loginRestrictedToNickname) { } @XmlElement + @Schema(description = "Whether the room is members-only: users need to be a member (or be invited) to enter.", example = "false") public boolean isMembersOnly() { return membersOnly; } @@ -250,6 +270,7 @@ public void setMembersOnly(boolean membersOnly) { } @XmlElement + @Schema(description = "Whether the room is moderated: only occupants with 'voice' can send messages to all occupants.", example = "false") public boolean isModerated() { return moderated; } @@ -271,6 +292,7 @@ public void setAllowPM(String allowPM) { @XmlElement(name = "broadcastPresenceRole") @XmlElementWrapper(name = "broadcastPresenceRoles") @JsonProperty(value = "broadcastPresenceRoles") + @ArraySchema(arraySchema = @Schema(description = "The roles of occupants of which presence is broadcast to the other occupants. Each is one of: 'moderator', 'participant', 'visitor'."), schema = @Schema(example = "moderator")) public List<String> getBroadcastPresenceRoles() { return broadcastPresenceRoles; } @@ -278,6 +300,7 @@ public List<String> getBroadcastPresenceRoles() { @XmlElementWrapper(name = "owners") @XmlElement(name = "owner") @JsonProperty(value = "owners") + @ArraySchema(arraySchema = @Schema(description = "The (bare) JIDs of the users that have an owner affiliation with the room. When creating a room without owners, the 'admin' user is made owner."), schema = @Schema(example = "admin@example.org")) public List<String> getOwners() { return owners; } @@ -285,6 +308,7 @@ public List<String> getOwners() { @XmlElementWrapper(name = "ownerGroups") @XmlElement(name = "ownerGroup") @JsonProperty(value = "ownerGroups") + @ArraySchema(arraySchema = @Schema(description = "The names of the user groups that have an owner affiliation with the room."), schema = @Schema(example = "Management")) public List<String> getOwnerGroups() { return ownerGroups; } @@ -300,6 +324,7 @@ public void setOwnerGroups(List<String> ownerGroups) { @XmlElementWrapper(name = "members") @XmlElement(name = "member") @JsonProperty(value = "members") + @ArraySchema(arraySchema = @Schema(description = "The (bare) JIDs of the users that have a member affiliation with the room."), schema = @Schema(example = "john@example.org")) public List<String> getMembers() { return members; } @@ -307,6 +332,7 @@ public List<String> getMembers() { @XmlElementWrapper(name = "memberGroups") @XmlElement(name = "memberGroup") @JsonProperty(value = "memberGroups") + @ArraySchema(arraySchema = @Schema(description = "The names of the user groups that have a member affiliation with the room."), schema = @Schema(example = "Sales")) public List<String> getMemberGroups() { return memberGroups; } @@ -322,6 +348,7 @@ public void setMemberGroups(List<String> memberGroups) { @XmlElementWrapper(name = "outcasts") @XmlElement(name = "outcast") @JsonProperty(value = "outcasts") + @ArraySchema(arraySchema = @Schema(description = "The (bare) JIDs of the users that have an outcast affiliation with the room: users that are banned from the room."), schema = @Schema(example = "spammer@example.org")) public List<String> getOutcasts() { return outcasts; } @@ -329,6 +356,7 @@ public List<String> getOutcasts() { @XmlElementWrapper(name = "outcastGroups") @XmlElement(name = "outcastGroup") @JsonProperty(value = "outcastGroups") + @ArraySchema(arraySchema = @Schema(description = "The names of the user groups that have an outcast affiliation with the room."), schema = @Schema(example = "Banned")) public List<String> getOutcastGroups() { return outcastGroups; } @@ -344,6 +372,7 @@ public void setOutcastGroups(List<String> outcastGroups) { @XmlElementWrapper(name = "admins") @XmlElement(name = "admin") @JsonProperty(value = "admins") + @ArraySchema(arraySchema = @Schema(description = "The (bare) JIDs of the users that have an admin affiliation with the room."), schema = @Schema(example = "jane@example.org")) public List<String> getAdmins() { return admins; } @@ -351,6 +380,7 @@ public List<String> getAdmins() { @XmlElementWrapper(name = "adminGroups") @XmlElement(name = "adminGroup") @JsonProperty(value = "adminGroups") + @ArraySchema(arraySchema = @Schema(description = "The names of the user groups that have an admin affiliation with the room."), schema = @Schema(example = "Moderators")) public List<String> getAdminGroups() { return adminGroups; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntities.java index abbbaaf4d..e1269dea3 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntities.java @@ -16,11 +16,16 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "messages") +@Schema(description = "A list of messages from the history of a multi-user chat room.") public class MUCRoomMessageEntities { List<MUCRoomMessageEntity> messages; @@ -32,6 +37,8 @@ public MUCRoomMessageEntities(List<MUCRoomMessageEntity> messages) { } @XmlElement(name = "message") + @JsonProperty(value = "message") + @ArraySchema(arraySchema = @Schema(description = "The messages.")) public List<MUCRoomMessageEntity> getMessages() { return messages; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntity.java index d108381c0..49404d354 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCRoomMessageEntity.java @@ -16,6 +16,9 @@ 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.XmlRootElement; import javax.xml.bind.annotation.XmlType; @@ -24,6 +27,7 @@ //xmlns="jabber:x:event"><composing/></x></message> @XmlRootElement(name = "message") @XmlType(propOrder = { "to", "from", "type", "body", "delayStamp", "delayFrom"}) +@Schema(description = "A message from the history of a multi-user chat room.") public class MUCRoomMessageEntity { String to; String from; @@ -33,6 +37,7 @@ public class MUCRoomMessageEntity { String delayFrom; @XmlElement + @Schema(description = "The JID of the addressee of the message.", example = "global@conference.example.org") public String getTo() { return to; } @@ -41,6 +46,7 @@ public void setTo(String to) { } @XmlElement + @Schema(description = "The JID of the sender of the message: the room JID, followed by the nickname of the occupant.", example = "global@conference.example.org/john") public String getFrom() { return from; } @@ -49,6 +55,7 @@ public void setFrom(String from) { } @XmlElement + @Schema(description = "The XMPP message type.", example = "groupchat") public String getType() { return type; } @@ -57,12 +64,15 @@ public void setType(String type) { } @XmlElement(name="delay_stamp") + @JsonProperty(value = "delay_stamp") + @Schema(description = "The moment at which the message was originally sent (XEP-0203 delayed delivery timestamp).", example = "2026-01-31T12:34:56.789Z") public String getDelayStamp() { return delayStamp; } public void setDelayStamp(String delayStamp) { this.delayStamp = delayStamp; } @XmlElement + @Schema(description = "The text of the message.", example = "Hello, everyone!") public String getBody() { return body; } @@ -71,6 +81,8 @@ public void setBody(String body) { } @XmlElement(name="delay_from") + @JsonProperty(value = "delay_from") + @Schema(description = "The JID of the entity that delayed the delivery of the message (XEP-0203).", example = "global@conference.example.org") public String getDelayFrom() { return delayFrom; } public void setDelayFrom(String delayFrom) { this.delayFrom = delayFrom; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntities.java index 4accdf871..250dab04e 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntities.java @@ -16,11 +16,16 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "chatServices") +@Schema(description = "A list of multi-user chat services.") public class MUCServiceEntities { List<MUCServiceEntity> services; @@ -32,6 +37,8 @@ public MUCServiceEntities(List<MUCServiceEntity> services) { } @XmlElement(name = "chatService") + @JsonProperty(value = "chatService") + @ArraySchema(arraySchema = @Schema(description = "The chat services.")) public List<MUCServiceEntity> getServices() { return services; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntity.java index e52d008fb..8e4535aeb 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MUCServiceEntity.java @@ -16,6 +16,7 @@ package org.jivesoftware.openfire.plugin.rest.entity; + import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; @@ -24,6 +25,7 @@ @XmlRootElement(name = "chatService") @XmlType(propOrder = { "serviceName", "description", "hidden" }) +@Schema(description = "A multi-user chat service.") public class MUCServiceEntity { private String serviceName; @@ -44,7 +46,7 @@ public MUCServiceEntity(String serviceName, String description, boolean hidden) } @XmlElement - @Schema(description = "The name of the chat service", example = "conference") + @Schema(description = "The name of the chat service, which is used as the subdomain of the service.", example = "conference", requiredMode = Schema.RequiredMode.REQUIRED) public String getServiceName() { return serviceName; } @@ -54,7 +56,7 @@ public void setServiceName(String serviceName) { } @XmlElement - @Schema(description = "The description of the chat service", example = "A public service") + @Schema(description = "The description of the chat service.", example = "A public service") public String getDescription() { return description; } @@ -64,7 +66,7 @@ public void setDescription(String description) { } @XmlElement - @Schema(description = "Whether the service is hidden", example = "false") + @Schema(description = "Whether the service is hidden from service discovery.", example = "false") public boolean isHidden() { return hidden; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MemberEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MemberEntities.java index 3a4b0f950..320141c97 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MemberEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MemberEntities.java @@ -17,12 +17,15 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "members") +@Schema(description = "A list of entities that have a member affiliation with a multi-user chat room.") public class MemberEntities extends AffiliatedEntities { List<String> members; @@ -36,6 +39,7 @@ public MemberEntities(List<String> members) { @XmlElement(name = "member") @JsonProperty(value = "members") + @ArraySchema(arraySchema = @Schema(description = "The JIDs (or names of local users) of the entities."), schema = @Schema(example = "john@example.org")) public List<String> getMembers() { return members; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java index 3458cc1af..98aac948f 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MessageEntity.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; @@ -23,6 +25,7 @@ * The Class MessageEntity. */ @XmlRootElement(name = "message") +@Schema(description = "A message.") public class MessageEntity { /** The body. */ @@ -40,6 +43,7 @@ public MessageEntity() { * @return the body */ @XmlElement + @Schema(description = "The text of the message.", example = "The server will be restarted in 5 minutes.", requiredMode = Schema.RequiredMode.REQUIRED) public String getBody() { return body; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MsgArchiveEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MsgArchiveEntity.java index f9d98b9f1..8f3b7999c 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/MsgArchiveEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/MsgArchiveEntity.java @@ -16,22 +16,25 @@ 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; /** * The Class MsgArchiveEntity. */ @XmlRootElement(name = "archive") +@XmlType(propOrder = { "jid", "count" }) +@Schema(description = "The number of unread messages of a user.") public class MsgArchiveEntity { - @XmlElement String jid; /** * unread messages count */ - @XmlElement int count; public MsgArchiveEntity() { @@ -42,4 +45,15 @@ public MsgArchiveEntity(String jid, int count) { this.count = count; } + @XmlElement + @Schema(description = "The JID of the user.", example = "john@example.org") + public String getJid() { + return jid; + } + + @XmlElement + @Schema(description = "The number of unread messages.", example = "3") + public int getCount() { + return count; + } } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java index 3d4b9b978..c61cd16bf 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntities.java @@ -17,12 +17,15 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "occupants") +@Schema(description = "A list of occupants of a multi-user chat room.") public class OccupantEntities { List<OccupantEntity> occupants; @@ -35,6 +38,7 @@ public OccupantEntities(List<OccupantEntity> occupants) { @XmlElement(name = "occupant") @JsonProperty(value = "occupants") + @ArraySchema(arraySchema = @Schema(description = "The occupants.")) public List<OccupantEntity> getOccupants() { return occupants; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntity.java index e546c74f1..d302ef31d 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OccupantEntity.java @@ -16,10 +16,13 @@ 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; @XmlRootElement(name = "occupant") +@Schema(description = "An occupant of a multi-user chat room.") public class OccupantEntity { private String jid; @@ -31,6 +34,7 @@ public OccupantEntity() { } @XmlElement + @Schema(description = "The occupant JID: the room JID, followed by the nickname of the occupant.", example = "global@conference.example.org/john") public String getJid() { return jid; } @@ -40,6 +44,7 @@ public void setJid(String jid) { } @XmlElement + @Schema(description = "The role of the occupant in the room. One of: 'moderator', 'participant', 'visitor', 'none'.", example = "participant") public String getRole() { return role; } @@ -49,6 +54,7 @@ public void setRole(String role) { } @XmlElement + @Schema(description = "The affiliation of the occupant with the room. One of: 'owner', 'admin', 'member', 'outcast', 'none'.", example = "member") public String getAffiliation() { return affiliation; } @@ -58,6 +64,7 @@ public void setAffiliation(String affiliation) { } @XmlElement + @Schema(description = "The real (full) JID of the user.", example = "john@example.org/laptop") public String getUserAddress() { return userAddress; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OutcastEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OutcastEntities.java index b22e06332..0b965683b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OutcastEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OutcastEntities.java @@ -17,12 +17,15 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "outcasts") +@Schema(description = "A list of entities that have an outcast affiliation with a multi-user chat room: entities that are banned from the room.") public class OutcastEntities extends AffiliatedEntities { List<String> outcasts; @@ -36,6 +39,7 @@ public OutcastEntities(List<String> outcasts) { @XmlElement(name = "outcast") @JsonProperty(value = "outcasts") + @ArraySchema(arraySchema = @Schema(description = "The JIDs (or names of local users) of the entities."), schema = @Schema(example = "spammer@example.org")) public List<String> getOutcasts() { return outcasts; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OwnerEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OwnerEntities.java index 5dc72f467..ca3cd2bad 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/OwnerEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/OwnerEntities.java @@ -17,12 +17,15 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import java.util.List; @XmlRootElement(name = "owners") +@Schema(description = "A list of entities that have an owner affiliation with a multi-user chat room.") public class OwnerEntities extends AffiliatedEntities { List<String> owners; @@ -36,6 +39,7 @@ public OwnerEntities(List<String> owners) { @XmlElement(name = "owner") @JsonProperty(value = "owners") + @ArraySchema(arraySchema = @Schema(description = "The JIDs (or names of local users) of the entities."), schema = @Schema(example = "admin@example.org")) public List<String> getOwners() { return owners; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java index 6003a4df3..1255d7fb6 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntities.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -24,6 +26,7 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "participants") +@Schema(description = "A list of occupants of a multi-user chat room.") public class ParticipantEntities { List<ParticipantEntity> participants; @@ -36,6 +39,7 @@ public ParticipantEntities(List<ParticipantEntity> participants) { @XmlElement(name = "participant") @JsonProperty(value = "participants") + @ArraySchema(arraySchema = @Schema(description = "The occupants.")) public List<ParticipantEntity> getParticipants() { return participants; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntity.java index cdcd29751..6fda36a9b 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/ParticipantEntity.java @@ -16,10 +16,13 @@ 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; @XmlRootElement(name = "participant") +@Schema(description = "An occupant of a multi-user chat room.") public class ParticipantEntity { private String jid; @@ -30,6 +33,7 @@ public ParticipantEntity() { } @XmlElement + @Schema(description = "The occupant JID: the room JID, followed by the nickname of the occupant.", example = "global@conference.example.org/john") public String getJid() { return jid; } @@ -39,6 +43,7 @@ public void setJid(String jid) { } @XmlElement + @Schema(description = "The role of the occupant in the room. One of: 'moderator', 'participant', 'visitor', 'none'.", example = "participant") public String getRole() { return role; } @@ -48,6 +53,7 @@ public void setRole(String role) { } @XmlElement + @Schema(description = "The affiliation of the occupant with the room. One of: 'owner', 'admin', 'member', 'outcast', 'none'.", example = "member") public String getAffiliation() { return affiliation; } 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 db18e0662..659711012 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntities.java @@ -17,6 +17,7 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Schema; import javax.xml.bind.annotation.XmlElement; @@ -28,6 +29,7 @@ @XmlRootElement(name = "results") @XmlType(propOrder = { "successResults", "failureResults", "otherResults" }) +@Schema(description = "The results of the creation of multiple multi-user chat rooms, grouped by result type.") public class RoomCreationResultEntities { List<RoomCreationResultEntity> successResults; List<RoomCreationResultEntity> failureResults; @@ -66,7 +68,7 @@ public void addResult(RoomCreationResultEntity resultToAdd) { @XmlElement(name = "result") @XmlElementWrapper(name = "success") @JsonProperty(value = "success") - @Schema(description = "All creation results of type success") + @ArraySchema(arraySchema = @Schema(description = "The results of the rooms that were created successfully.")) public List<RoomCreationResultEntity> getSuccessResults() { return successResults; } @@ -74,7 +76,7 @@ public List<RoomCreationResultEntity> getSuccessResults() { @XmlElement(name = "result") @XmlElementWrapper(name = "failure") @JsonProperty(value = "failure") - @Schema(description = "All creation results of type failure") + @ArraySchema(arraySchema = @Schema(description = "The results of the rooms that could not be created.")) public List<RoomCreationResultEntity> getFailureResults() { return failureResults; } @@ -82,7 +84,7 @@ public List<RoomCreationResultEntity> getFailureResults() { @XmlElement(name = "result") @XmlElementWrapper(name = "other") @JsonProperty(value = "other") - @Schema(description = "All creation results of a type other than success or failure") + @ArraySchema(arraySchema = @Schema(description = "The results of a type other than success or failure.")) public List<RoomCreationResultEntity> getOtherResults() { return otherResults; } 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 b421d0c03..1a4346b38 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RoomCreationResultEntity.java @@ -26,6 +26,7 @@ @XmlRootElement(name = "result") @XmlType(propOrder = { "roomName", "resultType", "message"}) +@Schema(description = "The result of the creation of one multi-user chat room.") public class RoomCreationResultEntity { public enum RoomCreationResultType { @@ -37,7 +38,7 @@ public enum RoomCreationResultType { String message; @XmlElement - @Schema(description = "The name of the room that was to be created", example = "open_chat") + @Schema(description = "The name of the room that was to be created.", example = "open_chat") public String getRoomName() { return roomName; } @@ -47,7 +48,7 @@ public void setRoomName(String roomName) { } @XmlElement - @Schema(description = "The result of creating the room", example = "Failure") + @Schema(description = "The result of creating the room.", example = "Failure") public RoomCreationResultType getResultType() { return resultType; } @@ -57,7 +58,7 @@ public void setResultType(RoomCreationResultType resultType) { } @XmlElement - @Schema(description = "A message describing the result", example = "Room already existed and therefore not created again") + @Schema(description = "A message that describes the result.", example = "Room already existed and therefore not created again") public String getMessage() { return message; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterEntities.java index 42f516d09..1f4292f1f 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterEntities.java @@ -16,6 +16,10 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.List; import javax.xml.bind.annotation.XmlElement; @@ -25,6 +29,7 @@ * The Class RosterEntities. */ @XmlRootElement(name = "roster") +@Schema(description = "The roster (contact list) of a user.") public class RosterEntities { /** The roster. */ @@ -53,6 +58,8 @@ public RosterEntities(List<RosterItemEntity> roster) { * @return the roster */ @XmlElement(name = "rosterItem") + @JsonProperty(value = "rosterItem") + @ArraySchema(arraySchema = @Schema(description = "The entries of the roster.")) public List<RosterItemEntity> getRoster() { return roster; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterItemEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterItemEntity.java index a2606cfac..8d50f562c 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterItemEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/RosterItemEntity.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -30,6 +32,7 @@ */ @XmlRootElement(name = "rosterItem") @XmlType(propOrder = { "jid", "nickname", "subscriptionType", "groups" }) +@Schema(description = "An entry in the roster (contact list) of a user.") public class RosterItemEntity { /** The jid. */ @@ -73,6 +76,7 @@ public RosterItemEntity(String jid, String nickname, int subscriptionType) { * @return the jid */ @XmlElement + @Schema(description = "The JID of the contact.", example = "jane@example.org", requiredMode = Schema.RequiredMode.REQUIRED) public String getJid() { return jid; } @@ -93,6 +97,7 @@ public void setJid(String jid) { * @return the nickname */ @XmlElement + @Schema(description = "The name of the contact, as shown in this roster.", example = "Jane") public String getNickname() { return nickname; } @@ -113,6 +118,7 @@ public void setNickname(String nickname) { * @return the subscription type */ @XmlElement + @Schema(description = "The presence subscription state of the contact. One of: -1 (remove), 0 (none), 1 (to: the user receives presence updates of the contact), 2 (from: the contact receives presence updates of the user), 3 (both).", example = "3") public int getSubscriptionType() { return subscriptionType; } @@ -135,6 +141,7 @@ public void setSubscriptionType(int subscriptionType) { @XmlElement(name = "group") @XmlElementWrapper(name = "groups") @JsonProperty(value = "groups") + @ArraySchema(arraySchema = @Schema(description = "The roster groups (for example 'Friends' or 'Co-workers') that this contact is organized under."), schema = @Schema(example = "Friends")) public List<String> getGroups() { return groups; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLog.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLog.java index 24bfc8b75..b6e696477 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLog.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLog.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; @@ -23,6 +25,7 @@ * The Class SecurityAuditLog. */ @XmlRootElement(name = "log") +@Schema(description = "An entry of the security audit log.") public class SecurityAuditLog { /** The log id. */ @@ -74,6 +77,7 @@ public SecurityAuditLog(long logId, String username, long timestamp, String summ * @return the log id */ @XmlElement + @Schema(description = "The unique identifier of the log entry.", example = "42") public long getLogId() { return logId; } @@ -93,6 +97,7 @@ public void setLogId(long logId) { * @return the username */ @XmlElement + @Schema(description = "The username of the user that performed the audited action.", example = "admin") public String getUsername() { return username; } @@ -112,6 +117,7 @@ public void setUsername(String username) { * @return the timestamp */ @XmlElement + @Schema(description = "The moment at which the audited action occurred, in seconds since the Unix epoch.", example = "1769862896") public long getTimestamp() { return timestamp; } @@ -131,6 +137,7 @@ public void setTimestamp(long timestamp) { * @return the summary */ @XmlElement + @Schema(description = "A short description of the audited action.", example = "Created new user john") public String getSummary() { return summary; } @@ -150,6 +157,7 @@ public void setSummary(String summary) { * @return the node */ @XmlElement + @Schema(description = "The node that triggered the audited action, usually a host name or IP address.", example = "xmpp1.example.org") public String getNode() { return node; } @@ -169,6 +177,7 @@ public void setNode(String node) { * @return the details */ @XmlElement + @Schema(description = "Detailed information about the audited action.", example = "name = John Doe, email = john@example.org") public String getDetails() { return details; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java index ac415f85f..23a0187e3 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SecurityAuditLogs.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -24,6 +26,7 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "logs") +@Schema(description = "A list of entries of the security audit log.") public class SecurityAuditLogs { List<SecurityAuditLog> securityAuditLog; @@ -36,6 +39,7 @@ public SecurityAuditLogs(List<SecurityAuditLog> securityAuditLog) { @XmlElement(name = "log") @JsonProperty(value = "logs") + @ArraySchema(arraySchema = @Schema(description = "The log entries.")) public List<SecurityAuditLog> getSecurityAuditLog() { return securityAuditLog; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java index 1d6699523..6f8107adf 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntities.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -24,6 +26,7 @@ import javax.xml.bind.annotation.XmlRootElement; @XmlRootElement(name = "sessions") +@Schema(description = "A list of client sessions.") public class SessionEntities { List<SessionEntity> sessions; @@ -36,6 +39,7 @@ public SessionEntities(List<SessionEntity> sessions) { @XmlElement(name = "session") @JsonProperty(value = "sessions") + @ArraySchema(arraySchema = @Schema(description = "The sessions.")) public List<SessionEntity> getSessions() { return sessions; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntity.java index e0497dc54..2db501b8a 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionEntity.java @@ -16,6 +16,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.Date; import javax.xml.bind.annotation.XmlElement; @@ -25,6 +27,7 @@ @XmlRootElement(name = "session") @XmlType(propOrder = { "sessionId", "username", "resource", "node", "sessionStatus", "presenceStatus", "presenceMessage", "priority", "hostAddress", "hostName", "creationDate", "lastActionDate", "secure" }) +@Schema(description = "A client session.") public class SessionEntity { private String sessionId; @@ -47,6 +50,7 @@ public SessionEntity() { } @XmlElement + @Schema(description = "The (full) JID of the session.", example = "john@example.org/laptop") public String getSessionId() { return sessionId; } @@ -56,6 +60,7 @@ public void setSessionId(String sessionId) { } @XmlElement + @Schema(description = "The username of the user of the session, or 'Anonymous' for anonymous sessions.", example = "john") public String getUsername() { return username; } @@ -65,6 +70,7 @@ public void setUsername(String username) { } @XmlElement + @Schema(description = "The resource part of the JID of the session.", example = "laptop") public String getResource() { return resource; } @@ -74,6 +80,7 @@ public void setResource(String resource) { } @XmlElement + @Schema(description = "Whether the session is connected to the cluster node that processes the request ('Local'), or to another cluster node ('Remote').", example = "Local") public String getNode() { return node; } @@ -83,6 +90,7 @@ public void setNode(String node) { } @XmlElement + @Schema(description = "The status of the session. One of: 'Closed', 'Connected', 'Authenticated', 'Unknown'.", example = "Authenticated") public String getSessionStatus() { return sessionStatus; } @@ -92,6 +100,7 @@ public void setSessionStatus(String sessionStatus) { } @XmlElement + @Schema(description = "The availability of the user of the session. One of: 'Online', 'Away', 'Available to Chat', 'Do Not Disturb', 'Extended Away', 'Unknown/Not Recognized'.", example = "Online") public String getPresenceStatus() { return presenceStatus; } @@ -100,6 +109,7 @@ public void setPresenceStatus(String presenceStatus) { this.presenceStatus = presenceStatus; } + @Schema(description = "The (optional) natural-language description of the availability of the user of the session.", example = "In a meeting") public String getPresenceMessage() { return presenceMessage; } @@ -109,6 +119,7 @@ public void setPresenceMessage(String presenceMessage) { } @XmlElement + @Schema(description = "The presence priority of the session, from -128 to 127.", example = "0") public int getPriority() { return priority; } @@ -118,6 +129,7 @@ public void setPriority(int priority) { } @XmlElement + @Schema(description = "The IP address of the client.", example = "192.168.0.20") public String getHostAddress() { return hostAddress; } @@ -127,6 +139,7 @@ public void setHostAddress(String hostAddress) { } @XmlElement + @Schema(description = "The host name of the client.", example = "laptop.example.org") public String getHostName() { return hostName; } @@ -136,6 +149,7 @@ public void setHostName(String hostName) { } @XmlElement + @Schema(description = "The moment at which the session was created.") public Date getCreationDate() { return creationDate; } @@ -145,6 +159,7 @@ public void setCreationDate(Date creationDate) { } @XmlElement + @Schema(description = "The moment at which the session last had activity.") public Date getLastActionDate() { return lastActionDate; } @@ -154,6 +169,7 @@ public void setLastActionDate(Date lastActionDate) { } @XmlElement + @Schema(description = "Whether the connection of the session is encrypted.", example = "true") public boolean isSecure() { return secure; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionsCount.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionsCount.java index ee8f489bf..0619a535a 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionsCount.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SessionsCount.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; @@ -23,6 +25,7 @@ * The Class SessionsCount. */ @XmlRootElement(name = "sessions") +@Schema(description = "The number of client sessions.") public class SessionsCount { /** The local sessions. */ @@ -55,6 +58,7 @@ public SessionsCount(int localSessions, int clusterSessions) { * @return the local sessions */ @XmlElement() + @Schema(description = "The number of authenticated client sessions (of both anonymous and non-anonymous users) on the cluster node that processes the request.", example = "12") public int getLocalSessions() { return localSessions; } @@ -74,6 +78,7 @@ public void setLocalSessions(int localSessions) { * @return the cluster sessions */ @XmlElement() + @Schema(description = "The number of authenticated client sessions (of both anonymous and non-anonymous users) in the entire cluster.", example = "30") public int getClusterSessions() { return clusterSessions; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperties.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperties.java index e8ca969f0..7ea50bcd2 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperties.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperties.java @@ -16,6 +16,10 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; + import java.util.List; import javax.xml.bind.annotation.XmlElement; @@ -25,6 +29,7 @@ * The Class SystemProperties. */ @XmlRootElement(name = "properties") +@Schema(description = "A list of Openfire system properties.") public class SystemProperties { /** The properties. */ @@ -43,6 +48,8 @@ public SystemProperties() { * @return the properties */ @XmlElement(name = "property") + @JsonProperty(value = "property") + @ArraySchema(arraySchema = @Schema(description = "The system properties.")) public List<SystemProperty> getProperties() { return properties; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperty.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperty.java index 6e80ba055..1dcf52304 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperty.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/SystemProperty.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.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; @@ -23,6 +25,7 @@ * The Class SystemProperty. */ @XmlRootElement(name = "property") +@Schema(description = "An Openfire system property.") public class SystemProperty { /** The key. */ @@ -55,6 +58,7 @@ public SystemProperty(String key, String value) { * @return the key */ @XmlAttribute + @Schema(description = "The name of the system property.", example = "xmpp.domain", requiredMode = Schema.RequiredMode.REQUIRED) public String getKey() { return key; } @@ -75,6 +79,7 @@ public void setKey(String key) { * @return the value */ @XmlAttribute + @Schema(description = "The value of the system property.", example = "example.org", requiredMode = Schema.RequiredMode.REQUIRED) public String getValue() { return value; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java index dba05f60c..a5d92d856 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntities.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -27,6 +29,7 @@ * The Class UserEntities. */ @XmlRootElement(name = "users") +@Schema(description = "A list of Openfire users.") public class UserEntities { /** The users. */ @@ -56,6 +59,7 @@ public UserEntities(List<UserEntity> users) { */ @XmlElement(name = "user") @JsonProperty(value = "users") + @ArraySchema(arraySchema = @Schema(description = "The users.")) public List<UserEntity> getUsers() { return users; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntity.java index 223257889..ce6505862 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserEntity.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -30,6 +32,7 @@ */ @XmlRootElement(name = "user") @XmlType(propOrder = { "username", "name", "email", "password", "properties" }) +@Schema(description = "An Openfire user.") public class UserEntity { /** The username. */ @@ -76,6 +79,7 @@ public UserEntity(String username, String name, String email) { * @return the username */ @XmlElement + @Schema(description = "The username of the user. Required when creating a user. When updating a user, providing a different username renames the user.", example = "john") public String getUsername() { return username; } @@ -96,6 +100,7 @@ public void setUsername(String username) { * @return the name */ @XmlElement + @Schema(description = "The name of the user.", example = "John Doe") public String getName() { return name; } @@ -116,6 +121,7 @@ public void setName(String name) { * @return the email */ @XmlElement + @Schema(description = "The email address of the user.", example = "john@example.org") public String getEmail() { return email; } @@ -135,6 +141,7 @@ public void setEmail(String email) { * * @return the password */ + @Schema(description = "The password of the user. Required when creating a user. Never included in responses.", example = "s3cr3t") public String getPassword() { return password; } @@ -157,6 +164,7 @@ public void setPassword(String password) { @XmlElement(name = "property") @XmlElementWrapper(name = "properties") @JsonProperty(value = "properties") + @ArraySchema(arraySchema = @Schema(description = "Custom properties of the user. Property keys are unique per user. When updating a user, all existing properties of the user are replaced by the provided properties: omitting this removes all properties of the user.")) public List<UserProperty> getProperties() { return properties; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserGroupsEntity.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserGroupsEntity.java index f507c70bf..8e304326e 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserGroupsEntity.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserGroupsEntity.java @@ -17,6 +17,8 @@ package org.jivesoftware.openfire.plugin.rest.entity; import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; @@ -28,6 +30,7 @@ * The Class UserGroupsEntity. */ @XmlRootElement(name = "groups") +@Schema(description = "A list of names of Openfire user groups.") public class UserGroupsEntity { /** The group names. */ @@ -57,6 +60,7 @@ public UserGroupsEntity(List<String> groupNames) { */ @XmlElement(name = "groupname") @JsonProperty(value = "groupnames") + @ArraySchema(arraySchema = @Schema(description = "The names of the groups."), schema = @Schema(example = "Sales")) public List<String> getGroupNames() { return groupNames; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserProperty.java b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserProperty.java index ceb48aa7c..bf5dcbcab 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserProperty.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/entity/UserProperty.java @@ -16,11 +16,14 @@ package org.jivesoftware.openfire.plugin.rest.entity; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlAttribute; /** * The Class UserProperty. */ +@Schema(description = "A custom property of an Openfire user.") public class UserProperty { /** The key. */ @@ -55,6 +58,7 @@ public UserProperty(String key, String value) { * @return the key */ @XmlAttribute + @Schema(description = "The key (name) of the property. Unique per user.", example = "department", requiredMode = Schema.RequiredMode.REQUIRED) public String getKey() { return key; } @@ -75,6 +79,7 @@ public void setKey(String key) { * @return the value */ @XmlAttribute + @Schema(description = "The value of the property.", example = "Sales", requiredMode = Schema.RequiredMode.REQUIRED) public String getValue() { return value; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/exceptions/ErrorResponse.java b/src/java/org/jivesoftware/openfire/plugin/rest/exceptions/ErrorResponse.java index 4e8f2b1fa..e467cfd24 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/exceptions/ErrorResponse.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/exceptions/ErrorResponse.java @@ -16,6 +16,8 @@ package org.jivesoftware.openfire.plugin.rest.exceptions; +import io.swagger.v3.oas.annotations.media.Schema; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -23,6 +25,7 @@ * The Class ErrorResponse. */ @XmlRootElement(name = "error") +@Schema(description = "A description of an error that occurred while processing a request.") public class ErrorResponse { /** The resource. */ @@ -43,6 +46,7 @@ public class ErrorResponse { * @return the resource */ @XmlElement(name = "resource") + @Schema(description = "The resource (for example, a username or room name) that the error relates to.", example = "john") public String getResource() { return resource; } @@ -62,6 +66,7 @@ public void setResource(String resource) { * @return the message */ @XmlElement(name = "message") + @Schema(description = "A description of the error.", example = "Could not get user") public String getMessage() { return message; } @@ -81,6 +86,7 @@ public void setMessage(String message) { * @return the exception */ @XmlElement(name = "exception") + @Schema(description = "The type of the error.", example = "UserNotFoundException") public String getException() { return exception; } @@ -100,6 +106,7 @@ public void setException(String exception) { * @return the exception stack */ @XmlElement(name = "exceptionStack") + @Schema(hidden = true) public String getExceptionStack() { return exceptionStack; } diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/ClusteringService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/ClusteringService.java index 54a72d63d..833d10aa8 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/ClusteringService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/ClusteringService.java @@ -53,7 +53,7 @@ public void init() { @GET @Path("/status") @Operation( summary = "Get clustering status", - description = "Describes the point-in-time state of Openfire's clustering with other servers.", + description = "Describes the point-in-time state of Openfire's clustering with other servers. The status is one of: 'SENIOR AND ONLY MEMBER', 'Senior member', 'Junior member', 'Starting up' or 'Disabled'.", responses = { @ApiResponse(responseCode = "200", description = "Status returned.", content = @Content(schema = @Schema(implementation = ClusteringEntity.class))), @ApiResponse(responseCode = "401", description = "Web service authentication failed."), diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAffiliationsService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAffiliationsService.java index 4fff80d7e..13dc81e5a 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAffiliationsService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomAffiliationsService.java @@ -170,7 +170,7 @@ public Response addMUCRoomAffiliations( }) public Response addMUCRoomAffiliation( @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 affiliated.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The entity that is to be affiliated: a (bare) JID, or the name of a local user.", example = "john@example.org", required = true) @PathParam("jid") String jid, @Parameter(description = "The type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'.", example = "members", required = true) @PathParam("affiliation") String affiliations, @Parameter(description = "The name of the MUC room to which an affiliation is to be added.", example = "lobby", required = true) @PathParam("roomName") String roomName, @Parameter(description = "Whether to send invitations to newly affiliated users.", example = "true", required = false) @DefaultValue("false") @QueryParam("sendInvitations") boolean sendInvitations) @@ -232,7 +232,7 @@ public Response addMUCRoomAffiliationGroup( @ApiResponse(responseCode = "500", description = "Unexpected, generic error condition.", content = @Content(schema = @Schema(implementation = ErrorResponse.class))) }) public Response deleteMUCRoomAffiliation( - @Parameter(description = "The (bare) JID of the entity for which the room affiliation is to be removed.", example = "john@example.org", required = true) @PathParam("jid") String jid, + @Parameter(description = "The entity for which the room affiliation is to be removed: a (bare) JID, or the name of a local user.", 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 type of affiliation. One of: 'admins', 'members', 'outcasts', 'owners'.", example = "members", required = true) @PathParam("affiliation") String affiliations, @Parameter(description = "The name of the MUC room from which an affiliation is to be removed.", example = "lobby", required = true) @PathParam("roomName") String roomName) 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 fdef79aab..9892247fe 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/MUCRoomService.java @@ -242,7 +242,7 @@ public MUCRoomMessageEntities getMUCRoomHistory( @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response inviteUserOrGroupToMUCRoom( @Parameter(description = "The name of the chat room to which to invite a user or group.", 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 entity to invite into the room: the JID of a user or group, or the name of a local user or group. When a group is invited, all of its members are invited.", 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 and whom to send it to.", required = true) MUCInvitationEntity mucInvitationEntity) throws ServiceException @@ -260,7 +260,7 @@ public Response inviteUserOrGroupToMUCRoom( @POST @Path("/{roomName}/invite") @Operation( summary = "Invite a collection of users and/or groups", - description = "Invites a collection of users and/or groups to join a specific multi-user chat room.", + description = "Invites a collection of users and/or groups to join a specific multi-user chat room. Each entity can be identified by the JID of a user or group, or by the name of a local user or group. When a group is invited, all of its members are invited.", responses = { @ApiResponse(responseCode = "200", description = "Invitation sent."), @ApiResponse(responseCode = "401", description = "Web service authentication failed."), diff --git a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserVCardService.java b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserVCardService.java index e8cfa4687..fdac4c8ae 100644 --- a/src/java/org/jivesoftware/openfire/plugin/rest/service/UserVCardService.java +++ b/src/java/org/jivesoftware/openfire/plugin/rest/service/UserVCardService.java @@ -19,6 +19,7 @@ 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; @@ -84,7 +85,22 @@ public String getUserVcard( @Consumes({MediaType.APPLICATION_XML}) public Response setUserVcard( @Parameter(description = "The username of the user for which to update the vCard.", required = true) @PathParam("username") String username, - @RequestBody(description = "The updated definition of the vCard.", required = true) String vCard) + @RequestBody(description = "The updated definition of the vCard, in the vcard-temp format of XEP-0054.", required = true, + content = @Content(mediaType = MediaType.APPLICATION_XML, schema = @Schema(type = "string"), examples = @ExampleObject(value = + "<vCard xmlns=\"vcard-temp\">\n" + + " <FN>Janice Francis Doe</FN>\n" + + " <N>\n" + + " <FAMILY>Doe</FAMILY>\n" + + " <GIVEN>Janice</GIVEN>\n" + + " <MIDDLE>Francis</MIDDLE>\n" + + " </N>\n" + + " <NICKNAME>Jane</NICKNAME>\n" + + " <EMAIL>\n" + + " <INTERNET/>\n" + + " <PREF/>\n" + + " <USERID>j.doe@example.org</USERID>\n" + + " </EMAIL>\n" + + "</vCard>"))) String vCard) throws ServiceException { plugin.setUserVCard(username, vCard); diff --git a/src/readme/footer.html b/src/readme/footer.html new file mode 100644 index 000000000..f7ae9d4eb --- /dev/null +++ b/src/readme/footer.html @@ -0,0 +1,70 @@ +</main> +<script> + (function () { + var headings = document.querySelectorAll('main h1, main h2, main h3, main h4, main h5, main h6'); + + // Make heading IDs unique, using the same suffix scheme that GitHub uses when rendering readme.md. + var seen = {}; + headings.forEach(function (heading) { + var anchor = heading.querySelector('a[id]'); + if (!anchor) { + return; + } + var id = anchor.id; + if (seen.hasOwnProperty(id)) { + seen[id]++; + anchor.id = id + '-' + seen[id]; + anchor.setAttribute('href', '#' + anchor.id); + } else { + seen[id] = 0; + } + }); + + // Build a table of contents from the top two heading levels. + var toc = document.getElementById('toc'); + var root = document.createElement('ul'); + var current = null; + headings.forEach(function (heading) { + var anchor = heading.querySelector('a[id]'); + if (!anchor || (heading.tagName !== 'H1' && heading.tagName !== 'H2')) { + return; + } + var item = document.createElement('li'); + var link = document.createElement('a'); + link.href = '#' + anchor.id; + link.textContent = heading.textContent.trim(); + item.appendChild(link); + if (heading.tagName === 'H1' || !current) { + root.appendChild(item); + current = item; + } else { + var list = current.querySelector('ul'); + if (!list) { + list = document.createElement('ul'); + current.appendChild(list); + } + list.appendChild(item); + } + }); + if (root.children.length > 0) { + // Collapsible on narrow screens, where the table of contents is shown above the content instead of beside it. + var details = document.createElement('details'); + var summary = document.createElement('summary'); + summary.textContent = 'Contents'; + details.appendChild(summary); + details.appendChild(root); + details.open = window.matchMedia('(min-width: 1100px)').matches; + toc.appendChild(details); + } + + // Scroll to the requested section now that all IDs are final. + if (location.hash) { + var target = document.getElementById(decodeURIComponent(location.hash.substring(1))); + if (target) { + target.scrollIntoView(); + } + } + })(); +</script> +</body> +</html> diff --git a/src/readme/header.html b/src/readme/header.html new file mode 100644 index 000000000..a6a6713b2 --- /dev/null +++ b/src/readme/header.html @@ -0,0 +1,95 @@ +<!DOCTYPE html> +<!-- + This file is generated from readme.md during the Maven build. Do not edit it directly: edit readme.md instead. + The header and footer templates are in src/readme/. +--> +<html lang="en"> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>titleToken + + + + + +