refactor analytics module
This commit is contained in:
@@ -1,12 +1,10 @@
|
|||||||
import { Provider } from '@nestjs/common';
|
|
||||||
import { AnalyticsService } from './AnalyticsService';
|
|
||||||
import type { IPageViewRepository } from '@core/analytics/domain/repositories/IPageViewRepository';
|
|
||||||
import type { IEngagementRepository } from '@core/analytics/domain/repositories/IEngagementRepository';
|
|
||||||
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
|
||||||
import type { RecordPageViewOutput } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
|
||||||
import type { RecordEngagementOutput } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
|
||||||
import type { GetDashboardDataOutput } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
|
||||||
import type { GetAnalyticsMetricsOutput } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
import type { GetAnalyticsMetricsOutput } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
||||||
|
import type { RecordEngagementOutput } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
||||||
|
import type { RecordPageViewOutput } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
||||||
|
import type { IEngagementRepository } from '@core/analytics/domain/repositories/IEngagementRepository';
|
||||||
|
import type { IPageViewRepository } from '@core/analytics/domain/repositories/IPageViewRepository';
|
||||||
|
import type { Logger, UseCaseOutputPort } from '@core/shared/application';
|
||||||
|
import { Provider } from '@nestjs/common';
|
||||||
|
|
||||||
const Logger_TOKEN = 'Logger_TOKEN';
|
const Logger_TOKEN = 'Logger_TOKEN';
|
||||||
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
const IPAGE_VIEW_REPO_TOKEN = 'IPageViewRepository_TOKEN';
|
||||||
@@ -20,21 +18,16 @@ const GET_ANALYTICS_METRICS_OUTPUT_PORT_TOKEN = 'GetAnalyticsMetricsOutputPort_T
|
|||||||
import { InMemoryEngagementRepository } from '@adapters/analytics/persistence/inmemory/InMemoryEngagementRepository';
|
import { InMemoryEngagementRepository } from '@adapters/analytics/persistence/inmemory/InMemoryEngagementRepository';
|
||||||
import { InMemoryPageViewRepository } from '@adapters/analytics/persistence/inmemory/InMemoryPageViewRepository';
|
import { InMemoryPageViewRepository } from '@adapters/analytics/persistence/inmemory/InMemoryPageViewRepository';
|
||||||
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
import { ConsoleLogger } from '@adapters/logging/ConsoleLogger';
|
||||||
import { RecordPageViewUseCase } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
|
||||||
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
|
||||||
import { GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
|
||||||
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
||||||
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';
|
import { GetDashboardDataOutput, GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
||||||
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
|
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
||||||
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
|
import { RecordPageViewUseCase } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
||||||
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
|
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
|
||||||
|
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
|
||||||
|
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
|
||||||
|
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';
|
||||||
|
|
||||||
export const AnalyticsProviders: Provider[] = [
|
export const AnalyticsProviders: Provider[] = [
|
||||||
AnalyticsService,
|
|
||||||
RecordPageViewPresenter,
|
|
||||||
RecordEngagementPresenter,
|
|
||||||
GetDashboardDataPresenter,
|
|
||||||
GetAnalyticsMetricsPresenter,
|
|
||||||
{
|
{
|
||||||
provide: Logger_TOKEN,
|
provide: Logger_TOKEN,
|
||||||
useClass: ConsoleLogger,
|
useClass: ConsoleLogger,
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
||||||
import type { RecordPageViewInputDTO } from './dtos/RecordPageViewInputDTO';
|
import { GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
||||||
import type { RecordPageViewOutputDTO } from './dtos/RecordPageViewOutputDTO';
|
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
||||||
|
import { RecordPageViewUseCase } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import type { GetAnalyticsMetricsOutputDTO } from './dtos/GetAnalyticsMetricsOutputDTO';
|
||||||
|
import type { GetDashboardDataOutputDTO } from './dtos/GetDashboardDataOutputDTO';
|
||||||
import type { RecordEngagementInputDTO } from './dtos/RecordEngagementInputDTO';
|
import type { RecordEngagementInputDTO } from './dtos/RecordEngagementInputDTO';
|
||||||
import type { RecordEngagementOutputDTO } from './dtos/RecordEngagementOutputDTO';
|
import type { RecordEngagementOutputDTO } from './dtos/RecordEngagementOutputDTO';
|
||||||
import type { GetDashboardDataOutputDTO } from './dtos/GetDashboardDataOutputDTO';
|
import type { RecordPageViewInputDTO } from './dtos/RecordPageViewInputDTO';
|
||||||
import type { GetAnalyticsMetricsOutputDTO } from './dtos/GetAnalyticsMetricsOutputDTO';
|
import type { RecordPageViewOutputDTO } from './dtos/RecordPageViewOutputDTO';
|
||||||
import { RecordPageViewUseCase } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
|
||||||
import { RecordEngagementUseCase } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
|
||||||
import { GetDashboardDataUseCase } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
|
||||||
import { GetAnalyticsMetricsUseCase } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
|
||||||
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';
|
|
||||||
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
|
|
||||||
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
|
|
||||||
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
|
import { GetAnalyticsMetricsPresenter } from './presenters/GetAnalyticsMetricsPresenter';
|
||||||
|
import { GetDashboardDataPresenter } from './presenters/GetDashboardDataPresenter';
|
||||||
|
import { RecordEngagementPresenter } from './presenters/RecordEngagementPresenter';
|
||||||
|
import { RecordPageViewPresenter } from './presenters/RecordPageViewPresenter';
|
||||||
|
|
||||||
type RecordPageViewInput = RecordPageViewInputDTO;
|
type RecordPageViewInput = RecordPageViewInputDTO;
|
||||||
type RecordEngagementInput = RecordEngagementInputDTO;
|
type RecordEngagementInput = RecordEngagementInputDTO;
|
||||||
@@ -24,45 +24,53 @@ export class AnalyticsService {
|
|||||||
@Inject(RecordEngagementUseCase) private readonly recordEngagementUseCase: RecordEngagementUseCase,
|
@Inject(RecordEngagementUseCase) private readonly recordEngagementUseCase: RecordEngagementUseCase,
|
||||||
@Inject(GetDashboardDataUseCase) private readonly getDashboardDataUseCase: GetDashboardDataUseCase,
|
@Inject(GetDashboardDataUseCase) private readonly getDashboardDataUseCase: GetDashboardDataUseCase,
|
||||||
@Inject(GetAnalyticsMetricsUseCase) private readonly getAnalyticsMetricsUseCase: GetAnalyticsMetricsUseCase,
|
@Inject(GetAnalyticsMetricsUseCase) private readonly getAnalyticsMetricsUseCase: GetAnalyticsMetricsUseCase,
|
||||||
private readonly recordPageViewPresenter: RecordPageViewPresenter,
|
|
||||||
private readonly recordEngagementPresenter: RecordEngagementPresenter,
|
|
||||||
private readonly getDashboardDataPresenter: GetDashboardDataPresenter,
|
|
||||||
private readonly getAnalyticsMetricsPresenter: GetAnalyticsMetricsPresenter,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutputDTO> {
|
async recordPageView(input: RecordPageViewInput): Promise<RecordPageViewOutputDTO> {
|
||||||
|
const presenter = new RecordPageViewPresenter();
|
||||||
|
|
||||||
const result = await this.recordPageViewUseCase.execute(input);
|
const result = await this.recordPageViewUseCase.execute(input);
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
throw new Error(error.details?.message ?? 'Failed to record page view');
|
throw new Error(error.details?.message ?? 'Failed to record page view');
|
||||||
}
|
}
|
||||||
return this.recordPageViewPresenter.getResponseModel();
|
|
||||||
|
return presenter.responseModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutputDTO> {
|
async recordEngagement(input: RecordEngagementInput): Promise<RecordEngagementOutputDTO> {
|
||||||
|
const presenter = new RecordEngagementPresenter();
|
||||||
|
|
||||||
const result = await this.recordEngagementUseCase.execute(input);
|
const result = await this.recordEngagementUseCase.execute(input);
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
throw new Error(error.details?.message ?? 'Failed to record engagement');
|
throw new Error(error.details?.message ?? 'Failed to record engagement');
|
||||||
}
|
}
|
||||||
return this.recordEngagementPresenter.getResponseModel();
|
|
||||||
|
return presenter.responseModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getDashboardData(): Promise<GetDashboardDataOutputDTO> {
|
async getDashboardData(): Promise<GetDashboardDataOutputDTO> {
|
||||||
|
const presenter = new GetDashboardDataPresenter();
|
||||||
|
|
||||||
const result = await this.getDashboardDataUseCase.execute({});
|
const result = await this.getDashboardDataUseCase.execute({});
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
throw new Error(error.details?.message ?? 'Failed to get dashboard data');
|
throw new Error(error.details?.message ?? 'Failed to get dashboard data');
|
||||||
}
|
}
|
||||||
return this.getDashboardDataPresenter.getResponseModel();
|
|
||||||
|
return presenter.responseModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAnalyticsMetrics(): Promise<GetAnalyticsMetricsOutputDTO> {
|
async getAnalyticsMetrics(): Promise<GetAnalyticsMetricsOutputDTO> {
|
||||||
|
const presenter = new GetAnalyticsMetricsPresenter();
|
||||||
|
|
||||||
const result = await this.getAnalyticsMetricsUseCase.execute({});
|
const result = await this.getAnalyticsMetricsUseCase.execute({});
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
throw new Error(error.details?.message ?? 'Failed to get analytics metrics');
|
throw new Error(error.details?.message ?? 'Failed to get analytics metrics');
|
||||||
}
|
}
|
||||||
return this.getAnalyticsMetricsPresenter.getResponseModel();
|
|
||||||
|
return presenter.responseModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,9 +27,19 @@ describe('GetAnalyticsMetricsPresenter', () => {
|
|||||||
averageSessionDuration: 300,
|
averageSessionDuration: 300,
|
||||||
bounceRate: 0.4,
|
bounceRate: 0.4,
|
||||||
});
|
});
|
||||||
|
expect(presenter.responseModel).toEqual({
|
||||||
|
pageViews: 1000,
|
||||||
|
uniqueVisitors: 500,
|
||||||
|
averageSessionDuration: 300,
|
||||||
|
bounceRate: 0.4,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getResponseModel throws if not presented', () => {
|
it('getResponseModel throws if not presented', () => {
|
||||||
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('responseModel throws if not presented', () => {
|
||||||
|
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
|
||||||
import type { GetAnalyticsMetricsOutput } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
import type { GetAnalyticsMetricsOutput } from '@core/analytics/application/use-cases/GetAnalyticsMetricsUseCase';
|
||||||
|
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||||
import type { GetAnalyticsMetricsOutputDTO } from '../dtos/GetAnalyticsMetricsOutputDTO';
|
import type { GetAnalyticsMetricsOutputDTO } from '../dtos/GetAnalyticsMetricsOutputDTO';
|
||||||
|
|
||||||
export class GetAnalyticsMetricsPresenter implements UseCaseOutputPort<GetAnalyticsMetricsOutput> {
|
export class GetAnalyticsMetricsPresenter implements UseCaseOutputPort<GetAnalyticsMetricsOutput> {
|
||||||
private responseModel: GetAnalyticsMetricsOutputDTO | null = null;
|
private model: GetAnalyticsMetricsOutputDTO | null = null;
|
||||||
|
|
||||||
present(result: GetAnalyticsMetricsOutput): void {
|
present(result: GetAnalyticsMetricsOutput): void {
|
||||||
this.responseModel = {
|
this.model = {
|
||||||
pageViews: result.pageViews,
|
pageViews: result.pageViews,
|
||||||
uniqueVisitors: result.uniqueVisitors,
|
uniqueVisitors: result.uniqueVisitors,
|
||||||
averageSessionDuration: result.averageSessionDuration,
|
averageSessionDuration: result.averageSessionDuration,
|
||||||
@@ -14,8 +14,13 @@ export class GetAnalyticsMetricsPresenter implements UseCaseOutputPort<GetAnalyt
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get responseModel(): GetAnalyticsMetricsOutputDTO {
|
||||||
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
getResponseModel(): GetAnalyticsMetricsOutputDTO {
|
getResponseModel(): GetAnalyticsMetricsOutputDTO {
|
||||||
if (!this.responseModel) throw new Error('Presenter not presented');
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
return this.responseModel;
|
return this.model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,19 @@ describe('GetDashboardDataPresenter', () => {
|
|||||||
totalRaces: 20,
|
totalRaces: 20,
|
||||||
totalLeagues: 5,
|
totalLeagues: 5,
|
||||||
});
|
});
|
||||||
|
expect(presenter.responseModel).toEqual({
|
||||||
|
totalUsers: 100,
|
||||||
|
activeUsers: 50,
|
||||||
|
totalRaces: 20,
|
||||||
|
totalLeagues: 5,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getResponseModel throws if not presented', () => {
|
it('getResponseModel throws if not presented', () => {
|
||||||
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('responseModel throws if not presented', () => {
|
||||||
|
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
|
||||||
import type { GetDashboardDataOutput } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
import type { GetDashboardDataOutput } from '@core/analytics/application/use-cases/GetDashboardDataUseCase';
|
||||||
|
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||||
import type { GetDashboardDataOutputDTO } from '../dtos/GetDashboardDataOutputDTO';
|
import type { GetDashboardDataOutputDTO } from '../dtos/GetDashboardDataOutputDTO';
|
||||||
|
|
||||||
export class GetDashboardDataPresenter implements UseCaseOutputPort<GetDashboardDataOutput> {
|
export class GetDashboardDataPresenter implements UseCaseOutputPort<GetDashboardDataOutput> {
|
||||||
private responseModel: GetDashboardDataOutputDTO | null = null;
|
private model: GetDashboardDataOutputDTO | null = null;
|
||||||
|
|
||||||
present(result: GetDashboardDataOutput): void {
|
present(result: GetDashboardDataOutput): void {
|
||||||
this.responseModel = {
|
this.model = {
|
||||||
totalUsers: result.totalUsers,
|
totalUsers: result.totalUsers,
|
||||||
activeUsers: result.activeUsers,
|
activeUsers: result.activeUsers,
|
||||||
totalRaces: result.totalRaces,
|
totalRaces: result.totalRaces,
|
||||||
@@ -14,8 +14,13 @@ export class GetDashboardDataPresenter implements UseCaseOutputPort<GetDashboard
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get responseModel(): GetDashboardDataOutputDTO {
|
||||||
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
getResponseModel(): GetDashboardDataOutputDTO {
|
getResponseModel(): GetDashboardDataOutputDTO {
|
||||||
if (!this.responseModel) throw new Error('Presenter not presented');
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
return this.responseModel;
|
return this.model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,9 +21,17 @@ describe('RecordEngagementPresenter', () => {
|
|||||||
eventId: 'event-123',
|
eventId: 'event-123',
|
||||||
engagementWeight: 10,
|
engagementWeight: 10,
|
||||||
});
|
});
|
||||||
|
expect(presenter.responseModel).toEqual({
|
||||||
|
eventId: 'event-123',
|
||||||
|
engagementWeight: 10,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getResponseModel throws if not presented', () => {
|
it('getResponseModel throws if not presented', () => {
|
||||||
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('responseModel throws if not presented', () => {
|
||||||
|
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,19 +1,24 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
|
||||||
import type { RecordEngagementOutput } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
import type { RecordEngagementOutput } from '@core/analytics/application/use-cases/RecordEngagementUseCase';
|
||||||
|
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||||
import type { RecordEngagementOutputDTO } from '../dtos/RecordEngagementOutputDTO';
|
import type { RecordEngagementOutputDTO } from '../dtos/RecordEngagementOutputDTO';
|
||||||
|
|
||||||
export class RecordEngagementPresenter implements UseCaseOutputPort<RecordEngagementOutput> {
|
export class RecordEngagementPresenter implements UseCaseOutputPort<RecordEngagementOutput> {
|
||||||
private responseModel: RecordEngagementOutputDTO | null = null;
|
private model: RecordEngagementOutputDTO | null = null;
|
||||||
|
|
||||||
present(result: RecordEngagementOutput): void {
|
present(result: RecordEngagementOutput): void {
|
||||||
this.responseModel = {
|
this.model = {
|
||||||
eventId: result.eventId,
|
eventId: result.eventId,
|
||||||
engagementWeight: result.engagementWeight,
|
engagementWeight: result.engagementWeight,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get responseModel(): RecordEngagementOutputDTO {
|
||||||
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
getResponseModel(): RecordEngagementOutputDTO {
|
getResponseModel(): RecordEngagementOutputDTO {
|
||||||
if (!this.responseModel) throw new Error('Presenter not presented');
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
return this.responseModel;
|
return this.model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,16 @@ describe('RecordPageViewPresenter', () => {
|
|||||||
expect(presenter.getResponseModel()).toEqual({
|
expect(presenter.getResponseModel()).toEqual({
|
||||||
pageViewId: 'pv-123',
|
pageViewId: 'pv-123',
|
||||||
});
|
});
|
||||||
|
expect(presenter.responseModel).toEqual({
|
||||||
|
pageViewId: 'pv-123',
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('getResponseModel throws if not presented', () => {
|
it('getResponseModel throws if not presented', () => {
|
||||||
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
expect(() => presenter.getResponseModel()).toThrow('Presenter not presented');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('responseModel throws if not presented', () => {
|
||||||
|
expect(() => presenter.responseModel).toThrow('Presenter not presented');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
import type { UseCaseOutputPort } from '@core/shared/application';
|
|
||||||
import type { RecordPageViewOutput } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
import type { RecordPageViewOutput } from '@core/analytics/application/use-cases/RecordPageViewUseCase';
|
||||||
|
import type { UseCaseOutputPort } from '@core/shared/application';
|
||||||
import type { RecordPageViewOutputDTO } from '../dtos/RecordPageViewOutputDTO';
|
import type { RecordPageViewOutputDTO } from '../dtos/RecordPageViewOutputDTO';
|
||||||
|
|
||||||
export class RecordPageViewPresenter implements UseCaseOutputPort<RecordPageViewOutput> {
|
export class RecordPageViewPresenter implements UseCaseOutputPort<RecordPageViewOutput> {
|
||||||
private responseModel: RecordPageViewOutputDTO | null = null;
|
private model: RecordPageViewOutputDTO | null = null;
|
||||||
|
|
||||||
present(result: RecordPageViewOutput): void {
|
present(result: RecordPageViewOutput): void {
|
||||||
this.responseModel = {
|
this.model = {
|
||||||
pageViewId: result.pageViewId,
|
pageViewId: result.pageViewId,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get responseModel(): RecordPageViewOutputDTO {
|
||||||
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
|
return this.model;
|
||||||
|
}
|
||||||
|
|
||||||
getResponseModel(): RecordPageViewOutputDTO {
|
getResponseModel(): RecordPageViewOutputDTO {
|
||||||
if (!this.responseModel) throw new Error('Presenter not presented');
|
if (!this.model) throw new Error('Presenter not presented');
|
||||||
return this.responseModel;
|
return this.model;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ export class AuthService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO no mapping in here, must use presenter
|
||||||
const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(User.fromStored(user));
|
const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(User.fromStored(user));
|
||||||
const apiSession = this.buildAuthSessionDTO(coreSession.token, authenticatedUserDTO);
|
const apiSession = this.buildAuthSessionDTO(coreSession.token, authenticatedUserDTO);
|
||||||
|
|
||||||
@@ -113,7 +114,7 @@ export class AuthService {
|
|||||||
this.logger.debug('[AuthService] Attempting logout.');
|
this.logger.debug('[AuthService] Attempting logout.');
|
||||||
|
|
||||||
const commandResultPresenter = new CommandResultPresenter();
|
const commandResultPresenter = new CommandResultPresenter();
|
||||||
const result = await this.logoutUseCase.execute();
|
const result = await this.logoutUseCase.execute(); // TODO
|
||||||
|
|
||||||
if (result.isErr()) {
|
if (result.isErr()) {
|
||||||
const error = result.unwrapErr();
|
const error = result.unwrapErr();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
|
||||||
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
import { DashboardOverviewUseCase } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
import { DashboardOverviewDTO } from './dtos/DashboardOverviewDTO';
|
import { DashboardOverviewDTO } from './dtos/DashboardOverviewDTO';
|
||||||
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresenter';
|
||||||
|
|
||||||
@@ -7,14 +7,14 @@ import { DashboardOverviewPresenter } from './presenters/DashboardOverviewPresen
|
|||||||
import type { Logger } from '@core/shared/application/Logger';
|
import type { Logger } from '@core/shared/application/Logger';
|
||||||
|
|
||||||
// Tokens
|
// Tokens
|
||||||
import { LOGGER_TOKEN, DASHBOARD_OVERVIEW_USE_CASE_TOKEN, DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN } from './DashboardProviders';
|
import { DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN, DASHBOARD_OVERVIEW_USE_CASE_TOKEN, LOGGER_TOKEN } from './DashboardProviders';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DashboardService {
|
export class DashboardService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
@Inject(LOGGER_TOKEN) private readonly logger: Logger,
|
||||||
@Inject(DASHBOARD_OVERVIEW_USE_CASE_TOKEN) private readonly dashboardOverviewUseCase: DashboardOverviewUseCase,
|
@Inject(DASHBOARD_OVERVIEW_USE_CASE_TOKEN) private readonly dashboardOverviewUseCase: DashboardOverviewUseCase,
|
||||||
@Inject(DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN) private readonly dashboardOverviewPresenter: DashboardOverviewPresenter,
|
@Inject(DASHBOARD_OVERVIEW_OUTPUT_PORT_TOKEN) private readonly dashboardOverviewPresenter: DashboardOverviewPresenter, // TODO no presenter injection
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async getDashboardOverview(driverId: string): Promise<DashboardOverviewDTO> {
|
async getDashboardOverview(driverId: string): Promise<DashboardOverviewDTO> {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
|
||||||
import { DashboardOverviewPresenter } from './DashboardOverviewPresenter';
|
|
||||||
import type { DashboardOverviewResult } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
import type { DashboardOverviewResult } from '@core/racing/application/use-cases/DashboardOverviewUseCase';
|
||||||
import { Driver } from '@core/racing/domain/entities/Driver';
|
import { Driver } from '@core/racing/domain/entities/Driver';
|
||||||
import { Race } from '@core/racing/domain/entities/Race';
|
|
||||||
import { League } from '@core/racing/domain/entities/League';
|
import { League } from '@core/racing/domain/entities/League';
|
||||||
|
import { Race } from '@core/racing/domain/entities/Race';
|
||||||
import { Standing } from '@core/racing/domain/entities/Standing';
|
import { Standing } from '@core/racing/domain/entities/Standing';
|
||||||
import { Result as RaceResult } from '@core/racing/domain/entities/result/Result';
|
import { Result as RaceResult } from '@core/racing/domain/entities/result/Result';
|
||||||
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
import type { FeedItem } from '@core/social/domain/types/FeedItem';
|
||||||
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
|
import { DashboardOverviewPresenter } from './DashboardOverviewPresenter';
|
||||||
|
|
||||||
const createOutput = (): DashboardOverviewResult => {
|
const createOutput = (): DashboardOverviewResult => {
|
||||||
const driver = Driver.create({ id: 'driver-1', iracingId: '12345', name: 'Test Driver', country: 'DE' });
|
const driver = Driver.create({ id: 'driver-1', iracingId: '12345', name: 'Test Driver', country: 'DE' });
|
||||||
@@ -148,15 +148,15 @@ describe('DashboardOverviewPresenter', () => {
|
|||||||
|
|
||||||
expect(dto.activeLeaguesCount).toBe(2);
|
expect(dto.activeLeaguesCount).toBe(2);
|
||||||
expect(dto.currentDriver?.id).toBe('driver-1');
|
expect(dto.currentDriver?.id).toBe('driver-1');
|
||||||
expect(dto.myUpcomingRaces[0].id).toBe('race-1');
|
expect(dto.myUpcomingRaces[0]!.id).toBe('race-1');
|
||||||
expect(dto.otherUpcomingRaces[0].id).toBe('race-2');
|
expect(dto.otherUpcomingRaces[0]!.id).toBe('race-2');
|
||||||
expect(dto.upcomingRaces).toHaveLength(2);
|
expect(dto.upcomingRaces).toHaveLength(2);
|
||||||
expect(dto.nextRace?.id).toBe('race-1');
|
expect(dto.nextRace?.id).toBe('race-1');
|
||||||
expect(dto.recentResults[0].raceId).toBe('race-3');
|
expect(dto.recentResults[0]!.raceId).toBe('race-3');
|
||||||
expect(dto.leagueStandingsSummaries[0].leagueId).toBe('league-1');
|
expect(dto.leagueStandingsSummaries[0]!.leagueId).toBe('league-1');
|
||||||
expect(dto.feedSummary.notificationCount).toBe(3);
|
expect(dto.feedSummary.notificationCount).toBe(3);
|
||||||
expect(dto.feedSummary.items[0].id).toBe('feed-1');
|
expect(dto.feedSummary.items[0]!.id).toBe('feed-1');
|
||||||
expect(dto.friends[0].id).toBe('friend-1');
|
expect(dto.friends[0]!.id).toBe('friend-1');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { describe, it, expect, beforeEach } from 'vitest';
|
import { GetDriversLeaderboardResult } from '@core/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
||||||
import { Result } from '@core/shared/application/Result';
|
import { Result } from '@core/shared/application/Result';
|
||||||
|
import { beforeEach, describe, expect, it } from 'vitest';
|
||||||
import { DriversLeaderboardPresenter } from './DriversLeaderboardPresenter';
|
import { DriversLeaderboardPresenter } from './DriversLeaderboardPresenter';
|
||||||
import type { GetDriversLeaderboardResult } from '../../../../../core/racing/application/use-cases/GetDriversLeaderboardUseCase';
|
|
||||||
|
// TODO fix eslint issues
|
||||||
|
|
||||||
describe('DriversLeaderboardPresenter', () => {
|
describe('DriversLeaderboardPresenter', () => {
|
||||||
let presenter: DriversLeaderboardPresenter;
|
let presenter: DriversLeaderboardPresenter;
|
||||||
@@ -54,7 +56,6 @@ describe('DriversLeaderboardPresenter', () => {
|
|||||||
|
|
||||||
presenter.present(result);
|
presenter.present(result);
|
||||||
|
|
||||||
const api = presenter.responseModel;
|
|
||||||
|
|
||||||
expect(result.drivers).toHaveLength(2);
|
expect(result.drivers).toHaveLength(2);
|
||||||
expect(result.drivers[0]).toEqual({
|
expect(result.drivers[0]).toEqual({
|
||||||
|
|||||||
Reference in New Issue
Block a user