Add uninstall button to application setting (#15988)

As title

<img width="878" height="668" alt="image"
src="https://github.com/user-attachments/assets/b0c9ae1e-036f-4bdd-9bd2-a2a37c2e3b99"
/>
This commit is contained in:
martmull
2025-11-21 14:45:07 +00:00
committed by GitHub
parent 1fa9c45879
commit 445b76fa26
25 changed files with 352 additions and 59 deletions
@@ -1,5 +1,5 @@
import { existsSync } from 'fs';
import { AppDeleteCommand } from '../../commands/app-delete.command';
import { AppUninstallCommand } from 'src/commands/app-uninstall.command';
import { AppSyncCommand } from '../../commands/app-sync.command';
import { COVERED_APPLICATION_FOLDERS } from './constants/covered-applications-folder.constant';
import { getTestedApplicationPath } from './utils/get-tested-application-path.util';
@@ -8,7 +8,7 @@ describe.each(COVERED_APPLICATION_FOLDERS)(
'Application: "%s" install delete and reinstall test suite',
(applicationName) => {
const syncCommand = new AppSyncCommand();
const deleteCommand = new AppDeleteCommand();
const deleteCommand = new AppUninstallCommand();
const appPath = getTestedApplicationPath(applicationName);
beforeAll(async () => {
@@ -5,7 +5,7 @@ import { ApiService } from '../services/api.service';
import { ApiResponse } from '../types/config.types';
import { loadManifest } from '../utils/load-manifest';
export class AppDeleteCommand {
export class AppUninstallCommand {
private apiService = new ApiService();
async execute({
@@ -16,31 +16,31 @@ export class AppDeleteCommand {
askForConfirmation: boolean;
}): Promise<ApiResponse<any>> {
try {
console.log(chalk.blue('🚀 Deleting Twenty Application'));
console.log(chalk.blue('🚀 Uninstall Twenty Application'));
console.log(chalk.gray(`📁 App Path: ${appPath}`));
console.log('');
if (askForConfirmation && !(await this.confirmationPrompt())) {
console.error(chalk.red('⛔️ Aborting deletion'));
console.error(chalk.red('⛔️ Aborting uninstall'));
process.exit(1);
}
const { manifest } = await loadManifest(appPath);
const result = await this.apiService.deleteApplication(
const result = await this.apiService.uninstallApplication(
manifest.application.universalIdentifier,
);
if (!result.success) {
console.error(chalk.red('❌ Deletion failed:'), result.error);
console.error(chalk.red('❌ Uninstall failed:'), result.error);
} else {
console.log(chalk.green('✅ Application deleted successfully'));
console.log(chalk.green('✅ Application uninstalled successfully'));
}
return result;
} catch (error) {
console.error(
chalk.red('Deletion failed:'),
chalk.red('Uninstall failed:'),
error instanceof Error ? error.message : error,
);
throw error;
@@ -52,7 +52,7 @@ export class AppDeleteCommand {
{
type: 'confirm',
name: 'confirmation',
message: 'Are you sure you want to delete this application?',
message: 'Are you sure you want to uninstall this application?',
default: false,
},
]);
@@ -5,7 +5,7 @@ import {
isSyncableEntity,
SyncableEntity,
} from './app-add.command';
import { AppDeleteCommand } from './app-delete.command';
import { AppUninstallCommand } from './app-uninstall.command';
import { AppDevCommand } from './app-dev.command';
import { AppInitCommand } from './app-init.command';
import { AppSyncCommand } from './app-sync.command';
@@ -15,7 +15,7 @@ import { AppGenerateCommand } from './app-generate.command';
export class AppCommand {
private devCommand = new AppDevCommand();
private syncCommand = new AppSyncCommand();
private deleteCommand = new AppDeleteCommand();
private uninstallCommand = new AppUninstallCommand();
private initCommand = new AppInitCommand();
private addCommand = new AppAddCommand();
private generateCommand = new AppGenerateCommand();
@@ -50,11 +50,29 @@ export class AppCommand {
});
appCommand
.command('delete [appPath]')
.command('uninstall [appPath]')
.description('Uninstall application from Twenty')
.action(async (appPath?: string) => {
try {
const result = await this.uninstallCommand.execute({
appPath: formatPath(appPath),
askForConfirmation: true,
});
if (!result.success) {
process.exit(1);
}
} catch {
process.exit(1);
}
});
// Keeping to avoid breaking changes
appCommand
.command('delete [appPath]', { hidden: true })
.description('Delete application from Twenty')
.action(async (appPath?: string) => {
try {
const result = await this.deleteCommand.execute({
const result = await this.uninstallCommand.execute({
appPath: formatPath(appPath),
askForConfirmation: true,
});
@@ -146,11 +146,13 @@ export class ApiService {
}
}
async deleteApplication(universalIdentifier: string): Promise<ApiResponse> {
async uninstallApplication(
universalIdentifier: string,
): Promise<ApiResponse> {
try {
const mutation = `
mutation DeleteApplication($universalIdentifier: String!) {
deleteApplication(universalIdentifier: $universalIdentifier)
mutation UninstallApplication($universalIdentifier: String!) {
uninstallApplication(universalIdentifier: $universalIdentifier)
}
`;
@@ -180,8 +182,8 @@ export class ApiService {
return {
success: true,
data: response.data.data.deleteApplication,
message: 'Successfully deleted application',
data: response.data.data.uninstallApplication,
message: 'Successfully uninstalled application',
};
} catch (error) {
if (axios.isAxiosError(error) && error.response) {