03 - esercizi
Rendere configurabile il valore orario
Adesso che abbiamo a disposizione un po' di risorse e abbiamo visto un esempio pratico implementate voi la logica degli altri due punti che erano stati richiesti:
- rendere configurabile il valore orario
- avere la possibilità di definire un valore minimo di durata sotto il quale il valore fatturabile è 0
Il valore orario deve essere preso da delle configurazioni legate all'utente. Attualmente non abbiamo un utente quindi dobbiamo creare il sistema predisposto ma falsificare l'origine dei risultati.
Le impostazioni sono nel formato:
interface HourlyRateSettings {
hourlyRate: number;
}
interface MinBillableSettings {
minDuration: number;
}
1 - soluzione
amount-settings.entity.ts
export interface HourlyRateSettings {
hourlyRate: number;
}
amount-settigs.ds.ts
import { HourlyRateSettings } from './amount-settings.entity';
import { Injectable } from "@nestjs/common";
@Injectable()
export abstract class AmountSettingsDataSource {
abstract getAmountSettings(): Promise<HourlyRateSettings>;
}
amount-settings.ds.static.ts
import { Inject } from "@nestjs/common";
import { AmountSettingsDataSource } from "./amount-settings.ds";
import { HourlyRateSettings } from "./amount-settings.entity";
export const STATIC_HOURLY_RATE = 'STATIC_HOURLY_RATE';
export class AmountSettingsStaticDataSource extends AmountSettingsDataSource {
constructor(@Inject(STATIC_HOURLY_RATE) protected hourlyRate: number) {
super();
}
async getAmountSettings(): Promise<HourlyRateSettings> {
return {
hourlyRate: this.hourlyRate
};
}
}
time-entry.module.ts
...
{provide: STATIC_HOURLY_RATE, useValue: 50},
{provide: AmountSettingsDataSource, useClass: AmountSettingsStaticDataSource}
...
fixed-amount.service.ts
import { Inject } from "@nestjs/common";
import { TimeEntryAmountService } from "./amount.service";
export const FIXED_AMOUNT_DEFAULT_VALUE = 'FIXED_AMOUNT_DEFAULT_VALUE';
export class FixedAmountService extends TimeEntryAmountService {
constructor(@Inject(FIXED_AMOUNT_DEFAULT_VALUE) protected hourlyRate: number){
super();
}
calcAmount(duration: number): number {
return duration * this.hourlyRate;
}
}
time-entry.controller.ts
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Param,
Post,
UsePipes,
ValidationPipe,
} from '@nestjs/common';
import { CreateTimeEntryDTO, TimeEntryResultDTO } from './entities/time-entry.dto';
import { TimeEntryDataSource } from './datasource/datasource.service';
import { TimeEntryResultFactory } from './entities/time-entry.result.factory';
import { DurationSettingsDataSource } from './duration-settings/duration-settings.ds';
import { DurationStrategySelectorService } from './duration/duration-strategy-selector.service';
import { AmountSettingsDataSource } from './amount-settings/amount-settings.ds';
import { FixedAmountService } from './amount/fixed-amount.service';
@Controller('time-entries')
export class TimeEntryController {
constructor(
protected readonly dataSource: TimeEntryDataSource,
protected readonly resultFactorySrv: TimeEntryResultFactory,
protected readonly durationSettingsSrv: DurationSettingsDataSource,
protected readonly durationStrategySelector: DurationStrategySelectorService,
protected readonly amountSettingsSrv: AmountSettingsDataSource
) {}
@Get()
async list(): Promise<TimeEntryResultDTO[]> {
const list = await this.dataSource.list();
const durationSettings = await this.durationSettingsSrv.getDurationSettings();
const durationSrv = this.durationStrategySelector.getStrategy(durationSettings.strategy);
const amountSettings = await this.amountSettingsSrv.getAmountSettings();
const amountSrv = new FixedAmountService(amountSettings.hourlyRate)
const resultFactory = this.resultFactorySrv.getFactory(durationSrv, amountSrv);
return list.map((e) => {
return resultFactory(e);
});
}
@Get(':id')
async detail(@Param('id') id: string): Promise<TimeEntryResultDTO> {
const record = await this.dataSource.get(id);
if (!record) {
throw new HttpException('Not found', HttpStatus.NOT_FOUND);
}
const durationSettings = await this.durationSettingsSrv.getDurationSettings();
const durationSrv = this.durationStrategySelector.getStrategy(durationSettings.strategy);
const amountSettings = await this.amountSettingsSrv.getAmountSettings();
const amountSrv = new FixedAmountService(amountSettings.hourlyRate)
const resultFactory = this.resultFactorySrv.getFactory(durationSrv, amountSrv);
return resultFactory(record);
}
@Post()
@UsePipes(new ValidationPipe({transform: true}))
async create(@Body() createTimeEntryDTO: CreateTimeEntryDTO): Promise<TimeEntryResultDTO> {
const record = await this.dataSource.create(createTimeEntryDTO);
const durationSettings = await this.durationSettingsSrv.getDurationSettings();
const durationSrv = this.durationStrategySelector.getStrategy(durationSettings.strategy);
const amountSettings = await this.amountSettingsSrv.getAmountSettings();
const amountSrv = new FixedAmountService(amountSettings.hourlyRate)
const resultFactory = this.resultFactorySrv.getFactory(durationSrv, amountSrv);
return resultFactory(record);
}
}
2- soluzione
Andiamo prima ad abbozzare la logica nel controller per vedere di cosa abbiamo bisogno:
@Get(':id')
async detail(@Param('id') id: string): Promise<TimeEntryResultDTO> {
const record = await this.dataSource.get(id);
if (!record) {
throw new HttpException('Not found', HttpStatus.NOT_FOUND);
}
const durationSettings = await this.durationSettingsSrv.getDurationSettings(FAKE_USERID);
const durationSrv = this.durationStrategySelector.getStrategy(durationSettings.strategy);
let amountSrv = await this.amountSrvProvider.getAmountService(FAKE_USERID);
if(durationSrv.getDuration(record.start, record.end) < some_param) {
amountSrv = new FixedAmountService(0);
}
const resultFactory = this.resultFactorySrv.getFactory(durationSrv, amountSrv);
return resultFactory(record);
}
Quindi abbiamo bisogno di un parametro che arrivi da qualche configurazione e poi dobbiamo andare a cambiare amountSrv a seconda della condizione.
Possiamo quindi vedere che le nostre logiche si stanno intrecciando, l'amountSrv non dipende più solo dallo userId ma anche dalla durata dell'item, per avere la durata ho bisogno di del durationSrv, che ha altre dipendenze, e così via.
Siamo quindi ancora di più nella condizione di avere una serie di "blocchetti" che dobbiamo combinare per ottenere un risultato, cerchiamo di vedere quali sono input e output generali della nostra logica e vediamo come possiamo migliorare il codice.
Inputs:
- userId
- record
Outputs:
- risultato nel formato TimeEntryResultDTO