Files
gridpilot.gg/core/racing/application/use-cases/TransferLeagueOwnershipUseCase.ts
2026-01-16 18:21:06 +01:00

117 lines
3.9 KiB
TypeScript

import type {
LeagueMembershipRepository,
} from '@core/racing/domain/repositories/LeagueMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
export interface TransferLeagueOwnershipInput {
leagueId: string;
currentOwnerId: string;
newOwnerId: string;
}
export type TransferLeagueOwnershipResult = {
leagueId: string;
previousOwnerId: string;
newOwnerId: string;
};
export type TransferLeagueOwnershipErrorCode =
| 'LEAGUE_NOT_FOUND'
| 'NOT_LEAGUE_OWNER'
| 'NEW_OWNER_NOT_MEMBER'
| 'REPOSITORY_ERROR';
export class TransferLeagueOwnershipUseCase {
constructor(
private readonly leagueRepository: LeagueRepository,
private readonly membershipRepository: LeagueMembershipRepository,
private readonly logger: Logger,
) {}
async execute(
input: TransferLeagueOwnershipInput,
): Promise<
Result<TransferLeagueOwnershipResult, ApplicationErrorCode<TransferLeagueOwnershipErrorCode, { message: string }>>
> {
const { leagueId, currentOwnerId, newOwnerId } = input;
try {
const league = await this.leagueRepository.findById(leagueId);
if (!league) {
return Result.err({
code: 'LEAGUE_NOT_FOUND',
details: { message: `League with id ${leagueId} not found` },
} as ApplicationErrorCode<TransferLeagueOwnershipErrorCode, { message: string }>);
}
if (league.ownerId.toString() !== currentOwnerId) {
return Result.err({
code: 'NOT_LEAGUE_OWNER',
details: { message: 'Current user is not the league owner' },
} as ApplicationErrorCode<TransferLeagueOwnershipErrorCode, { message: string }>);
}
const newOwnerMembership = await this.membershipRepository.getMembership(
leagueId,
newOwnerId,
);
if (!newOwnerMembership || newOwnerMembership.status.toString() !== 'active') {
return Result.err({
code: 'NEW_OWNER_NOT_MEMBER',
details: { message: 'New owner must be an active league member' },
} as ApplicationErrorCode<TransferLeagueOwnershipErrorCode, { message: string }>);
}
const currentOwnerMembership = await this.membershipRepository.getMembership(
leagueId,
currentOwnerId,
);
const updatedNewOwnerMembership = {
...(newOwnerMembership as unknown as { role: string }),
role: 'owner',
} as unknown as import('../../domain/entities/LeagueMembership').LeagueMembership;
await this.membershipRepository.saveMembership(updatedNewOwnerMembership);
if (currentOwnerMembership) {
const updatedCurrentOwnerMembership = {
...(currentOwnerMembership as unknown as { role: string }),
role: 'admin',
} as unknown as import('../../domain/entities/LeagueMembership').LeagueMembership;
await this.membershipRepository.saveMembership(updatedCurrentOwnerMembership);
}
const updatedLeague = league.update({ ownerId: newOwnerId });
await this.leagueRepository.update(updatedLeague);
const result: TransferLeagueOwnershipResult = {
leagueId,
previousOwnerId: currentOwnerId,
newOwnerId,
};
return Result.ok(result);
} catch (error) {
const message =
error instanceof Error && error.message
? error.message
: 'Failed to transfer league ownership';
this.logger.error(message, error as Error, {
leagueId,
currentOwnerId,
newOwnerId,
});
return Result.err({
code: 'REPOSITORY_ERROR',
details: { message },
} as ApplicationErrorCode<TransferLeagueOwnershipErrorCode, { message: string }>);
}
}
}