fix issues in core
This commit is contained in:
@@ -7,6 +7,7 @@
|
|||||||
"sourceType": "module",
|
"sourceType": "module",
|
||||||
"ecmaVersion": 2022
|
"ecmaVersion": 2022
|
||||||
},
|
},
|
||||||
|
"ignorePatterns": ["**/dist/**", "**/*.d.ts"],
|
||||||
"settings": {
|
"settings": {
|
||||||
"import/resolver": {
|
"import/resolver": {
|
||||||
"typescript": {}
|
"typescript": {}
|
||||||
@@ -83,6 +84,54 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"files": ["core/**/application/dto/**/*.ts", "core/**/application/dtos/**/*.ts"],
|
||||||
|
"rules": {
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "Program",
|
||||||
|
"message": "core/*/application/dto is forbidden. Use application result models + output ports; DTOs belong in API/website layers."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["core/**/infrastructure/**/*.ts"],
|
||||||
|
"rules": {
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "Program",
|
||||||
|
"message": "core/*/infrastructure is forbidden. Implementations must live in adapters/ and be wired in apps/."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["core/**/domain/ports/**/*.ts"],
|
||||||
|
"rules": {
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "Program",
|
||||||
|
"message": "core/*/domain/ports is forbidden. Ports belong in application/ports (or shared application layer), not domain."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": ["core/**/shared/presentation/**/*.ts"],
|
||||||
|
"rules": {
|
||||||
|
"no-restricted-syntax": [
|
||||||
|
"error",
|
||||||
|
{
|
||||||
|
"selector": "Program",
|
||||||
|
"message": "core/shared/presentation is forbidden. Presentation belongs in API or website layers."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"files": ["apps/website/**/*.ts"],
|
"files": ["apps/website/**/*.ts"],
|
||||||
"rules": {
|
"rules": {
|
||||||
|
|||||||
@@ -4,13 +4,12 @@
|
|||||||
* Manages user session using cookies. This is a placeholder implementation.
|
* Manages user session using cookies. This is a placeholder implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { AuthenticatedUserDTO } from '@core/identity/application/dto/AuthenticatedUserDTO';
|
import type { AuthenticatedUser } from '@core/identity/application/ports/IdentityProviderPort';
|
||||||
import type { AuthSessionDTO } from '@core/identity/application/dto/AuthSessionDTO';
|
import type { AuthSession, IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
|
||||||
import type { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
|
import type { Logger } from '@core/shared/application';
|
||||||
import { Logger } from '@core/shared/application';
|
|
||||||
|
|
||||||
export class CookieIdentitySessionAdapter implements IdentitySessionPort {
|
export class CookieIdentitySessionAdapter implements IdentitySessionPort {
|
||||||
private currentSession: AuthSessionDTO | null = null;
|
private currentSession: AuthSession | null = null;
|
||||||
|
|
||||||
constructor(private readonly logger: Logger) {
|
constructor(private readonly logger: Logger) {
|
||||||
this.logger.info('CookieIdentitySessionAdapter initialized.');
|
this.logger.info('CookieIdentitySessionAdapter initialized.');
|
||||||
@@ -18,14 +17,14 @@ export class CookieIdentitySessionAdapter implements IdentitySessionPort {
|
|||||||
// For demo, we'll start with no session.
|
// For demo, we'll start with no session.
|
||||||
}
|
}
|
||||||
|
|
||||||
async getCurrentSession(): Promise<AuthSessionDTO | null> {
|
async getCurrentSession(): Promise<AuthSession | null> {
|
||||||
this.logger.debug('[CookieIdentitySessionAdapter] Getting current session.');
|
this.logger.debug('[CookieIdentitySessionAdapter] Getting current session.');
|
||||||
return Promise.resolve(this.currentSession);
|
return Promise.resolve(this.currentSession);
|
||||||
}
|
}
|
||||||
|
|
||||||
async createSession(user: AuthenticatedUserDTO): Promise<AuthSessionDTO> {
|
async createSession(user: AuthenticatedUser): Promise<AuthSession> {
|
||||||
this.logger.debug(`[CookieIdentitySessionAdapter] Creating session for user: ${user.id}`);
|
this.logger.debug(`[CookieIdentitySessionAdapter] Creating session for user: ${user.id}`);
|
||||||
const newSession: AuthSessionDTO = {
|
const newSession: AuthSession = {
|
||||||
user: user,
|
user: user,
|
||||||
issuedAt: Date.now(),
|
issuedAt: Date.now(),
|
||||||
expiresAt: Date.now() + 3600 * 1000, // 1 hour expiration
|
expiresAt: Date.now() + 3600 * 1000, // 1 hour expiration
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDri
|
|||||||
import { InMemoryDriverStatsService } from '@adapters/racing/services/InMemoryDriverStatsService';
|
import { InMemoryDriverStatsService } from '@adapters/racing/services/InMemoryDriverStatsService';
|
||||||
import { InMemoryRankingService } from '@adapters/racing/services/InMemoryRankingService';
|
import { InMemoryRankingService } from '@adapters/racing/services/InMemoryRankingService';
|
||||||
import { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
import { IImageServicePort } from '@core/racing/application/ports/IImageServicePort';
|
||||||
import { InMemorySocialGraphRepository } from '@core/social/infrastructure/inmemory/InMemorySocialAndFeed';
|
import { InMemorySocialGraphRepository } from '@adapters/social/persistence/inmemory/InMemorySocialAndFeed';
|
||||||
|
|
||||||
// Import presenters
|
// Import presenters
|
||||||
import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPresenter';
|
import { CompleteOnboardingPresenter } from './presenters/CompleteOnboardingPresenter';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import type { GetLeagueProtestsResult } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase';
|
import type { GetLeagueProtestsResult } from '@core/racing/application/use-cases/GetLeagueProtestsUseCase';
|
||||||
import { LeagueAdminProtestsDTO } from '../dtos/LeagueAdminProtestsDTO';
|
import { LeagueAdminProtestsDTO } from '../dtos/LeagueAdminProtestsDTO';
|
||||||
import { ProtestDTO } from '../dtos/ProtestDTO';
|
import { ProtestDTO } from '../dtos/ProtestDTO';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import type { GetLeagueSeasonsResult } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
|
import type { GetLeagueSeasonsResult } from '@core/racing/application/use-cases/GetLeagueSeasonsUseCase';
|
||||||
import { LeagueSeasonSummaryDTO } from '../dtos/LeagueSeasonSummaryDTO';
|
import { LeagueSeasonSummaryDTO } from '../dtos/LeagueSeasonSummaryDTO';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { GetLeagueStandingsResult } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
|
import type { GetLeagueStandingsResult } from '@core/racing/application/use-cases/GetLeagueStandingsUseCase';
|
||||||
import { LeagueStandingsDTO } from '../dtos/LeagueStandingsDTO';
|
import { LeagueStandingsDTO } from '../dtos/LeagueStandingsDTO';
|
||||||
import type { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
|
|
||||||
export class LeagueStandingsPresenter implements Presenter<GetLeagueStandingsResult, LeagueStandingsDTO> {
|
export class LeagueStandingsPresenter implements Presenter<GetLeagueStandingsResult, LeagueStandingsDTO> {
|
||||||
private result: LeagueStandingsDTO | null = null;
|
private result: LeagueStandingsDTO | null = null;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { GetLeagueStatsResult } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
|
import type { GetLeagueStatsResult } from '@core/racing/application/use-cases/GetLeagueStatsUseCase';
|
||||||
import { LeagueStatsDTO } from '../dtos/LeagueStatsDTO';
|
import { LeagueStatsDTO } from '../dtos/LeagueStatsDTO';
|
||||||
import type { Presenter } from '@core/shared/presentation';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
|
|
||||||
export class LeagueStatsPresenter implements Presenter<GetLeagueStatsResult, LeagueStatsDTO> {
|
export class LeagueStatsPresenter implements Presenter<GetLeagueStatsResult, LeagueStatsDTO> {
|
||||||
private result: LeagueStatsDTO | null = null;
|
private result: LeagueStatsDTO | null = null;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { AwardPrizeResultDTO } from '../dtos/AwardPrizeDTO';
|
import { AwardPrizeResultDTO } from '../dtos/AwardPrizeDTO';
|
||||||
|
|
||||||
export interface IAwardPrizePresenter extends Presenter<AwardPrizeResultDTO, AwardPrizeResultDTO> {}
|
export interface IAwardPrizePresenter extends Presenter<AwardPrizeResultDTO, AwardPrizeResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { CreatePrizeResultDTO } from '../dtos/CreatePrizeDTO';
|
import { CreatePrizeResultDTO } from '../dtos/CreatePrizeDTO';
|
||||||
|
|
||||||
export interface ICreatePrizePresenter extends Presenter<CreatePrizeResultDTO, CreatePrizeResultDTO> {}
|
export interface ICreatePrizePresenter extends Presenter<CreatePrizeResultDTO, CreatePrizeResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { DeletePrizeResultDTO } from '../dtos/DeletePrizeDTO';
|
import { DeletePrizeResultDTO } from '../dtos/DeletePrizeDTO';
|
||||||
|
|
||||||
export interface IDeletePrizePresenter extends Presenter<DeletePrizeResultDTO, DeletePrizeResultDTO> {}
|
export interface IDeletePrizePresenter extends Presenter<DeletePrizeResultDTO, DeletePrizeResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { GetMembershipFeesResultDTO } from '../dtos/GetMembershipFeesDTO';
|
import { GetMembershipFeesResultDTO } from '../dtos/GetMembershipFeesDTO';
|
||||||
|
|
||||||
export interface IGetMembershipFeesPresenter extends Presenter<GetMembershipFeesResultDTO, GetMembershipFeesResultDTO> {}
|
export interface IGetMembershipFeesPresenter extends Presenter<GetMembershipFeesResultDTO, GetMembershipFeesResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { GetPrizesResultDTO } from '../dtos/GetPrizesDTO';
|
import { GetPrizesResultDTO } from '../dtos/GetPrizesDTO';
|
||||||
|
|
||||||
export interface IGetPrizesPresenter extends Presenter<GetPrizesResultDTO, GetPrizesResultDTO> {}
|
export interface IGetPrizesPresenter extends Presenter<GetPrizesResultDTO, GetPrizesResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { GetWalletResultDTO } from '../dtos/GetWalletDTO';
|
import { GetWalletResultDTO } from '../dtos/GetWalletDTO';
|
||||||
|
|
||||||
export interface IGetWalletPresenter extends Presenter<GetWalletResultDTO, GetWalletResultDTO> {}
|
export interface IGetWalletPresenter extends Presenter<GetWalletResultDTO, GetWalletResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { ProcessWalletTransactionResultDTO } from '../dtos/ProcessWalletTransactionDTO';
|
import { ProcessWalletTransactionResultDTO } from '../dtos/ProcessWalletTransactionDTO';
|
||||||
|
|
||||||
export interface IProcessWalletTransactionPresenter extends Presenter<ProcessWalletTransactionResultDTO, ProcessWalletTransactionResultDTO> {}
|
export interface IProcessWalletTransactionPresenter extends Presenter<ProcessWalletTransactionResultDTO, ProcessWalletTransactionResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { UpdateMemberPaymentResultDTO } from '../dtos/UpdateMemberPaymentDTO';
|
import { UpdateMemberPaymentResultDTO } from '../dtos/UpdateMemberPaymentDTO';
|
||||||
|
|
||||||
export interface IUpdateMemberPaymentPresenter extends Presenter<UpdateMemberPaymentResultDTO, UpdateMemberPaymentResultDTO> {}
|
export interface IUpdateMemberPaymentPresenter extends Presenter<UpdateMemberPaymentResultDTO, UpdateMemberPaymentResultDTO> {}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Presenter } from '@core/shared/presentation/Presenter';
|
import type { Presenter } from '../../../shared/presentation/Presenter';
|
||||||
import { UpsertMembershipFeeResultDTO } from '../dtos/UpsertMembershipFeeDTO';
|
import { UpsertMembershipFeeResultDTO } from '../dtos/UpsertMembershipFeeDTO';
|
||||||
|
|
||||||
export interface IUpsertMembershipFeePresenter extends Presenter<UpsertMembershipFeeResultDTO, UpsertMembershipFeeResultDTO> {}
|
export interface IUpsertMembershipFeePresenter extends Presenter<UpsertMembershipFeeResultDTO, UpsertMembershipFeeResultDTO> {}
|
||||||
|
|||||||
5
apps/api/src/shared/presentation/Presenter.ts
Normal file
5
apps/api/src/shared/presentation/Presenter.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export interface Presenter<Input, ResponseModel> {
|
||||||
|
present(input: Input): void;
|
||||||
|
getResponseModel(): ResponseModel | null;
|
||||||
|
reset(): void;
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
export interface Logger {
|
|
||||||
debug(message: string, ...args: unknown[]): void;
|
|
||||||
info(message: string, ...args: unknown[]): void;
|
|
||||||
warn(message: string, ...args: unknown[]): void;
|
|
||||||
error(message: string, ...args: unknown[]): void;
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||||
import { GetCurrentUserSessionUseCase } from './GetCurrentUserSessionUseCase';
|
import { GetCurrentUserSessionUseCase } from './GetCurrentUserSessionUseCase';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
|
||||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||||
|
|
||||||
describe('GetCurrentUserSessionUseCase', () => {
|
describe('GetCurrentUserSessionUseCase', () => {
|
||||||
@@ -11,7 +10,7 @@ describe('GetCurrentUserSessionUseCase', () => {
|
|||||||
clearSession: Mock;
|
clearSession: Mock;
|
||||||
};
|
};
|
||||||
let logger: Logger;
|
let logger: Logger;
|
||||||
let output: UseCaseOutputPort<AuthSessionDTO | null> & { present: Mock };
|
let output: UseCaseOutputPort<AuthSession | null> & { present: Mock };
|
||||||
let useCase: GetCurrentUserSessionUseCase;
|
let useCase: GetCurrentUserSessionUseCase;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -40,7 +39,7 @@ describe('GetCurrentUserSessionUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('returns the current auth session when one exists', async () => {
|
it('returns the current auth session when one exists', async () => {
|
||||||
const session: AuthSessionDTO = {
|
const session: AuthSession = {
|
||||||
user: {
|
user: {
|
||||||
id: 'user-1',
|
id: 'user-1',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
|
|||||||
@@ -2,9 +2,8 @@ import { describe, it, expect, vi, type Mock } from 'vitest';
|
|||||||
import { HandleAuthCallbackUseCase } from './HandleAuthCallbackUseCase';
|
import { HandleAuthCallbackUseCase } from './HandleAuthCallbackUseCase';
|
||||||
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
|
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
import type { AuthCallbackCommandDTO } from '../dto/AuthCallbackCommandDTO';
|
import type { AuthCallbackCommand, AuthenticatedUser } from '../ports/IdentityProviderPort';
|
||||||
import type { AuthenticatedUserDTO } from '../dto/AuthenticatedUserDTO';
|
import type { AuthSession } from '../ports/IdentitySessionPort';
|
||||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
|
||||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||||
|
|
||||||
describe('HandleAuthCallbackUseCase', () => {
|
describe('HandleAuthCallbackUseCase', () => {
|
||||||
@@ -17,7 +16,7 @@ describe('HandleAuthCallbackUseCase', () => {
|
|||||||
clearSession: Mock;
|
clearSession: Mock;
|
||||||
};
|
};
|
||||||
let logger: Logger;
|
let logger: Logger;
|
||||||
let output: UseCaseOutputPort<AuthSessionDTO> & { present: Mock };
|
let output: UseCaseOutputPort<AuthSession> & { present: Mock };
|
||||||
let useCase: HandleAuthCallbackUseCase;
|
let useCase: HandleAuthCallbackUseCase;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -48,20 +47,20 @@ describe('HandleAuthCallbackUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('completes auth and creates a session', async () => {
|
it('completes auth and creates a session', async () => {
|
||||||
const command: AuthCallbackCommandDTO = {
|
const command: AuthCallbackCommand = {
|
||||||
provider: 'IRACING_DEMO',
|
provider: 'IRACING_DEMO',
|
||||||
code: 'auth-code',
|
code: 'auth-code',
|
||||||
state: 'state-123',
|
state: 'state-123',
|
||||||
returnTo: 'https://app/callback',
|
returnTo: 'https://app/callback',
|
||||||
};
|
};
|
||||||
|
|
||||||
const user: AuthenticatedUserDTO = {
|
const user: AuthenticatedUser = {
|
||||||
id: 'user-1',
|
id: 'user-1',
|
||||||
email: 'test@example.com',
|
email: 'test@example.com',
|
||||||
displayName: 'Test User',
|
displayName: 'Test User',
|
||||||
};
|
};
|
||||||
|
|
||||||
const session: AuthSessionDTO = {
|
const session: AuthSession = {
|
||||||
user,
|
user,
|
||||||
issuedAt: Date.now(),
|
issuedAt: Date.now(),
|
||||||
expiresAt: Date.now() + 1000,
|
expiresAt: Date.now() + 1000,
|
||||||
|
|||||||
@@ -2,8 +2,7 @@ import { describe, it, expect, vi, type Mock } from 'vitest';
|
|||||||
import { SignupWithEmailUseCase } from './SignupWithEmailUseCase';
|
import { SignupWithEmailUseCase } from './SignupWithEmailUseCase';
|
||||||
import type { SignupWithEmailInput } from './SignupWithEmailUseCase';
|
import type { SignupWithEmailInput } from './SignupWithEmailUseCase';
|
||||||
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
|
||||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||||
|
|
||||||
type SignupWithEmailOutput = unknown;
|
type SignupWithEmailOutput = unknown;
|
||||||
@@ -58,7 +57,7 @@ describe('SignupWithEmailUseCase', () => {
|
|||||||
|
|
||||||
userRepository.findByEmail.mockResolvedValue(null);
|
userRepository.findByEmail.mockResolvedValue(null);
|
||||||
|
|
||||||
const session: AuthSessionDTO = {
|
const session: AuthSession = {
|
||||||
user: {
|
user: {
|
||||||
id: 'user-1',
|
id: 'user-1',
|
||||||
email: command.email.toLowerCase(),
|
email: command.email.toLowerCase(),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||||
import type { AuthenticatedUserDTO } from '../dto/AuthenticatedUserDTO';
|
import type { AuthenticatedUser } from '../ports/IdentityProviderPort';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
@@ -103,7 +103,7 @@ export class SignupWithEmailUseCase {
|
|||||||
await this.userRepository.create(newUser);
|
await this.userRepository.create(newUser);
|
||||||
|
|
||||||
// Create session
|
// Create session
|
||||||
const authenticatedUser: AuthenticatedUserDTO = {
|
const authenticatedUser: AuthenticatedUser = {
|
||||||
id: newUser.id,
|
id: newUser.id,
|
||||||
displayName: newUser.displayName,
|
displayName: newUser.displayName,
|
||||||
email: newUser.email,
|
email: newUser.email,
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
/**
|
|
||||||
* Infrastructure layer exports for notifications package
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This infrastructure layer is empty as the actual implementations
|
|
||||||
// are in the adapters directory
|
|
||||||
@@ -2,5 +2,4 @@ export * from './application/Result';
|
|||||||
export * as application from './application';
|
export * as application from './application';
|
||||||
export * as domain from './domain';
|
export * as domain from './domain';
|
||||||
export * as errors from './errors';
|
export * as errors from './errors';
|
||||||
export * from './presentation';
|
|
||||||
export * from './application/AsyncUseCase';
|
export * from './application/AsyncUseCase';
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// This must not be used within core. It's in presentation layer, e.g. to be used in an API.
|
|
||||||
export interface Presenter<InputDTO, ResponseModel> {
|
|
||||||
present(input: InputDTO): void;
|
|
||||||
getResponseModel(): ResponseModel | null;
|
|
||||||
reset(): void;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export * from './Presenter';
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
export type FeedItemType =
|
|
||||||
| 'race_result'
|
|
||||||
| 'championship_standing'
|
|
||||||
| 'league_announcement'
|
|
||||||
| 'friend_joined_league'
|
|
||||||
| 'friend_won_race';
|
|
||||||
|
|
||||||
export interface FeedItemDTO {
|
|
||||||
id: string;
|
|
||||||
timestamp: string;
|
|
||||||
type: FeedItemType;
|
|
||||||
actorFriendId?: string;
|
|
||||||
actorDriverId?: string;
|
|
||||||
leagueId?: string;
|
|
||||||
raceId?: string;
|
|
||||||
teamId?: string;
|
|
||||||
position?: number;
|
|
||||||
headline: string;
|
|
||||||
body?: string;
|
|
||||||
ctaLabel?: string;
|
|
||||||
ctaHref?: string;
|
|
||||||
}
|
|
||||||
@@ -1,8 +1,13 @@
|
|||||||
export interface CurrentUserSocialDTO {
|
export type SocialUserSummary = {
|
||||||
driverId: string;
|
driverId: string;
|
||||||
displayName: string;
|
displayName: string;
|
||||||
avatarUrl: string;
|
avatarUrl: string;
|
||||||
countryCode: string;
|
countryCode: string;
|
||||||
primaryTeamId?: string;
|
primaryTeamId?: string;
|
||||||
primaryLeagueId?: string;
|
primaryLeagueId?: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export type SocialFriendSummary = SocialUserSummary & {
|
||||||
|
isOnline: boolean;
|
||||||
|
lastSeen: Date;
|
||||||
|
};
|
||||||
@@ -2,8 +2,7 @@ import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
|||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { ISocialGraphRepository } from '../../domain/repositories/ISocialGraphRepository';
|
import type { ISocialGraphRepository } from '../../domain/repositories/ISocialGraphRepository';
|
||||||
import type { CurrentUserSocialDTO } from '../dto/CurrentUserSocialDTO';
|
import type { SocialFriendSummary, SocialUserSummary } from '../types/SocialUser';
|
||||||
import type { FriendDTO } from '../dto/FriendDTO';
|
|
||||||
|
|
||||||
export interface GetCurrentUserSocialParams {
|
export interface GetCurrentUserSocialParams {
|
||||||
driverId: string;
|
driverId: string;
|
||||||
@@ -12,8 +11,8 @@ export interface GetCurrentUserSocialParams {
|
|||||||
export type GetCurrentUserSocialInput = GetCurrentUserSocialParams;
|
export type GetCurrentUserSocialInput = GetCurrentUserSocialParams;
|
||||||
|
|
||||||
export interface GetCurrentUserSocialResult {
|
export interface GetCurrentUserSocialResult {
|
||||||
currentUser: CurrentUserSocialDTO;
|
currentUser: SocialUserSummary;
|
||||||
friends: FriendDTO[];
|
friends: SocialFriendSummary[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type GetCurrentUserSocialErrorCode = 'REPOSITORY_ERROR';
|
export type GetCurrentUserSocialErrorCode = 'REPOSITORY_ERROR';
|
||||||
@@ -61,15 +60,16 @@ export class GetCurrentUserSocialUseCase {
|
|||||||
|
|
||||||
// The social graph context currently only knows about relationships.
|
// The social graph context currently only knows about relationships.
|
||||||
// Profile fields for the current user are expected to be enriched by identity/profile contexts.
|
// Profile fields for the current user are expected to be enriched by identity/profile contexts.
|
||||||
const friends: FriendDTO[] = friendsDomain.map((friend) => ({
|
const friends: SocialFriendSummary[] = friendsDomain.map((friend) => ({
|
||||||
driverId: friend.id,
|
driverId: friend.id,
|
||||||
displayName: friend.name.toString(),
|
displayName: friend.name.toString(),
|
||||||
avatarUrl: '',
|
avatarUrl: '',
|
||||||
|
countryCode: '',
|
||||||
isOnline: false,
|
isOnline: false,
|
||||||
lastSeen: new Date(),
|
lastSeen: new Date(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const currentUser: CurrentUserSocialDTO = {
|
const currentUser: SocialUserSummary = {
|
||||||
driverId,
|
driverId,
|
||||||
displayName: '',
|
displayName: '',
|
||||||
avatarUrl: '',
|
avatarUrl: '',
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { createStaticRacingSeed } from '@core/testing-support';
|
import type {
|
||||||
import type { IdentityProviderPort } from '../../application/ports/IdentityProviderPort';
|
AuthCallbackCommand,
|
||||||
import type { StartAuthCommandDTO } from '../../application/dto/StartAuthCommandDTO';
|
AuthenticatedUser,
|
||||||
import type { AuthCallbackCommandDTO } from '../../application/dto/AuthCallbackCommandDTO';
|
IdentityProviderPort,
|
||||||
import type { AuthenticatedUserDTO } from '../../application/dto/AuthenticatedUserDTO';
|
StartAuthCommand,
|
||||||
|
} from '@core/identity/application/ports/IdentityProviderPort';
|
||||||
|
|
||||||
export class IracingDemoIdentityProviderAdapter implements IdentityProviderPort {
|
export class IracingDemoIdentityProviderAdapter implements IdentityProviderPort {
|
||||||
private readonly seedDriverId: string;
|
private readonly seedDriverId: string;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const seed = createStaticRacingSeed(42);
|
this.seedDriverId = 'driver-1';
|
||||||
this.seedDriverId = seed.drivers[0]?.id ?? 'driver-1';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async startAuth(command: StartAuthCommandDTO): Promise<{ redirectUrl: string; state: string }> {
|
async startAuth(command: StartAuthCommand): Promise<{ redirectUrl: string; state: string }> {
|
||||||
const state = randomUUID();
|
const state = randomUUID();
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
@@ -29,7 +29,7 @@ export class IracingDemoIdentityProviderAdapter implements IdentityProviderPort
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async completeAuth(command: AuthCallbackCommandDTO): Promise<AuthenticatedUserDTO> {
|
async completeAuth(command: AuthCallbackCommand): Promise<AuthenticatedUser> {
|
||||||
if (!command.code) {
|
if (!command.code) {
|
||||||
throw new Error('Missing auth code');
|
throw new Error('Missing auth code');
|
||||||
}
|
}
|
||||||
@@ -37,7 +37,7 @@ export class IracingDemoIdentityProviderAdapter implements IdentityProviderPort
|
|||||||
throw new Error('Missing auth state');
|
throw new Error('Missing auth state');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user: AuthenticatedUserDTO = {
|
const user: AuthenticatedUser = {
|
||||||
id: 'demo-user',
|
id: 'demo-user',
|
||||||
displayName: 'GridPilot Demo Driver',
|
displayName: 'GridPilot Demo Driver',
|
||||||
iracingCustomerId: '000000',
|
iracingCustomerId: '000000',
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { League } from '@core/racing/domain/entities/League';
|
|||||||
import { Race } from '@core/racing/domain/entities/Race';
|
import { Race } from '@core/racing/domain/entities/Race';
|
||||||
import type { Result } from '@core/racing/domain/entities/Result';
|
import type { Result } from '@core/racing/domain/entities/Result';
|
||||||
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
||||||
import type { FriendDTO } from '@core/social/application/dto/FriendDTO';
|
import type { SocialFriendSummary } from '@core/social/application/types/SocialUser';
|
||||||
import { faker } from '../../helpers/faker/faker';
|
import { faker } from '../../helpers/faker/faker';
|
||||||
import { getLeagueBanner, getDriverAvatar } from '../../helpers/images/images';
|
import { getLeagueBanner, getDriverAvatar } from '../../helpers/images/images';
|
||||||
import type { Friendship, RacingMembership } from './RacingSeedCore';
|
import type { Friendship, RacingMembership } from './RacingSeedCore';
|
||||||
@@ -166,11 +166,11 @@ export function createFeedEvents(
|
|||||||
export function buildFriends(
|
export function buildFriends(
|
||||||
drivers: Driver[],
|
drivers: Driver[],
|
||||||
memberships: RacingMembership[],
|
memberships: RacingMembership[],
|
||||||
): FriendDTO[] {
|
): SocialFriendSummary[] {
|
||||||
return drivers.map((driver) => {
|
return drivers.map((driver) => {
|
||||||
const membership = memberships.find((m) => m.driverId === driver.id);
|
const membership = memberships.find((m) => m.driverId === driver.id);
|
||||||
|
|
||||||
const base: FriendDTO = {
|
const base: SocialFriendSummary = {
|
||||||
driverId: driver.id,
|
driverId: driver.id,
|
||||||
displayName: driver.name,
|
displayName: driver.name,
|
||||||
avatarUrl: getDriverAvatar(driver.id),
|
avatarUrl: getDriverAvatar(driver.id),
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Result } from '@core/racing/domain/entities/Result';
|
|||||||
import { Standing } from '@core/racing/domain/entities/Standing';
|
import { Standing } from '@core/racing/domain/entities/Standing';
|
||||||
|
|
||||||
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
||||||
import type { FriendDTO } from '@core/social/application/dto/FriendDTO';
|
import type { SocialFriendSummary } from '@core/social/application/types/SocialUser';
|
||||||
|
|
||||||
import { faker } from '../../helpers/faker/faker';
|
import { faker } from '../../helpers/faker/faker';
|
||||||
import { getTeamLogo } from '../../helpers/images/images';
|
import { getTeamLogo } from '../../helpers/images/images';
|
||||||
@@ -128,7 +128,7 @@ export const sponsorshipPricings = staticSeed.sponsorshipPricings;
|
|||||||
* Derived friend DTOs for UI consumption.
|
* Derived friend DTOs for UI consumption.
|
||||||
* This preserves the previous demo-data `friends` shape.
|
* This preserves the previous demo-data `friends` shape.
|
||||||
*/
|
*/
|
||||||
export const friends: FriendDTO[] = buildFriends(staticSeed.drivers, staticSeed.memberships);
|
export const friends: SocialFriendSummary[] = buildFriends(staticSeed.drivers, staticSeed.memberships);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Top leagues with banner URLs for UI.
|
* Top leagues with banner URLs for UI.
|
||||||
|
|||||||
Reference in New Issue
Block a user