Files
gridpilot.gg/core/racing/application/use-cases/TransferLeagueOwnershipUseCase.ts
2025-12-21 00:43:42 +01:00

120 lines
4.0 KiB
TypeScript

import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort, Logger } from '@core/shared/application';
import type {
ILeagueMembershipRepository,
} from '@core/racing/domain/repositories/ILeagueMembershipRepository';
import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeagueRepository';
export type 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: ILeagueRepository,
private readonly membershipRepository: ILeagueMembershipRepository,
private readonly logger: Logger,
private readonly output: UseCaseOutputPort<TransferLeagueOwnershipResult>,
) {}
async execute(
input: TransferLeagueOwnershipInput,
): Promise<
Result<void, 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,
};
this.output.present(result);
return Result.ok(undefined);
} 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 }>);
}
}
}