Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: send xgeeks board phase to slack #1156

Merged
merged 52 commits into from
Feb 28, 2023
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
a0acd9b
feat: button start voting phase
GoncaloCanteiro Feb 17, 2023
8160f76
feat: create boardPhases enum
GoncaloCanteiro Feb 20, 2023
a300ff3
feat: add websocket name constants
GoncaloCanteiro Feb 21, 2023
c37449e
feat: add phase to schemas, dtos and types
GoncaloCanteiro Feb 21, 2023
c82bd32
feat: add phases enum to frontend
GoncaloCanteiro Feb 21, 2023
bd8b27d
feat: add update boardId phase endpoint and sevice
GoncaloCanteiro Feb 21, 2023
efa89e8
feat: updated phase websocket
GoncaloCanteiro Feb 21, 2023
cbcda07
refactor: remove handleUserStartedVotingPahse
GoncaloCanteiro Feb 21, 2023
eda34c2
feat: send updatePhase to endpoint
GoncaloCanteiro Feb 21, 2023
be322a4
feat: shows current phase in header
GoncaloCanteiro Feb 21, 2023
448c613
refactor: updateBoardPhase to accept phase
GoncaloCanteiro Feb 21, 2023
9286b7f
fix: hide button when board in votingPhase
GoncaloCanteiro Feb 21, 2023
86e05ee
feat: disable voting button based on phase
GoncaloCanteiro Feb 21, 2023
3a4a403
fix: voting permission
GoncaloCanteiro Feb 21, 2023
c2f5524
fix: only add phase to split boards
GoncaloCanteiro Feb 21, 2023
467d0b1
fix: only show button vote when all teams merged
GoncaloCanteiro Feb 21, 2023
a4f887d
Merge branch 'main' into 1048-feature-add-the-voting-phase
GoncaloCanteiro Feb 22, 2023
f013793
fix: change import of Icon
GoncaloCanteiro Feb 22, 2023
04a45ea
fix: voting permissions
GoncaloCanteiro Feb 22, 2023
f5c8291
fix: button votingPhase to show when all merged
GoncaloCanteiro Feb 22, 2023
8865d3c
refactor: showButtonToVote to accept no cards
GoncaloCanteiro Feb 22, 2023
1213fdc
refactor: change BoardVotePhaseDto
GoncaloCanteiro Feb 22, 2023
8115c1e
refactor: change updateVotingPhase to updatePhase
GoncaloCanteiro Feb 22, 2023
d46cc16
refactor: updateBoardPhaseRequest
GoncaloCanteiro Feb 22, 2023
10f015e
refactor: change uUserStartedVoteEvent name
GoncaloCanteiro Feb 22, 2023
18fbd34
refactor: updateBoardPhaseRequest return type
GoncaloCanteiro Feb 23, 2023
f964bdd
feat: add update phase to useBoard hook
GoncaloCanteiro Feb 23, 2023
d4c835e
fix: remove html tag
GoncaloCanteiro Feb 23, 2023
03f9739
fix: add updateBoardPhase dto
GoncaloCanteiro Feb 23, 2023
e75ec00
refactor: phase type
GoncaloCanteiro Feb 23, 2023
b22891d
fix: remove if phase validation exists
GoncaloCanteiro Feb 23, 2023
6c389ab
refactor: code improvement in updatePhase
GoncaloCanteiro Feb 23, 2023
e579153
fix: add setReady to updateBoardPhase
GoncaloCanteiro Feb 23, 2023
bfe1397
feat: add slack-send-message service and producer
GoncaloCanteiro Feb 23, 2023
c98ce25
refactor: change SlackSendMessagesService name
GoncaloCanteiro Feb 23, 2023
527c21a
feat: add SendMessageInterface
GoncaloCanteiro Feb 23, 2023
e629e96
feat: add SlackSendMessageService type
GoncaloCanteiro Feb 24, 2023
3ebd5ee
feat: add sendSlackMessage to update phase
GoncaloCanteiro Feb 24, 2023
48b33ed
refactor: sendMessage producer logger text
GoncaloCanteiro Feb 24, 2023
b829bef
feat: add send-message.consumer
GoncaloCanteiro Feb 24, 2023
d6165a7
feat: add send-message.consumer
GoncaloCanteiro Feb 27, 2023
44e8a1d
feat: add postMessageChannel on comm-application
GoncaloCanteiro Feb 27, 2023
52497ae
feat: send message to slack when phase updated
GoncaloCanteiro Feb 27, 2023
8311912
Merge branch 'main' into feat/update-phase-slack
GoncaloCanteiro Feb 27, 2023
9402ee9
fix: imports tests backend
GoncaloCanteiro Feb 27, 2023
362e529
fix: remove duplicated variable
GoncaloCanteiro Feb 27, 2023
6f9ce5c
feat: add updatePhase to boardRepository
GoncaloCanteiro Feb 27, 2023
59d5b78
refactor: change retro board message to lower case
GoncaloCanteiro Feb 27, 2023
01f0ec0
fix: change team type and change phase name
GoncaloCanteiro Feb 27, 2023
c150c7c
fix: add SendMessageApplication
GoncaloCanteiro Feb 28, 2023
cfd0339
fix: remove postMessage from communication.app
GoncaloCanteiro Feb 28, 2023
a47f6b4
fix: remove users from query
GoncaloCanteiro Feb 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions backend/src/libs/constants/slack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ export const SLACK_API_BOT_TOKEN = 'slack.botToken';
export const SLACK_CHANNEL_PREFIX = 'slack.channelPrefix';

