Skip to content

Commit

Permalink
343/feature communication between create board and slack service (#437)
Browse files Browse the repository at this point in the history
* chore: add property to create board dto

* chore: call slack communication after add new board

* chore: add slack enable checkbox to create board form

* chore: call slack communication servive after a new board

* fix: board controller test

* chore: include month and year to slack channels name

* fix: provider name

* fix: channel name special characters

* fix: save slack result in log file

* fix: add any type in create card item

* chore: remove console.logs
  • Loading branch information
mourabraz authored Sep 16, 2022
1 parent c0e8e7e commit 0cbcc67
Show file tree
Hide file tree
Showing 19 changed files with 118 additions and 43 deletions.
5 changes: 4 additions & 1 deletion backend/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@ lerna-debug.log*
.env.dev
.env.staging

.vscode
.vscode

# Log file for slack
slackLog.txt
2 changes: 2 additions & 0 deletions backend/src/modules/boards/boards.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
mongooseBoardModule,
mongooseBoardUserModule
} from 'infrastructure/database/mongoose.module';
import { CommunicationModule } from 'modules/communication/communication.module';
import { SchedulesModule } from 'modules/schedules/schedules.module';
import TeamsModule from 'modules/teams/teams.module';
import UsersModule from 'modules/users/users.module';
Expand All @@ -25,6 +26,7 @@ import BoardsController from './controller/boards.controller';
UsersModule,
TeamsModule,
SchedulesModule,
CommunicationModule,
mongooseBoardModule,
mongooseBoardUserModule
],
Expand Down
5 changes: 5 additions & 0 deletions backend/src/modules/boards/dto/board.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,9 @@ export default class BoardDto {
@IsNotEmpty()
@IsBoolean()
isSubBoard?: boolean;

@ApiPropertyOptional({ default: false })
@IsOptional()
@IsBoolean()
slackEnable?: boolean;
}
36 changes: 32 additions & 4 deletions backend/src/modules/boards/services/create.board.service.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { Inject, Injectable } from '@nestjs/common';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { LeanDocument, Model } from 'mongoose';

import { BoardRoles } from 'libs/enum/board.roles';
import { TeamRoles } from 'libs/enum/team.roles';
import {
fillDividedBoardsUsersWithTeamUsers,
translateBoard
} from 'libs/utils/communication-helpers';
import { getDay, getNextMonth } from 'libs/utils/dates';
import { generateBoardDtoData, generateSubBoardDtoData } from 'libs/utils/generateBoardData';
import isEmpty from 'libs/utils/isEmpty';
import { GetBoardServiceInterface } from 'modules/boards/interfaces/services/get.board.service.interface';
import { TYPES } from 'modules/boards/interfaces/types';
import { ExecuteCommunicationInterface } from 'modules/communication/interfaces/execute-communication.interface';
import * as CommunicationsType from 'modules/communication/interfaces/types';
import { AddCronJobDto } from 'modules/schedules/dto/add.cronjob.dto';
import { CreateSchedulesServiceInterface } from 'modules/schedules/interfaces/services/create.schedules.service';
import * as SchedulesType from 'modules/schedules/interfaces/types';
import TeamDto from 'modules/teams/dto/team.dto';
import { GetTeamServiceInterface } from 'modules/teams/interfaces/services/get.team.service.interface';
import { TYPES } from 'modules/teams/interfaces/types';
import { TYPES as TeamType } from 'modules/teams/interfaces/types';
import TeamUser, { TeamUserDocument } from 'modules/teams/schemas/team.user.schema';
import { UserDocument } from 'modules/users/schemas/user.schema';

Expand All @@ -31,14 +39,20 @@ export interface CreateBoardDto {

@Injectable()
export default class CreateBoardServiceImpl implements CreateBoardService {
private logger = new Logger(CreateBoardServiceImpl.name);

constructor(
@InjectModel(Board.name) private boardModel: Model<BoardDocument>,
@InjectModel(BoardUser.name)
private boardUserModel: Model<BoardUserDocument>,
@Inject(TYPES.services.GetTeamService)
@Inject(TeamType.services.GetTeamService)
private getTeamService: GetTeamServiceInterface,
@Inject(TYPES.services.GetBoardService)
private getBoardService: GetBoardServiceInterface,
@Inject(SchedulesType.TYPES.services.CreateSchedulesService)
private createSchedulesService: CreateSchedulesServiceInterface
private createSchedulesService: CreateSchedulesServiceInterface,
@Inject(CommunicationsType.TYPES.services.ExecuteCommunication)
private slackCommunicationService: ExecuteCommunicationInterface
) {}

saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) {
Expand Down Expand Up @@ -133,6 +147,20 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
this.createFirstCronJob(addCronJobDto);
}

this.logger.verbose(`Communication Slack Enable is set to "${boardData.slackEnable}".`);
if (boardData.slackEnable) {
const result = await this.getBoardService.getBoard(newBoard._id, userId);
if (result?.board) {
this.logger.verbose(`Call Slack Communication Service for board id "${newBoard._id}".`);
const board = fillDividedBoardsUsersWithTeamUsers(translateBoard(result.board));
this.slackCommunicationService.execute(board);
} else {
this.logger.error(
`Call Slack Communication Service for board id "${newBoard._id}" fails. Board not found.`
);
}
}

return newBoard;
}

