Skip to content

Commit f1b6cdd

Browse files
fix: Refactoring user.info
1 parent e1347e7 commit f1b6cdd

4 files changed

Lines changed: 170 additions & 179 deletions

File tree

apps/meteor/app/api/server/v1/users.ts

Lines changed: 158 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { MeteorError, Team, api, Calendar } from '@rocket.chat/core-services';
2-
import type { IExportOperation, ILoginToken, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings';
2+
import type { IExportOperation, ILoginToken, ISubscription, IPersonalAccessToken, IUser, UserStatus } from '@rocket.chat/core-typings';
33
import { Users, Subscriptions, Sessions } from '@rocket.chat/models';
44
import {
55
isUserCreateParamsPOST,
@@ -425,92 +425,6 @@ API.v1.addRoute(
425425
},
426426
);
427427

428-
const usersInfoEndpoint = API.v1.get(
429-
'users.info',
430-
{
431-
authRequired: true,
432-
query: ajv.compile<
433-
| { userId: string; username?: never; importId?: never; includeUserRooms?: string }
434-
| { username: string; userId?: never; importId?: never; includeUserRooms?: string }
435-
| { importId: string; userId?: never; username?: never; includeUserRooms?: string }
436-
>({
437-
anyOf: [
438-
{
439-
type: 'object',
440-
properties: {
441-
userId: { type: 'string' },
442-
includeUserRooms: { type: 'string' },
443-
},
444-
required: ['userId'],
445-
additionalProperties: false,
446-
},
447-
{
448-
type: 'object',
449-
properties: {
450-
username: { type: 'string' },
451-
includeUserRooms: { type: 'string' },
452-
},
453-
required: ['username'],
454-
additionalProperties: false,
455-
},
456-
{
457-
type: 'object',
458-
properties: {
459-
importId: { type: 'string' },
460-
includeUserRooms: { type: 'string' },
461-
},
462-
required: ['importId'],
463-
additionalProperties: false,
464-
},
465-
],
466-
}),
467-
response: {
468-
400: validateBadRequestErrorResponse,
469-
401: validateUnauthorizedErrorResponse,
470-
200: ajv.compile<{ user: IUser; success: true }>({
471-
type: 'object',
472-
properties: {
473-
user: { type: 'object' },
474-
success: { type: 'boolean', enum: [true] },
475-
},
476-
required: ['user', 'success'],
477-
additionalProperties: false,
478-
}),
479-
},
480-
},
481-
async function action() {
482-
const searchTerms: [string, 'id' | 'username' | 'importId'] | false =
483-
('userId' in this.queryParams && !!this.queryParams.userId && [this.queryParams.userId, 'id']) ||
484-
('username' in this.queryParams && !!this.queryParams.username && [this.queryParams.username, 'username']) ||
485-
('importId' in this.queryParams && !!this.queryParams.importId && [this.queryParams.importId, 'importId']);
486-
487-
if (!searchTerms) {
488-
return API.v1.failure('Invalid search query.');
489-
}
490-
491-
const user = await getFullUserDataByIdOrUsernameOrImportId(this.userId, ...searchTerms);
492-
493-
if (!user) {
494-
return API.v1.failure('User not found.');
495-
}
496-
497-
const myself = user._id === this.userId;
498-
if (this.queryParams.includeUserRooms === 'true' && (myself || (await hasPermissionAsync(this.userId, 'view-other-user-channels')))) {
499-
return API.v1.success({
500-
user: {
501-
...user,
502-
rooms: await Subscriptions.findByUserId(user._id, {
503-
projection: { rid: 1, name: 1, t: 1, roles: 1, unread: 1, federated: 1 },
504-
sort: { t: 1, name: 1 },
505-
}).toArray(),
506-
},
507-
});
508-
}
509-
510-
return API.v1.success({ user });
511-
},
512-
);
513-
514428
API.v1.addRoute(
515429
'users.list',
516430
{
@@ -788,6 +702,69 @@ API.v1.addRoute(
788702
},
789703
);
790704

705+
type UsersInfoParamsGet = ({ userId: string } | { username: string } | { importId: string }) & {
706+
fields?: string;
707+
includeUserRooms?: string;
708+
};
709+
710+
const UsersInfoParamsGetSchema = {
711+
anyOf: [
712+
{
713+
type: 'object',
714+
properties: {
715+
userId: {
716+
type: 'string',
717+
},
718+
includeUserRooms: {
719+
type: 'string',
720+
},
721+
fields: {
722+
type: 'string',
723+
nullable: true,
724+
},
725+
},
726+
required: ['userId'],
727+
additionalProperties: false,
728+
},
729+
{
730+
type: 'object',
731+
properties: {
732+
username: {
733+
type: 'string',
734+
},
735+
includeUserRooms: {
736+
type: 'string',
737+
},
738+
fields: {
739+
type: 'string',
740+
nullable: true,
741+
},
742+
},
743+
required: ['username'],
744+
additionalProperties: false,
745+
},
746+
{
747+
type: 'object',
748+
properties: {
749+
importId: {
750+
type: 'string',
751+
},
752+
includeUserRooms: {
753+
type: 'string',
754+
},
755+
fields: {
756+
type: 'string',
757+
nullable: true,
758+
},
759+
},
760+
required: ['importId'],
761+
additionalProperties: false,
762+
},
763+
],
764+
};
765+
766+
const isUsersInfoParamsGetProps = ajv.compile<UsersInfoParamsGet>(UsersInfoParamsGetSchema);
767+
791768
const usersEndpoints = API.v1
792769
.post(
793770
'users.createToken',
@@ -913,7 +890,99 @@ const usersEndpoints = API.v1
913890

914891
return API.v1.success({ suggestions });
915892
},
916-
);
893+
)
894+
.get(
895+
'users.info',
896+
{
897+
authRequired: true,
898+
query: isUsersInfoParamsGetProps,
899+
response: {
900+
400: validateBadRequestErrorResponse,
901+
401: validateUnauthorizedErrorResponse,
902+
903+
200: ajv.compile<{
904+
user: IUser & { rooms?: Pick<ISubscription, 'rid' | 'name' | 't' | 'roles' | 'unread'> & { federated?: boolean }[] };
905+
success: true;
906+
}>({
907+
type: 'object',
908+
properties: {
909+
user: {
910+
allOf: [
911+
{ $ref: '#/components/schemas/IUser' },
912+
{
913+
type: 'object',
914+
properties: {
915+
rooms: {
916+
type: 'array',
917+
items: {
918+
type: 'object',
919+
properties: {
920+
rid: { type: 'string' },
921+
name: { type: 'string' },
922+
t: { type: 'string' },
923+
roles: { type: 'array', items: { type: 'string' } },
924+
unread: { type: 'number' },
925+
federated: { type: 'boolean' },
926+
},
927+
required: ['rid', 't', 'unread', 'name'],
928+
additionalProperties: false,
929+
},
930+
},
931+
},
932+
additionalProperties: true,
933+
},
934+
],
935+
},
936+
success: { type: 'boolean', enum: [true] },
937+
},
938+
required: ['user', 'success'],
939+
additionalProperties: false,
940+
}),
941+
},
942+
},
943+
async function action() {
944+
const searchTerms: [string, 'id' | 'username' | 'importId'] | false =
945+
('userId' in this.queryParams && !!this.queryParams.userId && [this.queryParams.userId, 'id']) ||
946+
('username' in this.queryParams && !!this.queryParams.username && [this.queryParams.username, 'username']) ||
947+
('importId' in this.queryParams && !!this.queryParams.importId && [this.queryParams.importId, 'importId']);
948+
949+
if (!searchTerms) {
950+
return API.v1.failure('Invalid search query.');
951+
}
952+
953+
const user = await getFullUserDataByIdOrUsernameOrImportId(this.userId, ...searchTerms);
954+
955+
if (!user) {
956+
return API.v1.failure('User not found.');
957+
}
958+
const myself = user._id === this.userId;
959+
if (this.queryParams.includeUserRooms === 'true' && (myself || (await hasPermissionAsync(this.userId, 'view-other-user-channels')))) {
960+
return API.v1.success({
961+
user: {
962+
...user,
963+
rooms: await Subscriptions.findByUserId(user._id, {
964+
projection: {
965+
rid: 1,
966+
name: 1,
967+
t: 1,
968+
roles: 1,
969+
unread: 1,
970+
federated: 1,
971+
},
972+
sort: {
973+
t: 1,
974+
name: 1,
975+
},
976+
}).toArray(),
977+
},
978+
});
979+
}
980+
981+
return API.v1.success({
982+
user,
983+
});
984+
},
985+
);
917986

