Добавлены улучшатели задачи

This commit is contained in:
Pavel Gnedov 2022-07-19 18:00:38 +07:00
parent d858c7e228
commit 2cf5ccf118
6 changed files with 129 additions and 7 deletions

View file

@ -1,4 +1,10 @@
import { DynamicModule, Logger, Module, OnModuleInit } from '@nestjs/common'; import {
DynamicModule,
Inject,
Logger,
Module,
OnModuleInit,
} from '@nestjs/common';
import { EventEmitterService } from './event-emitter.service'; import { EventEmitterService } from './event-emitter.service';
import { RedmineEventsGateway } from './events/redmine-events.gateway'; import { RedmineEventsGateway } from './events/redmine-events.gateway';
import { ServeStaticModule } from '@nestjs/serve-static'; import { ServeStaticModule } from '@nestjs/serve-static';
@ -18,6 +24,9 @@ import { RedmineTypes } from './models/redmine-types';
import { UsersService } from './users/users.service'; import { UsersService } from './users/users.service';
import { IssuesService } from './issues/issues.service'; import { IssuesService } from './issues/issues.service';
import { IssuesController } from './issues/issues.controller'; import { IssuesController } from './issues/issues.controller';
import { IssueEnhancerInterface } from './issue-enhancers/issue-enhancer-interface';
import { TimestampEnhancer } from './issue-enhancers/timestamps-enhancer';
import { ModuleRef } from '@nestjs/core';
@Module({}) @Module({})
export class EventEmitterModule implements OnModuleInit { export class EventEmitterModule implements OnModuleInit {
@ -41,6 +50,11 @@ export class EventEmitterModule implements OnModuleInit {
RedmineUserCacheWriterService, RedmineUserCacheWriterService,
UsersService, UsersService,
IssuesService, IssuesService,
TimestampEnhancer,
{
provide: 'ENHANCERS',
useValue: params?.enhancers || null,
},
], ],
exports: [ exports: [
EventEmitterService, EventEmitterService,
@ -53,6 +67,7 @@ export class EventEmitterModule implements OnModuleInit {
RedmineUserCacheWriterService, RedmineUserCacheWriterService,
UsersService, UsersService,
IssuesService, IssuesService,
TimestampEnhancer,
], ],
controllers: [MainController, UsersController, IssuesController], controllers: [MainController, UsersController, IssuesController],
}; };
@ -63,7 +78,12 @@ export class EventEmitterModule implements OnModuleInit {
constructor( constructor(
private redmineEventsGateway: RedmineEventsGateway, private redmineEventsGateway: RedmineEventsGateway,
private redmineIssuesCacheWriterService: RedmineIssuesCacheWriterService, private redmineIssuesCacheWriterService: RedmineIssuesCacheWriterService,
) {} private moduleRef: ModuleRef,
@Inject('ENHANCERS')
private enhancers: IssueEnhancerInterface[],
) {
this.enhancers.forEach((e) => e.init(this.moduleRef));
}
onModuleInit() { onModuleInit() {
const queue = this.redmineEventsGateway.getIssuesChangesQueue(); const queue = this.redmineEventsGateway.getIssuesChangesQueue();
@ -72,7 +92,7 @@ export class EventEmitterModule implements OnModuleInit {
this.logger.debug(`Changed issues = ${JSON.stringify(issues)}`); this.logger.debug(`Changed issues = ${JSON.stringify(issues)}`);
for (let i = 0; i < issues.length; i++) { for (let i = 0; i < issues.length; i++) {
const issue: RedmineTypes.Issue = issues[i]; const issue: RedmineTypes.Issue = await this.enhanceIssue(issues[i]);
try { try {
this.logger.debug( this.logger.debug(
@ -94,4 +114,18 @@ export class EventEmitterModule implements OnModuleInit {
} }
}); });
} }
private async enhanceIssue(
issue: RedmineTypes.Issue & Record<string, any>,
): Promise<RedmineTypes.Issue & Record<string, any>> {
const enhancers = this.enhancers;
for (let i = 0; i < enhancers.length; i++) {
const enhancer = enhancers[i];
issue = await enhancer.enhance(issue);
this.logger.debug(
`Issue after enhancer: issue = ${JSON.stringify(issue)}`,
);
}
return issue;
}
} }

View file

@ -1,5 +1,9 @@
import { ModuleRef } from '@nestjs/core';
import { RedmineTypes } from '../models/redmine-types'; import { RedmineTypes } from '../models/redmine-types';
export interface IssueEnhancerInterface { export interface IssueEnhancerInterface {
enhance(issue: RedmineTypes.Issue): RedmineTypes.Issue & Record<string, any>; init(moduleRef: ModuleRef);
enhance(
issue: RedmineTypes.Issue,
): Promise<RedmineTypes.Issue & Record<string, any>>;
} }

View file

@ -1,8 +1,16 @@
import { Injectable } from '@nestjs/common';
import { RedmineTypes } from '../models/redmine-types'; import { RedmineTypes } from '../models/redmine-types';
import { IssueEnhancerInterface } from './issue-enhancer-interface'; import { IssueEnhancerInterface } from './issue-enhancer-interface';
@Injectable()
export class TimestampEnhancer implements IssueEnhancerInterface { export class TimestampEnhancer implements IssueEnhancerInterface {
enhance(issue: RedmineTypes.Issue): RedmineTypes.Issue & Record<string, any> { init() {
return;
}
async enhance(
issue: RedmineTypes.Issue,
): Promise<RedmineTypes.Issue & Record<string, any>> {
return this.createTimestamp(this.enhanceJournal(issue), [ return this.createTimestamp(this.enhanceJournal(issue), [
'closed_on', 'closed_on',
'created_on', 'created_on',

View file

@ -1,5 +1,7 @@
import { IssueEnhancerInterface } from '../issue-enhancers/issue-enhancer-interface';
import { MainConfigModel } from './main-config-model'; import { MainConfigModel } from './main-config-model';
export type ModuleParams = { export type ModuleParams = {
config?: MainConfigModel; config?: MainConfigModel;
enhancers?: IssueEnhancerInterface[];
}; };

View file

@ -1,19 +1,28 @@
import { EventEmitterModule } from '@app/event-emitter'; import { EventEmitterModule } from '@app/event-emitter';
import { TimestampEnhancer } from '@app/event-emitter/issue-enhancers/timestamps-enhancer';
import { MainController } from '@app/event-emitter/main/main.controller'; import { MainController } from '@app/event-emitter/main/main.controller';
import { UsersService } from '@app/event-emitter/users/users.service';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config'; import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller'; import { AppController } from './app.controller';
import { AppService } from './app.service'; import { AppService } from './app.service';
import configuration from './configs/app'; import configuration from './configs/app';
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
@Module({ @Module({
imports: [ imports: [
EventEmitterModule.register({ EventEmitterModule.register({
config: configuration().redmineIssueEventEmitterConfig, config: configuration().redmineIssueEventEmitterConfig,
enhancers: [new TimestampEnhancer(), new CustomFieldsEnhancer()],
}), }),
ConfigModule.forRoot({ load: [configuration] }), ConfigModule.forRoot({ load: [configuration] }),
], ],
controllers: [AppController, MainController], controllers: [AppController, MainController],
providers: [AppService], providers: [AppService, UsersService, CustomFieldsEnhancer],
}) })
export class AppModule {} export class AppModule {
constructor(
private timestampEnhancer: TimestampEnhancer,
private customFieldsEnhancer: CustomFieldsEnhancer,
) {}
}

View file

@ -0,0 +1,65 @@
import { IssueEnhancerInterface } from '@app/event-emitter/issue-enhancers/issue-enhancer-interface';
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
import { UsersService } from '@app/event-emitter/users/users.service';
import { Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
@Injectable()
export class CustomFieldsEnhancer implements IssueEnhancerInterface {
private usersService: UsersService;
init(moduleRef: ModuleRef): void {
this.usersService = moduleRef.get(UsersService);
}
async enhance(
issue: RedmineTypes.Issue,
): Promise<RedmineTypes.Issue & Record<string, any>> {
const res: RedmineTypes.Issue & Record<string, any> = { ...issue };
const customFields = issue.custom_fields;
if (!customFields) {
return issue;
}
const qa = customFields.find(
(cf) => cf.name === 'Quality Assurance' || cf.name === 'QA',
);
if (qa && this.isNumber(qa.value)) {
const qaUserId = Number(qa.value);
const qaUser = await this.usersService.getUser(qaUserId);
if (qaUser) {
res.qa = { ...qaUser };
}
}
const cr = customFields.find(
(cf) => cf.name === 'Code Reviewer' || cf.name === 'CR',
);
if (cr && this.isNumber(cr.value)) {
const crUserId = Number(cr.value);
const crUser = await this.usersService.getUser(crUserId);
if (crUser) {
res.cr = { ...crUser };
}
}
const tags = customFields.find((cf) => cf.name === 'Tags');
if (tags && tags.value) {
res.tags = tags.value.split(',; ');
}
const sp = customFields.find(
(cf) => cf.name === 'Story Points' || cf.name === 'SP',
);
if (sp && this.isNumber(sp.value)) {
res.sp = Number(sp.value);
}
return res;
}
private isNumber(value): boolean {
return !Number.isNaN(value) && Number.isFinite(Number(value));
}
}