Browse Source

feat: instant base delete

pull/6528/head
mertmit 1 year ago
parent
commit
4b9b8fde62
  1. 19
      packages/nocodb/src/controllers/bases.controller.ts
  2. 1
      packages/nocodb/src/interface/Jobs.ts
  3. 4
      packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts
  4. 16
      packages/nocodb/src/meta/migrations/v2/nc_036_base_deleted.ts
  5. 102
      packages/nocodb/src/models/Base.ts
  6. 6
      packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts
  7. 4
      packages/nocodb/src/modules/jobs/jobs.module.ts
  8. 36
      packages/nocodb/src/modules/jobs/jobs/base-delete/base-delete.controller.ts
  9. 23
      packages/nocodb/src/modules/jobs/jobs/base-delete/base-delete.processor.ts
  10. 17
      packages/nocodb/src/services/bases.service.ts
  11. 2
      packages/nocodb/src/services/columns.service.ts

19
packages/nocodb/src/controllers/bases.controller.ts

@ -1,12 +1,4 @@
import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
UseGuards,
} from '@nestjs/common';
import { Body, Controller, Get, Param, Patch, UseGuards } from '@nestjs/common';
import { BaseReqType } from 'nocodb-sdk';
import { GlobalGuard } from '~/guards/global/global.guard';
import { PagedResponseImpl } from '~/helpers/PagedResponse';
@ -66,13 +58,4 @@ export class BasesController {
limit: bases.length,
});
}
@Delete('/api/v1/db/meta/projects/:projectId/bases/:baseId')
@Acl('baseDelete')
async baseDelete(@Param('baseId') baseId: string) {
const result = await this.basesService.baseDelete({
baseId,
});
return result;
}
}

1
packages/nocodb/src/interface/Jobs.ts

@ -6,6 +6,7 @@ export enum JobTypes {
AtImport = 'at-import',
MetaSync = 'meta-sync',
BaseCreate = 'base-create',
BaseDelete = 'base-delete',
}
export enum JobStatus {

4
packages/nocodb/src/meta/migrations/XcMigrationSourcev2.ts

@ -22,6 +22,7 @@ import * as nc_031_remove_fk_and_add_idx from './v2/nc_031_remove_fk_and_add_idx
import * as nc_033_add_group_by from './v2/nc_033_add_group_by';
import * as nc_034_erd_filter_and_notification from './v2/nc_034_erd_filter_and_notification';
import * as nc_035_add_username_to_users from './v2/nc_035_add_username_to_users';
import * as nc_036_base_deleted from './v2/nc_036_base_deleted';
// Create a custom migration source class
export default class XcMigrationSourcev2 {
@ -55,6 +56,7 @@ export default class XcMigrationSourcev2 {
'nc_033_add_group_by',
'nc_034_erd_filter_and_notification',
'nc_035_add_username_to_users',
'nc_036_base_deleted',
]);
}
@ -112,6 +114,8 @@ export default class XcMigrationSourcev2 {
return nc_034_erd_filter_and_notification;
case 'nc_035_add_username_to_users':
return nc_035_add_username_to_users;
case 'nc_036_base_deleted':
return nc_036_base_deleted;
}
}
}

16
packages/nocodb/src/meta/migrations/v2/nc_036_base_deleted.ts

@ -0,0 +1,16 @@
import type { Knex } from 'knex';
import { MetaTable } from '~/utils/globals';
const up = async (knex: Knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.boolean('deleted').defaultTo(false);
});
};
const down = async (knex: Knex) => {
await knex.schema.alterTable(MetaTable.BASES, (table) => {
table.dropColumn('deleted');
});
};
export { up, down };

102
packages/nocodb/src/models/Base.ts

