From 8ae76558c8c045df0d96aa1d3fd9ccea084487fe Mon Sep 17 00:00:00 2001 From: Kaustubh2k5 Date: Tue, 31 Mar 2026 11:24:49 +0530 Subject: [PATCH 1/4] refactor: migrate AppUploadsConverter to TypeScript --- .../converters/{uploads.js => uploads.ts} | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) rename apps/meteor/app/apps/server/converters/{uploads.js => uploads.ts} (71%) diff --git a/apps/meteor/app/apps/server/converters/uploads.js b/apps/meteor/app/apps/server/converters/uploads.ts similarity index 71% rename from apps/meteor/app/apps/server/converters/uploads.js rename to apps/meteor/app/apps/server/converters/uploads.ts index 60f85a8aa72f1..84e02dc5e6a7d 100644 --- a/apps/meteor/app/apps/server/converters/uploads.js +++ b/apps/meteor/app/apps/server/converters/uploads.ts @@ -1,19 +1,23 @@ +import type { IUpload } from '@rocket.chat/core-typings'; +import type { IUpload as IAppsUpload } from '@rocket.chat/apps-engine/definition/uploads'; import { Uploads } from '@rocket.chat/models'; import { transformMappedData } from './transformMappedData'; export class AppUploadsConverter { - constructor(orch) { + private orch: any; + + constructor(orch: any) { this.orch = orch; } - async convertById(id) { + async convertById(id: string): Promise { const upload = await Uploads.findOneById(id); return this.convertToApp(upload); } - async convertToApp(upload) { + async convertToApp(upload: any): Promise { if (!upload) { return undefined; } @@ -35,12 +39,12 @@ export class AppUploadsConverter { url: 'url', updatedAt: '_updatedAt', uploadedAt: 'uploadedAt', - room: async (upload) => { + room: async (upload: any) => { const result = await this.orch.getConverters().get('rooms').convertById(upload.rid); delete upload.rid; return result; }, - user: async (upload) => { + user: async (upload: any) => { if (!upload.userId) { return undefined; } @@ -49,7 +53,7 @@ export class AppUploadsConverter { delete upload.userId; return result; }, - visitor: async (upload) => { + visitor: async (upload: any) => { if (!upload.visitorToken) { return undefined; } @@ -60,19 +64,19 @@ export class AppUploadsConverter { }, }; - return transformMappedData(upload, map); + return transformMappedData(upload, map) as unknown as IAppsUpload; } - convertToRocketChat(upload) { + convertToRocketChat(upload: any): IUpload | undefined { if (!upload) { return undefined; } const { id: userId } = upload.user || {}; const { token: visitorToken } = upload.visitor || {}; - const { id: rid } = upload.room; + const { id: rid } = (upload.room || {}) as any; - const newUpload = { + const newUpload: any = { _id: upload.id, name: upload.name, size: upload.size, @@ -93,6 +97,6 @@ export class AppUploadsConverter { visitorToken, }; - return Object.assign(newUpload, upload._unmappedProperties_); + return Object.assign(newUpload, (upload as any)._unmappedProperties_) as IUpload; } -} +} From d49f2105d44f43a113fc19839227972bbec9cba6 Mon Sep 17 00:00:00 2001 From: Kaustubh2k5 Date: Thu, 2 Apr 2026 22:10:10 +0530 Subject: [PATCH 2/4] Removed the use of any and added fail fast validation for missing rid and room.id --- .../app/apps/server/converters/uploads.ts | 65 ++++++++++++------- 1 file changed, 43 insertions(+), 22 deletions(-) diff --git a/apps/meteor/app/apps/server/converters/uploads.ts b/apps/meteor/app/apps/server/converters/uploads.ts index 84e02dc5e6a7d..4815156c0f50e 100644 --- a/apps/meteor/app/apps/server/converters/uploads.ts +++ b/apps/meteor/app/apps/server/converters/uploads.ts @@ -4,20 +4,34 @@ import { Uploads } from '@rocket.chat/models'; import { transformMappedData } from './transformMappedData'; +interface IConverters { + get(name: 'rooms'): { convertById(id: string): Promise }; + get(name: 'users'): { convertById(id: string): Promise }; + get(name: 'visitors'): { convertByToken(token: string): Promise }; +} + +interface IOrchestrator { + getConverters(): IConverters; +} + +type AppUploadWithUnmapped = IAppsUpload & { _unmappedProperties_?: Partial }; +type IUploadWithVisitorToken = IUpload & { visitorToken?: string }; +type IAppsUploadWithDescription = IAppsUpload & { description?: string }; + export class AppUploadsConverter { - private orch: any; + private orch: IOrchestrator; - constructor(orch: any) { + constructor(orch: IOrchestrator) { this.orch = orch; } async convertById(id: string): Promise { const upload = await Uploads.findOneById(id); - return this.convertToApp(upload); + return this.convertToApp(upload ?? undefined); } - async convertToApp(upload: any): Promise { + async convertToApp(upload: IUpload | undefined): Promise { if (!upload) { return undefined; } @@ -39,27 +53,30 @@ export class AppUploadsConverter { url: 'url', updatedAt: '_updatedAt', uploadedAt: 'uploadedAt', - room: async (upload: any) => { + room: async (upload: IUpload) => { + if (!upload.rid) { + throw new Error('Invalid upload payload: missing rid'); + } const result = await this.orch.getConverters().get('rooms').convertById(upload.rid); - delete upload.rid; + delete (upload as Partial).rid; return result; }, - user: async (upload: any) => { + user: async (upload: IUpload) => { if (!upload.userId) { return undefined; } const result = await this.orch.getConverters().get('users').convertById(upload.userId); - delete upload.userId; + delete (upload as Partial).userId; return result; }, - visitor: async (upload: any) => { - if (!upload.visitorToken) { + visitor: async (upload: IUpload) => { + const uploadWithToken = upload as IUploadWithVisitorToken; + if (!uploadWithToken.visitorToken) { return undefined; } - - const result = await this.orch.getConverters().get('visitors').convertByToken(upload.visitorToken); - delete upload.visitorToken; + const result = await this.orch.getConverters().get('visitors').convertByToken(uploadWithToken.visitorToken); + delete (upload as IUploadWithVisitorToken).visitorToken; return result; }, }; @@ -67,22 +84,26 @@ export class AppUploadsConverter { return transformMappedData(upload, map) as unknown as IAppsUpload; } - convertToRocketChat(upload: any): IUpload | undefined { + convertToRocketChat(upload: IAppsUpload | undefined): IUpload | undefined { if (!upload) { return undefined; } - const { id: userId } = upload.user || {}; - const { token: visitorToken } = upload.visitor || {}; - const { id: rid } = (upload.room || {}) as any; + const rid = upload.room?.id; + if (!rid) { + throw new Error('Invalid app upload payload: missing room.id'); + } + + const { id: userId } = upload.user ?? {}; + const { token: visitorToken } = upload.visitor ?? {}; - const newUpload: any = { + const newUpload: Record = { _id: upload.id, name: upload.name, - size: upload.size, + size: upload.size as unknown as number, type: upload.type, extension: upload.extension, - description: upload.description, + description: (upload as IAppsUploadWithDescription).description, store: upload.store, etag: upload.etag, complete: upload.complete, @@ -97,6 +118,6 @@ export class AppUploadsConverter { visitorToken, }; - return Object.assign(newUpload, (upload as any)._unmappedProperties_) as IUpload; + return Object.assign(newUpload, (upload as AppUploadWithUnmapped)._unmappedProperties_) as IUpload; } -} +} \ No newline at end of file From fa817705cf4c8e8bd7b584d2d0ff987ffed7589f Mon Sep 17 00:00:00 2001 From: Kaustubh2k5 Date: Thu, 2 Apr 2026 22:33:11 +0530 Subject: [PATCH 3/4] fixed preserving path field in AppUploadsConverter round trip conversion --- apps/meteor/app/apps/server/converters/uploads.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/converters/uploads.ts b/apps/meteor/app/apps/server/converters/uploads.ts index 4815156c0f50e..74506529446cc 100644 --- a/apps/meteor/app/apps/server/converters/uploads.ts +++ b/apps/meteor/app/apps/server/converters/uploads.ts @@ -96,6 +96,7 @@ export class AppUploadsConverter { const { id: userId } = upload.user ?? {}; const { token: visitorToken } = upload.visitor ?? {}; + const uploadWithExtras = upload as IAppsUploadWithDescription & { path?: IUpload['path'] }; const newUpload: Record = { _id: upload.id, @@ -103,7 +104,8 @@ export class AppUploadsConverter { size: upload.size as unknown as number, type: upload.type, extension: upload.extension, - description: (upload as IAppsUploadWithDescription).description, + description: uploadWithExtras.description, + path: uploadWithExtras.path, store: upload.store, etag: upload.etag, complete: upload.complete, From 56315ac123b0838965caa49fd9e1249bd5eaea39 Mon Sep 17 00:00:00 2001 From: Kaustubh2k5 Date: Thu, 2 Apr 2026 22:56:47 +0530 Subject: [PATCH 4/4] proper conversion of value to maintain type safety and added undefined guard --- apps/meteor/app/apps/server/converters/uploads.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/meteor/app/apps/server/converters/uploads.ts b/apps/meteor/app/apps/server/converters/uploads.ts index 74506529446cc..abc25f8e1c2e5 100644 --- a/apps/meteor/app/apps/server/converters/uploads.ts +++ b/apps/meteor/app/apps/server/converters/uploads.ts @@ -101,7 +101,7 @@ export class AppUploadsConverter { const newUpload: Record = { _id: upload.id, name: upload.name, - size: upload.size as unknown as number, + size: upload.size !== undefined ? Number(upload.size) : undefined, type: upload.type, extension: upload.extension, description: uploadWithExtras.description,