Browse Source

feat: improved duplicate logic

Signed-off-by: mertmit <mertmit99@gmail.com>
feat/export-nest-dir-restructure
mertmit 2 years ago committed by starbirdtech383
parent
commit
09e9e525f6
  1. 38
      packages/nc-gui/pages/index/index/index.vue
  2. 36
      packages/nocodb-nest/src/modules/jobs/export-import/duplicate.controller.ts
  3. 60
      packages/nocodb-nest/src/modules/jobs/export-import/duplicate.processor.ts

38
packages/nc-gui/pages/index/index/index.vue

@ -39,9 +39,17 @@ const filterQuery = ref('')
const projects = ref<ProjectType[]>()
const activePage = ref(1)
const pageChange = (p: number) => {
activePage.value = p
}
const loadProjects = async () => {
const lastPage = activePage.value
const response = await api.project.list({})
projects.value = response.list
activePage.value = lastPage
}
const filteredProjects = computed(
@ -85,12 +93,14 @@ const duplicateProject = (project: ProjectType) => {
try {
const jobData = await api.project.duplicate(project.id as string)
await loadProjects()
$events.subscribe(jobData.name, jobData.id, async (data: { status: string }) => {
console.log('dataCB', data)
if (data.status === 'completed' || data.status === 'refresh') {
if (data.status === 'completed') {
await loadProjects()
} else if (data.status === 'failed') {
message.error('Failed to duplicate project')
await loadProjects()
}
})
@ -224,7 +234,7 @@ const copyProjectMeta = async () => {
v-else
:custom-row="customRow"
:data-source="filteredProjects"
:pagination="{ position: ['bottomCenter'] }"
:pagination="{ 'position': ['bottomCenter'], 'current': activePage, 'onUpdate:current': pageChange }"
:table-layout="md ? 'auto' : 'fixed'"
>
<template #emptyText>
@ -301,19 +311,27 @@ const copyProjectMeta = async () => {
@click.stop="navigateTo(`/${text}`)"
/>
<component
:is="iconMap.copy"
v-e="['c:project:duplicate']"
class="nc-action-btn"
@click.stop="duplicateProject(record)"
/>
<component
:is="iconMap.delete"
class="nc-action-btn"
:data-testid="`delete-project-${record.title}`"
@click.stop="deleteProject(record)"
/>
<a-dropdown :trigger="['click']" overlay-class-name="nc-dropdown-import-menu" @click.stop>
<GeneralIcon icon="threeDotVertical" class="nc-import-menu outline-0" />
<template #overlay>
<a-menu class="!py-0 rounded text-sm">
<a-menu-item key="duplicate" v-e="['c:project:duplicate']" @click.stop="duplicateProject(record)">
<div class="color-transition nc-project-menu-item group">
<GeneralIcon icon="copy" class="group-hover:text-accent" />
Duplicate
</div>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</div>
</template>
</a-table-column>

36
packages/nocodb-nest/src/modules/jobs/export-import/duplicate.controller.ts

@ -14,6 +14,9 @@ import {
Acl,
ExtractProjectIdMiddleware,
} from 'src/middlewares/extract-project-id/extract-project-id.middleware';
import { ProjectsService } from 'src/services/projects.service';
import { Base, Project } from 'src/models';
import { generateUniqueName } from 'src/helpers/exportImportHelpers';
import { QueueService } from '../fallback-queue.service';
@Controller()
@ -23,6 +26,7 @@ export class DuplicateController {
constructor(
@InjectQueue('jobs') private readonly jobsQueue: Queue,
private readonly fallbackQueueService: QueueService,
private readonly projectsService: ProjectsService,
) {
this.activeQueue = process.env.NC_REDIS_URL
? this.jobsQueue
@ -37,14 +41,42 @@ export class DuplicateController {
@Param('projectId') projectId: string,
@Param('baseId') baseId?: string,
) {
const project = await Project.get(projectId);
if (!project) {
throw new Error(`Project not found for id '${projectId}'`);
}
const base = baseId
? await Base.get(baseId)
: (await project.getBases())[0];
if (!base) {
throw new Error(`Base not found!`);
}
const projects = await Project.list({});
const uniqueTitle = generateUniqueName(
`${project.title} copy`,
projects.map((p) => p.title),
);
const dupProject = await this.projectsService.projectCreate({
project: { title: uniqueTitle, status: 'job' },
user: { id: req.user.id },
});
const job = await this.activeQueue.add('duplicate', {
projectId,
baseId,
project,
base,
dupProject,
req: {
user: req.user,
clientIp: req.clientIp,
},
});
return { id: job.id, name: job.name };
}
}

60
packages/nocodb-nest/src/modules/jobs/export-import/duplicate.processor.ts

@ -6,15 +6,12 @@ import {
Process,
Processor,
} from '@nestjs/bull';
import { Base, Column, Model, Project } from 'src/models';
import { Column, Model } from 'src/models';
import { Job } from 'bull';
import { ProjectsService } from 'src/services/projects.service';
import boxen from 'boxen';
import papaparse from 'papaparse';
import {
findWithIdentifier,
generateUniqueName,
} from 'src/helpers/exportImportHelpers';
import { findWithIdentifier } from 'src/helpers/exportImportHelpers';
import { BulkDataAliasService } from 'src/services/bulk-data-alias.service';
import { UITypes } from 'nocodb-sdk';
import { forwardRef, Inject } from '@nestjs/common';
@ -76,6 +73,9 @@ export class DuplicateProcessor {
@Process('duplicate')
async duplicateBase(job: Job) {
const { project, base, dupProject, req } = job.data;
try {
let start = process.hrtime();
const debugLog = function (...args: any[]) {
@ -91,23 +91,7 @@ export class DuplicateProcessor {
start = process.hrtime();
};
const param: { projectId: string; baseId?: string; req: any } = job.data;
const user = (param.req as any).user;
const project = await Project.get(param.projectId);
if (!project) {
throw new Error(`Base not found for id '${param.baseId}'`);
}
const base = param?.baseId
? await Base.get(param.baseId)
: (await project.getBases())[0];
if (!base) {
throw new Error(`Base not found!`);
}
const user = (req as any).user;
const models = (await base.getModels()).filter(
(m) => !m.mm && m.type === 'table',
@ -123,24 +107,6 @@ export class DuplicateProcessor {
throw new Error(`Export failed for base '${base.id}'`);
}
const projects = await Project.list({});
const uniqueTitle = generateUniqueName(
`${project.title} copy`,
projects.map((p) => p.title),
);
const dupProject = await this.projectsService.projectCreate({
project: { title: uniqueTitle, status: 'job' },
user: { id: user.id },
});
this.jobsGateway.jobStatus({
name: job.name,
id: job.id.toString(),
status: 'refresh',
});
const dupBaseId = dupProject.bases[0].id;
elapsedTime('projectCreate');
@ -150,7 +116,7 @@ export class DuplicateProcessor {
projectId: dupProject.id,
baseId: dupBaseId,
data: exportedModels,
req: param.req,
req: req,
});
elapsedTime('importModels');
@ -181,7 +147,9 @@ export class DuplicateProcessor {
const headers: string[] = [];
let chunk = [];
const model = await Model.get(findWithIdentifier(idMap, sourceModel.id));
const model = await Model.get(
findWithIdentifier(idMap, sourceModel.id),
);
await new Promise((resolve) => {
papaparse.parse(dataStream, {
@ -366,5 +334,13 @@ export class DuplicateProcessor {
status: null,
},
});
} catch (e) {
if (dupProject?.id) {
await this.projectsService.projectSoftDelete({
projectId: dupProject.id,
});
}
throw e;
}
}
}

Loading…
Cancel
Save