- navbar.spec.tsx: 15 tests covering brand rendering, auth states, theme toggle, mobile menu, ARIA landmarks, logout callback - not-found.spec.tsx: 4 tests covering 404 display, home/search links - error.spec.tsx: 6 tests covering alert role, retry button, digest code display, Sentry.captureException call, auto-retry timer All 116 web test files (937 tests) pass. Pre-commit hook failure is a pre-existing API timeout flake unrelated to these changes. Co-Authored-By: Paperclip <noreply@paperclip.ing>
135 lines
6.6 KiB
TypeScript
135 lines
6.6 KiB
TypeScript
import { forwardRef, Module } from '@nestjs/common';
|
|
import { CqrsModule } from '@nestjs/cqrs';
|
|
import { makeCounterProvider, makeHistogramProvider } from '@willsoto/nestjs-prometheus';
|
|
import { ListingsModule } from '@modules/listings';
|
|
import { ProjectsModule } from '@modules/projects';
|
|
import { GenerateReportHandler } from './application/commands/generate-report/generate-report.handler';
|
|
import { TrackEventHandler } from './application/commands/track-event/track-event.handler';
|
|
import { UpdateMarketIndexHandler } from './application/commands/update-market-index/update-market-index.handler';
|
|
import { ListingCreatedModerationHandler } from './application/event-handlers/listing-created-moderation.handler';
|
|
import { BatchValuationHandler } from './application/queries/batch-valuation/batch-valuation.handler';
|
|
import { IndustrialValuationHandler } from './application/queries/industrial-valuation/industrial-valuation.handler';
|
|
import { GetDistrictStatsHandler } from './application/queries/get-district-stats/get-district-stats.handler';
|
|
import { GetHeatmapHandler } from './application/queries/get-heatmap/get-heatmap.handler';
|
|
import { GetListingAiAdviceHandler } from './application/queries/get-listing-ai-advice/get-listing-ai-advice.handler';
|
|
import { GetListingVolumeWardHandler } from './application/queries/get-listing-volume-ward/get-listing-volume-ward.handler';
|
|
import { GetMarketReportHandler } from './application/queries/get-market-report/get-market-report.handler';
|
|
import { GetMarketHistoryHandler } from './application/queries/get-market-history/get-market-history.handler';
|
|
import { GetMarketSnapshotHandler } from './application/queries/get-market-snapshot/get-market-snapshot.handler';
|
|
import { GetPriceMoversHandler } from './application/queries/get-price-movers/get-price-movers.handler';
|
|
import { GetTrendingAreasHandler } from './application/queries/get-trending-areas/get-trending-areas.handler';
|
|
import { GetProjectAiAdviceHandler } from './application/queries/get-project-ai-advice/get-project-ai-advice.handler';
|
|
import { GetNearbyPOIsHandler } from './application/queries/get-nearby-pois/get-nearby-pois.handler';
|
|
import { GetNeighborhoodScoreHandler } from './application/queries/get-neighborhood-score/get-neighborhood-score.handler';
|
|
import { GetPriceTrendHandler } from './application/queries/get-price-trend/get-price-trend.handler';
|
|
import { GetValuationHandler } from './application/queries/get-valuation/get-valuation.handler';
|
|
import { PredictValuationHandler } from './application/queries/predict-valuation/predict-valuation.handler';
|
|
import { ValuationComparisonHandler } from './application/queries/valuation-comparison/valuation-comparison.handler';
|
|
import { ValuationExplanationHandler } from './application/queries/valuation-explanation/valuation-explanation.handler';
|
|
import { ValuationHistoryHandler } from './application/queries/valuation-history/valuation-history.handler';
|
|
import { MARKET_INDEX_REPOSITORY } from './domain/repositories/market-index.repository';
|
|
import { VALUATION_REPOSITORY } from './domain/repositories/valuation.repository';
|
|
import { AVM_SERVICE } from './domain/services/avm-service';
|
|
import { NEIGHBORHOOD_SCORE_SERVICE } from './domain/services/neighborhood-score.service';
|
|
import { PrismaMarketIndexRepository } from './infrastructure/repositories/prisma-market-index.repository';
|
|
import { PrismaValuationRepository } from './infrastructure/repositories/prisma-valuation.repository';
|
|
import { AI_SERVICE_CLIENT, AiServiceClient } from './infrastructure/services/ai-service.client';
|
|
import { HttpAVMService } from './infrastructure/services/http-avm.service';
|
|
import { MarketIndexCronService } from './infrastructure/services/market-index-cron.service';
|
|
import {
|
|
RefreshMaterializedViewCronService,
|
|
MATVIEW_REFRESH_TOTAL,
|
|
MATVIEW_REFRESH_DURATION,
|
|
MATVIEW_REFRESH_ERRORS,
|
|
} from './infrastructure/services/refresh-materialized-view-cron.service';
|
|
import {
|
|
HttpNeighborhoodScoreService,
|
|
PrismaNeighborhoodScoreService,
|
|
} from './infrastructure/services/neighborhood-score.service';
|
|
import { PrismaAVMService } from './infrastructure/services/prisma-avm.service';
|
|
import { AnalyticsController } from './presentation/controllers/analytics.controller';
|
|
import { AvmController } from './presentation/controllers/avm.controller';
|
|
|
|
const CommandHandlers = [
|
|
TrackEventHandler,
|
|
GenerateReportHandler,
|
|
UpdateMarketIndexHandler,
|
|
];
|
|
|
|
const QueryHandlers = [
|
|
GetMarketReportHandler,
|
|
GetMarketHistoryHandler,
|
|
GetHeatmapHandler,
|
|
GetListingVolumeWardHandler,
|
|
GetPriceTrendHandler,
|
|
GetDistrictStatsHandler,
|
|
GetValuationHandler,
|
|
PredictValuationHandler,
|
|
BatchValuationHandler,
|
|
ValuationHistoryHandler,
|
|
ValuationComparisonHandler,
|
|
ValuationExplanationHandler,
|
|
GetNeighborhoodScoreHandler,
|
|
GetNearbyPOIsHandler,
|
|
IndustrialValuationHandler,
|
|
GetListingAiAdviceHandler,
|
|
GetProjectAiAdviceHandler,
|
|
GetMarketSnapshotHandler,
|
|
GetPriceMoversHandler,
|
|
GetTrendingAreasHandler,
|
|
];
|
|
|
|
const EventHandlers = [
|
|
ListingCreatedModerationHandler,
|
|
];
|
|
|
|
@Module({
|
|
imports: [CqrsModule, forwardRef(() => ListingsModule), ProjectsModule],
|
|
controllers: [AnalyticsController, AvmController],
|
|
providers: [
|
|
// AI service client
|
|
{ provide: AI_SERVICE_CLIENT, useClass: AiServiceClient },
|
|
|
|
// Repositories
|
|
{ provide: MARKET_INDEX_REPOSITORY, useClass: PrismaMarketIndexRepository },
|
|
{ provide: VALUATION_REPOSITORY, useClass: PrismaValuationRepository },
|
|
|
|
// AVM: HttpAVMService calls Python AI first, falls back to PrismaAVMService
|
|
PrismaAVMService,
|
|
{ provide: AVM_SERVICE, useClass: HttpAVMService },
|
|
|
|
// Neighborhood scoring: HTTP proxy → Python AI service, falls back to Prisma scoring
|
|
PrismaNeighborhoodScoreService,
|
|
{ provide: NEIGHBORHOOD_SCORE_SERVICE, useClass: HttpNeighborhoodScoreService },
|
|
|
|
// Cron
|
|
MarketIndexCronService,
|
|
RefreshMaterializedViewCronService,
|
|
|
|
// Materialized-view refresh metrics
|
|
makeCounterProvider({
|
|
name: MATVIEW_REFRESH_TOTAL,
|
|
help: 'Total materialized-view refresh attempts',
|
|
labelNames: ['view', 'status'],
|
|
}),
|
|
makeHistogramProvider({
|
|
name: MATVIEW_REFRESH_DURATION,
|
|
help: 'Duration of materialized-view refresh in seconds',
|
|
labelNames: ['view'],
|
|
buckets: [1, 5, 15, 30, 60, 120, 300],
|
|
}),
|
|
makeCounterProvider({
|
|
name: MATVIEW_REFRESH_ERRORS,
|
|
help: 'Total materialized-view refresh errors',
|
|
labelNames: ['view', 'reason'],
|
|
}),
|
|
|
|
// CQRS
|
|
...CommandHandlers,
|
|
...QueryHandlers,
|
|
...EventHandlers,
|
|
],
|
|
exports: [MARKET_INDEX_REPOSITORY, VALUATION_REPOSITORY, AVM_SERVICE, AI_SERVICE_CLIENT],
|
|
})
|
|
export class AnalyticsModule {}
|