export const SLACK_MASTER_CHANNEL_ID = 'slack.masterChannelId';

export const SLACK_ENABLE = 'slack.enable';
12 changes: 12 additions & 0 deletions backend/src/modules/boards/controller/boards.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ConfigService } from '@nestjs/config';
import configService from 'src/libs/test-utils/mocks/configService.mock';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { getModelToken } from '@nestjs/mongoose';
import { SchedulerRegistry } from '@nestjs/schedule';
Expand Down Expand Up @@ -84,6 +86,10 @@ describe('BoardsController', () => {
provide: getModelToken('TeamUser'),
useValue: {}
},
{
provide: ConfigService,
useValue: configService
},
{
provide: getModelToken('Schedules'),
useValue: {
Expand All @@ -101,6 +107,12 @@ describe('BoardsController', () => {
useValue: {
execute: jest.fn()
}
},
{
provide: CommunicationsType.TYPES.services.SlackSendMessageService,
useValue: {
execute: jest.fn()
}
}
]
}).compile();
Expand Down
50 changes: 48 additions & 2 deletions backend/src/modules/boards/services/update.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ import { BOARD_PHASE_SERVER_UPDATED } from 'src/libs/constants/phase';
import { EventEmitter2 } from '@nestjs/event-emitter';
import { BoardPhaseDto } from 'src/libs/dto/board-phase.dto';
import PhaseChangeEvent from 'src/modules/socket/events/user-updated-phase.event';
import { SendMessageServiceInterface } from 'src/modules/communication/interfaces/send-message.service.interface';
import { SlackMessageDto } from 'src/modules/communication/dto/slack.message.dto';
import { SLACK_ENABLE, SLACK_MASTER_CHANNEL_ID } from 'src/libs/constants/slack';
import { ConfigService } from '@nestjs/config';
import { BoardPhases } from 'src/libs/enum/board.phases';


@Injectable()
export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterface {
Expand All @@ -43,14 +49,19 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
private getTeamService: GetTeamServiceInterface,
@Inject(CommunicationsType.TYPES.services.SlackCommunicationService)
private slackCommunicationService: CommunicationServiceInterface,
@Inject(CommunicationsType.TYPES.services.SlackSendMessageService)
private slackSendMessageService: SendMessageServiceInterface,

@InjectModel(BoardUser.name)
private boardUserModel: Model<BoardUserDocument>,
private socketService: SocketGateway,
@Inject(Cards.TYPES.services.DeleteCardService)
private deleteCardService: DeleteCardService,
@Inject(Boards.TYPES.repositories.BoardRepository)
private readonly boardRepository: BoardRepositoryInterface,
private eventEmitter: EventEmitter2
private eventEmitter: EventEmitter2,
private configService: ConfigService

) {}

/**
Expand Down Expand Up @@ -483,7 +494,7 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
async updatePhase(boardPhaseDto: BoardPhaseDto) {
try {
const { boardId, phase } = boardPhaseDto;
await this.boardModel
const board = await this.boardModel
.findOneAndUpdate(
{
_id: boardId
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -495,11 +506,46 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
.exec();

this.eventEmitter.emit(BOARD_PHASE_SERVER_UPDATED, new PhaseChangeEvent(boardPhaseDto));
const team = await this.getTeamService.getTeam((board.team as ObjectId).toString());
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved

//Sends message to SLACK
if (
team.name === 'xgeeks' &&
board.slackEnable === true &&
board.phase !== BoardPhases.ADDCARDS &&
this.configService.getOrThrow(SLACK_ENABLE)
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
) {
const message = this.generateMessage(phase, boardId);
const slackMessageDto = new SlackMessageDto(
this.configService.getOrThrow(SLACK_MASTER_CHANNEL_ID),
message
);
this.slackSendMessageService.execute(slackMessageDto);
}
} catch (err) {
throw new BadRequestException(UPDATE_FAILED);
}
}

private generateMessage(phase: string, boardId: string): string {
const today = new Date();

if (phase === BoardPhases.VOTINGPHASE) {
return `Hello team, <https://split.kigroup.de/boards/${boardId}|here> is the ${today.toLocaleString(
'default',
{
month: 'long'
}
)} Retro Board \n\n <https://split.kigroup.de/boards/${boardId}> \n\n Take a look and please add your votes. \n\nThank you for your collaboration! :ok_hand: Keep rocking :rocket:`;
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
}

if (phase == BoardPhases.SUBMITED) {
return `Hello team, the ${today.toLocaleString('default', {
month: 'long'
})} Retro Board was submited \n\nThank you for your collaboration! :ok_hand: Keep rocking :rocket:`;
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
}
}

private async addBoardUsers(boardUsers: BoardUserDto[]) {
const createdBoardUsers = await this.boardUserModel.insertMany(boardUsers);

Expand Down
12 changes: 12 additions & 0 deletions backend/src/modules/columns/controller/columns.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ConfigService } from '@nestjs/config';
import configService from 'src/libs/test-utils/mocks/configService.mock';
import { EventEmitterModule } from '@nestjs/event-emitter';
import { getModelToken } from '@nestjs/mongoose';
import { SchedulerRegistry } from '@nestjs/schedule';
Expand Down Expand Up @@ -84,6 +86,10 @@ describe('ColumnsController', () => {
provide: getModelToken('TeamUser'),
useValue: {}
},
{
provide: ConfigService,
useValue: configService
},
{
provide: getModelToken('Schedules'),
useValue: {
Expand All @@ -101,6 +107,12 @@ describe('ColumnsController', () => {
useValue: {
execute: jest.fn()
}
},
{
provide: CommunicationsType.TYPES.services.SlackSendMessageService,
useValue: {
execute: jest.fn()
}
}
]
}).compile();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ChatHandlerInterface } from 'src/modules/communication/interfaces/chat.
import { CommunicationApplicationInterface } from 'src/modules/communication/interfaces/communication.application.interface';
import { ConversationsHandlerInterface } from 'src/modules/communication/interfaces/conversations.handler.interface';
import { UsersHandlerInterface } from 'src/modules/communication/interfaces/users.handler.interface';
import { SlackMessageDto } from '../dto/slack.message.dto';

export class SlackCommunicationApplication implements CommunicationApplicationInterface {
private logger = new Logger(SlackCommunicationApplication.name);
Expand All @@ -30,6 +31,10 @@ export class SlackCommunicationApplication implements CommunicationApplicationIn
return teams;
}

public async postMessageOnChannel(data: SlackMessageDto): Promise<void> {
this.chatHandler.postMessage(data.slackChannelId, data.message);
}

GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
private async postMessageOnMasterChannel(teams: TeamDto[]): Promise<void> {
const textGeneralTeams = teams
.filter((i) => i.for === BoardRoles.MEMBER)
Expand Down
19 changes: 18 additions & 1 deletion backend/src/modules/communication/communication.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ConversationsHandler,
MergeBoardApplication,
ResponsibleApplication,
SendMessageService,
UsersHandler
} from 'src/modules/communication/communication.providers';
import { SlackArchiveChannelConsumer } from 'src/modules/communication/consumers/slack-archive-channel.consumer';
Expand All @@ -22,9 +23,11 @@ import { SlackCommunicationProducer } from 'src/modules/communication/producers/
import { SlackAddUserToChannelConsumer } from './consumers/slack-add-user-channel.consummer';
import { SlackMergeBoardConsumer } from './consumers/slack-merge-board.consumer';
import { SlackResponsibleConsumer } from './consumers/slack-responsible.consumer';
import { SlackSendMessageConsumer } from './consumers/slack-send-message.consumer';
import { SlackAddUserToChannelProducer } from './producers/slack-add-user-channel.producer';
import { SlackMergeBoardProducer } from './producers/slack-merge-board.producer';
import { SlackResponsibleProducer } from './producers/slack-responsible.producer';
import { SlackSendMessageProducer } from './producers/slack-send-message-channel.producer';

@Module({
imports: [
Expand Down Expand Up @@ -85,13 +88,25 @@ import { SlackResponsibleProducer } from './producers/slack-responsible.producer
removeOnComplete: SlackAddUserToChannelProducer.REMOVE_ON_COMPLETE,
priority: SlackAddUserToChannelProducer.PRIORITY
}
}),
BullModule.registerQueue({
name: SlackSendMessageProducer.QUEUE_NAME,
defaultJobOptions: {
attempts: SlackSendMessageProducer.ATTEMPTS,
backoff: SlackSendMessageProducer.BACKOFF,
delay: SlackSendMessageProducer.DELAY,
removeOnFail: SlackSendMessageProducer.REMOVE_ON_FAIL,
removeOnComplete: SlackSendMessageProducer.REMOVE_ON_COMPLETE,
priority: SlackSendMessageProducer.PRIORITY
}
})
]
: [])
],
providers: [
CommunicationService,
ArchiveChannelService,
SendMessageService,
...(configuration().slack.enable
? [
CommunicationGateAdapter,
Expand All @@ -103,6 +118,8 @@ import { SlackResponsibleProducer } from './producers/slack-responsible.producer
ResponsibleApplication,
MergeBoardApplication,
AddUserIntoChannelApplication,
SlackSendMessageConsumer,
SlackSendMessageProducer,
SlackCommunicationProducer,
SlackCommunicationConsumer,
SlackResponsibleProducer,
Expand All @@ -116,6 +133,6 @@ import { SlackResponsibleProducer } from './producers/slack-responsible.producer
]
: [])
],
exports: [CommunicationService, ArchiveChannelService]
exports: [CommunicationService, ArchiveChannelService, SendMessageService]
})
export class CommunicationModule {}
8 changes: 8 additions & 0 deletions backend/src/modules/communication/communication.providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { SlackCommunicationService } from 'src/modules/communication/services/sl
import { SlackDisabledCommunicationService } from 'src/modules/communication/services/slack-disabled-communication.service';
import { SlackAddUserIntoChannelApplication } from './applications/slack-add-user-channel.application';
import { UsersSlackHandler } from './handlers/users-slack.handler';
import { SlackSendMessageService } from './services/slack-send-messages.service';

export const CommunicationGateAdapter = {
provide: SlackCommunicationGateAdapter,
Expand Down Expand Up @@ -146,3 +147,10 @@ export const ArchiveChannelService = {
? SlackArchiveChannelService
: SlackDisabledCommunicationService
};

export const SendMessageService = {
provide: TYPES.services.SlackSendMessageService,
useClass: configuration().slack.enable
? SlackSendMessageService
: SlackDisabledCommunicationService
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Process, Processor } from '@nestjs/bull';
import { SlackSendMessageProducer } from '../producers/slack-send-message-channel.producer';
import { SlackMessageType } from 'src/modules/communication/dto/types';
import { TYPES } from 'src/modules/communication/interfaces/types';
import { Job } from 'bull';
import { Inject, Logger } from '@nestjs/common';
import { SlackCommunicationEventListeners } from './slack-communication-event-listeners';
import { CommunicationApplicationInterface } from 'src/modules/communication/interfaces/communication.application.interface';

@Processor(SlackSendMessageProducer.QUEUE_NAME)
export class SlackSendMessageConsumer extends SlackCommunicationEventListeners<
SlackMessageType,
SlackMessageType
> {
constructor(
@Inject(TYPES.application.SlackCommunicationApplication)
private readonly application: CommunicationApplicationInterface
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
) {
const logger = new Logger(SlackSendMessageProducer.name);
super(logger);
}

@Process()
override async communication(job: Job<SlackMessageType>) {
const { slackChannelId } = job.data;

this.logger.verbose(
`execute communication for board with id: "${slackChannelId}" and Job id: "${job.id}" (pid ${process.pid})`
);

this.application.postMessageOnChannel(job.data);
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
}
}
9 changes: 9 additions & 0 deletions backend/src/modules/communication/dto/slack.message.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class SlackMessageDto {
slackChannelId!: string;
message!: string;

constructor(slackChannelId: string, message: string) {
this.slackChannelId = slackChannelId;
this.message = message;
}
}
5 changes: 5 additions & 0 deletions backend/src/modules/communication/dto/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,8 @@ export type ArchiveChannelData = {
export type AddUserMainChannelType = {
email: string;
};

export type SlackMessageType = {
slackChannelId: string;
message: string;
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TeamDto } from 'src/modules/communication/dto/team.dto';
import { BoardType } from 'src/modules/communication/dto/types';
import { BoardType, SlackMessageType } from 'src/modules/communication/dto/types';

export interface CommunicationApplicationInterface {
execute(board: BoardType): Promise<TeamDto[]>;
postMessageOnChannel(slackMessage: SlackMessageType): Promise<void>;
GoncaloCanteiro marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { SlackMessageType } from '../dto/types';

export interface SendMessageServiceInterface {
execute(data: SlackMessageType): Promise<void>;
}
3 changes: 2 additions & 1 deletion backend/src/modules/communication/interfaces/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export const TYPES = {
},
services: {
SlackCommunicationService: 'SlackCommunicationService',
SlackArchiveChannelService: 'SlackArchiveChannelService'
SlackArchiveChannelService: 'SlackArchiveChannelService',
SlackSendMessageService: 'SlackSendMessageService'
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { InjectQueue } from '@nestjs/bull';
import { Injectable, Logger } from '@nestjs/common';
import { Job, Queue } from 'bull';
import { SlackMessageType } from 'src/modules/communication/dto/types';

@Injectable()
export class SlackSendMessageProducer {
private logger = new Logger(SlackSendMessageProducer.name);

public static readonly QUEUE_NAME = SlackSendMessageProducer.name;

public static readonly ATTEMPTS = 3;

public static readonly BACKOFF = 3;

public static readonly DELAY = 0;

public static readonly REMOVE_ON_COMPLETE = true;

public static readonly REMOVE_ON_FAIL = true;

public static readonly PRIORITY = 1;

constructor(
@InjectQueue(SlackSendMessageProducer.QUEUE_NAME)
private readonly queue: Queue
) {}

// Job Options https://docs.nestjs.com/techniques/queues#job-options
async send(data: SlackMessageType): Promise<Job<SlackMessageType>> {
const job = await this.queue.add(data);

this.logger.verbose(
`Add SlackMessage with SlackChannelid: "${data.slackChannelId}" to queue with Job id: "${job.id}"`
);

return job;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Inject, Injectable } from '@nestjs/common';
import { SlackMessageType } from '../dto/types';
import { SendMessageServiceInterface } from '../interfaces/send-message.service.interface';
import { SlackSendMessageProducer } from '../producers/slack-send-message-channel.producer';

@Injectable()
export class SlackSendMessageService implements SendMessageServiceInterface {
constructor(
@Inject(SlackSendMessageProducer)
private readonly slackSendMessageProducer: SlackSendMessageProducer
) {}

//TODO: send message to the producer
public async execute(data: SlackMessageType): Promise<void> {
this.slackSendMessageProducer.send(data);
}
}
Loading