Skip to content

Commit dc6acda

Browse files
ahmed-n-abdeltwabcardosoggazzo
authored
feat: Add OpenAPI Support to Permissions API (RocketChat#35985)
Co-authored-by: Matheus Cardoso <matheus@cardo.so> Co-authored-by: Guilherme Gazzo <guilherme@gazzo.xyz>
1 parent e16efff commit dc6acda

5 files changed

Lines changed: 138 additions & 88 deletions

File tree

.changeset/tough-ravens-shop.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@rocket.chat/meteor": patch
3+
"@rocket.chat/rest-typings": patch
4+
---
5+
6+
Add OpenAPI support for the Rocket.Chat Permissions API endpoints by migrating to a centralized syntax and utilizing shared AJV schemas for validation. This will enhance API documentation and ensure type safety through response validation.

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

Lines changed: 130 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,101 @@
11
import type { IPermission } from '@rocket.chat/core-typings';
22
import { Permissions, Roles } from '@rocket.chat/models';
3-
import { isBodyParamsValidPermissionUpdate } from '@rocket.chat/rest-typings';
3+
import {
4+
ajv,
5+
validateUnauthorizedErrorResponse,
6+
validateBadRequestErrorResponse,
7+
validateForbiddenErrorResponse,
8+
} from '@rocket.chat/rest-typings';
49
import { Meteor } from 'meteor/meteor';
510

611
import { permissionsGetMethod } from '../../../authorization/server/streamer/permissions';
712
import { notifyOnPermissionChangedById } from '../../../lib/server/lib/notifyListener';
13+
import type { ExtractRoutesFromAPI } from '../ApiClass';
814
import { API } from '../api';
915

10-
API.v1.addRoute(
11-
'permissions.listAll',
12-
{ authRequired: true },
13-
{
14-
async get() {
16+
type PermissionsListAllProps = {
17+
updatedSince?: string;
18+
};
19+
20+
type PermissionsUpdateProps = {
21+
permissions: { _id: string; roles: string[] }[];
22+
};
23+
24+
const permissionListAllSchema = {
25+
type: 'object',
26+
properties: {
27+
updatedSince: {
28+
type: 'string',
29+
nullable: true,
30+
},
31+
},
32+
required: [],
33+
additionalProperties: false,
34+
};
35+
36+
const permissionUpdatePropsSchema = {
37+
type: 'object',
38+
properties: {
39+
permissions: {
40+
type: 'array',
41+
items: {
42+
type: 'object',
43+
properties: {
44+
_id: { type: 'string' },
45+
roles: {
46+
type: 'array',
47+
items: { type: 'string' },
48+
uniqueItems: true,
49+
},
50+
},
51+
additionalProperties: false,
52+
required: ['_id', 'roles'],
53+
},
54+
},
55+
},
56+
required: ['permissions'],
57+
additionalProperties: false,
58+
};
59+
60+
const isPermissionsListAll = ajv.compile<PermissionsListAllProps>(permissionListAllSchema);
61+
62+
const isBodyParamsValidPermissionUpdate = ajv.compile<PermissionsUpdateProps>(permissionUpdatePropsSchema);
63+
64+
const permissionsEndpoints = API.v1
65+
.get(
66+
'permissions.listAll',
67+
{
68+
authRequired: true,
69+
query: isPermissionsListAll,
70+
response: {
71+
200: ajv.compile<{
72+
update: IPermission[];
73+
remove: IPermission[];
74+
}>({
75+
type: 'object',
76+
properties: {
77+
update: {
78+
type: 'array',
79+
items: { $ref: '#/components/schemas/IPermission' },
80+
},
81+
remove: {
82+
type: 'array',
83+
items: { $ref: '#/components/schemas/IPermission' },
84+
},
85+
success: {
86+
type: 'boolean',
87+
enum: [true],
88+
description: 'Indicates if the request was successful.',
89+
},
90+
},
91+
required: ['update', 'remove', 'success'],
92+
additionalProperties: false,
93+
}),
94+
400: validateBadRequestErrorResponse,
95+
401: validateUnauthorizedErrorResponse,
96+
},
97+
},
98+
async function action() {
1599
const { updatedSince } = this.queryParams;
16100

17101
let updatedSinceDate: Date | undefined;
@@ -36,14 +120,38 @@ API.v1.addRoute(
36120

37121
return API.v1.success(result);
38122
},
39-
},
40-
);
41-
42-
API.v1.addRoute(
43-
'permissions.update',
44-
{ authRequired: true, permissionsRequired: ['access-permissions'] },
45-
{
46-
async post() {
123+
)
124+
.post(
125+
'permissions.update',
126+
{
127+
authRequired: true,
128+
permissionsRequired: ['access-permissions'],
129+
body: isBodyParamsValidPermissionUpdate,
130+
response: {
131+
200: ajv.compile<{
132+
permissions: IPermission[];
133+
}>({
134+
type: 'object',
135+
properties: {
136+
permissions: {
137+
type: 'array',
138+
items: { $ref: '#/components/schemas/IPermission' },
139+
},
140+
success: {
141+
type: 'boolean',
142+
enum: [true],
143+
description: 'Indicates if the request was successful.',
144+
},
145+
},
146+
required: ['permissions', 'success'],
147+
additionalProperties: false,
148+
}),
149+
400: validateBadRequestErrorResponse,
150+
401: validateUnauthorizedErrorResponse,
151+
403: validateForbiddenErrorResponse,
152+
},
153+
},
154+
async function action() {
47155
const { bodyParams } = this;
48156

49157
if (!isBodyParamsValidPermissionUpdate(bodyParams)) {
@@ -76,5 +184,11 @@ API.v1.addRoute(
76184
permissions: result,
77185
});
78186
},
79-
},
80-
);
187+
);
188+
189+
export type PermissionsEndpoints = ExtractRoutesFromAPI<typeof permissionsEndpoints>;
190+
191+
declare module '@rocket.chat/rest-typings' {
192+
// eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface
193+
interface Endpoints extends PermissionsEndpoints {}
194+
}

packages/core-typings/src/Ajv.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ICustomSound } from './ICustomSound';
44
import type { IInvite } from './IInvite';
55
import type { IMessage } from './IMessage';
66
import type { IOAuthApps } from './IOAuthApps';
7+
import type { IPermission } from './IPermission';
78
import type { ISubscription } from './ISubscription';
89

9-
export const schemas = typia.json.schemas<[ISubscription | IInvite | ICustomSound | IMessage | IOAuthApps], '3.0'>();
10+
export const schemas = typia.json.schemas<[ISubscription | IInvite | ICustomSound | IMessage | IOAuthApps | IPermission], '3.0'>();

packages/rest-typings/src/index.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ import type { MiscEndpoints } from './v1/misc';
3535
import type { ModerationEndpoints } from './v1/moderation';
3636
import type { OAuthAppsEndpoint } from './v1/oauthapps';
3737
import type { OmnichannelEndpoints } from './v1/omnichannel';
38-
import type { PermissionsEndpoints } from './v1/permissions';
3938
import type { PresenceEndpoints } from './v1/presence';
4039
import type { PushEndpoints } from './v1/push';
4140
import type { RolesEndpoints } from './v1/roles';
@@ -79,7 +78,6 @@ export interface Endpoints
7978
StatisticsEndpoints,
8079
LicensesEndpoints,
8180
MiscEndpoints,
82-
PermissionsEndpoints,
8381
PresenceEndpoints,
8482
InstancesEndpoints,
8583
IntegrationsEndpoints,
@@ -214,7 +212,6 @@ export type UrlParams<T extends string> = string extends T
214212
export type MethodOf<TPathPattern extends PathPattern> = TPathPattern extends any ? keyof Endpoints[TPathPattern] : never;
215213

216214
export * from './apps';
217-
export * from './v1/permissions';
218215
export * from './v1/presence';
219216
export * from './v1/roles';
220217
export * from './v1/settings';

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

Lines changed: 0 additions & 68 deletions
This file was deleted.

0 commit comments

Comments
 (0)