Skip to content

Commit af15765

Browse files
committed
refactor: convert apps/meteor/ee/server/apps/orchestrator.js to TypeScript
1 parent 8d35c14 commit af15765

File tree

3 files changed

+82
-71
lines changed

3 files changed

+82
-71
lines changed

apps/meteor/app/apps/server/converters/threads.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { IAppRoomsConverter, IAppThreadsConverter, IAppUsersConverter, IAppsMessage, IAppsUser } from '@rocket.chat/apps';
1+
import type { IAppServerOrchestrator, IAppThreadsConverter, IAppUsersConverter, IAppsMessage, IAppsUser } from '@rocket.chat/apps';
22
import type { IMessage as AppsEngineMessage, IMessageAttachment } from '@rocket.chat/apps-engine/definition/messages';
33
import type { IRoom } from '@rocket.chat/apps-engine/definition/rooms';
44
import { isEditedMessage } from '@rocket.chat/core-typings';
@@ -8,25 +8,8 @@ import { Messages } from '@rocket.chat/models';
88
import { cachedFunction } from './cachedFunction';
99
import { transformMappedData } from './transformMappedData';
1010

11-
// eslint-disable-next-line @typescript-eslint/naming-convention
12-
interface Orchestrator {
13-
rooms: () => {
14-
convertById: IAppRoomsConverter['convertById'];
15-
};
16-
users: () => {
17-
convertById: IAppUsersConverter['convertById'];
18-
convertToApp: IAppUsersConverter['convertToApp'];
19-
};
20-
}
21-
2211
export class AppThreadsConverter implements IAppThreadsConverter {
23-
constructor(
24-
private readonly orch: {
25-
getConverters: () => {
26-
get: <O extends keyof Orchestrator>(key: O) => ReturnType<Orchestrator[O]>;
27-
};
28-
},
29-
) {
12+
constructor(private readonly orch: IAppServerOrchestrator) {
3013
this.orch = orch;
3114
}
3215

@@ -66,8 +49,8 @@ export class AppThreadsConverter implements IAppThreadsConverter {
6649
async convertMessage(
6750
msgObj: IMessage,
6851
room: IRoom,
69-
convertUserById: ReturnType<Orchestrator['users']>['convertById'],
70-
convertToApp: ReturnType<Orchestrator['users']>['convertToApp'],
52+
convertUserById: IAppUsersConverter['convertById'],
53+
convertToApp: IAppUsersConverter['convertToApp'],
7154
): Promise<AppsEngineMessage> {
7255
const map = {
7356
id: '_id',

apps/meteor/ee/server/apps/communication/websockets.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1+
import type { IAppServerOrchestrator } from '@rocket.chat/apps';
12
import type { AppStatus } from '@rocket.chat/apps-engine/definition/AppStatus';
23
import { AppStatusUtils } from '@rocket.chat/apps-engine/definition/AppStatus';
34
import type { ISetting as AppsSetting } from '@rocket.chat/apps-engine/definition/settings';
45
import { api } from '@rocket.chat/core-services';
56
import type { IStreamer } from 'meteor/rocketchat:streamer';
67

8+
import { AppEvents } from './events';
79
import notifications from '../../../../app/notifications/server/lib/Notifications';
810
import { SystemLogger } from '../../../../server/lib/logger/system';
9-
import type { AppServerOrchestrator } from '../orchestrator';
10-
import { AppEvents } from './events';
1111

1212
export { AppEvents };
1313
export class AppServerListener {
14-
private orch: AppServerOrchestrator;
14+
private orch: IAppServerOrchestrator;
1515

1616
engineStreamer: IStreamer<'apps-engine'>;
1717

@@ -20,7 +20,7 @@ export class AppServerListener {
2020
received;
2121

2222
constructor(
23-
orch: AppServerOrchestrator,
23+
orch: IAppServerOrchestrator,
2424
engineStreamer: IStreamer<'apps-engine'>,
2525
clientStreamer: IStreamer<'apps'>,
2626
received: Map<any, any>,
@@ -90,13 +90,13 @@ export class AppServerListener {
9090

9191
const storageItem = await this.orch.getStorage()!.retrieveOne(appId);
9292

93-
const appPackage = await this.orch.getAppSourceStorage()!.fetch(storageItem);
93+
const appPackage = await this.orch.getAppSourceStorage()!.fetch(storageItem!);
9494

95-
const isEnabled = AppStatusUtils.isEnabled(storageItem.status);
95+
const isEnabled = AppStatusUtils.isEnabled(storageItem!.status);
9696
if (isEnabled) {
97-
await this.orch.getManager()!.updateAndStartupLocal(storageItem, appPackage);
97+
await this.orch.getManager()!.updateAndStartupLocal(storageItem!, appPackage);
9898
} else {
99-
await this.orch.getManager()!.updateAndInitializeLocal(storageItem, appPackage);
99+
await this.orch.getManager()!.updateAndInitializeLocal(storageItem!, appPackage);
100100
}
101101

102102
this.clientStreamer.emitWithoutBroadcast(AppEvents.APP_UPDATED, appId);
@@ -143,7 +143,7 @@ export class AppServerNotifier {
143143

144144
listener: AppServerListener;
145145

146-
constructor(orch: AppServerOrchestrator) {
146+
constructor(orch: IAppServerOrchestrator) {
147147
this.engineStreamer = notifications.streamAppsEngine;
148148

149149
// This is used to broadcast to the web clients

apps/meteor/ee/server/apps/orchestrator.js renamed to apps/meteor/ee/server/apps/orchestrator.ts

Lines changed: 69 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ import * as os from 'os';
33
import * as path from 'path';
44

55
import { registerOrchestrator } from '@rocket.chat/apps';
6+
import type { IAppConvertersMap, IAppServerOrchestrator } from '@rocket.chat/apps';
67
import { EssentialAppDisabledException } from '@rocket.chat/apps-engine/definition/exceptions';
78
import { AppManager } from '@rocket.chat/apps-engine/server/AppManager';
9+
import type { ProxiedApp } from '@rocket.chat/apps-engine/server/ProxiedApp';
10+
import { AppInstallationSource } from '@rocket.chat/apps-engine/server/storage';
811
import { Logger } from '@rocket.chat/logger';
912
import { AppLogs, Apps as AppsModel, AppsPersistence, Statistics } from '@rocket.chat/models';
1013
import { Meteor } from 'meteor/meteor';
@@ -32,9 +35,41 @@ import { canEnableApp } from '../../app/license/server/canEnableApp';
3235

3336
const DISABLED_PRIVATE_APP_INSTALLATION = ['yes', 'true'].includes(String(process.env.DISABLE_PRIVATE_APP_INSTALLATION).toLowerCase());
3437

35-
export class AppServerOrchestrator {
38+
export class AppServerOrchestrator implements IAppServerOrchestrator {
39+
_isInitialized: boolean;
40+
41+
private _rocketchatLogger!: Logger;
42+
43+
private _model: typeof AppsModel;
44+
45+
private _logModel: typeof AppLogs;
46+
47+
private _persistModel: typeof AppsPersistence;
48+
49+
private _statisticsModel: typeof Statistics;
50+
51+
private _storage!: AppRealStorage;
52+
53+
private _logStorage!: AppRealLogStorage;
54+
55+
private _appSourceStorage!: ConfigurableAppSourceStorage;
56+
57+
private _converters!: IAppConvertersMap;
58+
59+
private _bridges!: RealAppBridges;
60+
61+
private _manager!: AppManager;
62+
63+
private _communicators!: Map<string, any>;
64+
65+
public marketplaceClient: MarketplaceAPIClient;
66+
3667
constructor() {
3768
this._isInitialized = false;
69+
this._model = AppsModel;
70+
this._logModel = AppLogs;
71+
this._persistModel = AppsPersistence;
72+
this._statisticsModel = Statistics;
3873

3974
this.marketplaceClient = new MarketplaceAPIClient();
4075
}
@@ -57,18 +92,18 @@ export class AppServerOrchestrator {
5792
settings.get('Apps_Framework_Source_Package_Storage_FileSystem_Path'),
5893
);
5994

60-
this._converters = new Map();
95+
this._converters = new Map() as IAppConvertersMap;
6196
this._converters.set('messages', new AppMessagesConverter(this));
6297
this._converters.set('rooms', new AppRoomsConverter(this));
6398
this._converters.set('settings', new AppSettingsConverter(this));
6499
this._converters.set('users', new AppUsersConverter(this));
65100
this._converters.set('visitors', new AppVisitorsConverter(this));
66-
this._converters.set('contacts', new AppContactsConverter(this));
101+
this._converters.set('contacts', new AppContactsConverter());
67102
this._converters.set('departments', new AppDepartmentsConverter(this));
68103
this._converters.set('uploads', new AppUploadsConverter(this));
69104
this._converters.set('videoConferences', new AppVideoConferencesConverter());
70105
this._converters.set('threads', new AppThreadsConverter(this));
71-
this._converters.set('roles', new AppRolesConverter(this));
106+
this._converters.set('roles', new AppRolesConverter());
72107

73108
this._bridges = new RealAppBridges(this);
74109

@@ -77,7 +112,7 @@ export class AppServerOrchestrator {
77112
try {
78113
// We call this only once at server startup, so using the synchronous version is fine
79114
fs.mkdirSync(tempFilePath);
80-
} catch (err) {
115+
} catch (err: any) {
81116
// If the temp directory already exists, we can continue
82117
if (err.code !== 'EEXIST') {
83118
throw new Error('Failed to initialize the Apps-Engine', { cause: err });
@@ -108,9 +143,6 @@ export class AppServerOrchestrator {
108143
return this._model;
109144
}
110145

111-
/**
112-
* @returns {AppsPersistenceModel}
113-
*/
114146
getPersistenceModel() {
115147
return this._persistModel;
116148
}
@@ -171,16 +203,14 @@ export class AppServerOrchestrator {
171203
return DISABLED_PRIVATE_APP_INSTALLATION;
172204
}
173205

174-
/**
175-
* @returns {Logger}
176-
*/
177206
getRocketChatLogger() {
178207
return this._rocketchatLogger;
179208
}
180209

181-
debugLog(...args) {
210+
debugLog(...args: any[]) {
182211
if (this.isDebugging()) {
183-
this.getRocketChatLogger().debug(...args);
212+
// FIXME: Logger.debug expects only one argument, but the method signature allows multiple
213+
this.getRocketChatLogger().debug(...(args as [any]));
184214
}
185215
}
186216

@@ -202,7 +232,7 @@ export class AppServerOrchestrator {
202232
await canEnableApp(app.getStorageItem());
203233

204234
await this.getManager().loadOne(app.getID(), true);
205-
} catch (error) {
235+
} catch (error: any) {
206236
this._rocketchatLogger.warn({
207237
msg: 'App could not be enabled',
208238
appName: app.getInfo().name,
@@ -222,14 +252,14 @@ export class AppServerOrchestrator {
222252
}
223253

224254
async migratePrivateApps() {
225-
const apps = await this.getManager().get({ installationSource: 'private' });
255+
const apps = await this.getManager().get({ installationSource: AppInstallationSource.PRIVATE });
226256

227257
await Promise.all(apps.map((app) => this.getManager().migrate(app.getID())));
228258
await Promise.all(apps.map((app) => this.getNotifier().appUpdated(app.getID())));
229259
}
230260

231-
async findMajorVersionUpgradeDate(targetVersion = 7) {
232-
let upgradeToV7Date = null;
261+
async findMajorVersionUpgradeDate(targetVersion = 7): Promise<Date | null> {
262+
let upgradeToV7Date: Date | null = null;
233263
let hadPreTargetVersion = false;
234264

235265
try {
@@ -239,7 +269,9 @@ export class AppServerOrchestrator {
239269
return upgradeToV7Date;
240270
}
241271

242-
const statsAscendingByInstallDate = statistics.sort((a, b) => new Date(a.installedAt) - new Date(b.installedAt));
272+
const statsAscendingByInstallDate = statistics.sort(
273+
(a, b) => new Date(a.installedAt || 0).getTime() - new Date(b.installedAt || 0).getTime(),
274+
);
243275
for (const stat of statsAscendingByInstallDate) {
244276
const version = stat.version || '';
245277

@@ -257,7 +289,7 @@ export class AppServerOrchestrator {
257289
}
258290

259291
if (hadPreTargetVersion && majorVersion >= targetVersion) {
260-
upgradeToV7Date = new Date(stat.installedAt);
292+
upgradeToV7Date = new Date(stat.installedAt!);
261293
this._rocketchatLogger.info({
262294
msg: 'Found upgrade to target version date',
263295
targetVersion,
@@ -266,7 +298,7 @@ export class AppServerOrchestrator {
266298
break;
267299
}
268300
}
269-
} catch (err) {
301+
} catch (err: any) {
270302
this._rocketchatLogger.error({
271303
msg: 'Error checking statistics for version history',
272304
err,
@@ -277,34 +309,34 @@ export class AppServerOrchestrator {
277309
}
278310

279311
async disableMarketplaceApps() {
280-
return this.disableApps('marketplace', false, 5);
312+
return this.disableApps(AppInstallationSource.MARKETPLACE, false, 5);
281313
}
282314

283315
async disablePrivateApps() {
284-
return this.disableApps('private', true, 0);
316+
return this.disableApps(AppInstallationSource.PRIVATE, true, 0);
285317
}
286318

287-
async disableApps(installationSource, grandfatherApps, maxApps) {
319+
async disableApps(installationSource: AppInstallationSource, grandfatherApps: boolean, maxApps: number): Promise<void> {
288320
const upgradeToV7Date = await this.findMajorVersionUpgradeDate();
289321
const apps = await this.getManager().get({ installationSource });
290322

291-
const grandfathered = [];
292-
const toKeep = [];
293-
const toDisable = [];
323+
const grandfathered: ProxiedApp[] = [];
324+
const toKeep: ProxiedApp[] = [];
325+
const toDisable: ProxiedApp[] = [];
294326

295327
for (const app of apps) {
296328
const storageItem = app.getStorageItem();
297329
const isEnabled = ['enabled', 'manually_enabled', 'auto_enabled'].includes(storageItem.status);
298-
const marketplaceInfo = storageItem.marketplaceInfo && storageItem.marketplaceInfo[0];
330+
const marketplaceInfo = storageItem.marketplaceInfo?.[0];
299331

300-
const wasInstalledBeforeV7 = upgradeToV7Date && storageItem.createdAt && new Date(storageItem.createdAt) < upgradeToV7Date;
332+
const wasInstalledBeforeV7 = upgradeToV7Date && storageItem.createdAt && new Date(storageItem.createdAt || 0) < upgradeToV7Date;
301333

302334
if (wasInstalledBeforeV7 && isEnabled && grandfatherApps) {
303335
grandfathered.push(app);
304336
continue;
305337
}
306338

307-
if (marketplaceInfo?.isEnterpriseOnly === true && installationSource === 'marketplace') {
339+
if (marketplaceInfo?.isEnterpriseOnly === true && installationSource === AppInstallationSource.MARKETPLACE) {
308340
toDisable.push(app);
309341
continue;
310342
}
@@ -314,7 +346,7 @@ export class AppServerOrchestrator {
314346
}
315347
}
316348

317-
toKeep.sort((a, b) => new Date(a.getStorageItem().createdAt || 0) - new Date(b.getStorageItem().createdAt || 0));
349+
toKeep.sort((a, b) => new Date(a.getStorageItem().createdAt || 0).getTime() - new Date(b.getStorageItem().createdAt || 0).getTime());
318350

319351
if (toKeep.length > maxApps) {
320352
toDisable.push(...toKeep.splice(maxApps));
@@ -337,7 +369,7 @@ export class AppServerOrchestrator {
337369
keptCount: grandfathered.length + toKeep.length,
338370
disabledCount: toDisable.length,
339371
});
340-
} catch (error) {
372+
} catch (error: any) {
341373
this._rocketchatLogger.error({
342374
msg: 'Error disabling apps',
343375
err: error,
@@ -352,45 +384,41 @@ export class AppServerOrchestrator {
352384
return;
353385
}
354386

355-
return this._manager
356-
.unload()
387+
return (this._manager.unload as any)()
357388
.then(() => this._rocketchatLogger.info('Unloaded the Apps Framework.'))
358-
.catch((err) =>
389+
.catch((err: any) =>
359390
this._rocketchatLogger.error({
360391
msg: 'Failed to unload the Apps Framework!',
361392
err,
362393
}),
363394
);
364395
}
365396

366-
async updateAppsMarketplaceInfo(apps = []) {
397+
async updateAppsMarketplaceInfo(apps: any[] = []) {
367398
if (!this.isLoaded()) {
368399
return;
369400
}
370401

371402
return this._manager.updateAppsMarketplaceInfo(apps).then(() => this._manager.get());
372403
}
373404

374-
/**
375-
* @returns {Promise<import('@rocket.chat/apps-engine/server/ProxiedApp').ProxiedApp[] | undefined>}
376-
*/
377-
async installedApps(filter = {}) {
405+
async installedApps(filter: Record<string, any> = {}): Promise<ProxiedApp[] | undefined> {
378406
if (!this.isLoaded()) {
379407
return;
380408
}
381409

382410
return this._manager.get(filter);
383411
}
384412

385-
async triggerEvent(event, ...payload) {
413+
async triggerEvent(event: string, ...payload: any[]) {
386414
if (!this.isLoaded()) {
387415
return;
388416
}
389417

390418
return this.getBridges()
391419
.getListenerBridge()
392420
.handleEvent({ event, payload })
393-
.catch((error) => {
421+
.catch((error: any) => {
394422
if (error instanceof EssentialAppDisabledException) {
395423
throw new Meteor.Error('error-essential-app-disabled');
396424
}

0 commit comments

Comments
 (0)