918987
API.v1.addRoute(
919988
'users.getPreferences',
@@ -1591,8 +1660,7 @@ settings.watch<number>('Rate_Limiter_Limit_RegisterUser', (value) => {
15911660
});
15921661

15931662
type UsersEndpoints = ExtractRoutesFromAPI<typeof usersEndpoints>;
1594-
type UsersInfoEndpoint = ExtractRoutesFromAPI<typeof usersInfoEndpoint>;
15951663
declare module '@rocket.chat/rest-typings' {
15961664
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
1597-
interface Endpoints extends UsersEndpoints, UsersInfoEndpoint {}
1665+
interface Endpoints extends UsersEndpoints {}
15981666
}

packages/core-typings/src/Ajv.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,15 @@ import type { ISubscription } from './ISubscription';
1111
import type { SlashCommand } from './SlashCommands';
1212
import type { IMediaCall } from './mediaCalls/IMediaCall';
1313

14+
import type { IUser } from './IUser';
15+
1416
export const schemas = typia.json.schemas<
1517
[
1618
ISubscription | IInvite | ICustomSound | IMessage | IOAuthApps | IPermission | IMediaCall,
1719
CallHistoryItem,
1820
ICustomUserStatus,
1921
SlashCommand,
22+
IUser,
2023
],
2124
'3.0'
2225
>();

packages/rest-typings/src/v1/users.ts

Lines changed: 9 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import type { UserLogoutParamsPOST } from './users/UserLogoutParamsPOST';
99
import type { UserRegisterParamsPOST } from './users/UserRegisterParamsPOST';
1010
import type { UserSetActiveStatusParamsPOST } from './users/UserSetActiveStatusParamsPOST';
1111
import type { UsersAutocompleteParamsGET } from './users/UsersAutocompleteParamsGET';
12-
import type { UsersInfoParamsGet } from './users/UsersInfoParamsGet';
1312
import type { UsersListStatusParamsGET } from './users/UsersListStatusParamsGET';
1413
import type { UsersListTeamsParamsGET } from './users/UsersListTeamsParamsGET';
1514
import type { UsersSendConfirmationEmailParamsPOST } from './users/UsersSendConfirmationEmailParamsPOST';
@@ -18,26 +17,11 @@ import type { UsersSetPreferencesParamsPOST } from './users/UsersSetPreferencePa
1817
import type { UsersUpdateOwnBasicInfoParamsPOST } from './users/UsersUpdateOwnBasicInfoParamsPOST';
1918
import type { UsersUpdateParamsPOST } from './users/UsersUpdateParamsPOST';
2019

21-
type UsersInfo = { userId?: IUser['_id']; username?: IUser['username'] };
22-
23-
const UsersInfoSchema = {
24-
type: 'object',
25-
properties: {
26-
userId: {
27-
type: 'string',
28-
nullable: true,
29-
},
30-
username: {
31-
type: 'string',
32-
nullable: true,
33-
},
34-
},
35-
required: [],
36-
additionalProperties: false,
20+
type UsersInfoParamsGet = ({ userId: string } | { username: string } | { importId: string }) & {
21+
fields?: string;
22+
includeUserRooms?: string;
3723
};
3824

39-
export const isUsersInfoProps = ajv.compile<UsersInfo>(UsersInfoSchema);
40-
4125
type Users2faSendEmailCode = { emailOrUsername: string };
4226

4327
const Users2faSendEmailCodeSchema = {
@@ -271,6 +255,12 @@ export type UsersEndpoints = {
271255
};
272256
};
273257

258+
'/v1/users.info': {
259+
GET: (params: UsersInfoParamsGet) => {
260+
user: IUser & { rooms?: Pick<ISubscription, 'rid' | 'name' | 't' | 'roles' | 'unread'>[] };
261+
};
262+
};
263+
274264
'/v1/users.create': {
275265
POST: (params: UserCreateParamsPOST) => {
276266
user: IUser;
@@ -327,11 +317,6 @@ export type UsersEndpoints = {
327317
};
328318
};
329319

330-
'/v1/users.info': {
331-
GET: (params: UsersInfoParamsGet) => {
332-
user: IUser & { rooms?: Pick<ISubscription, 'rid' | 'name' | 't' | 'roles' | 'unread'>[] };
333-
};
334-
};
335320

336321
'/v1/users.register': {
337322
POST: (params: UserRegisterParamsPOST) => {
@@ -373,7 +358,6 @@ export type UsersEndpoints = {
373358
export * from './users/UserCreateParamsPOST';
374359
export * from './users/UserSetActiveStatusParamsPOST';
375360
export * from './users/UserDeactivateIdleParamsPOST';
376-
export * from './users/UsersInfoParamsGet';
377361
export * from './users/UsersListStatusParamsGET';
378362
export * from './users/UsersSendWelcomeEmailParamsPOST';
379363
export * from './users/UserRegisterParamsPOST';

0 commit comments

Comments
 (0)