mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-11-16 10:46:14 +01:00
feat: GET /chats/:roomId/users [breaking]
This commit is contained in:
@@ -140,6 +140,8 @@ paths:
|
|||||||
$ref: 'write/chats.yaml'
|
$ref: 'write/chats.yaml'
|
||||||
/chats/{roomId}:
|
/chats/{roomId}:
|
||||||
$ref: 'write/chats/roomId.yaml'
|
$ref: 'write/chats/roomId.yaml'
|
||||||
|
/chats/{roomId}/users:
|
||||||
|
$ref: 'write/chats/roomId/users.yaml'
|
||||||
/chats/{roomId}/{mid}:
|
/chats/{roomId}/{mid}:
|
||||||
$ref: 'write/chats/roomId/mid.yaml'
|
$ref: 'write/chats/roomId/mid.yaml'
|
||||||
/flags/:
|
/flags/:
|
||||||
|
|||||||
61
public/openapi/write/chats/roomId/users.yaml
Normal file
61
public/openapi/write/chats/roomId/users.yaml
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- chats
|
||||||
|
summary: get chat room users
|
||||||
|
description: This operation retrieves the users in a chat room message
|
||||||
|
parameters:
|
||||||
|
- in: path
|
||||||
|
name: roomId
|
||||||
|
schema:
|
||||||
|
type: number
|
||||||
|
required: true
|
||||||
|
description: a valid chat room id
|
||||||
|
example: 1
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Users successfully retrieved
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
status:
|
||||||
|
$ref: ../../../components/schemas/Status.yaml#/Status
|
||||||
|
response:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
users:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
uid:
|
||||||
|
type: number
|
||||||
|
description: A user identifier
|
||||||
|
username:
|
||||||
|
type: string
|
||||||
|
description: A friendly name for a given user account
|
||||||
|
picture:
|
||||||
|
nullable: true
|
||||||
|
type: string
|
||||||
|
status:
|
||||||
|
type: string
|
||||||
|
displayname:
|
||||||
|
type: string
|
||||||
|
description: This is either username or fullname depending on forum and user settings
|
||||||
|
icon:text:
|
||||||
|
type: string
|
||||||
|
description: A single-letter representation of a username. This is used in the
|
||||||
|
auto-generated icon given to users
|
||||||
|
without an avatar
|
||||||
|
icon:bgColor:
|
||||||
|
type: string
|
||||||
|
description: A six-character hexadecimal colour code assigned to the user. This
|
||||||
|
value is used in conjunction with
|
||||||
|
`icon:text` for the user's
|
||||||
|
auto-generated icon
|
||||||
|
example: "#f44336"
|
||||||
|
isOwner:
|
||||||
|
type: boolean
|
||||||
|
canKick:
|
||||||
|
type: boolean
|
||||||
@@ -308,20 +308,15 @@ define('forum/chats', [
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chats.refreshParticipantsList = function (roomId, modal) {
|
Chats.refreshParticipantsList = function (roomId, modal) {
|
||||||
socket.emit('modules.chats.getUsersInRoom', { roomId: roomId }, function (err, users) {
|
|
||||||
const listEl = modal.find('.list-group');
|
const listEl = modal.find('.list-group');
|
||||||
|
api.get(`/chats/${roomId}/users`, {}).then(({ users }) => {
|
||||||
if (err) {
|
app.parseAndTranslate('partials/modals/manage_room_users', { users }, function (html) {
|
||||||
return translator.translate('[[error:invalid-data]]', function (translated) {
|
|
||||||
listEl.find('li').text(translated);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
app.parseAndTranslate('partials/modals/manage_room_users', {
|
|
||||||
users: users,
|
|
||||||
}, function (html) {
|
|
||||||
listEl.html(html);
|
listEl.html(html);
|
||||||
});
|
});
|
||||||
|
}).catch(() => {
|
||||||
|
translator.translate('[[error:invalid-data]]', function (translated) {
|
||||||
|
listEl.find('li').text(translated);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -72,3 +72,14 @@ chatsAPI.rename = async (caller, data) => {
|
|||||||
roomId: data.roomId,
|
roomId: data.roomId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
chatsAPI.users = async (caller, data) => {
|
||||||
|
const [isOwner, users] = await Promise.all([
|
||||||
|
messaging.isRoomOwner(caller.uid, data.roomId),
|
||||||
|
messaging.getUsersInRoom(data.roomId, 0, -1),
|
||||||
|
]);
|
||||||
|
users.forEach((user) => {
|
||||||
|
user.canKick = (parseInt(user.uid, 10) !== parseInt(caller.uid, 10)) && isOwner;
|
||||||
|
});
|
||||||
|
return { users };
|
||||||
|
};
|
||||||
|
|||||||
@@ -54,7 +54,10 @@ Chats.rename = async (req, res) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Chats.users = async (req, res) => {
|
Chats.users = async (req, res) => {
|
||||||
// ...
|
const users = await api.chats.users(req, {
|
||||||
|
...req.params,
|
||||||
|
});
|
||||||
|
helpers.formatApiResponse(200, res, users);
|
||||||
};
|
};
|
||||||
|
|
||||||
Chats.invite = async (req, res) => {
|
Chats.invite = async (req, res) => {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ module.exports = function () {
|
|||||||
setupApiRoute(router, 'put', '/:roomId', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['name'])], controllers.write.chats.rename);
|
setupApiRoute(router, 'put', '/:roomId', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['name'])], controllers.write.chats.rename);
|
||||||
// no route for room deletion, noted here just in case...
|
// no route for room deletion, noted here just in case...
|
||||||
|
|
||||||
// setupApiRoute(router, 'get', '/:roomId/users', [...middlewares, middleware.assert.room], controllers.write.chats.users);
|
setupApiRoute(router, 'get', '/:roomId/users', [...middlewares, middleware.assert.room], controllers.write.chats.users);
|
||||||
// setupApiRoute(router, 'put', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.invite);
|
// setupApiRoute(router, 'put', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.invite);
|
||||||
// setupApiRoute(router, 'delete', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.kick);
|
// setupApiRoute(router, 'delete', '/:roomId/users', [...middlewares, middleware.assert.room, middleware.checkRequired.bind(null, ['uids'])], controllers.write.chats.kick);
|
||||||
|
|
||||||
|
|||||||
@@ -84,21 +84,17 @@ SocketModules.chats.loadRoom = async function (socket, data) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.getUsersInRoom = async function (socket, data) {
|
SocketModules.chats.getUsersInRoom = async function (socket, data) {
|
||||||
|
sockets.warnDeprecated(socket, 'GET /api/v3/chats/:roomId/user');
|
||||||
|
|
||||||
if (!data || !data.roomId) {
|
if (!data || !data.roomId) {
|
||||||
throw new Error('[[error:invalid-data]]');
|
throw new Error('[[error:invalid-data]]');
|
||||||
}
|
}
|
||||||
const [isUserInRoom, isOwner, userData] = await Promise.all([
|
const isUserInRoom = await Messaging.isUserInRoom(socket.uid, data.roomId);
|
||||||
Messaging.isUserInRoom(socket.uid, data.roomId),
|
|
||||||
Messaging.isRoomOwner(socket.uid, data.roomId),
|
|
||||||
Messaging.getUsersInRoom(data.roomId, 0, -1),
|
|
||||||
]);
|
|
||||||
if (!isUserInRoom) {
|
if (!isUserInRoom) {
|
||||||
throw new Error('[[error:no-privileges]]');
|
throw new Error('[[error:no-privileges]]');
|
||||||
}
|
}
|
||||||
userData.forEach((user) => {
|
|
||||||
user.canKick = (parseInt(user.uid, 10) !== parseInt(socket.uid, 10)) && isOwner;
|
return api.chats.users(socket, data);
|
||||||
});
|
|
||||||
return userData;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
SocketModules.chats.addUserToRoom = async function (socket, data) {
|
SocketModules.chats.addUserToRoom = async function (socket, data) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ describe('Messaging Library', () => {
|
|||||||
const mocks = {
|
const mocks = {
|
||||||
users: {
|
users: {
|
||||||
foo: {}, // the admin
|
foo: {}, // the admin
|
||||||
|
bar: {},
|
||||||
baz: {}, // the user with chat restriction enabled
|
baz: {}, // the user with chat restriction enabled
|
||||||
herp: {},
|
herp: {},
|
||||||
},
|
},
|
||||||
@@ -53,10 +54,12 @@ describe('Messaging Library', () => {
|
|||||||
// Create 3 users: 1 admin, 2 regular
|
// Create 3 users: 1 admin, 2 regular
|
||||||
({
|
({
|
||||||
foo: mocks.users.foo.uid,
|
foo: mocks.users.foo.uid,
|
||||||
|
bar: mocks.users.bar.uid,
|
||||||
baz: mocks.users.baz.uid,
|
baz: mocks.users.baz.uid,
|
||||||
herp: mocks.users.herp.uid,
|
herp: mocks.users.herp.uid,
|
||||||
} = await utils.promiseParallel({
|
} = await utils.promiseParallel({
|
||||||
foo: User.create({ username: 'foo', password: 'barbar' }), // admin
|
foo: User.create({ username: 'foo', password: 'barbar' }), // admin
|
||||||
|
bar: User.create({ username: 'bar', password: 'bazbaz' }), // admin
|
||||||
baz: User.create({ username: 'baz', password: 'quuxquux' }), // restricted user
|
baz: User.create({ username: 'baz', password: 'quuxquux' }), // restricted user
|
||||||
herp: User.create({ username: 'herp', password: 'derpderp' }), // a regular user
|
herp: User.create({ username: 'herp', password: 'derpderp' }), // a regular user
|
||||||
}));
|
}));
|
||||||
@@ -65,6 +68,7 @@ describe('Messaging Library', () => {
|
|||||||
await User.setSetting(mocks.users.baz.uid, 'restrictChat', '1');
|
await User.setSetting(mocks.users.baz.uid, 'restrictChat', '1');
|
||||||
|
|
||||||
({ jar: mocks.users.foo.jar, csrf_token: mocks.users.foo.csrf } = await util.promisify(helpers.loginUser)('foo', 'barbar'));
|
({ jar: mocks.users.foo.jar, csrf_token: mocks.users.foo.csrf } = await util.promisify(helpers.loginUser)('foo', 'barbar'));
|
||||||
|
({ jar: mocks.users.bar.jar, csrf_token: mocks.users.bar.csrf } = await util.promisify(helpers.loginUser)('bar', 'bazbaz'));
|
||||||
({ jar: mocks.users.baz.jar, csrf_token: mocks.users.baz.csrf } = await util.promisify(helpers.loginUser)('baz', 'quuxquux'));
|
({ jar: mocks.users.baz.jar, csrf_token: mocks.users.baz.csrf } = await util.promisify(helpers.loginUser)('baz', 'quuxquux'));
|
||||||
({ jar: mocks.users.herp.jar, csrf_token: mocks.users.herp.csrf } = await util.promisify(helpers.loginUser)('herp', 'derpderp'));
|
({ jar: mocks.users.herp.jar, csrf_token: mocks.users.herp.csrf } = await util.promisify(helpers.loginUser)('herp', 'derpderp'));
|
||||||
|
|
||||||
@@ -190,17 +194,16 @@ describe('Messaging Library', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should get users in room', async () => {
|
it('should get users in room', async () => {
|
||||||
const data = await socketModules.chats.getUsersInRoom({ uid: mocks.users.foo.uid }, { roomId: roomId });
|
const { body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'foo');
|
||||||
assert(Array.isArray(data));
|
assert(Array.isArray(body.response.users));
|
||||||
assert.strictEqual(data.length, 3);
|
assert.strictEqual(body.response.users.length, 3);
|
||||||
|
console.log(body.response.users);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error if user is not in room', async () => {
|
it('should throw error if user is not in room', async () => {
|
||||||
try {
|
const { statusCode, body } = await callv3API('get', `/chats/${roomId}/users`, {}, 'bar');
|
||||||
const data = await socketModules.chats.getUsersInRoom({ uid: 123123123 }, { roomId: roomId });
|
assert.strictEqual(statusCode, 403);
|
||||||
} catch (err) {
|
assert.equal(body.status.message, await translator.translate('[[error:no-privileges]]'));
|
||||||
assert.equal(err.message, '[[error:no-privileges]]');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should fail to add users to room if max is reached', (done) => {
|
it('should fail to add users to room if max is reached', (done) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user