From 9167fa7f2421282ca3b20aadf0e7356a411a1eb3 Mon Sep 17 00:00:00 2001 From: ahmed-n-abdeltwab Date: Thu, 31 Jul 2025 15:11:44 +0300 Subject: [PATCH 1/6] feat: add openapi support to oauth-apps update api --- apps/meteor/app/api/server/v1/oauthapps.ts | 108 +++++++++++++++++++-- 1 file changed, 98 insertions(+), 10 deletions(-) diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index 2a611337d3500..034e0323b57d8 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -1,6 +1,6 @@ import type { IOAuthApps } from '@rocket.chat/core-typings'; import { OAuthApps } from '@rocket.chat/models'; -import { ajv, isUpdateOAuthAppParams, isOauthAppsGetParams, isDeleteOAuthAppParams } from '@rocket.chat/rest-typings'; +import { ajv, isOauthAppsGetParams, isDeleteOAuthAppParams } from '@rocket.chat/rest-typings'; import { hasPermissionAsync } from '../../../authorization/server/functions/hasPermission'; import { apiDeprecationLogger } from '../../../lib/server/lib/deprecationWarningLogger'; @@ -49,21 +49,105 @@ API.v1.addRoute( }, ); -API.v1.addRoute( +type UpdateOAuthAppParams = { + appId: string; + name: string; + active: boolean; + clientId?: string | undefined; + clientSecret?: string | undefined; + redirectUri: string; +}; + +const UpdateOAuthAppParamsSchema = { + type: 'object', + properties: { + appId: { + type: 'string', + }, + name: { + type: 'string', + }, + active: { + type: 'boolean', + }, + redirectUri: { + type: 'string', + }, + }, + required: ['appId', 'name', 'active', 'redirectUri'], + additionalProperties: false, +}; + +const isUpdateOAuthAppParams = ajv.compile(UpdateOAuthAppParamsSchema); + +const oauthAppsuUpdateEndpoints = API.v1.post( 'oauth-apps.update', { authRequired: true, - validateParams: isUpdateOAuthAppParams, + body: isUpdateOAuthAppParams, permissionsRequired: ['manage-oauth-apps'], + response: { + 400: ajv.compile<{ + error?: string; + errorType?: string; + stack?: string; + details?: object; + }>({ + type: 'object', + properties: { + success: { type: 'boolean', enum: [false] }, + stack: { type: 'string' }, + error: { type: 'string' }, + errorType: { type: 'string' }, + details: { type: 'object' }, + }, + required: ['success'], + additionalProperties: false, + }), + 401: ajv.compile({ + type: 'object', + properties: { + success: { type: 'boolean', enum: [false] }, + status: { type: 'string' }, + message: { type: 'string' }, + error: { type: 'string' }, + errorType: { type: 'string' }, + }, + required: ['success'], + additionalProperties: false, + }), + 403: ajv.compile({ + type: 'object', + properties: { + success: { type: 'boolean', enum: [false] }, + status: { type: 'string' }, + message: { type: 'string' }, + error: { type: 'string' }, + errorType: { type: 'string' }, + }, + required: ['success'], + additionalProperties: false, + }), + 200: ajv.compile({ + allOf: [ + { anyOf: [{ $ref: '#/components/schemas/IOAuthApps' }, { type: 'null' }] }, + { + type: 'object', + properties: { + success: { type: 'boolean', enum: [true] }, + }, + required: ['success'], + }, + ], + }), + }, }, - { - async post() { - const { appId } = this.bodyParams; + async function action() { + const { appId } = this.bodyParams; - const result = await updateOAuthApp(this.userId, appId, this.bodyParams); + const result = await updateOAuthApp(this.userId, appId, this.bodyParams); - return API.v1.success(result); - }, + return API.v1.success(result); }, ); @@ -182,9 +266,13 @@ const oauthAppsCreateEndpoints = API.v1.post( type OauthAppsCreateEndpoints = ExtractRoutesFromAPI; -export type OAuthAppsEndpoints = OauthAppsCreateEndpoints; +type OauthAppsuUpdateEndpoints = ExtractRoutesFromAPI; + +export type OAuthAppsEndpoints = OauthAppsCreateEndpoints | OauthAppsuUpdateEndpoints; declare module '@rocket.chat/rest-typings' { // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface interface Endpoints extends OauthAppsCreateEndpoints {} + // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface + interface Endpoints extends OauthAppsuUpdateEndpoints {} } From 1d72a279d7054ba2435289fc30864823c12df56b Mon Sep 17 00:00:00 2001 From: ahmed-n-abdeltwab Date: Thu, 31 Jul 2025 15:12:11 +0300 Subject: [PATCH 2/6] refactor: remove UpdateOAuthAppParams type and related endpoint from rest-typing --- packages/rest-typings/src/index.ts | 1 - packages/rest-typings/src/v1/oauthapps.ts | 5 --- .../v1/oauthapps/UpdateOAuthAppParamsPOST.ts | 32 ------------------- 3 files changed, 38 deletions(-) delete mode 100644 packages/rest-typings/src/v1/oauthapps/UpdateOAuthAppParamsPOST.ts diff --git a/packages/rest-typings/src/index.ts b/packages/rest-typings/src/index.ts index 6bad8905cb4b9..42bdba0acb0a7 100644 --- a/packages/rest-typings/src/index.ts +++ b/packages/rest-typings/src/index.ts @@ -235,7 +235,6 @@ export * from './v1/integrations'; export * from './v1/licenses'; export * from './v1/omnichannel'; export * from './v1/oauthapps'; -export * from './v1/oauthapps/UpdateOAuthAppParamsPOST'; export * from './v1/oauthapps/OAuthAppsGetParamsGET'; export * from './v1/oauthapps/DeleteOAuthAppParamsDELETE'; export * from './helpers/PaginatedRequest'; diff --git a/packages/rest-typings/src/v1/oauthapps.ts b/packages/rest-typings/src/v1/oauthapps.ts index 2e42cf12c877b..00ab22fe0ae75 100644 --- a/packages/rest-typings/src/v1/oauthapps.ts +++ b/packages/rest-typings/src/v1/oauthapps.ts @@ -2,7 +2,6 @@ import type { IOAuthApps, IUser } from '@rocket.chat/core-typings'; import type { DeleteOAuthAppParams } from './oauthapps/DeleteOAuthAppParamsDELETE'; import type { OauthAppsGetParams } from './oauthapps/OAuthAppsGetParamsGET'; -import type { UpdateOAuthAppParams } from './oauthapps/UpdateOAuthAppParamsPOST'; export type OAuthAppsEndpoint = { '/v1/oauth-apps.list': { @@ -17,10 +16,6 @@ export type OAuthAppsEndpoint = { }; }; - '/v1/oauth-apps.update': { - POST: (params: UpdateOAuthAppParams) => IOAuthApps | null; - }; - '/v1/oauth-apps.delete': { POST: (params: DeleteOAuthAppParams) => boolean; }; diff --git a/packages/rest-typings/src/v1/oauthapps/UpdateOAuthAppParamsPOST.ts b/packages/rest-typings/src/v1/oauthapps/UpdateOAuthAppParamsPOST.ts deleted file mode 100644 index da1a2b1f326b8..0000000000000 --- a/packages/rest-typings/src/v1/oauthapps/UpdateOAuthAppParamsPOST.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { ajv } from '../Ajv'; - -export type UpdateOAuthAppParams = { - appId: string; - name: string; - active: boolean; - clientId?: string | undefined; - clientSecret?: string | undefined; - redirectUri: string; -}; - -const UpdateOAuthAppParamsSchema = { - type: 'object', - properties: { - appId: { - type: 'string', - }, - name: { - type: 'string', - }, - active: { - type: 'boolean', - }, - redirectUri: { - type: 'string', - }, - }, - required: ['appId', 'name', 'active', 'redirectUri'], - additionalProperties: false, -}; - -export const isUpdateOAuthAppParams = ajv.compile(UpdateOAuthAppParamsSchema); From 8d18f347ec7c8b7847b8c2042d7619ea2ece4e3e Mon Sep 17 00:00:00 2001 From: Ahmed Nasser Date: Thu, 31 Jul 2025 15:16:01 +0300 Subject: [PATCH 3/6] Create clever-trees-occur.md --- .changeset/clever-trees-occur.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/clever-trees-occur.md diff --git a/.changeset/clever-trees-occur.md b/.changeset/clever-trees-occur.md new file mode 100644 index 0000000000000..7816309c20ea0 --- /dev/null +++ b/.changeset/clever-trees-occur.md @@ -0,0 +1,6 @@ +--- +"@rocket.chat/meteor": patch +"@rocket.chat/rest-typings": patch +--- + +Add OpenAPI support for the Rocket.Chat oauth-apps.update API endpoints by migrating to a modern chained route definition syntax and utilizing shared AJV schemas for validation to enhance API documentation and ensure type safety through response validation. From 9e9a11ecd9750c9cb876e9a92da7524eab26ada0 Mon Sep 17 00:00:00 2001 From: ahmed-n-abdeltwab Date: Thu, 31 Jul 2025 19:43:43 +0300 Subject: [PATCH 4/6] refactor: simplify error response in oauth-apps.update endpoint --- apps/meteor/app/api/server/v1/oauthapps.ts | 44 ++-------------------- 1 file changed, 3 insertions(+), 41 deletions(-) diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index af03ffc59eea4..15c0cd105e050 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -94,47 +94,9 @@ const oauthAppsuUpdateEndpoints = API.v1.post( body: isUpdateOAuthAppParams, permissionsRequired: ['manage-oauth-apps'], response: { - 400: ajv.compile<{ - error?: string; - errorType?: string; - stack?: string; - details?: object; - }>({ - type: 'object', - properties: { - success: { type: 'boolean', enum: [false] }, - stack: { type: 'string' }, - error: { type: 'string' }, - errorType: { type: 'string' }, - details: { type: 'object' }, - }, - required: ['success'], - additionalProperties: false, - }), - 401: ajv.compile({ - type: 'object', - properties: { - success: { type: 'boolean', enum: [false] }, - status: { type: 'string' }, - message: { type: 'string' }, - error: { type: 'string' }, - errorType: { type: 'string' }, - }, - required: ['success'], - additionalProperties: false, - }), - 403: ajv.compile({ - type: 'object', - properties: { - success: { type: 'boolean', enum: [false] }, - status: { type: 'string' }, - message: { type: 'string' }, - error: { type: 'string' }, - errorType: { type: 'string' }, - }, - required: ['success'], - additionalProperties: false, - }), + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, 200: ajv.compile({ allOf: [ { anyOf: [{ $ref: '#/components/schemas/IOAuthApps' }, { type: 'null' }] }, From 897e1e860f72340654a0b64f8b2aab97d302b904 Mon Sep 17 00:00:00 2001 From: ahmed-n-abdeltwab Date: Fri, 1 Aug 2025 17:30:03 +0300 Subject: [PATCH 5/6] fix: lint --- apps/meteor/app/api/server/v1/oauthapps.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index a84d3a09f3765..5b5dc783c6722 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -236,12 +236,11 @@ type OauthAppsListEndpoints = ExtractRoutesFromAPI Date: Mon, 4 Aug 2025 17:40:00 +0300 Subject: [PATCH 6/6] fix: lint --- apps/meteor/app/api/server/v1/oauthapps.ts | 57 +++++++++++----------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/apps/meteor/app/api/server/v1/oauthapps.ts b/apps/meteor/app/api/server/v1/oauthapps.ts index df71e725d9201..c12522b753a37 100644 --- a/apps/meteor/app/api/server/v1/oauthapps.ts +++ b/apps/meteor/app/api/server/v1/oauthapps.ts @@ -187,38 +187,38 @@ const oauthAppsEndpoints = API.v1 return API.v1.success({ application }); }, ) -.post( - 'oauth-apps.update', - { - authRequired: true, - body: isUpdateOAuthAppParams, - permissionsRequired: ['manage-oauth-apps'], - response: { - 400: validateBadRequestErrorResponse, - 401: validateUnauthorizedErrorResponse, - 403: validateForbiddenErrorResponse, - 200: ajv.compile({ - allOf: [ - { anyOf: [{ $ref: '#/components/schemas/IOAuthApps' }, { type: 'null' }] }, - { - type: 'object', - properties: { - success: { type: 'boolean', enum: [true] }, + .post( + 'oauth-apps.update', + { + authRequired: true, + body: isUpdateOAuthAppParams, + permissionsRequired: ['manage-oauth-apps'], + response: { + 400: validateBadRequestErrorResponse, + 401: validateUnauthorizedErrorResponse, + 403: validateForbiddenErrorResponse, + 200: ajv.compile({ + allOf: [ + { anyOf: [{ $ref: '#/components/schemas/IOAuthApps' }, { type: 'null' }] }, + { + type: 'object', + properties: { + success: { type: 'boolean', enum: [true] }, + }, + required: ['success'], }, - required: ['success'], - }, - ], - }), + ], + }), + }, }, - }, - async function action() { - const { appId } = this.bodyParams; + async function action() { + const { appId } = this.bodyParams; - const result = await updateOAuthApp(this.userId, appId, this.bodyParams); + const result = await updateOAuthApp(this.userId, appId, this.bodyParams); - return API.v1.success(result); - }, -); + return API.v1.success(result); + }, + ); API.v1.addRoute( 'oauth-apps.get', @@ -252,5 +252,4 @@ export type OauthAppsEndpoints = ExtractRoutesFromAPI declare module '@rocket.chat/rest-typings' { // eslint-disable-next-line @typescript-eslint/naming-convention, @typescript-eslint/no-empty-interface interface Endpoints extends OauthAppsEndpoints {} - }