diff --git a/packages/nc-gui/components/dashboard/TreeView.vue b/packages/nc-gui/components/dashboard/TreeView.vue index b655cf8d03..0a8499d9f8 100644 --- a/packages/nc-gui/components/dashboard/TreeView.vue +++ b/packages/nc-gui/components/dashboard/TreeView.vue @@ -9,6 +9,7 @@ import type { VNodeRef } from '#imports' import { ClientType, Empty, + JobStatus, TabType, computed, extractSdkResponseErrorMsg, @@ -400,9 +401,9 @@ const duplicateTable = async (table: TableType) => { 'table': table, 'onOk': async (jobData: { name: string; id: string }) => { $jobs.subscribe({ name: jobData.name, id: jobData.id }, undefined, async (status: string) => { - if (status === 'completed') { + if (status === JobStatus.COMPLETED) { await loadTables() - } else if (status === 'failed') { + } else if (status === JobStatus.FAILED) { message.error('Failed to duplicate table') await loadTables() } diff --git a/packages/nc-gui/components/dlg/AirtableImport.vue b/packages/nc-gui/components/dlg/AirtableImport.vue index 15c0b15e4c..20124083f5 100644 --- a/packages/nc-gui/components/dlg/AirtableImport.vue +++ b/packages/nc-gui/components/dlg/AirtableImport.vue @@ -2,6 +2,7 @@ import type { Card as AntCard } from 'ant-design-vue' import { Form, + JobStatus, computed, extractSdkResponseErrorMsg, fieldRequiredValidator, @@ -67,7 +68,7 @@ const syncSource = ref({ }, }) -const pushProgress = async (message: string, status: 'completed' | 'failed' | 'progress') => { +const pushProgress = async (message: string, status: JobStatus | 'progress') => { progress.value.push({ msg: message, status }) await nextTick(() => { @@ -81,13 +82,13 @@ const onSubscribe = () => { step.value = 2 } -const onStatus = async (status: 'active' | 'completed' | 'failed' | 'refresh', error?: any) => { - if (status === 'completed') { +const onStatus = async (status: JobStatus, error?: any) => { + if (status === JobStatus.COMPLETED) { showGoToDashboardButton.value = true await loadTables() pushProgress('Done!', status) // TODO: add tab of the first table - } else if (status === 'failed') { + } else if (status === JobStatus.FAILED) { pushProgress(error, status) } } @@ -370,7 +371,7 @@ onMounted(async () => {
-
+
{{ msg }} @@ -387,7 +388,8 @@ onMounted(async () => { v-if=" !progress || !progress.length || - (progress[progress.length - 1].status !== 'completed' && progress[progress.length - 1].status !== 'failed') + (progress[progress.length - 1].status !== JobStatus.COMPLETED && + progress[progress.length - 1].status !== JobStatus.FAILED) " class="flex items-center" > diff --git a/packages/nc-gui/components/smartsheet/Grid.vue b/packages/nc-gui/components/smartsheet/Grid.vue index acebc53d36..85b008fdda 100644 --- a/packages/nc-gui/components/smartsheet/Grid.vue +++ b/packages/nc-gui/components/smartsheet/Grid.vue @@ -756,7 +756,7 @@ const closeAddColumnDropdown = () => { const confirmDeleteRow = (row: number) => { Modal.confirm({ title: `Do you want to delete this row?`, - wrapClassName: 'nc-modal-attachment-delete', + wrapClassName: 'nc-modal-row-delete', okText: 'Yes', okType: 'danger', cancelText: 'No', diff --git a/packages/nc-gui/lib/enums.ts b/packages/nc-gui/lib/enums.ts index b4aadc53c6..3727a39f47 100644 --- a/packages/nc-gui/lib/enums.ts +++ b/packages/nc-gui/lib/enums.ts @@ -105,3 +105,13 @@ export enum AutomationLogLevel { ERROR = 'ERROR', ALL = 'ALL', } + +export enum JobStatus { + COMPLETED = 'completed', + WAITING = 'waiting', + ACTIVE = 'active', + DELAYED = 'delayed', + FAILED = 'failed', + PAUSED = 'paused', + REFRESH = 'refresh', +} diff --git a/packages/nc-gui/nuxt-shim.d.ts b/packages/nc-gui/nuxt-shim.d.ts index 8be52b0ec9..dec8a6e113 100644 --- a/packages/nc-gui/nuxt-shim.d.ts +++ b/packages/nc-gui/nuxt-shim.d.ts @@ -1,6 +1,6 @@ import type { Api as BaseAPI } from 'nocodb-sdk' import type { UseGlobalReturn } from './composables/useGlobal/types' -import type { NocoI18n } from './lib' +import type { JobStatus, NocoI18n } from './lib' import type { TabType } from './composables' declare module '#app/nuxt' { @@ -22,7 +22,7 @@ declare module '#app/nuxt' { } | any, subscribedCb?: () => void, - statusCb?: ((status: 'active' | 'completed' | 'failed' | 'refresh', error?: any) => void) | undefined, + statusCb?: ((status: JobStatus, error?: any) => void) | undefined, logCb?: ((data: { message: string }) => void) | undefined, ): void getStatus(name: string, id: string): Promise diff --git a/packages/nc-gui/pages/index/index/index.vue b/packages/nc-gui/pages/index/index/index.vue index 77a7b14bf1..bd794a1365 100644 --- a/packages/nc-gui/pages/index/index/index.vue +++ b/packages/nc-gui/pages/index/index/index.vue @@ -4,6 +4,7 @@ import tinycolor from 'tinycolor2' import { breakpointsTailwind } from '@vueuse/core' import { Empty, + JobStatus, Modal, computed, definePageMeta, @@ -92,9 +93,9 @@ const duplicateProject = (project: ProjectType) => { await loadProjects() $jobs.subscribe({ name: jobData.name, id: jobData.id }, undefined, async (status: string) => { - if (status === 'completed') { + if (status === JobStatus.COMPLETED) { await loadProjects() - } else if (status === 'failed') { + } else if (status === JobStatus.FAILED) { message.error('Failed to duplicate project') await loadProjects() } diff --git a/packages/nc-gui/plugins/jobs.ts b/packages/nc-gui/plugins/jobs.ts index ceede03259..fd003acdbd 100644 --- a/packages/nc-gui/plugins/jobs.ts +++ b/packages/nc-gui/plugins/jobs.ts @@ -1,6 +1,6 @@ import type { Socket } from 'socket.io-client' import io from 'socket.io-client' -import { defineNuxtPlugin, useGlobal, watch } from '#imports' +import { JobStatus, defineNuxtPlugin, useGlobal, watch } from '#imports' export default defineNuxtPlugin(async (nuxtApp) => { const { appInfo } = $(useGlobal()) @@ -41,7 +41,7 @@ export default defineNuxtPlugin(async (nuxtApp) => { subscribe( job: { id: string; name: string } | any, subscribedCb?: () => void, - statusCb?: (status: 'active' | 'completed' | 'failed' | 'refresh', error?: any) => void, + statusCb?: (status: JobStatus, error?: any) => void, logCb?: (data: { message: string }) => void, ) { const logFn = (data: { id: string; name: string; data: { message: string } }) => { @@ -52,7 +52,7 @@ export default defineNuxtPlugin(async (nuxtApp) => { const statusFn = (data: any) => { if (data.id === job.id) { if (statusCb) statusCb(data.status, data.error) - if (data.status === 'completed' || data.status === 'failed') { + if (data.status === JobStatus.COMPLETED || data.status === JobStatus.FAILED) { socket?.off('status', statusFn) socket?.off('log', logFn) } diff --git a/packages/nocodb/src/interface/Jobs.ts b/packages/nocodb/src/interface/Jobs.ts index ed650bf7d5..26209e5f72 100644 --- a/packages/nocodb/src/interface/Jobs.ts +++ b/packages/nocodb/src/interface/Jobs.ts @@ -5,3 +5,18 @@ export enum JobTypes { DuplicateModel = 'duplicate-model', AtImport = 'at-import', } + +export enum JobStatus { + COMPLETED = 'completed', + WAITING = 'waiting', + ACTIVE = 'active', + DELAYED = 'delayed', + FAILED = 'failed', + PAUSED = 'paused', + REFRESH = 'refresh', +} + +export enum JobEvents { + STATUS = 'job.status', + LOG = 'job.log', +} diff --git a/packages/nocodb/src/modules/jobs/fallback-queue.service.ts b/packages/nocodb/src/modules/jobs/fallback-queue.service.ts index a7f51a4b53..b18da96a0e 100644 --- a/packages/nocodb/src/modules/jobs/fallback-queue.service.ts +++ b/packages/nocodb/src/modules/jobs/fallback-queue.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import PQueue from 'p-queue'; import Emittery from 'emittery'; -import { JobTypes } from '../../interface/Jobs'; +import { JobStatus, JobTypes } from '../../interface/Jobs'; import { DuplicateProcessor } from './export-import/duplicate.processor'; import { JobsEventService } from './jobs-event.service'; import { AtImportProcessor } from './at-import/at-import.processor'; @@ -26,27 +26,27 @@ export class QueueService { private readonly duplicateProcessor: DuplicateProcessor, private readonly atImportProcessor: AtImportProcessor, ) { - this.emitter.on('active', (data: any) => { + this.emitter.on(JobStatus.ACTIVE, (data: any) => { const job = this.queueMemory.find( (job) => job.id === data.id && job.name === data.name, ); - job.status = 'active'; + job.status = JobStatus.ACTIVE; this.jobsEventService.onActive.apply(this.jobsEventService, [job as any]); }); - this.emitter.on('completed', (data: any) => { + this.emitter.on(JobStatus.COMPLETED, (data: any) => { const job = this.queueMemory.find( (job) => job.id === data.id && job.name === data.name, ); - job.status = 'completed'; + job.status = JobStatus.COMPLETED; this.jobsEventService.onCompleted.apply(this.jobsEventService, [ data as any, ]); }); - this.emitter.on('failed', (data: { job: Job; error: Error }) => { + this.emitter.on(JobStatus.FAILED, (data: { job: Job; error: Error }) => { const job = this.queueMemory.find( (job) => job.id === data.job.id && job.name === data.job.name, ); - job.status = 'failed'; + job.status = JobStatus.FAILED; this.jobsEventService.onFailed.apply(this.jobsEventService, [ data.job as any, data.error, @@ -70,12 +70,12 @@ export class QueueService { }; async jobWrapper(job: Job) { - this.emitter.emit('active', job); + this.emitter.emit(JobStatus.ACTIVE, job); try { await this.jobMap[job.name].fn.apply(this.jobMap[job.name].this, [job]); - this.emitter.emit('completed', job); + this.emitter.emit(JobStatus.COMPLETED, job); } catch (error) { - this.emitter.emit('failed', { job, error }); + this.emitter.emit(JobStatus.FAILED, { job, error }); } } @@ -101,7 +101,7 @@ export class QueueService { async add(name: string, data: any) { const id = `${this.queueIndex++}`; - const job = { id: `${id}`, name, status: 'waiting', data }; + const job = { id: `${id}`, name, status: JobStatus.WAITING, data }; this.queueMemory.push(job); this.queue.add(() => this.jobWrapper(job)); return { id, name }; diff --git a/packages/nocodb/src/modules/jobs/jobs-event.service.ts b/packages/nocodb/src/modules/jobs/jobs-event.service.ts index 6a783c2f37..1dd8a29cf1 100644 --- a/packages/nocodb/src/modules/jobs/jobs-event.service.ts +++ b/packages/nocodb/src/modules/jobs/jobs-event.service.ts @@ -7,7 +7,7 @@ import { import { Job } from 'bull'; import boxen from 'boxen'; import { EventEmitter2 } from '@nestjs/event-emitter'; -import { JOBS_QUEUE } from '../../interface/Jobs'; +import { JobEvents, JOBS_QUEUE, JobStatus } from '../../interface/Jobs'; @Processor(JOBS_QUEUE) export class JobsEventService { @@ -15,10 +15,10 @@ export class JobsEventService { @OnQueueActive() onActive(job: Job) { - this.eventEmitter.emit('job.status', { + this.eventEmitter.emit(JobEvents.STATUS, { name: job.name, id: job.id.toString(), - status: 'active', + status: JobStatus.ACTIVE, }); } @@ -35,25 +35,25 @@ export class JobsEventService { ), ); - this.eventEmitter.emit('job.status', { + this.eventEmitter.emit(JobEvents.STATUS, { name: job.name, id: job.id.toString(), - status: 'failed', + status: JobStatus.FAILED, error: error?.message, }); } @OnQueueCompleted() onCompleted(job: Job) { - this.eventEmitter.emit('job.status', { + this.eventEmitter.emit(JobEvents.STATUS, { name: job.name, id: job.id.toString(), - status: 'completed', + status: JobStatus.COMPLETED, }); } sendLog(job: Job, data: { message: string }) { - this.eventEmitter.emit('job.log', { + this.eventEmitter.emit(JobEvents.LOG, { name: job.name, id: job.id.toString(), data, diff --git a/packages/nocodb/src/modules/jobs/jobs.gateway.ts b/packages/nocodb/src/modules/jobs/jobs.gateway.ts index c7c3420777..b40f5626ff 100644 --- a/packages/nocodb/src/modules/jobs/jobs.gateway.ts +++ b/packages/nocodb/src/modules/jobs/jobs.gateway.ts @@ -9,7 +9,9 @@ import { Server, Socket } from 'socket.io'; import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host'; import { AuthGuard } from '@nestjs/passport'; import { OnEvent } from '@nestjs/event-emitter'; +import { JobEvents } from '../../interface/Jobs'; import { JobsService } from './jobs.service'; +import type { JobStatus } from '../../interface/Jobs'; import type { OnModuleInit } from '@nestjs/common'; @WebSocketGateway({ @@ -89,18 +91,11 @@ export class JobsGateway implements OnModuleInit { }); } - @OnEvent('job.status') + @OnEvent(JobEvents.STATUS) async sendJobStatus(data: { name: string; id: string; - status: - | 'completed' - | 'waiting' - | 'active' - | 'delayed' - | 'failed' - | 'paused' - | 'refresh'; + status: JobStatus; error?: any; }): Promise { this.server.to(`${data.name}-${data.id}`).emit('status', { @@ -111,7 +106,7 @@ export class JobsGateway implements OnModuleInit { }); } - @OnEvent('job.log') + @OnEvent(JobEvents.LOG) async sendJobLog(data: { name: string; id: string; diff --git a/packages/nocodb/src/modules/jobs/jobs.service.ts b/packages/nocodb/src/modules/jobs/jobs.service.ts index 38defe10db..11614d55be 100644 --- a/packages/nocodb/src/modules/jobs/jobs.service.ts +++ b/packages/nocodb/src/modules/jobs/jobs.service.ts @@ -1,7 +1,7 @@ import { InjectQueue } from '@nestjs/bull'; import { Injectable } from '@nestjs/common'; import { Queue } from 'bull'; -import { JOBS_QUEUE } from '../../interface/Jobs'; +import { JOBS_QUEUE, JobStatus } from '../../interface/Jobs'; import { QueueService } from './fallback-queue.service'; @Injectable() @@ -22,18 +22,23 @@ export class JobsService { async jobList(jobType: string) { return ( - await this.activeQueue.getJobs(['active', 'waiting', 'delayed', 'paused']) + await this.activeQueue.getJobs([ + JobStatus.ACTIVE, + JobStatus.WAITING, + JobStatus.DELAYED, + JobStatus.PAUSED, + ]) ).filter((j) => j.name === jobType); } async getJobWithData(data: any) { const jobs = await this.activeQueue.getJobs([ // 'completed', - 'waiting', - 'active', - 'delayed', + JobStatus.WAITING, + JobStatus.ACTIVE, + JobStatus.DELAYED, // 'failed', - 'paused', + JobStatus.PAUSED, ]); const job = jobs.find((j) => {