inmemory to postgres
This commit is contained in:
@@ -52,6 +52,37 @@ export class User {
|
||||
return new User(props);
|
||||
}
|
||||
|
||||
public static rehydrate(props: {
|
||||
id: string;
|
||||
displayName: string;
|
||||
email?: string;
|
||||
passwordHash?: PasswordHash;
|
||||
iracingCustomerId?: string;
|
||||
primaryDriverId?: string;
|
||||
avatarUrl?: string;
|
||||
}): User {
|
||||
const email =
|
||||
props.email !== undefined
|
||||
? (() => {
|
||||
const result: EmailValidationResult = validateEmail(props.email);
|
||||
if (!result.success) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
return result.email;
|
||||
})()
|
||||
: undefined;
|
||||
|
||||
return new User({
|
||||
id: UserId.fromString(props.id),
|
||||
displayName: props.displayName,
|
||||
...(email !== undefined ? { email } : {}),
|
||||
...(props.passwordHash !== undefined ? { passwordHash: props.passwordHash } : {}),
|
||||
...(props.iracingCustomerId !== undefined ? { iracingCustomerId: props.iracingCustomerId } : {}),
|
||||
...(props.primaryDriverId !== undefined ? { primaryDriverId: props.primaryDriverId } : {}),
|
||||
...(props.avatarUrl !== undefined ? { avatarUrl: props.avatarUrl } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
public static fromStored(stored: StoredUser): User {
|
||||
const passwordHash = stored.passwordHash
|
||||
? PasswordHash.fromHash(stored.passwordHash)
|
||||
|
||||
@@ -18,4 +18,10 @@ export interface MemberPayment {
|
||||
status: MemberPaymentStatus;
|
||||
dueDate: Date;
|
||||
paidAt?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export const MemberPayment = {
|
||||
rehydrate(props: MemberPayment): MemberPayment {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
@@ -17,4 +17,10 @@ export interface MembershipFee {
|
||||
enabled: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export const MembershipFee = {
|
||||
rehydrate(props: MembershipFee): MembershipFee {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
@@ -32,4 +32,10 @@ export interface Payment {
|
||||
status: PaymentStatus;
|
||||
createdAt: Date;
|
||||
completedAt?: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export const Payment = {
|
||||
rehydrate(props: Payment): Payment {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
@@ -21,4 +21,10 @@ export interface Prize {
|
||||
awardedTo?: string;
|
||||
awardedAt?: Date;
|
||||
createdAt: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export const Prize = {
|
||||
rehydrate(props: Prize): Prize {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
@@ -13,6 +13,12 @@ export interface Wallet {
|
||||
createdAt: Date;
|
||||
}
|
||||
|
||||
export const Wallet = {
|
||||
rehydrate(props: Wallet): Wallet {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
|
||||
export enum TransactionType {
|
||||
DEPOSIT = 'deposit',
|
||||
WITHDRAWAL = 'withdrawal',
|
||||
@@ -34,4 +40,10 @@ export interface Transaction {
|
||||
referenceId?: string;
|
||||
referenceType?: ReferenceType;
|
||||
createdAt: Date;
|
||||
}
|
||||
}
|
||||
|
||||
export const Transaction = {
|
||||
rehydrate(props: Transaction): Transaction {
|
||||
return { ...props };
|
||||
},
|
||||
};
|
||||
@@ -62,6 +62,24 @@ export class Driver implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
iracingId: string;
|
||||
name: string;
|
||||
country: string;
|
||||
bio?: string;
|
||||
joinedAt: Date;
|
||||
}): Driver {
|
||||
return new Driver({
|
||||
id: props.id,
|
||||
iracingId: IRacingId.create(props.iracingId),
|
||||
name: DriverName.create(props.name),
|
||||
country: CountryCode.create(props.country),
|
||||
...(props.bio !== undefined ? { bio: DriverBio.create(props.bio) } : {}),
|
||||
joinedAt: JoinedAt.create(props.joinedAt),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy with updated properties
|
||||
*/
|
||||
|
||||
@@ -20,4 +20,11 @@ export class Game implements IEntity<GameId> {
|
||||
name,
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: { id: string; name: string }): Game {
|
||||
return new Game({
|
||||
id: GameId.create(props.id),
|
||||
name: GameName.create(props.name),
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -49,6 +49,22 @@ export class JoinRequest implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
requestedAt: Date;
|
||||
message?: string;
|
||||
}): JoinRequest {
|
||||
return new JoinRequest({
|
||||
id: props.id,
|
||||
leagueId: props.leagueId,
|
||||
driverId: props.driverId,
|
||||
requestedAt: props.requestedAt,
|
||||
...(props.message !== undefined && { message: props.message }),
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: JoinRequestProps): void {
|
||||
if (!props.leagueId || props.leagueId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('League ID is required');
|
||||
|
||||
@@ -70,6 +70,24 @@ export class LeagueMembership implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
role: MembershipRoleValue;
|
||||
status: MembershipStatusValue;
|
||||
joinedAt: Date;
|
||||
}): LeagueMembership {
|
||||
return new LeagueMembership({
|
||||
id: props.id,
|
||||
leagueId: LeagueId.create(props.leagueId),
|
||||
driverId: DriverId.create(props.driverId),
|
||||
role: MembershipRole.create(props.role),
|
||||
status: MembershipStatus.create(props.status),
|
||||
joinedAt: JoinedAt.create(props.joinedAt),
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: LeagueMembershipProps): void {
|
||||
if (!props.leagueId || props.leagueId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('League ID is required');
|
||||
|
||||
@@ -116,6 +116,61 @@ export class Protest implements IEntity<string> {
|
||||
return new Protest(protestProps);
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
raceId: string;
|
||||
protestingDriverId: string;
|
||||
accusedDriverId: string;
|
||||
incident: { lap: number; description: string; timeInRace?: number };
|
||||
comment?: string;
|
||||
proofVideoUrl?: string;
|
||||
status: string;
|
||||
reviewedBy?: string;
|
||||
decisionNotes?: string;
|
||||
filedAt: Date;
|
||||
reviewedAt?: Date;
|
||||
defense?: { statement: string; videoUrl?: string; submittedAt: Date };
|
||||
defenseRequestedAt?: Date;
|
||||
defenseRequestedBy?: string;
|
||||
}): Protest {
|
||||
const id = ProtestId.create(props.id);
|
||||
const raceId = RaceId.create(props.raceId);
|
||||
const protestingDriverId = DriverId.create(props.protestingDriverId);
|
||||
const accusedDriverId = DriverId.create(props.accusedDriverId);
|
||||
const incident = ProtestIncident.create(props.incident.lap, props.incident.description, props.incident.timeInRace);
|
||||
const comment = props.comment ? ProtestComment.create(props.comment) : undefined;
|
||||
const proofVideoUrl = props.proofVideoUrl ? VideoUrl.create(props.proofVideoUrl) : undefined;
|
||||
const status = ProtestStatus.create(props.status);
|
||||
const reviewedBy = props.reviewedBy ? StewardId.create(props.reviewedBy) : undefined;
|
||||
const decisionNotes = props.decisionNotes ? DecisionNotes.create(props.decisionNotes) : undefined;
|
||||
const filedAt = FiledAt.create(props.filedAt);
|
||||
const reviewedAt = props.reviewedAt ? ReviewedAt.create(props.reviewedAt) : undefined;
|
||||
const defense = props.defense ? ProtestDefense.create(props.defense.statement, props.defense.submittedAt, props.defense.videoUrl) : undefined;
|
||||
const defenseRequestedAt = props.defenseRequestedAt ? DefenseRequestedAt.create(props.defenseRequestedAt) : undefined;
|
||||
const defenseRequestedBy = props.defenseRequestedBy ? StewardId.create(props.defenseRequestedBy) : undefined;
|
||||
|
||||
const protestProps: ProtestProps = {
|
||||
id,
|
||||
raceId,
|
||||
protestingDriverId,
|
||||
accusedDriverId,
|
||||
incident,
|
||||
status,
|
||||
filedAt,
|
||||
};
|
||||
|
||||
if (comment !== undefined) protestProps.comment = comment;
|
||||
if (proofVideoUrl !== undefined) protestProps.proofVideoUrl = proofVideoUrl;
|
||||
if (reviewedBy !== undefined) protestProps.reviewedBy = reviewedBy;
|
||||
if (decisionNotes !== undefined) protestProps.decisionNotes = decisionNotes;
|
||||
if (reviewedAt !== undefined) protestProps.reviewedAt = reviewedAt;
|
||||
if (defense !== undefined) protestProps.defense = defense;
|
||||
if (defenseRequestedAt !== undefined) protestProps.defenseRequestedAt = defenseRequestedAt;
|
||||
if (defenseRequestedBy !== undefined) protestProps.defenseRequestedBy = defenseRequestedBy;
|
||||
|
||||
return new Protest(protestProps);
|
||||
}
|
||||
|
||||
get id(): string { return this.props.id.toString(); }
|
||||
get raceId(): string { return this.props.raceId.toString(); }
|
||||
get protestingDriverId(): string { return this.props.protestingDriverId.toString(); }
|
||||
|
||||
@@ -55,6 +55,15 @@ export class RaceRegistration implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: { id: string; raceId: string; driverId: string; registeredAt: Date }): RaceRegistration {
|
||||
return new RaceRegistration({
|
||||
id: props.id,
|
||||
raceId: RaceId.create(props.raceId),
|
||||
driverId: DriverId.create(props.driverId),
|
||||
registeredAt: RegisteredAt.create(props.registeredAt),
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: RaceRegistrationProps): void {
|
||||
if (!props.raceId || props.raceId.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Race ID is required');
|
||||
|
||||
@@ -71,6 +71,10 @@ export class SponsorshipRequest implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: SponsorshipRequestProps): SponsorshipRequest {
|
||||
return new SponsorshipRequest(props);
|
||||
}
|
||||
|
||||
private static validate(props: Omit<SponsorshipRequestProps, 'createdAt' | 'status'>): void {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('SponsorshipRequest ID is required');
|
||||
|
||||
@@ -74,6 +74,30 @@ export class Standing implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
driverId: string;
|
||||
points: number;
|
||||
wins: number;
|
||||
position: number;
|
||||
racesCompleted: number;
|
||||
}): Standing {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Standing ID is required');
|
||||
}
|
||||
|
||||
return new Standing({
|
||||
id: props.id,
|
||||
leagueId: LeagueId.create(props.leagueId),
|
||||
driverId: DriverId.create(props.driverId),
|
||||
points: Points.create(props.points),
|
||||
wins: props.wins,
|
||||
position: Position.create(props.position),
|
||||
racesCompleted: props.racesCompleted,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain validation logic
|
||||
*/
|
||||
|
||||
@@ -73,6 +73,34 @@ export class Team implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
name: string;
|
||||
tag: string;
|
||||
description: string;
|
||||
ownerId: string;
|
||||
leagues: string[];
|
||||
createdAt: Date;
|
||||
}): Team {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Team ID is required');
|
||||
}
|
||||
|
||||
if (!Array.isArray(props.leagues)) {
|
||||
throw new RacingDomainValidationError('Team leagues must be an array');
|
||||
}
|
||||
|
||||
return new Team({
|
||||
id: props.id,
|
||||
name: TeamName.create(props.name),
|
||||
tag: TeamTag.create(props.tag),
|
||||
description: TeamDescription.create(props.description),
|
||||
ownerId: DriverId.create(props.ownerId),
|
||||
leagues: props.leagues.map(leagueId => LeagueId.create(leagueId)),
|
||||
createdAt: TeamCreatedAt.create(props.createdAt),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy with updated properties.
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,26 @@ export class LeagueWallet implements IEntity<LeagueWalletId> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
balance: Money;
|
||||
transactionIds: string[];
|
||||
createdAt: Date;
|
||||
}): LeagueWallet {
|
||||
const id = LeagueWalletId.create(props.id);
|
||||
const leagueId = LeagueId.create(props.leagueId);
|
||||
const transactionIds = props.transactionIds.map(tid => TransactionId.create(tid));
|
||||
|
||||
return new LeagueWallet({
|
||||
id,
|
||||
leagueId,
|
||||
balance: props.balance,
|
||||
transactionIds,
|
||||
createdAt: props.createdAt,
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
|
||||
@@ -8,8 +8,8 @@ import { RacingDomainValidationError, RacingDomainInvariantError } from '../../e
|
||||
|
||||
import type { Money } from '../../value-objects/Money';
|
||||
import type { IEntity } from '@core/shared/domain';
|
||||
import type { TransactionId } from './TransactionId';
|
||||
import type { LeagueWalletId } from './LeagueWalletId';
|
||||
import { TransactionId } from './TransactionId';
|
||||
import { LeagueWalletId } from './LeagueWalletId';
|
||||
|
||||
export type TransactionType =
|
||||
| 'sponsorship_payment'
|
||||
@@ -79,6 +79,34 @@ export class Transaction implements IEntity<TransactionId> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
walletId: string;
|
||||
type: TransactionType;
|
||||
amount: Money;
|
||||
platformFee: Money;
|
||||
netAmount: Money;
|
||||
status: TransactionStatus;
|
||||
createdAt: Date;
|
||||
completedAt?: Date;
|
||||
description?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}): Transaction {
|
||||
return new Transaction({
|
||||
id: TransactionId.create(props.id),
|
||||
walletId: LeagueWalletId.create(props.walletId),
|
||||
type: props.type,
|
||||
amount: props.amount,
|
||||
platformFee: props.platformFee,
|
||||
netAmount: props.netAmount,
|
||||
status: props.status,
|
||||
createdAt: props.createdAt,
|
||||
completedAt: props.completedAt,
|
||||
description: props.description,
|
||||
metadata: props.metadata,
|
||||
});
|
||||
}
|
||||
|
||||
private static validate(props: Omit<TransactionProps, 'createdAt' | 'status' | 'platformFee' | 'netAmount'>): void {
|
||||
if (!props.id) {
|
||||
throw new RacingDomainValidationError('Transaction ID is required');
|
||||
|
||||
@@ -114,6 +114,46 @@ export class Penalty implements IEntity<string> {
|
||||
return new Penalty(penaltyProps);
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
leagueId: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
type: string;
|
||||
value?: number;
|
||||
reason: string;
|
||||
protestId?: string;
|
||||
issuedBy: string;
|
||||
status: string;
|
||||
issuedAt: Date;
|
||||
appliedAt?: Date;
|
||||
notes?: string;
|
||||
}): Penalty {
|
||||
const penaltyType = PenaltyType.create(props.type);
|
||||
|
||||
if (penaltyTypeRequiresValue(penaltyType.toString())) {
|
||||
if (props.value === undefined || props.value <= 0) {
|
||||
throw new RacingDomainValidationError(`${penaltyType.toString()} requires a positive value`);
|
||||
}
|
||||
}
|
||||
|
||||
return new Penalty({
|
||||
id: PenaltyId.create(props.id),
|
||||
leagueId: LeagueId.create(props.leagueId),
|
||||
raceId: RaceId.create(props.raceId),
|
||||
driverId: DriverId.create(props.driverId),
|
||||
type: penaltyType,
|
||||
...(props.value !== undefined ? { value: PenaltyValue.create(props.value) } : {}),
|
||||
reason: PenaltyReason.create(props.reason),
|
||||
...(props.protestId !== undefined ? { protestId: ProtestId.create(props.protestId) } : {}),
|
||||
issuedBy: StewardId.create(props.issuedBy),
|
||||
status: PenaltyStatus.create(props.status),
|
||||
issuedAt: IssuedAt.create(props.issuedAt),
|
||||
...(props.appliedAt !== undefined ? { appliedAt: AppliedAt.create(props.appliedAt) } : {}),
|
||||
...(props.notes !== undefined ? { notes: PenaltyNotes.create(props.notes) } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
get id(): string { return this.props.id.toString(); }
|
||||
get leagueId(): string { return this.props.leagueId.toString(); }
|
||||
get raceId(): string { return this.props.raceId.toString(); }
|
||||
|
||||
@@ -72,6 +72,30 @@ export class Result implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
raceId: string;
|
||||
driverId: string;
|
||||
position: number;
|
||||
fastestLap: number;
|
||||
incidents: number;
|
||||
startPosition: number;
|
||||
}): Result {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('Result ID is required');
|
||||
}
|
||||
|
||||
return new Result({
|
||||
id: props.id,
|
||||
raceId: RaceId.create(props.raceId),
|
||||
driverId: DriverId.create(props.driverId),
|
||||
position: Position.create(props.position),
|
||||
fastestLap: LapTime.create(props.fastestLap),
|
||||
incidents: IncidentCount.create(props.incidents),
|
||||
startPosition: Position.create(props.startPosition),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Domain validation logic
|
||||
*/
|
||||
|
||||
@@ -83,6 +83,10 @@ export class SeasonSponsorship implements IEntity<string> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: SeasonSponsorshipProps): SeasonSponsorship {
|
||||
return new SeasonSponsorship(props);
|
||||
}
|
||||
|
||||
private static validate(props: Omit<SeasonSponsorshipProps, 'createdAt' | 'status'>): void {
|
||||
if (!props.id || props.id.trim().length === 0) {
|
||||
throw new RacingDomainValidationError('SeasonSponsorship ID is required');
|
||||
|
||||
@@ -62,6 +62,31 @@ export class Sponsor implements IEntity<SponsorId> {
|
||||
});
|
||||
}
|
||||
|
||||
static rehydrate(props: {
|
||||
id: string;
|
||||
name: string;
|
||||
contactEmail: string;
|
||||
logoUrl?: string;
|
||||
websiteUrl?: string;
|
||||
createdAt: Date;
|
||||
}): Sponsor {
|
||||
const id = SponsorId.create(props.id);
|
||||
const name = SponsorName.create(props.name);
|
||||
const contactEmail = SponsorEmail.create(props.contactEmail);
|
||||
const logoUrl = props.logoUrl ? Url.create(props.logoUrl) : undefined;
|
||||
const websiteUrl = props.websiteUrl ? Url.create(props.websiteUrl) : undefined;
|
||||
const createdAt = SponsorCreatedAt.create(props.createdAt);
|
||||
|
||||
return new Sponsor({
|
||||
id,
|
||||
name,
|
||||
contactEmail,
|
||||
createdAt,
|
||||
...(logoUrl !== undefined ? { logoUrl } : {}),
|
||||
...(websiteUrl !== undefined ? { websiteUrl } : {}),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Update sponsor information
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user