inmemory to postgres

This commit is contained in:
2025-12-29 18:34:12 +01:00
parent 9e17d0752a
commit f5639a367f
176 changed files with 10175 additions and 468 deletions

View File

@@ -2,7 +2,6 @@ import { Test, TestingModule } from '@nestjs/testing';
import { AnalyticsModule } from './AnalyticsModule';
import { AnalyticsController } from './AnalyticsController';
import { AnalyticsService } from './AnalyticsService';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
describe('AnalyticsModule', () => {
let module: TestingModule;
@@ -10,10 +9,7 @@ describe('AnalyticsModule', () => {
beforeEach(async () => {
module = await Test.createTestingModule({
imports: [AnalyticsModule],
})
.overrideProvider('Logger_TOKEN')
.useClass(ConsoleLogger)
.compile();
}).compile();
});
it('should compile the module', () => {

View File

@@ -1,10 +1,12 @@
import { Module } from '@nestjs/common';
import { AnalyticsPersistenceModule } from '../../persistence/analytics/AnalyticsPersistenceModule';
import { AnalyticsController } from './AnalyticsController';
import { AnalyticsService } from './AnalyticsService';
import { AnalyticsProviders } from './AnalyticsProviders';
@Module({
imports: [],
imports: [AnalyticsPersistenceModule],
controllers: [AnalyticsController],
providers: AnalyticsProviders,
exports: [AnalyticsService],

View File

@@ -6,18 +6,18 @@ import type { IPageViewRepository } from '@core/analytics/application/repositori
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import { Provider } from '@nestjs/common';
const Logger_TOKEN = 'Logger_TOKEN';
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
const IENGAGEMENT_REPO_TOKEN = 'IEngagementRepository_TOKEN';
import {
ANALYTICS_ENGAGEMENT_REPOSITORY_TOKEN,
ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN,
} from '../../persistence/analytics/AnalyticsPersistenceTokens';
const LOGGER_TOKEN = 'Logger';
const RECORD_PAGE_VIEW_OUTPUT_PORT_TOKEN = 'RecordPageViewOutputPort_TOKEN';
const RECORD_ENGAGEMENT_OUTPUT_PORT_TOKEN = 'RecordEngagementOutputPort_TOKEN';
const GET_DASHBOARD_DATA_OUTPUT_PORT_TOKEN = 'GetDashboardDataOutputPort_TOKEN';
const GET_ANALYTICS_METRICS_OUTPUT_PORT_TOKEN = 'GetAnalyticsMetricsOutputPort_TOKEN';
import { InMemoryEngagementRepository } from '@adapters/analytics/persistence/inmemory/InMemoryEngagementRepository';
import { InMemoryPageViewRepository } from '@adapters/analytics/persistence/inmemory/InMemoryPageViewRepository';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
import { GetDashboardDataOutput, GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
@@ -34,20 +34,6 @@ export const AnalyticsProviders: Provider[] = [
RecordEngagementPresenter,
GetDashboardDataPresenter,
GetAnalyticsMetricsPresenter,
{
provide: Logger_TOKEN,
useClass: ConsoleLogger,
},
{
provide: IPAGE_VIEW_REPO_TOKEN,
useFactory: (logger: Logger) => new InMemoryPageViewRepository(logger),
inject: [Logger_TOKEN],
},
{
provide: IENGAGEMENT_REPO_TOKEN,
useFactory: (logger: Logger) => new InMemoryEngagementRepository(logger),
inject: [Logger_TOKEN],
},
{
provide: RECORD_PAGE_VIEW_OUTPUT_PORT_TOKEN,
useExisting: RecordPageViewPresenter,
@@ -68,24 +54,24 @@ export const AnalyticsProviders: Provider[] = [
provide: RecordPageViewUseCase,
useFactory: (repo: IPageViewRepository, logger: Logger, output: UseCaseOutputPort<RecordPageViewOutput>) =>
new RecordPageViewUseCase(repo, logger, output),
inject: [IPAGE_VIEW_REPO_TOKEN, Logger_TOKEN, RECORD_PAGE_VIEW_OUTPUT_PORT_TOKEN],
inject: [ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN, LOGGER_TOKEN, RECORD_PAGE_VIEW_OUTPUT_PORT_TOKEN],
},
{
provide: RecordEngagementUseCase,
useFactory: (repo: IEngagementRepository, logger: Logger, output: UseCaseOutputPort<RecordEngagementOutput>) =>
new RecordEngagementUseCase(repo, logger, output),
inject: [IENGAGEMENT_REPO_TOKEN, Logger_TOKEN, RECORD_ENGAGEMENT_OUTPUT_PORT_TOKEN],
inject: [ANALYTICS_ENGAGEMENT_REPOSITORY_TOKEN, LOGGER_TOKEN, RECORD_ENGAGEMENT_OUTPUT_PORT_TOKEN],
},
{
provide: GetDashboardDataUseCase,
useFactory: (logger: Logger, output: UseCaseOutputPort<GetDashboardDataOutput>) =>
new GetDashboardDataUseCase(logger, output),
inject: [Logger_TOKEN, GET_DASHBOARD_DATA_OUTPUT_PORT_TOKEN],
inject: [LOGGER_TOKEN, GET_DASHBOARD_DATA_OUTPUT_PORT_TOKEN],
},
{
provide: GetAnalyticsMetricsUseCase,
useFactory: (logger: Logger, output: UseCaseOutputPort<GetAnalyticsMetricsOutput>, repo: IPageViewRepository) =>
new GetAnalyticsMetricsUseCase(logger, output, repo),
inject: [Logger_TOKEN, GET_ANALYTICS_METRICS_OUTPUT_PORT_TOKEN, IPAGE_VIEW_REPO_TOKEN],
inject: [LOGGER_TOKEN, GET_ANALYTICS_METRICS_OUTPUT_PORT_TOKEN, ANALYTICS_PAGE_VIEW_REPOSITORY_TOKEN],
},
];

View File

@@ -1,4 +1,5 @@
import { Module } from '@nestjs/common';
import { IdentityPersistenceModule } from '../../persistence/identity/IdentityPersistenceModule';
import { AuthService } from './AuthService';
import { AuthController } from './AuthController';
import { AuthProviders } from './AuthProviders';
@@ -7,14 +8,9 @@ import { AuthorizationGuard } from './AuthorizationGuard';
import { AuthorizationService } from './AuthorizationService';
@Module({
imports: [IdentityPersistenceModule],
controllers: [AuthController],
providers: [
AuthService,
...AuthProviders,
AuthenticationGuard,
AuthorizationService,
AuthorizationGuard,
],
providers: [AuthService, ...AuthProviders, AuthenticationGuard, AuthorizationService, AuthorizationGuard],
exports: [AuthService, AuthenticationGuard, AuthorizationService, AuthorizationGuard],
})
export class AuthModule {}

View File

@@ -1,30 +1,28 @@
import { Provider } from '@nestjs/common';
// Import interfaces and concrete implementations
import { StoredUser } from '@core/identity/domain/repositories/IUserRepository';
import type { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import { InMemoryAuthRepository } from '@adapters/identity/persistence/inmemory/InMemoryAuthRepository';
import { InMemoryUserRepository } from '@adapters/identity/persistence/inmemory/InMemoryUserRepository';
import { InMemoryPasswordHashingService } from '@adapters/identity/services/InMemoryPasswordHashingService';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
import { CookieIdentitySessionAdapter } from '@adapters/identity/session/CookieIdentitySessionAdapter';
import { LoginUseCase } from '@core/identity/application/use-cases/LoginUseCase';
import { LogoutUseCase } from '@core/identity/application/use-cases/LogoutUseCase';
import { SignupUseCase } from '@core/identity/application/use-cases/SignupUseCase';
import type { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import type { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import type { LoginResult } from '@core/identity/application/use-cases/LoginUseCase';
import type { LogoutResult } from '@core/identity/application/use-cases/LogoutUseCase';
import type { SignupResult } from '@core/identity/application/use-cases/SignupUseCase';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import {
AUTH_REPOSITORY_TOKEN,
PASSWORD_HASHING_SERVICE_TOKEN,
USER_REPOSITORY_TOKEN,
} from '../../persistence/identity/IdentityPersistenceTokens';
import { AuthSessionPresenter } from './presenters/AuthSessionPresenter';
import { CommandResultPresenter } from './presenters/CommandResultPresenter';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import type { LoginResult } from '@core/identity/application/use-cases/LoginUseCase';
import type { SignupResult } from '@core/identity/application/use-cases/SignupUseCase';
import type { LogoutResult } from '@core/identity/application/use-cases/LogoutUseCase';
import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import type { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
// Define the tokens for dependency injection
export const AUTH_REPOSITORY_TOKEN = 'IAuthRepository';
export const USER_REPOSITORY_TOKEN = 'IUserRepository';
export const PASSWORD_HASHING_SERVICE_TOKEN = 'IPasswordHashingService';
export { AUTH_REPOSITORY_TOKEN, USER_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN };
export const LOGGER_TOKEN = 'Logger';
export const IDENTITY_SESSION_PORT_TOKEN = 'IdentitySessionPort';
export const LOGIN_USE_CASE_TOKEN = 'LoginUseCase';
@@ -35,38 +33,6 @@ export const AUTH_SESSION_OUTPUT_PORT_TOKEN = 'AuthSessionOutputPort';
export const COMMAND_RESULT_OUTPUT_PORT_TOKEN = 'CommandResultOutputPort';
export const AuthProviders: Provider[] = [
{
provide: AUTH_REPOSITORY_TOKEN,
useFactory: (userRepository: InMemoryUserRepository, passwordHashingService: IPasswordHashingService, logger: Logger) =>
new InMemoryAuthRepository(userRepository, passwordHashingService, logger),
inject: [USER_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN],
},
{
provide: USER_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => {
const initialUsers: StoredUser[] = [
{
// Match seeded racing driver id so dashboard works in inmemory mode.
id: 'driver-1',
email: 'admin@gridpilot.local',
passwordHash: 'demo_salt_321nimda', // InMemoryPasswordHashingService: "admin123" reversed.
displayName: 'Admin',
salt: '',
createdAt: new Date(),
},
];
return new InMemoryUserRepository(logger, initialUsers);
},
inject: [LOGGER_TOKEN],
},
{
provide: PASSWORD_HASHING_SERVICE_TOKEN,
useClass: InMemoryPasswordHashingService,
},
{
provide: LOGGER_TOKEN,
useClass: ConsoleLogger,
},
{
provide: IDENTITY_SESSION_PORT_TOKEN,
useFactory: (logger: Logger) => new CookieIdentitySessionAdapter(logger),

View File

@@ -4,11 +4,11 @@ import { SeedRacingData, type RacingSeedDependencies } from '../../../../../adap
import { Inject, Module, OnModuleInit } from '@nestjs/common';
import { getApiPersistence, getEnableBootstrap } from '../../env';
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
import { SocialPersistenceModule } from '../../persistence/social/SocialPersistenceModule';
import { BootstrapProviders, ENSURE_INITIAL_DATA_TOKEN } from './BootstrapProviders';
@Module({
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
imports: [RacingPersistenceModule, SocialPersistenceModule],
providers: BootstrapProviders,
})
export class BootstrapModule implements OnModuleInit {

View File

@@ -1,4 +1,5 @@
import { Provider } from '@nestjs/common';
import { SOCIAL_FEED_REPOSITORY_TOKEN, SOCIAL_GRAPH_REPOSITORY_TOKEN } from '../../persistence/social/SocialPersistenceTokens';
import { EnsureInitialData } from '../../../../../adapters/bootstrap/EnsureInitialData';
import type { RacingSeedDependencies } from '../../../../../adapters/bootstrap/SeedRacingData';
import { SignupWithEmailUseCase, type SignupWithEmailResult } from '@core/identity/application/use-cases/SignupWithEmailUseCase';
@@ -105,8 +106,8 @@ export const BootstrapProviders: Provider[] = [
'ITeamRepository',
'ITeamMembershipRepository',
'ISponsorRepository',
'IFeedRepository',
'ISocialGraphRepository',
SOCIAL_FEED_REPOSITORY_TOKEN,
SOCIAL_GRAPH_REPOSITORY_TOKEN,
],
},
{

View File

@@ -1,12 +1,12 @@
import { Module } from '@nestjs/common';
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
import { SocialPersistenceModule } from '../../persistence/social/SocialPersistenceModule';
import { DashboardService } from './DashboardService';
import { DashboardController } from './DashboardController';
import { DashboardProviders } from './DashboardProviders';
@Module({
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
imports: [RacingPersistenceModule, SocialPersistenceModule],
controllers: [DashboardController],
providers: [DashboardService, ...DashboardProviders],
exports: [DashboardService],

View File

@@ -11,6 +11,8 @@ import { ILeagueMembershipRepository } from '@core/racing/domain/repositories/IL
import { IRaceRegistrationRepository } from '@core/racing/domain/repositories/IRaceRegistrationRepository';
import { IFeedRepository } from '@core/social/domain/repositories/IFeedRepository';
import { ISocialGraphRepository } from '@core/social/domain/repositories/ISocialGraphRepository';
import { SOCIAL_FEED_REPOSITORY_TOKEN, SOCIAL_GRAPH_REPOSITORY_TOKEN } from '../../persistence/social/SocialPersistenceTokens';
import { ImageServicePort } from '@core/media/application/ports/ImageServicePort';
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
@@ -28,8 +30,6 @@ export const LEAGUE_REPOSITORY_TOKEN = 'ILeagueRepository';
export const STANDING_REPOSITORY_TOKEN = 'IStandingRepository';
export const LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN = 'ILeagueMembershipRepository';
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
export const FEED_REPOSITORY_TOKEN = 'IFeedRepository';
export const SOCIAL_GRAPH_REPOSITORY_TOKEN = 'ISocialGraphRepository';
export const IMAGE_SERVICE_TOKEN = 'IImageServicePort';
export const DASHBOARD_OVERVIEW_USE_CASE_TOKEN = 'DashboardOverviewUseCase';
export const DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN = 'DashboardOverviewOutputPort';
@@ -86,7 +86,7 @@ export const DashboardProviders: Provider[] = [
STANDING_REPOSITORY_TOKEN,
LEAGUE_MEMBERSHIP_REPOSITORY_TOKEN,
RACE_REGISTRATION_REPOSITORY_TOKEN,
FEED_REPOSITORY_TOKEN,
SOCIAL_FEED_REPOSITORY_TOKEN,
SOCIAL_GRAPH_REPOSITORY_TOKEN,
IMAGE_SERVICE_TOKEN,
DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN,

View File

@@ -1,12 +1,12 @@
import { Module } from '@nestjs/common';
import { RacingPersistenceModule } from '../../persistence/racing/RacingPersistenceModule';
import { InMemorySocialPersistenceModule } from '../../persistence/inmemory/InMemorySocialPersistenceModule';
import { SocialPersistenceModule } from '../../persistence/social/SocialPersistenceModule';
import { DriverService } from './DriverService';
import { DriverController } from './DriverController';
import { DriverProviders } from './DriverProviders';
@Module({
imports: [RacingPersistenceModule, InMemorySocialPersistenceModule],
imports: [RacingPersistenceModule, SocialPersistenceModule],
controllers: [DriverController],
providers: [DriverService, ...DriverProviders],
exports: [DriverService],

View File

@@ -7,8 +7,10 @@ export const IMAGE_SERVICE_PORT_TOKEN = 'IImageServicePort';
export const RACE_REGISTRATION_REPOSITORY_TOKEN = 'IRaceRegistrationRepository';
export const NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN = 'INotificationPreferenceRepository';
export const TEAM_REPOSITORY_TOKEN = 'ITeamRepository';
import { SOCIAL_GRAPH_REPOSITORY_TOKEN } from '../../persistence/social/SocialPersistenceTokens';
export const TEAM_MEMBERSHIP_REPOSITORY_TOKEN = 'ITeamMembershipRepository';
export const SOCIAL_GRAPH_REPOSITORY_TOKEN = 'ISocialGraphRepository';
export { SOCIAL_GRAPH_REPOSITORY_TOKEN };
export const LOGGER_TOKEN = 'Logger';
export const GET_DRIVERS_LEADERBOARD_USE_CASE_TOKEN = 'GetDriversLeaderboardUseCase';

View File

@@ -1,9 +1,13 @@
import { Module } from '@nestjs/common';
import { PaymentsPersistenceModule } from '../../persistence/payments/PaymentsPersistenceModule';
import { PaymentsService } from './PaymentsService';
import { PaymentsController } from './PaymentsController';
import { PaymentsProviders } from './PaymentsProviders';
@Module({
imports: [PaymentsPersistenceModule],
controllers: [PaymentsController],
providers: [PaymentsService, ...PaymentsProviders],
exports: [PaymentsService],

View File

@@ -5,7 +5,7 @@ import type { IPaymentRepository } from '@core/payments/domain/repositories/IPay
import type { IMembershipFeeRepository, IMemberPaymentRepository } from '@core/payments/domain/repositories/IMembershipFeeRepository';
import type { IPrizeRepository } from '@core/payments/domain/repositories/IPrizeRepository';
import type { IWalletRepository, ITransactionRepository } from '@core/payments/domain/repositories/IWalletRepository';
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
import type { UseCaseOutputPort } from '@core/shared/application';
// Import use cases
import { GetPaymentsUseCase } from '@core/payments/application/use-cases/GetPaymentsUseCase';
@@ -22,10 +22,6 @@ import { GetWalletUseCase } from '@core/payments/application/use-cases/GetWallet
import { ProcessWalletTransactionUseCase } from '@core/payments/application/use-cases/ProcessWalletTransactionUseCase';
// Import concrete in-memory implementations
import { InMemoryPaymentRepository } from '@adapters/payments/persistence/inmemory/InMemoryPaymentRepository';
import { InMemoryMembershipFeeRepository, InMemoryMemberPaymentRepository } from '@adapters/payments/persistence/inmemory/InMemoryMembershipFeeRepository';
import { InMemoryPrizeRepository } from '@adapters/payments/persistence/inmemory/InMemoryPrizeRepository';
import { InMemoryWalletRepository, InMemoryTransactionRepository } from '@adapters/payments/persistence/inmemory/InMemoryWalletRepository';
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
// Presenters
@@ -150,37 +146,7 @@ export const PaymentsProviders: Provider[] = [
useClass: ConsoleLogger,
},
// Repositories (repositories are injected into use cases, NOT into services)
{
provide: PAYMENT_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryPaymentRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: MEMBERSHIP_FEE_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryMembershipFeeRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: MEMBER_PAYMENT_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryMemberPaymentRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: PRIZE_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryPrizeRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: WALLET_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryWalletRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: TRANSACTION_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryTransactionRepository(logger),
inject: [LOGGER_TOKEN],
},
// Repositories are provided by PaymentsPersistenceModule behind tokens.
// Use cases (use cases receive repositories, services receive use cases)
{

View File

@@ -1,9 +1,19 @@
export const PAYMENT_REPOSITORY_TOKEN = 'IPaymentRepository';
export const MEMBERSHIP_FEE_REPOSITORY_TOKEN = 'IMembershipFeeRepository';
export const MEMBER_PAYMENT_REPOSITORY_TOKEN = 'IMemberPaymentRepository';
export const PRIZE_REPOSITORY_TOKEN = 'IPrizeRepository';
export const WALLET_REPOSITORY_TOKEN = 'IWalletRepository';
export const TRANSACTION_REPOSITORY_TOKEN = 'ITransactionRepository';
import {
PAYMENTS_MEMBER_PAYMENT_REPOSITORY_TOKEN,
PAYMENTS_MEMBERSHIP_FEE_REPOSITORY_TOKEN,
PAYMENTS_PAYMENT_REPOSITORY_TOKEN,
PAYMENTS_PRIZE_REPOSITORY_TOKEN,
PAYMENTS_TRANSACTION_REPOSITORY_TOKEN,
PAYMENTS_WALLET_REPOSITORY_TOKEN,
} from '../../persistence/payments/PaymentsPersistenceTokens';
export const PAYMENT_REPOSITORY_TOKEN = PAYMENTS_PAYMENT_REPOSITORY_TOKEN;
export const MEMBERSHIP_FEE_REPOSITORY_TOKEN = PAYMENTS_MEMBERSHIP_FEE_REPOSITORY_TOKEN;
export const MEMBER_PAYMENT_REPOSITORY_TOKEN = PAYMENTS_MEMBER_PAYMENT_REPOSITORY_TOKEN;
export const PRIZE_REPOSITORY_TOKEN = PAYMENTS_PRIZE_REPOSITORY_TOKEN;
export const WALLET_REPOSITORY_TOKEN = PAYMENTS_WALLET_REPOSITORY_TOKEN;
export const TRANSACTION_REPOSITORY_TOKEN = PAYMENTS_TRANSACTION_REPOSITORY_TOKEN;
export const LOGGER_TOKEN = 'Logger';
export const GET_PAYMENTS_USE_CASE_TOKEN = 'GetPaymentsUseCase';