Expand Down
2 changes: 1 addition & 1 deletion backend/src/modules/cards/services/create.card.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export default class CreateCardServiceImpl implements CreateCardService {
card.createdBy = userId;

if (isEmpty(card.items)) {
card.items.push({
(card.items as any[]).push({
text: card.text,
createdBy: userId,
comments: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,12 @@ export class SlackExecuteCommunication implements ExecuteCommunicationInterface
}

private async createAllChannels(teams: TeamDto[]): Promise<TeamDto[]> {
const today = new Date();
const year = today.getFullYear();
const month = today.toLocaleString('default', { month: 'long' }).toLowerCase();
const createChannelsPromises = teams.map((i) =>
this.conversationsHandler.createChannel(
`${i.normalName}${i.for === BoardRoles.RESPONSIBLE ? '-responsibles' : ''}`
`${i.normalName}${i.for === BoardRoles.RESPONSIBLE ? '-responsibles' : ''}-${month}-${year}`
)
);

Expand All @@ -121,10 +124,10 @@ export class SlackExecuteCommunication implements ExecuteCommunicationInterface
errors.forEach((i) => this.logger.warn(i));

success.forEach(({ id: channelId, name: channelName }) => {
const board = teams.find(
(i) =>
channelName ===
const board = teams.find((i) =>
channelName.includes(
`${i.normalName}${i.for === BoardRoles.RESPONSIBLE ? '-responsibles' : ''}`
)
);
if (board) {
board.channelId = channelId;
Expand Down
10 changes: 5 additions & 5 deletions backend/src/modules/communication/communication.module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { BullModule } from '@nestjs/bull';
import { Module } from '@nestjs/common';
import { forwardRef, Module } from '@nestjs/common';
import { join } from 'path';

import BoardsModule from 'modules/boards/boards.module';
Expand All @@ -8,14 +8,14 @@ import {
CommunicationGateAdapter,
ConversationsHandler,
ExecuteCommunication,
ExecuteCommunicationService,
UsersHandler
} from 'modules/communication/communication.providers';
import { CommunicationProducerService } from 'modules/communication/producers/slack-communication.producer.service';
import { SlackExecuteCommunicationService } from 'modules/communication/services/slack-execute-communication.service';

@Module({
imports: [
BoardsModule,
forwardRef(() => BoardsModule),
BullModule.registerQueueAsync({
name: CommunicationProducerService.QUEUE_NAME,
useFactory: async () => ({
Expand All @@ -25,14 +25,14 @@ import { SlackExecuteCommunicationService } from 'modules/communication/services
})
],
providers: [
SlackExecuteCommunicationService,
ExecuteCommunicationService,
CommunicationGateAdapter,
ChatHandler,
ConversationsHandler,
UsersHandler,
ExecuteCommunication,
CommunicationProducerService
],
exports: [SlackExecuteCommunicationService]
exports: [ExecuteCommunicationService]
})
export class CommunicationModule {}
7 changes: 7 additions & 0 deletions backend/src/modules/communication/communication.providers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import { UsersSlackHandler } from 'modules/communication/handlers/users-slack.ha
import { ChatHandlerInterface } from 'modules/communication/interfaces/chat.handler.interface';
import { CommunicationGateInterface } from 'modules/communication/interfaces/communication-gate.interface';
import { ConversationsHandlerInterface } from 'modules/communication/interfaces/conversations.handler.interface';
import { TYPES } from 'modules/communication/interfaces/types';
import { UsersHandlerInterface } from 'modules/communication/interfaces/users.handler.interface';
import { SlackExecuteCommunicationService } from 'modules/communication/services/slack-execute-communication.service';

export const CommunicationGateAdapter = {
provide: SlackCommunicationGateAdapter,
Expand Down Expand Up @@ -75,3 +77,8 @@ export const ExecuteCommunication = {
},
inject: [ConfigService, ConversationsSlackHandler, UsersSlackHandler, ChatSlackHandler]
};

export const ExecuteCommunicationService = {
provide: TYPES.services.ExecuteCommunication,
useClass: SlackExecuteCommunicationService
};
5 changes: 5 additions & 0 deletions backend/src/modules/communication/interfaces/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const TYPES = {
services: {
ExecuteCommunication: 'ExecuteCommunication'
}
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { InjectQueue } from '@nestjs/bull';
import { Injectable, Logger } from '@nestjs/common';
import { Job, Queue } from 'bull';
import { createWriteStream } from 'fs';

import { JobType } from 'modules/communication/dto/types';

Expand All @@ -15,8 +16,9 @@ export class CommunicationProducerService {
private readonly queue: Queue
) {
// https://github.com/OptimalBits/bull/blob/develop/REFERENCE.md#events
this.queue.on('completed', (job: Job<JobType>) => {
this.queue.on('completed', (job: Job<JobType>, result: any) => {
this.logger.verbose(`Completed Job id: "${job.id}"`);
this.saveLog(result);
job.remove();
});
this.queue.on('error', (error: Error) => {
Expand All @@ -37,4 +39,14 @@ export class CommunicationProducerService {

return job;
}

saveLog(data: any) {
try {
const stream = createWriteStream('slackLog.txt', { flags: 'a' });
stream.write(`${JSON.stringify(data)}\n`);
stream.end();
} catch (error) {
this.logger.error(error);
}
}
}
7 changes: 7 additions & 0 deletions backend/src/test/boards/boards.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
updateBoardService
} from 'modules/boards/boards.providers';
import BoardsController from 'modules/boards/controller/boards.controller';
import * as CommunicationsType from 'modules/communication/interfaces/types';
import { createSchedulesService } from 'modules/schedules/schedules.providers';
import SocketGateway from 'modules/socket/gateway/socket.gateway';
import { createTeamService, getTeamApplication, getTeamService } from 'modules/teams/providers';
Expand Down Expand Up @@ -61,6 +62,12 @@ describe('BoardsController', () => {
{
provide: getModelToken('Schedules'),
useValue: {}
},
{
provide: CommunicationsType.TYPES.services.ExecuteCommunication,
useValue: {
execute: jest.fn()
}
}
]
}).compile();
Expand Down
7 changes: 3 additions & 4 deletions frontend/src/components/Board/Settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useRecoilValue } from 'recoil';
import { joiResolver } from '@hookform/resolvers/joi';
import { Accordion } from '@radix-ui/react-accordion';
import { Dialog, DialogClose, DialogTrigger } from '@radix-ui/react-dialog';
import { deepClone } from 'fast-json-patch';
import { Dispatch, SetStateAction, useEffect, useMemo, useRef, useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { useRecoilValue } from 'recoil';

import Icon from 'components/icons/Icon';
import Avatar from 'components/Primitives/Avatar';
Expand Down Expand Up @@ -225,7 +225,6 @@ const BoardSettings = ({

const keyDownHandler = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
console.log('---');
event.preventDefault();

if (submitBtnRef.current) {
Expand Down
14 changes: 4 additions & 10 deletions frontend/src/components/CreateBoard/SubTeamsTab/MainBoardCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,6 @@ const MainBoardCard = React.memo(({ team, timesOpen }: MainBoardCardInterface) =
teamMembers
} = useCreateBoard(team);

const slackGroupHandler = () => {
setCreateBoardData((prev) => ({
...prev,
board: { ...prev.board, slackGroup: !board.slackGroup }
}));
};

const teamMembersCount = teamMembers?.length ?? 0;

useEffect(() => {
Expand Down Expand Up @@ -208,10 +201,11 @@ const MainBoardCard = React.memo(({ team, timesOpen }: MainBoardCardInterface) =
</Flex>
</MainContainer>
<SubBoardList dividedBoards={board.dividedBoards} setBoard={setCreateBoardData} />
<Box onClick={slackGroupHandler}>
<Box>
{/* onClick={slackEnableHandler} */}
<Checkbox
checked={board.slackGroup}
id="slack"
// checked={board.slackEnable}
id="slackEnable"
label="Create Slack group for each sub-team"
size="16"
/>
Expand Down
7 changes: 6 additions & 1 deletion frontend/src/components/Primitives/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { Dispatch, useState } from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import { styled } from '@stitches/react';
import React, { Dispatch, useState } from 'react';
import { useFormContext } from 'react-hook-form';

import Icon from 'components/icons/Icon';
import Flex from './Flex';
Expand Down Expand Up @@ -77,13 +78,17 @@ const Checkbox: React.FC<{
setCheckedTerms: null
};

const { setValue } = useFormContext();

const [currentCheckValue, setCurrentCheckValue] = useState<
boolean | undefined | 'indeterminate'
>(checked);
const handleCheckedChange = (isChecked: boolean | 'indeterminate') => {
if (handleChange) handleChange(id);
setCurrentCheckValue(isChecked);
if (setCheckedTerms != null) setCheckedTerms(!!isChecked);

setValue('slackEnable', !!isChecked);
};

return (
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/hooks/useBoard.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AxiosError } from 'axios';
import { useMutation, useQuery } from 'react-query';
import { useSetRecoilState } from 'recoil';
import { AxiosError } from 'axios';

import {
createBoardRequest,
Expand Down
Loading

0 comments on commit 0cbcc67

Please sign in to comment.