Browse Source

refactor: use enum for job status and job events

Signed-off-by: mertmit <mertmit99@gmail.com>
feat/export-nest
mertmit 1 year ago
parent
commit
545e41f2c5
  1. 5
      packages/nc-gui/components/dashboard/TreeView.vue
  2. 14
      packages/nc-gui/components/dlg/AirtableImport.vue
  3. 2
      packages/nc-gui/components/smartsheet/Grid.vue
  4. 10
      packages/nc-gui/lib/enums.ts
  5. 4
      packages/nc-gui/nuxt-shim.d.ts
  6. 5
      packages/nc-gui/pages/index/index/index.vue
  7. 6
      packages/nc-gui/plugins/jobs.ts
  8. 15
      packages/nocodb/src/interface/Jobs.ts
  9. 22
      packages/nocodb/src/modules/jobs/fallback-queue.service.ts
  10. 16
      packages/nocodb/src/modules/jobs/jobs-event.service.ts
  11. 15
      packages/nocodb/src/modules/jobs/jobs.gateway.ts
  12. 17
      packages/nocodb/src/modules/jobs/jobs.service.ts

5
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()
}

14
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 () => {
<a-card ref="logRef" :body-style="{ backgroundColor: '#000000', height: '400px', overflow: 'auto' }">
<div v-for="({ msg, status }, i) in progress" :key="i">
<div v-if="status === 'failed'" class="flex items-center">
<div v-if="status === JobStatus.FAILED" class="flex items-center">
<component :is="iconMap.closeCircle" class="text-red-500" />
<span class="text-red-500 ml-2">{{ msg }}</span>
@ -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"
>

2
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',

10
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',
}

4
packages/nc-gui/nuxt-shim.d.ts vendored

@ -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<string>

5
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()
}

6
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)
}

15
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',
}

22
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 };

16
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,

15
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<void> {
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;

17
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) => {

Loading…
Cancel
Save