Добавлено автообнаружение в виджетах cron-задач
* Поиск обновлённых виджетов daily-eccm-v2 * Автоматическая инициализация поиска со старта приложения
This commit is contained in:
parent
f284d70d6b
commit
a2131e9b03
4 changed files with 107 additions and 40 deletions
|
|
@ -4,6 +4,7 @@ import * as DashboardModel from '../models/dashboard';
|
||||||
import nano from 'nano';
|
import nano from 'nano';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { createAppError } from '../utils/result';
|
import { createAppError } from '../utils/result';
|
||||||
|
import { TimestampNowFill } from '../utils/timestamp-now-fill';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DashboardsService {
|
export class DashboardsService {
|
||||||
|
|
@ -56,18 +57,18 @@ export class DashboardsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(id: string, data: DashboardModel.Data): Promise<void> {
|
async save(id: string, data: DashboardModel.Data): Promise<void> {
|
||||||
this.logger.debug(
|
this.logger.log(
|
||||||
`Save dashboard id - ${id}, data - ${JSON.stringify(data)}`,
|
`Save dashboard id - ${id}, title - ${JSON.stringify(data.title)}`,
|
||||||
);
|
);
|
||||||
const ds = await this.db.getDatasource();
|
const ds = await this.db.getDatasource();
|
||||||
const prevValue = await this.loadRawData(id);
|
const prevValue = await this.loadRawData(id);
|
||||||
|
|
||||||
const newValue = {
|
const newValue = TimestampNowFill({
|
||||||
_id: prevValue._id,
|
_id: prevValue._id,
|
||||||
_rev: prevValue._rev,
|
_rev: prevValue._rev,
|
||||||
id: prevValue.id,
|
id: prevValue.id,
|
||||||
data: data,
|
data: data,
|
||||||
};
|
});
|
||||||
await ds.insert(newValue);
|
await ds.insert(newValue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -89,17 +90,33 @@ export class DashboardsService {
|
||||||
|
|
||||||
async findDashboardsByWidgetType(
|
async findDashboardsByWidgetType(
|
||||||
widgetType: string,
|
widgetType: string,
|
||||||
|
updatedAfter?: number,
|
||||||
): Promise<DashboardModel.Dashboard[]> {
|
): Promise<DashboardModel.Dashboard[]> {
|
||||||
const ds = await this.db.getDatasource();
|
const ds = await this.db.getDatasource();
|
||||||
const data = await ds.find({
|
const selector = {
|
||||||
selector: {
|
'data.widgets': {
|
||||||
'data.widgets': {
|
$elemMatch: {
|
||||||
$elemMatch: {
|
type: widgetType,
|
||||||
type: widgetType,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
if (updatedAfter > 0) {
|
||||||
|
selector['timestamp__'] = {
|
||||||
|
$gte: updatedAfter,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.logger.debug(
|
||||||
|
`Find dashboards by widget type - ${widgetType} ` +
|
||||||
|
`and selector - ${JSON.stringify(selector)}`,
|
||||||
|
);
|
||||||
|
const data = await ds.find({
|
||||||
|
selector: selector,
|
||||||
});
|
});
|
||||||
|
this.logger.debug(
|
||||||
|
`Found dashboards by widget type - ${widgetType} ` +
|
||||||
|
`, selector - ${JSON.stringify(selector)}` +
|
||||||
|
`, result - ${JSON.stringify(data)}`,
|
||||||
|
);
|
||||||
if (!data.docs) throw createAppError('DASHBOARDS_NOT_FOUND');
|
if (!data.docs) throw createAppError('DASHBOARDS_NOT_FOUND');
|
||||||
return data.docs;
|
return data.docs;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,9 @@ import { CalendarEnhancer } from '@app/event-emitter/issue-enhancers/calendar-en
|
||||||
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
|
import { Dashboards as DashboardsDs } from '@app/event-emitter/couchdb-datasources/dashboards';
|
||||||
import { DashboardInitService } from './dashboards/dashboard-init.service';
|
import { DashboardInitService } from './dashboards/dashboard-init.service';
|
||||||
import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
|
import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
|
||||||
|
import { DailyEccmV2ReportService } from './reports/daily-eccm-v2.report.service';
|
||||||
|
import { DashboardsService } from '@app/event-emitter/dashboards/dashboards.service';
|
||||||
|
import { DailyEccmReportsV2Datasource } from './couchdb-datasources/daily-eccm-reports-v2.datasource';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -115,6 +118,9 @@ import { TelegramBotController } from './telegram-bot/telegram-bot.controller';
|
||||||
},
|
},
|
||||||
CreateTagManagerServiceProvider('TAG_MANAGER_SERVICE'),
|
CreateTagManagerServiceProvider('TAG_MANAGER_SERVICE'),
|
||||||
DashboardInitService,
|
DashboardInitService,
|
||||||
|
DashboardsService,
|
||||||
|
DailyEccmReportsV2Datasource,
|
||||||
|
DailyEccmV2ReportService,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule implements OnModuleInit {
|
export class AppModule implements OnModuleInit {
|
||||||
|
|
@ -143,16 +149,22 @@ export class AppModule implements OnModuleInit {
|
||||||
private calendarEnhancer: CalendarEnhancer,
|
private calendarEnhancer: CalendarEnhancer,
|
||||||
|
|
||||||
private dashboardInitService: DashboardInitService,
|
private dashboardInitService: DashboardInitService,
|
||||||
|
|
||||||
|
private dashboardsService: DashboardsService,
|
||||||
|
private dailyEccmV2ReportService: DailyEccmV2ReportService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
onModuleInit() {
|
onModuleInit() {
|
||||||
Issues.getDatasource();
|
const datasources = [
|
||||||
Users.getDatasource();
|
Issues.getDatasource(),
|
||||||
Changes.getDatasource();
|
Users.getDatasource(),
|
||||||
UserMetaInfo.getDatasource();
|
Changes.getDatasource(),
|
||||||
DailyEccmReportsDatasource.getDatasource();
|
UserMetaInfo.getDatasource(),
|
||||||
DailyEccmReportsUserCommentsDatasource.getDatasource();
|
DailyEccmReportsDatasource.getDatasource(),
|
||||||
DashboardsDs.getDatasource();
|
DailyEccmReportsUserCommentsDatasource.getDatasource(),
|
||||||
|
DashboardsDs.getDatasource(),
|
||||||
|
DailyEccmReportsV2Datasource.getDatasource(),
|
||||||
|
];
|
||||||
|
|
||||||
this.enhancerService.addEnhancer([
|
this.enhancerService.addEnhancer([
|
||||||
this.timestampEnhancer,
|
this.timestampEnhancer,
|
||||||
|
|
@ -222,6 +234,13 @@ export class AppModule implements OnModuleInit {
|
||||||
|
|
||||||
this.initDailyEccmUserCommentsPipeline();
|
this.initDailyEccmUserCommentsPipeline();
|
||||||
this.initDashbordProviders();
|
this.initDashbordProviders();
|
||||||
|
|
||||||
|
Promise.all(datasources).then(() => {
|
||||||
|
this.dailyEccmV2ReportService.setDashboardsService(
|
||||||
|
this.dashboardsService,
|
||||||
|
);
|
||||||
|
this.dailyEccmV2ReportService.initAutoScanJobs();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private initDailyEccmUserCommentsPipeline(): void {
|
private initDailyEccmUserCommentsPipeline(): void {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { CouchDb } from '@app/event-emitter/couchdb-datasources/couchdb';
|
import { CouchDb } from '@app/event-emitter/couchdb-datasources/couchdb';
|
||||||
import nano from 'nano';
|
import nano from 'nano';
|
||||||
import {
|
import { Report } from 'src/reports/daily-eccm-v2.report.service';
|
||||||
DailyEccmV2ReportService,
|
|
||||||
Report,
|
|
||||||
} from 'src/reports/daily-eccm-v2.report.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DailyEccmReportsV2Datasource {
|
export class DailyEccmReportsV2Datasource {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { DashboardsService } from '@app/event-emitter/dashboards/dashboards.service';
|
import { DashboardsService } from '@app/event-emitter/dashboards/dashboards.service';
|
||||||
import { Dashboard, Widget } from '@app/event-emitter/models/dashboard';
|
import { Dashboard, Widget } from '@app/event-emitter/models/dashboard';
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { Cron, SchedulerRegistry } from '@nestjs/schedule';
|
import { SchedulerRegistry } from '@nestjs/schedule';
|
||||||
import { CronJob } from 'cron';
|
import { CronJob } from 'cron';
|
||||||
|
|
||||||
export type Params = {
|
export type Params = {
|
||||||
|
|
@ -23,19 +23,38 @@ export type Job = {
|
||||||
params: Params;
|
params: Params;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WIDGET_TYPE = 'daily-eccm-v2';
|
export const WIDGET_TYPE = 'daily_eccm_v2';
|
||||||
|
|
||||||
export const JOB_PREFIX = 'daily_eccm_v2';
|
export const JOB_PREFIX = 'daily_eccm_v2';
|
||||||
|
|
||||||
|
export const UPDATE_RATE = 60 * 1000;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DailyEccmV2ReportService {
|
export class DailyEccmV2ReportService {
|
||||||
private logger = new Logger(DailyEccmV2ReportService.name);
|
private logger = new Logger(DailyEccmV2ReportService.name);
|
||||||
|
|
||||||
private dashboardsService: DashboardsService;
|
private dashboardsService: DashboardsService;
|
||||||
|
|
||||||
|
private previousAutoScanTime = 0;
|
||||||
|
|
||||||
|
private cronJobs: Record<string, CronJob> = {};
|
||||||
|
|
||||||
constructor(private schedulerRegistry: SchedulerRegistry) {}
|
constructor(private schedulerRegistry: SchedulerRegistry) {}
|
||||||
|
|
||||||
@Cron('* * * * *')
|
/**
|
||||||
|
* Auto scan jobs every UPDATE_RATE seconds.
|
||||||
|
* First call to autoScanJobs is done immediately.
|
||||||
|
* Each subsequent call is done after the timeout.
|
||||||
|
* The timeout is reset after each call to autoScanJobs.
|
||||||
|
*/
|
||||||
|
initAutoScanJobs() {
|
||||||
|
const tick = () => {
|
||||||
|
setTimeout(tick, UPDATE_RATE);
|
||||||
|
this.autoScanJobs();
|
||||||
|
};
|
||||||
|
tick();
|
||||||
|
}
|
||||||
|
|
||||||
async autoScanJobs() {
|
async autoScanJobs() {
|
||||||
this.logger.debug('Auto scan jobs started');
|
this.logger.debug('Auto scan jobs started');
|
||||||
const dbs = this.getDashboardsService();
|
const dbs = this.getDashboardsService();
|
||||||
|
|
@ -44,32 +63,43 @@ export class DailyEccmV2ReportService {
|
||||||
this.logger.debug('Auto scan jobs finished');
|
this.logger.debug('Auto scan jobs finished');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dashboards = await dbs.findDashboardsByWidgetType(WIDGET_TYPE);
|
const nowTime = new Date().getTime();
|
||||||
|
const dashboards = await dbs.findDashboardsByWidgetType(
|
||||||
|
WIDGET_TYPE,
|
||||||
|
this.previousAutoScanTime,
|
||||||
|
);
|
||||||
|
this.previousAutoScanTime = nowTime;
|
||||||
for (let i = 0; i < dashboards.length; i++) {
|
for (let i = 0; i < dashboards.length; i++) {
|
||||||
const dashboard: Dashboard = dashboards[i];
|
const dashboard: Dashboard = dashboards[i];
|
||||||
for (let j = 0; j < dashboard.data.widgets.length; j++) {
|
for (let j = 0; j < dashboard.data.widgets.length; j++) {
|
||||||
const widget = dashboard.data.widgets[j];
|
const widget = dashboard.data.widgets[j];
|
||||||
if (widget.type === WIDGET_TYPE) {
|
if (widget.type === WIDGET_TYPE) {
|
||||||
const jobId = `${JOB_PREFIX}_${dashboard.id}_${widget.id}`;
|
const jobId = `${JOB_PREFIX}_${dashboard.id}_${widget.id}`;
|
||||||
let cronJob = this.schedulerRegistry.getCronJob(jobId);
|
this.updateCronJob(jobId, widget, dashboard);
|
||||||
if (!cronJob) {
|
|
||||||
cronJob = new CronJob(
|
|
||||||
widget.dataLoaderParams.schedule,
|
|
||||||
async () => {
|
|
||||||
this.logger.debug(`Cron job ${jobId} started`);
|
|
||||||
// const report = await dbs.getReport(dashboard.id, widget.id);
|
|
||||||
this.logger.debug(`Cron job ${jobId} finished`);
|
|
||||||
// return report;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.schedulerRegistry.addCronJob(jobId, cronJob);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.debug('Auto scan jobs finished');
|
this.logger.debug('Auto scan jobs finished');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateCronJob(
|
||||||
|
jobId: string,
|
||||||
|
widget: Widget,
|
||||||
|
dashboard: Dashboard,
|
||||||
|
): void {
|
||||||
|
if (this.cronJobs[jobId]) {
|
||||||
|
this.cronJobs[jobId].stop();
|
||||||
|
this.schedulerRegistry.deleteCronJob(jobId);
|
||||||
|
}
|
||||||
|
const job = new CronJob(
|
||||||
|
widget.dataLoaderParams?.schedule || '* * * * *',
|
||||||
|
this.createJobHandler(jobId, dashboard, widget),
|
||||||
|
);
|
||||||
|
this.cronJobs[jobId] = job;
|
||||||
|
this.schedulerRegistry.addCronJob(jobId, job);
|
||||||
|
job.start();
|
||||||
|
}
|
||||||
|
|
||||||
getDashboardsService(): DashboardsService | null {
|
getDashboardsService(): DashboardsService | null {
|
||||||
if (!this.dashboardsService) {
|
if (!this.dashboardsService) {
|
||||||
this.logger.warn('Dashboards service not initialized');
|
this.logger.warn('Dashboards service not initialized');
|
||||||
|
|
@ -88,8 +118,12 @@ export class DailyEccmV2ReportService {
|
||||||
widget: Widget,
|
widget: Widget,
|
||||||
): () => void {
|
): () => void {
|
||||||
return async () => {
|
return async () => {
|
||||||
this.logger.debug(`Cron job ${jobId} started`);
|
this.logger.debug(
|
||||||
this.logger.debug(`Cron job ${jobId} finished`);
|
`Cron job ${jobId} started - dashboard ${dashboard.id}, widget ${widget.id}`,
|
||||||
|
);
|
||||||
|
this.logger.debug(
|
||||||
|
`Cron job ${jobId} finished - dashboard ${dashboard.id}, widget ${widget.id}`,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue