Skip to content

Commit a05b8f7

Browse files
authored
feat(apps): stop persisting intermediate status transitions to database (#37167)
1 parent 7b176ff commit a05b8f7

File tree

5 files changed

+39
-73
lines changed

5 files changed

+39
-73
lines changed

.changeset/real-pans-confess.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
'@rocket.chat/apps-engine': minor
3+
'@rocket.chat/meteor': minor
4+
---
5+
6+
Changes a behavior that would store the result of every status transition that happened to apps
7+
8+
This caused intermediate status to be saved to the database, which could prevent apps from being restored to the desired status when restarted or during server startup.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,8 +474,8 @@ export class AppsRestApi {
474474
try {
475475
await canEnableApp(aff.getApp().getStorageItem());
476476

477-
const success = await manager.enable(info.id);
478-
info.status = success ? AppStatus.AUTO_ENABLED : info.status;
477+
const success = await manager.changeStatus(info.id, AppStatus.MANUALLY_ENABLED);
478+
info.status = await success.getStatus();
479479
} catch (error) {
480480
orchestrator.getRocketChatLogger().warn(`App "${info.id}" was installed but could not be enabled: `, error);
481481
}

apps/meteor/tests/end-to-end/api/livechat/00-rooms.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ describe('LIVECHAT - rooms', () => {
101101
expect(res.body).to.have.a.property('app');
102102
expect(res.body.app).to.have.a.property('id');
103103
expect(res.body.app).to.have.a.property('version');
104-
expect(res.body.app).to.have.a.property('status').and.to.be.equal('auto_enabled');
104+
expect(res.body.app).to.have.a.property('status').and.to.be.equal('manually_enabled');
105105

106106
appId = res.body.app.id;
107107
});

apps/meteor/tests/end-to-end/apps/installation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ describe('Apps - Installation', () => {
5252
expect(res.body).to.have.a.property('app');
5353
expect(res.body.app).to.have.a.property('id');
5454
expect(res.body.app).to.have.a.property('version');
55-
expect(res.body.app).to.have.a.property('status').and.to.be.equal('auto_enabled');
55+
expect(res.body.app).to.have.a.property('status').and.to.be.equal('manually_enabled');
5656

5757
app = res.body.app;
5858
})

packages/apps-engine/src/server/AppManager.ts

Lines changed: 27 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ export class AppManager {
310310
continue;
311311
}
312312

313-
await this.initializeApp(rl.getStorageItem(), rl, false, true).catch(console.error);
313+
await this.initializeApp(rl, true).catch(console.error);
314314
}
315315

316316
// Let's ensure the required settings are all set
@@ -329,7 +329,7 @@ export class AppManager {
329329
for (const app of this.apps.values()) {
330330
const status = await app.getStatus();
331331
if (!AppStatusUtils.isDisabled(status) && AppStatusUtils.isEnabled(app.getPreviousStatus())) {
332-
await this.enableApp(app.getStorageItem(), app, true, app.getPreviousStatus() === AppStatus.MANUALLY_ENABLED).catch(console.error);
332+
await this.enableApp(app).catch(console.error);
333333
} else if (!AppStatusUtils.isError(status)) {
334334
this.listenerManager.lockEssentialEvents(app);
335335
this.uiActionButtonManager.clearAppActionButtons(app.getID());
@@ -457,14 +457,7 @@ export class AppManager {
457457
throw new Error(`Could not enable an App with the id of "${id}" as it doesn't exist.`);
458458
}
459459

460-
const isSetup = await this.runStartUpProcess(storageItem, rl, true, false);
461-
462-
if (isSetup) {
463-
storageItem.status = await rl.getStatus();
464-
// This is async, but we don't care since it only updates in the database
465-
// and it should not mutate any properties we care about
466-
await this.appMetadataStorage.updateStatus(storageItem._id, storageItem.status).catch(() => {});
467-
}
460+
const isSetup = await this.runStartUpProcess(storageItem, rl, false);
468461

469462
return isSetup;
470463
}
@@ -495,12 +488,7 @@ export class AppManager {
495488
const storageItem = await this.appMetadataStorage.retrieveOne(id);
496489

497490
app.getStorageItem().marketplaceInfo = storageItem.marketplaceInfo;
498-
await app.validateLicense().catch();
499-
500-
storageItem.status = await app.getStatus();
501-
// This is async, but we don't care since it only updates in the database
502-
// and it should not mutate any properties we care about
503-
await this.appMetadataStorage.updateStatus(storageItem._id, storageItem.status).catch(() => {});
491+
await app.validateLicense().catch(() => {});
504492

505493
return true;
506494
}
@@ -570,7 +558,7 @@ export class AppManager {
570558
const descriptor: IAppStorageItem = {
571559
id: result.info.id,
572560
info: result.info,
573-
status: AppStatus.UNKNOWN,
561+
status: enable ? AppStatus.MANUALLY_ENABLED : AppStatus.MANUALLY_DISABLED,
574562
settings: {},
575563
implemented: result.implemented.getValues(),
576564
installationSource: marketplaceInfo ? AppInstallationSource.MARKETPLACE : AppInstallationSource.PRIVATE,
@@ -645,15 +633,15 @@ export class AppManager {
645633
// If an error occurs during this, oh well.
646634
});
647635

648-
await this.installApp(created, app, user);
636+
await this.installApp(app, user);
649637

650638
// Should enable === true, then we go through the entire start up process
651639
// Otherwise, we only initialize it.
652640
if (enable) {
653641
// Start up the app
654-
await this.runStartUpProcess(created, app, false, false);
642+
await this.runStartUpProcess(created, app, false);
655643
} else {
656-
await this.initializeApp(created, app, true);
644+
await this.initializeApp(app);
657645
}
658646

659647
return aff;
@@ -727,15 +715,10 @@ export class AppManager {
727715

728716
const descriptor: IAppStorageItem = {
729717
...old,
730-
createdAt: old.createdAt,
731718
id: result.info.id,
732719
info: result.info,
733-
status: (await this.apps.get(old.id)?.getStatus()) || old.status,
734720
languageContent: result.languageContent,
735-
settings: old.settings,
736721
implemented: result.implemented.getValues(),
737-
...(old.marketplaceInfo && { marketplaceInfo: old.marketplaceInfo }),
738-
...(old.sourcePath && { sourcePath: old.sourcePath }),
739722
};
740723

741724
if (!permissionsGranted) {
@@ -833,12 +816,12 @@ export class AppManager {
833816

834817
public async updateAndStartupLocal(stored: IAppStorageItem, appPackageOrInstance: ProxiedApp | Buffer) {
835818
const app = await this.updateLocal(stored, appPackageOrInstance);
836-
await this.runStartUpProcess(stored, app, false, true);
819+
await this.runStartUpProcess(stored, app, true);
837820
}
838821

839822
public async updateAndInitializeLocal(stored: IAppStorageItem, appPackageOrInstance: ProxiedApp | Buffer) {
840823
const app = await this.updateLocal(stored, appPackageOrInstance);
841-
await this.initializeApp(stored, app, true, true);
824+
await this.initializeApp(app, true);
842825
}
843826

844827
public getLanguageContent(): { [key: string]: object } {
@@ -870,19 +853,27 @@ export class AppManager {
870853
throw new Error('Can not change the status of an App which does not currently exist.');
871854
}
872855

856+
const storageItem = await rl.getStorageItem();
857+
873858
if (AppStatusUtils.isEnabled(status)) {
874859
// Then enable it
875860
if (AppStatusUtils.isEnabled(await rl.getStatus())) {
876861
throw new Error('Can not enable an App which is already enabled.');
877862
}
878863

879864
await this.enable(rl.getID());
865+
866+
storageItem.status = AppStatus.MANUALLY_ENABLED;
867+
await this.appMetadataStorage.updateStatus(storageItem._id, AppStatus.MANUALLY_ENABLED);
880868
} else {
881869
if (!AppStatusUtils.isEnabled(await rl.getStatus())) {
882870
throw new Error('Can not disable an App which is not enabled.');
883871
}
884872

885873
await this.disable(rl.getID(), AppStatus.MANUALLY_DISABLED);
874+
875+
storageItem.status = AppStatus.MANUALLY_DISABLED;
876+
await this.appMetadataStorage.updateStatus(storageItem._id, AppStatus.MANUALLY_DISABLED);
886877
}
887878

888879
return rl;
@@ -973,27 +964,22 @@ export class AppManager {
973964

974965
const item = rl.getStorageItem();
975966

976-
await this.initializeApp(item, rl, false, silenceStatus);
967+
await this.initializeApp(rl, silenceStatus);
977968

978969
if (!this.areRequiredSettingsSet(item)) {
979970
await rl.setStatus(AppStatus.INVALID_SETTINGS_DISABLED);
980971
}
981972

982973
if (!AppStatusUtils.isDisabled(await rl.getStatus()) && AppStatusUtils.isEnabled(rl.getPreviousStatus())) {
983-
await this.enableApp(item, rl, false, rl.getPreviousStatus() === AppStatus.MANUALLY_ENABLED, silenceStatus);
974+
await this.enableApp(rl, silenceStatus);
984975
}
985976

986977
return this.apps.get(item.id);
987978
}
988979

989-
private async runStartUpProcess(
990-
storageItem: IAppStorageItem,
991-
app: ProxiedApp,
992-
isManual: boolean,
993-
silenceStatus: boolean,
994-
): Promise<boolean> {
980+
private async runStartUpProcess(storageItem: IAppStorageItem, app: ProxiedApp, silenceStatus: boolean): Promise<boolean> {
995981
if ((await app.getStatus()) !== AppStatus.INITIALIZED) {
996-
const isInitialized = await this.initializeApp(storageItem, app, true, silenceStatus);
982+
const isInitialized = await this.initializeApp(app, silenceStatus);
997983
if (!isInitialized) {
998984
return false;
999985
}
@@ -1004,10 +990,10 @@ export class AppManager {
1004990
return false;
1005991
}
1006992

1007-
return this.enableApp(storageItem, app, true, isManual, silenceStatus);
993+
return this.enableApp(app, silenceStatus);
1008994
}
1009995

1010-
private async installApp(_storageItem: IAppStorageItem, app: ProxiedApp, user: IUser): Promise<boolean> {
996+
private async installApp(app: ProxiedApp, user: IUser): Promise<boolean> {
1011997
let result: boolean;
1012998
const context = { user };
1013999

@@ -1044,7 +1030,7 @@ export class AppManager {
10441030
return result;
10451031
}
10461032

1047-
private async initializeApp(storageItem: IAppStorageItem, app: ProxiedApp, saveToDb = true, silenceStatus = false): Promise<boolean> {
1033+
private async initializeApp(app: ProxiedApp, silenceStatus = false): Promise<boolean> {
10481034
let result: boolean;
10491035

10501036
try {
@@ -1072,17 +1058,6 @@ export class AppManager {
10721058
result = false;
10731059

10741060
await app.setStatus(status, silenceStatus);
1075-
1076-
// If some error has happened in initialization, like license or installations invalidation
1077-
// we need to store this on the DB regardless of what the parameter requests
1078-
saveToDb = true;
1079-
}
1080-
1081-
if (saveToDb) {
1082-
// This is async, but we don't care since it only updates in the database
1083-
// and it should not mutate any properties we care about
1084-
storageItem.status = await app.getStatus();
1085-
await this.appMetadataStorage.updateStatus(storageItem._id, storageItem.status).catch(() => {});
10861061
}
10871062

10881063
return result;
@@ -1133,13 +1108,7 @@ export class AppManager {
11331108
return result;
11341109
}
11351110

1136-
private async enableApp(
1137-
storageItem: IAppStorageItem,
1138-
app: ProxiedApp,
1139-
saveToDb = true,
1140-
isManual: boolean,
1141-
silenceStatus = false,
1142-
): Promise<boolean> {
1111+
private async enableApp(app: ProxiedApp, silenceStatus = false): Promise<boolean> {
11431112
let enable: boolean;
11441113
let status = AppStatus.ERROR_DISABLED;
11451114

@@ -1150,7 +1119,7 @@ export class AppManager {
11501119
enable = (await app.call(AppMethod.ONENABLE)) as boolean;
11511120

11521121
if (enable) {
1153-
status = isManual ? AppStatus.MANUALLY_ENABLED : AppStatus.AUTO_ENABLED;
1122+
status = AppStatus.MANUALLY_ENABLED;
11541123
} else {
11551124
status = AppStatus.DISABLED;
11561125
console.warn(`The App (${app.getID()}) disabled itself when being enabled. \nCheck the "onEnable" implementation for details.`);
@@ -1167,10 +1136,6 @@ export class AppManager {
11671136
}
11681137

11691138
console.error(e);
1170-
1171-
// If some error has happened during enabling, like license or installations invalidation
1172-
// we need to store this on the DB regardless of what the parameter requests
1173-
saveToDb = true;
11741139
}
11751140

11761141
if (enable) {
@@ -1188,13 +1153,6 @@ export class AppManager {
11881153
});
11891154
}
11901155

1191-
if (saveToDb) {
1192-
storageItem.status = status;
1193-
// This is async, but we don't care since it only updates in the database
1194-
// and it should not mutate any properties we care about
1195-
await this.appMetadataStorage.updateStatus(storageItem._id, storageItem.status).catch(() => {});
1196-
}
1197-
11981156
await app.setStatus(status, silenceStatus);
11991157

12001158
return enable;

0 commit comments

Comments
 (0)