@ -87,7 +87,7 @@ export default class Base implements BaseType {
);
// call before reorder to update cache
const returnBase = await this.get(id, ncMeta);
const returnBase = await this.get(id, false, ncMeta);
await this.reorderBases(base.projectId);
@ -100,10 +100,11 @@ export default class Base implements BaseType {
projectId: string;
skipReorder?: boolean;
meta?: any;
deleted?: boolean;
},
ncMeta = Noco.ncMeta,
) {
const oldBase = await Base.get(baseId, ncMeta);
const oldBase = await Base.get(baseId, false, ncMeta);
if (!oldBase) NcError.badRequest('Wrong base id!');
@ -123,6 +124,7 @@ export default class Base implements BaseType {
'order',
'enabled',
'meta',
'deleted',
]);
if (updateObj.config) {
@ -156,7 +158,7 @@ export default class Base implements BaseType {
);
// call before reorder to update cache
const returnBase = await this.get(oldBase.id, ncMeta);
const returnBase = await this.get(oldBase.id, false, ncMeta);
if (!base.skipReorder)
await this.reorderBases(base.projectId, returnBase.id, ncMeta);
@ -179,6 +181,20 @@ export default class Base implements BaseType {
null,
MetaTable.BASES,
{
xcCondition: {
_or: [
{
deleted: {
neq: true,
},
},
{
deleted: {
eq: null,
},
},
],
},
orderBy: {
order: 'asc',
},
@ -202,7 +218,11 @@ export default class Base implements BaseType {
});
}
static async get(id: string, ncMeta = Noco.ncMeta): Promise<Base> {
static async get(
id: string,
force = false,
ncMeta = Noco.ncMeta,
): Promise<Base> {
let baseData =
id &&
(await NocoCache.get(
@ -210,7 +230,29 @@ export default class Base implements BaseType {
CacheGetType.TYPE_OBJECT,
));
if (!baseData) {
baseData = await ncMeta.metaGet2(null, null, MetaTable.BASES, id);
baseData = await ncMeta.metaGet2(
null,
null,
MetaTable.BASES,
id,
null,
force
? {}
: {
_or: [
{
deleted: {
neq: true,
},
},
{
deleted: {
eq: null,
},
},
],
},
);
if (baseData) {
baseData.meta = parseMetaProp(baseData, 'meta');
@ -222,9 +264,29 @@ export default class Base implements BaseType {
}
static async getByUUID(uuid: string, ncMeta = Noco.ncMeta) {
const base = await ncMeta.metaGet2(null, null, MetaTable.BASES, {
const base = await ncMeta.metaGet2(
null,
null,
MetaTable.BASES,
{
erd_uuid: uuid,
});
},
null,
{
_or: [
{
deleted: {
neq: true,
},
},
{
deleted: {
eq: null,
},
},
],
},
);
if (!base) return null;
@ -398,6 +460,32 @@ export default class Base implements BaseType {
return await ncMeta.metaDelete(null, null, MetaTable.BASES, this.id);
}
async softDelete(ncMeta = Noco.ncMeta, { force }: { force?: boolean } = {}) {
const bases = await Base.list({ projectId: this.project_id }, ncMeta);
if (bases[0].id === this.id && !force) {
NcError.badRequest('Cannot delete first base');
}
await ncMeta.metaUpdate(
this.project_id,
null,
MetaTable.BASES,
{
deleted: true,
},
this.id,
);
await NocoCache.deepDel(
CacheScope.BASE,
`${CacheScope.BASE}:${this.id}`,
CacheDelDirection.CHILD_TO_PARENT,
);
await NocoCache.del(`${CacheScope.BASE}:${this.id}`);
}
async getModels(ncMeta = Noco.ncMeta) {
return await Model.list(
{ project_id: this.project_id, base_id: this.id },

6
packages/nocodb/src/modules/jobs/fallback/fallback-queue.service.ts

@ -5,6 +5,7 @@ import { DuplicateProcessor } from '../jobs/export-import/duplicate.processor';
import { AtImportProcessor } from '../jobs/at-import/at-import.processor';
import { MetaSyncProcessor } from '../jobs/meta-sync/meta-sync.processor';
import { BaseCreateProcessor } from '../jobs/base-create/base-create.processor';
import { BaseDeleteProcessor } from '../jobs/base-delete/base-delete.processor';
import { JobsEventService } from './jobs-event.service';
import { JobStatus, JobTypes } from '~/interface/Jobs';
@ -29,6 +30,7 @@ export class QueueService {
private readonly atImportProcessor: AtImportProcessor,
private readonly metaSyncProcessor: MetaSyncProcessor,
private readonly baseCreateProcessor: BaseCreateProcessor,
private readonly baseDeleteProcessor: BaseDeleteProcessor,
) {
this.emitter.on(JobStatus.ACTIVE, (data: { job: Job }) => {
const job = this.queueMemory.find((job) => job.id === data.job.id);
@ -78,6 +80,10 @@ export class QueueService {
this: this.baseCreateProcessor,
fn: this.baseCreateProcessor.job,
},
[JobTypes.BaseDelete]: {
this: this.baseDeleteProcessor,
fn: this.baseDeleteProcessor.job,
},
};
async jobWrapper(job: Job) {

4
packages/nocodb/src/modules/jobs/jobs.module.ts

@ -12,6 +12,8 @@ import { MetaSyncController } from './jobs/meta-sync/meta-sync.controller';
import { MetaSyncProcessor } from './jobs/meta-sync/meta-sync.processor';
import { BaseCreateController } from './jobs/base-create/base-create.controller';
import { BaseCreateProcessor } from './jobs/base-create/base-create.processor';
import { BaseDeleteController } from './jobs/base-delete/base-delete.controller';
import { BaseDeleteProcessor } from './jobs/base-delete/base-delete.processor';
// Jobs Module Related
import { JobsLogService } from './jobs/jobs-log.service';
@ -54,6 +56,7 @@ import { GlobalModule } from '~/modules/global/global.module';
AtImportController,
MetaSyncController,
BaseCreateController,
BaseDeleteController,
]
: []),
],
@ -75,6 +78,7 @@ import { GlobalModule } from '~/modules/global/global.module';
AtImportProcessor,
MetaSyncProcessor,
BaseCreateProcessor,
BaseDeleteProcessor,
],
})
export class JobsModule {}

36
packages/nocodb/src/modules/jobs/jobs/base-delete/base-delete.controller.ts

@ -0,0 +1,36 @@
import { Controller, Delete, Inject, Param, UseGuards } from '@nestjs/common';
import { GlobalGuard } from '~/guards/global/global.guard';
import { Acl } from '~/middlewares/extract-ids/extract-ids.middleware';
import { NcError } from '~/helpers/catchError';
import { JobTypes } from '~/interface/Jobs';
import { BasesService } from '~/services/bases.service';
@Controller()
@UseGuards(GlobalGuard)
export class BaseDeleteController {
constructor(
@Inject('JobsService') private readonly jobsService,
private readonly basesService: BasesService,
) {}
@Delete('/api/v1/db/meta/projects/:projectId/bases/:baseId')
@Acl('baseDelete')
async baseDelete(@Param('baseId') baseId: string) {
const jobs = await this.jobsService.jobList();
const fnd = jobs.find(
(j) => j.name === JobTypes.BaseDelete && j.data.baseId === baseId,
);
if (fnd) {
NcError.badRequest('There is already a job running to delete this base.');
}
await this.basesService.baseSoftDelete({ baseId });
const job = await this.jobsService.add(JobTypes.BaseDelete, {
baseId,
});
return { id: job.id };
}
}

23
packages/nocodb/src/modules/jobs/jobs/base-delete/base-delete.processor.ts

@ -0,0 +1,23 @@
import debug from 'debug';
import { Process, Processor } from '@nestjs/bull';
import { Job } from 'bull';
import { JOBS_QUEUE, JobTypes } from '~/interface/Jobs';
import { BasesService } from '~/services/bases.service';
@Processor(JOBS_QUEUE)
export class BaseDeleteProcessor {
private readonly debugLog = debug('nc:meta-sync:processor');
constructor(private readonly basesService: BasesService) {}
@Process(JobTypes.BaseDelete)
async job(job: Job) {
const { baseId } = job.data;
await this.basesService.baseDelete({
baseId,
});
return true;
}
}

17
packages/nocodb/src/services/bases.service.ts

@ -6,6 +6,7 @@ import { populateMeta, validatePayload } from '~/helpers';
import { populateRollupColumnAndHideLTAR } from '~/helpers/populateMeta';
import { syncBaseMigration } from '~/helpers/syncMigration';
import { Base, Project } from '~/models';
import { NcError } from '~/helpers/catchError';
@Injectable()
export class BasesService {
@ -51,11 +52,25 @@ export class BasesService {
}
async baseDelete(param: { baseId: string }) {
const base = await Base.get(param.baseId);
try {
const base = await Base.get(param.baseId, true);
await base.delete();
this.appHooksService.emit(AppEvents.BASE_DELETE, {
base,
});
} catch (e) {
NcError.badRequest(e);
}
return true;
}
async baseSoftDelete(param: { baseId: string }) {
try {
const base = await Base.get(param.baseId);
await base.softDelete();
} catch (e) {
NcError.badRequest(e);
}
return true;
}

2
packages/nocodb/src/services/columns.service.ts

@ -1266,7 +1266,7 @@ export class ColumnsService {
),
);
const base = await reuseOrSave('base', reuse, async () =>
Base.get(table.base_id, ncMeta),
Base.get(table.base_id, false, ncMeta),
);
const sqlMgr = await reuseOrSave('sqlMgr', reuse, async () =>

Loading…
Cancel
Save