feat: emit notification events on player active/role changes
This commit is contained in:
@@ -18,6 +18,7 @@ describe('TeamMembersService', () => {
|
||||
let dataSource: any;
|
||||
let logger: any;
|
||||
let access: any;
|
||||
let eventEmitter: any;
|
||||
let service: TeamMembersService;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -44,7 +45,8 @@ describe('TeamMembersService', () => {
|
||||
dataSource = { transaction: jest.fn((work) => work(manager)) };
|
||||
logger = { info: jest.fn() };
|
||||
access = { assertAtLeast: jest.fn(() => Promise.resolve()) };
|
||||
service = new TeamMembersService(dataSource, logger, access as any);
|
||||
eventEmitter = { emit: jest.fn() };
|
||||
service = new TeamMembersService(dataSource, logger, access as any, eventEmitter as any);
|
||||
});
|
||||
|
||||
it('checks the team-manager permission before touching the database', async () => {
|
||||
@@ -188,6 +190,53 @@ describe('TeamMembersService', () => {
|
||||
).rejects.toBeInstanceOf(NotFoundException);
|
||||
});
|
||||
|
||||
it('emits a player-active-changed event after a real deactivation', async () => {
|
||||
player.balance = 42;
|
||||
treasurers = [player, makePlayer(102, true, TeamRolesEnum.treasurer, 0)];
|
||||
|
||||
await service.setActive(5, teamId, player.id, false);
|
||||
|
||||
expect(eventEmitter.emit).toHaveBeenCalledWith(
|
||||
'notifications.player.active_changed',
|
||||
expect.objectContaining({
|
||||
teamId,
|
||||
actorUserId: 5,
|
||||
playerId: player.id,
|
||||
playerName: 'Pat Player',
|
||||
active: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not emit when the active state is unchanged (idempotent)', async () => {
|
||||
player = makePlayer(101, true, TeamRolesEnum.player, 0);
|
||||
lockedPlayerQuery = chain({ getOne: jest.fn(() => player) });
|
||||
playerRepository.createQueryBuilder = jest.fn((alias: string) =>
|
||||
alias === 'lockedPlayer' ? lockedPlayerQuery : treasurerLockQuery,
|
||||
);
|
||||
|
||||
await service.setActive(5, teamId, player.id, true);
|
||||
|
||||
expect(eventEmitter.emit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('emits a player-role-changed event after a real role change', async () => {
|
||||
treasurers = [player, makePlayer(102, true, TeamRolesEnum.treasurer, 0)];
|
||||
|
||||
await service.setTeamRole(5, teamId, player.id, TeamRolesEnum.captain);
|
||||
|
||||
expect(eventEmitter.emit).toHaveBeenCalledWith(
|
||||
'notifications.player.role_changed',
|
||||
expect.objectContaining({
|
||||
teamId,
|
||||
actorUserId: 5,
|
||||
playerId: player.id,
|
||||
playerName: 'Pat Player',
|
||||
teamRoleId: TeamRolesEnum.captain,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
function makePlayer(
|
||||
id: number,
|
||||
active: boolean,
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||
import { DataSource, EntityManager, Repository } from 'typeorm';
|
||||
import { LoggingService } from '../database/logging/logging.service';
|
||||
import { NOTIFICATION_EVENT_NAME } from '../notifications/events/notification-event-names';
|
||||
import { PlayerActiveChangedEvent } from '../notifications/events/player-active-changed.event';
|
||||
import { PlayerRoleChangedEvent } from '../notifications/events/player-role-changed.event';
|
||||
import { Player } from '../players/entities/player.entity';
|
||||
import { TeamRole } from '../team-roles/entities/team-roles.entity';
|
||||
import { TeamRolesEnum } from '../team-roles/team-roles.enum';
|
||||
@@ -18,6 +22,7 @@ export class TeamMembersService {
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly logger: LoggingService,
|
||||
private readonly access: TeamAccessService,
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
) {}
|
||||
|
||||
async setActive(
|
||||
@@ -33,12 +38,12 @@ export class TeamMembersService {
|
||||
TeamRolesEnum.captain,
|
||||
);
|
||||
|
||||
return this.dataSource.transaction(async (manager) => {
|
||||
const result = await this.dataSource.transaction(async (manager) => {
|
||||
const activeTreasurers = await this.lockActiveTreasurers(manager, teamId);
|
||||
const playerRepository = manager.getRepository(Player);
|
||||
const player = await this.findLockedPlayer(playerRepository, playerId, teamId);
|
||||
|
||||
if (player.active === active) return player;
|
||||
if (player.active === active) return { player, changed: false };
|
||||
|
||||
const isDeactivation = player.active && !active;
|
||||
if (
|
||||
@@ -66,8 +71,23 @@ export class TeamMembersService {
|
||||
actorUserId,
|
||||
`teamId=${teamId} playerId=${playerId} active=${active}`,
|
||||
);
|
||||
return player;
|
||||
return { player, changed: true };
|
||||
});
|
||||
|
||||
if (result.changed) {
|
||||
this.eventEmitter.emit(
|
||||
NOTIFICATION_EVENT_NAME.playerActiveChanged,
|
||||
new PlayerActiveChangedEvent(
|
||||
teamId,
|
||||
actorUserId,
|
||||
playerId,
|
||||
`${result.player.firstName} ${result.player.lastName}`,
|
||||
active,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result.player;
|
||||
}
|
||||
|
||||
async setTeamRole(
|
||||
@@ -83,12 +103,12 @@ export class TeamMembersService {
|
||||
TeamRolesEnum.captain,
|
||||
);
|
||||
|
||||
return this.dataSource.transaction(async (manager) => {
|
||||
const result = await this.dataSource.transaction(async (manager) => {
|
||||
const activeTreasurers = await this.lockActiveTreasurers(manager, teamId);
|
||||
const playerRepository = manager.getRepository(Player);
|
||||
const player = await this.findLockedPlayer(playerRepository, playerId, teamId);
|
||||
|
||||
if (player.teamRole?.id === teamRoleId) return player;
|
||||
if (player.teamRole?.id === teamRoleId) return { player, changed: false };
|
||||
|
||||
const isDemotionFromTreasurer =
|
||||
player.active &&
|
||||
@@ -108,8 +128,23 @@ export class TeamMembersService {
|
||||
actorUserId,
|
||||
`teamId=${teamId} playerId=${playerId} teamRoleId=${teamRoleId}`,
|
||||
);
|
||||
return player;
|
||||
return { player, changed: true };
|
||||
});
|
||||
|
||||
if (result.changed) {
|
||||
this.eventEmitter.emit(
|
||||
NOTIFICATION_EVENT_NAME.playerRoleChanged,
|
||||
new PlayerRoleChangedEvent(
|
||||
teamId,
|
||||
actorUserId,
|
||||
playerId,
|
||||
`${result.player.firstName} ${result.player.lastName}`,
|
||||
teamRoleId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return result.player;
|
||||
}
|
||||
|
||||
// insert() statt save(): umgeht bewusst @BeforeInsert setBalance() auf Transaction,
|
||||
|
||||
Reference in New Issue
Block a user