Создан сервис обработки изменений статусов
This commit is contained in:
parent
030f7c8101
commit
8dc886de49
3 changed files with 187 additions and 1 deletions
|
|
@ -8,9 +8,12 @@ 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 { RedminePublicUrlConverter } from './converters/redmine-public-url.converter';
|
||||||
|
import { Changes } from './couchdb-datasources/changes';
|
||||||
import { CurrentUserEnhancer } from './issue-enhancers/current-user-enhancer';
|
import { CurrentUserEnhancer } from './issue-enhancers/current-user-enhancer';
|
||||||
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
|
import { CustomFieldsEnhancer } from './issue-enhancers/custom-fields-enhancer';
|
||||||
import { PersonalNotificationsService } from './notifications/personal-notifications.service';
|
import { PersonalNotificationsService } from './notifications/personal-notifications.service';
|
||||||
|
import { StatusChangeNotificationsService } from './notifications/status-change-notifications.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
@ -25,6 +28,9 @@ import { PersonalNotificationsService } from './notifications/personal-notificat
|
||||||
CustomFieldsEnhancer,
|
CustomFieldsEnhancer,
|
||||||
CurrentUserEnhancer,
|
CurrentUserEnhancer,
|
||||||
PersonalNotificationsService,
|
PersonalNotificationsService,
|
||||||
|
StatusChangeNotificationsService,
|
||||||
|
Changes,
|
||||||
|
RedminePublicUrlConverter,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AppModule implements OnModuleInit {
|
export class AppModule implements OnModuleInit {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||||
|
|
||||||
export type ChangeMessage = {
|
export type ChangeMessage = {
|
||||||
changes_message?: string | null;
|
change_message?: string | null;
|
||||||
notification_message?: string | null;
|
notification_message?: string | null;
|
||||||
recipient?: RedmineTypes.PublicUser | null;
|
recipient?: RedmineTypes.PublicUser | null;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
180
src/notifications/status-change-notifications.service.ts
Normal file
180
src/notifications/status-change-notifications.service.ts
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
import { RedmineTypes } from '@app/event-emitter/models/redmine-types';
|
||||||
|
import { SaveResponse } from '@app/event-emitter/models/save-response';
|
||||||
|
import { UsersService } from '@app/event-emitter/users/users.service';
|
||||||
|
import { TimestampConverter } from '@app/event-emitter/utils/timestamp-converter';
|
||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { RedminePublicUrlConverter } from 'src/converters/redmine-public-url.converter';
|
||||||
|
import { Change } from 'src/models/change.model';
|
||||||
|
import { StatusChangesConfig } from 'src/models/status-changes-config.model';
|
||||||
|
import { StatusesConfig } from 'src/models/statuses-config.model';
|
||||||
|
import Handlebars from 'handlebars';
|
||||||
|
import { ChangeMessage } from 'src/models/change-message.model';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class StatusChangeNotificationsService {
|
||||||
|
private logger = new Logger(StatusChangeNotificationsService.name);
|
||||||
|
private statuses: StatusesConfig.Config;
|
||||||
|
private statusChanges: StatusChangesConfig.Config;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private usersService: UsersService,
|
||||||
|
private config: ConfigService,
|
||||||
|
private redminePublicUrlConverter: RedminePublicUrlConverter,
|
||||||
|
) {
|
||||||
|
this.statuses = this.config.get<StatusesConfig.Config>('redmineStatuses');
|
||||||
|
this.statusChanges = this.config.get<StatusChangesConfig.Config>(
|
||||||
|
'redmineStatusChanges',
|
||||||
|
);
|
||||||
|
this.logger.debug(
|
||||||
|
`StatusChangeNotificationsService created, ` +
|
||||||
|
`statuses = ${JSON.stringify(this.statuses.map((s) => s.name))}, ` +
|
||||||
|
`statusChanges.length = ${this.statusChanges.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getChanges(saveResponse: SaveResponse): Promise<Change[]> {
|
||||||
|
this.logger.debug(
|
||||||
|
`Analize change statuses for issue #${saveResponse.current.id} ` +
|
||||||
|
`(${saveResponse.current.subject}) for journalsDiff.length = ${saveResponse.journalsDiff.length} start`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const changes: Change[] = [];
|
||||||
|
|
||||||
|
const issue = saveResponse.current;
|
||||||
|
|
||||||
|
for (let i = 0; i < saveResponse.journalsDiff.length; i++) {
|
||||||
|
const journal = saveResponse.journalsDiff[i];
|
||||||
|
const change = await this.getMessagesForChangeStatus(issue, journal);
|
||||||
|
if (change) {
|
||||||
|
changes.push(change);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.logger.debug(
|
||||||
|
`Analize change statuses for issue #${saveResponse.current.id} ` +
|
||||||
|
`(${saveResponse.current.subject}) for journalsDiff.length = ${saveResponse.journalsDiff.length} finished`,
|
||||||
|
);
|
||||||
|
return changes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getStatusChangeDetails(
|
||||||
|
journal: RedmineTypes.Journal,
|
||||||
|
): RedmineTypes.JournalDetail | null {
|
||||||
|
if (!journal?.details || journal?.details.length <= 0) return null;
|
||||||
|
const details: RedmineTypes.JournalDetail[] = journal?.details;
|
||||||
|
return (
|
||||||
|
details.find((d) => {
|
||||||
|
return d.name === 'status_id';
|
||||||
|
}) || null
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getMessagesForChangeStatus(
|
||||||
|
issue: any,
|
||||||
|
journal: any,
|
||||||
|
): Promise<Change | null> {
|
||||||
|
const statusChangeDetails = this.getStatusChangeDetails(journal);
|
||||||
|
if (!statusChangeDetails) return null;
|
||||||
|
const change: Change = {
|
||||||
|
initiator: await this.usersService.getUser(journal?.user?.id),
|
||||||
|
dev: await this.usersService.getUser(issue?.dev?.id),
|
||||||
|
qa: await this.usersService.getUser(issue?.qa?.id),
|
||||||
|
cr: await this.usersService.getUser(issue?.cr?.id),
|
||||||
|
current_user: await this.usersService.getUser(issue?.current_user?.id),
|
||||||
|
author: await this.usersService.getUser(issue?.author?.id),
|
||||||
|
old_status: this.findStatusById(statusChangeDetails.old_value),
|
||||||
|
new_status: this.findStatusById(statusChangeDetails.new_value),
|
||||||
|
issue_id: issue.id,
|
||||||
|
issue_url: this.redminePublicUrlConverter.convert(issue.id),
|
||||||
|
issue_tracker: issue.tracker?.name || '',
|
||||||
|
issue_subject: issue.subject || '',
|
||||||
|
created_on: journal.created_on,
|
||||||
|
created_on_timestamp: TimestampConverter.toTimestamp(journal.created_on),
|
||||||
|
journal_note: journal.notes || '',
|
||||||
|
messages: [],
|
||||||
|
};
|
||||||
|
const messages = await this.generateMessages(statusChangeDetails, change);
|
||||||
|
if (messages) {
|
||||||
|
change.messages = messages;
|
||||||
|
}
|
||||||
|
return change;
|
||||||
|
}
|
||||||
|
|
||||||
|
private findStatusById(id: number | string): StatusesConfig.Item | null {
|
||||||
|
if (typeof id === 'string' && !Number.isNaN(id) && Number.isFinite(id)) {
|
||||||
|
id = Number(id);
|
||||||
|
}
|
||||||
|
return this.statuses.find((s) => s.id === id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateMessages(
|
||||||
|
detail: RedmineTypes.JournalDetail,
|
||||||
|
change: Change,
|
||||||
|
): Promise<any> {
|
||||||
|
const oldStatus = this.findStatusById(detail.old_value);
|
||||||
|
const newStatus = this.findStatusById(detail.new_value);
|
||||||
|
if (!oldStatus || !newStatus) return null;
|
||||||
|
const changeParams = this.findChangeParams(oldStatus.name, newStatus.name);
|
||||||
|
if (!changeParams || !changeParams.messages) return null;
|
||||||
|
const filledMessages = await Promise.all(
|
||||||
|
changeParams.messages.map(async (messageParams: any) => {
|
||||||
|
return await this.generateMessage(messageParams, change);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
return filledMessages.filter((m) => m);
|
||||||
|
}
|
||||||
|
|
||||||
|
private findChangeParams(
|
||||||
|
oldStatus: string,
|
||||||
|
newStatus: string,
|
||||||
|
): StatusChangesConfig.Item | null {
|
||||||
|
let foundParam: StatusChangesConfig.Item | null = null;
|
||||||
|
foundParam = this.statusChanges.find(
|
||||||
|
(p) => p.from == oldStatus && p.to == newStatus,
|
||||||
|
);
|
||||||
|
if (!foundParam) {
|
||||||
|
foundParam = this.statusChanges.find((p) => !p.from && p.to == newStatus);
|
||||||
|
}
|
||||||
|
if (!foundParam) {
|
||||||
|
foundParam = this.statusChanges.find((p) => p.from == newStatus && !p.to);
|
||||||
|
}
|
||||||
|
if (!foundParam) {
|
||||||
|
foundParam = this.statusChanges.find((p) => p.default);
|
||||||
|
}
|
||||||
|
return foundParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async generateMessage(
|
||||||
|
messageParams: StatusChangesConfig.Message,
|
||||||
|
change: Change,
|
||||||
|
): Promise<ChangeMessage | null> {
|
||||||
|
if (!messageParams) return null;
|
||||||
|
const recipientUser = await this.usersService.getUser(
|
||||||
|
change[messageParams.recipient]?.id,
|
||||||
|
);
|
||||||
|
if (!recipientUser) return null;
|
||||||
|
|
||||||
|
let changeMessage = null;
|
||||||
|
if (messageParams.changes_message) {
|
||||||
|
const changeMessageTemplate = Handlebars.compile(
|
||||||
|
messageParams.changes_message,
|
||||||
|
);
|
||||||
|
changeMessage = changeMessageTemplate(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
let notificationMessage = null;
|
||||||
|
if (messageParams.notification_message) {
|
||||||
|
const notificationMessageTemplate = Handlebars.compile(
|
||||||
|
messageParams.notification_message,
|
||||||
|
);
|
||||||
|
notificationMessage = notificationMessageTemplate(change);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
recipient: recipientUser,
|
||||||
|
change_message: changeMessage,
|
||||||
|
notification_message: notificationMessage,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue