import { Injectable } from "@angular/core" ; import { AthleteModel , DatedAthleteSettingsModel } from "@elevate/shared/models" ; import { AthleteDao } from "../../dao/athlete/athlete-dao.service" ; import * as _ from "lodash" ; import { AppError } from "../../models/app-error.model" ; /**
 * The latest managed period must have "since" as "forever"
 */ @ Injectable ( ) export class AthleteService { constructor ( public athleteModelDao ) { } returns { Promise < AthleteModel > } public fetch ( ) { return this . athleteModelDao . fetch ( ) . then ( ( athleteModel ) => { athleteModel . datedAthleteSettings = _ . sortBy ( athleteModel . datedAthleteSettings , ( model ) => { const sortOnDate = ( _ . isNull ( model . since ) ) ? new Date ( 0 ) : new Date ( model . since ) ; return sortOnDate . getTime ( ) * - 1 ; } ) ; return Promise . resolve ( athleteModel ) ; } ) ; } param { DatedAthleteSettingsModel } datedAthleteSettings returns { Promise < DatedAthleteSettingsModel [ ] > } public addSettings ( datedAthleteSettings ) { return this . validateSingle ( datedAthleteSettings ) . then ( ( ) => { return this . fetch ( ) ; } ) . then ( ( athleteModel ) => { const alreadyExistingDatedSettings = _ . find ( athleteModel . datedAthleteSettings , { since : datedAthleteSettings . since } ) ; if ( ! _ . isEmpty ( alreadyExistingDatedSettings ) ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_EXISTS , "Dated athlete settings already exists. You should edit it instead." ) ) ; } if ( athleteModel . datedAthleteSettings . length > 0 ) { athleteModel . datedAthleteSettings = _ . flatten ( [ datedAthleteSettings , athleteModel . datedAthleteSettings ] ) ; } else { athleteModel . datedAthleteSettings . push ( datedAthleteSettings ) ; const defaultForeverDatedAthleteSettings = _ . cloneDeep ( datedAthleteSettings ) ; defaultForeverDatedAthleteSettings . since = null ; athleteModel . datedAthleteSettings . push ( defaultForeverDatedAthleteSettings ) ; } return this . save ( athleteModel ) . then ( savedAthleteModel => { return Promise . resolve ( savedAthleteModel . datedAthleteSettings ) ; } ) ; } ) ; } param athleteModel returns { Promise < AthleteModel > } public save ( athleteModel ) { return this . validate ( athleteModel . datedAthleteSettings ) . then ( ( ) => { return ( < Promise < AthleteModel > > this . athleteModelDao . save ( athleteModel ) ) ; } ) ; } param path param value public saveProperty < V > ( path , value ) { return this . athleteModelDao . upsertProperty < V > ( path , value ) ; } returns { Promise < DatedAthleteSettingsModel [ ] > } public resetSettings ( ) { return this . fetch ( ) . then ( ( athleteModel ) => { athleteModel . datedAthleteSettings = [ ] ; return this . athleteModelDao . save ( athleteModel ) ; } ) . then ( ( ) => { return this . addSettings ( DatedAthleteSettingsModel . DEFAULT_MODEL ) ; } ) ; } param { string } sinceIdentifier param { DatedAthleteSettingsModel } datedAthleteSettings returns { Promise < DatedAthleteSettingsModel [ ] > } public editSettings ( sinceIdentifier , datedAthleteSettings ) { return this . validateSingle ( datedAthleteSettings ) . then ( ( ) => { return this . fetch ( ) ; } ) . then ( ( athleteModel ) => { const isEditChangingSince = ( sinceIdentifier !== datedAthleteSettings . since ) ; const isEditOverridingExistingSettings = ( _ . findIndex ( athleteModel . datedAthleteSettings , { since : datedAthleteSettings . since } ) !== - 1 ) ; const isEditConflictWithExistingSettings = ( isEditChangingSince && isEditOverridingExistingSettings ) ; if ( isEditConflictWithExistingSettings ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_EXISTS , "Dated athlete settings do not exists. You should add it instead." ) ) ; } const indexOfSettingsToEdit = _ . findIndex ( athleteModel . datedAthleteSettings , { since : sinceIdentifier } ) ; if ( indexOfSettingsToEdit === - 1 ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_DO_NOT_EXISTS , "Dated athlete settings do not exists. You should add it instead." ) ) ; } athleteModel . datedAthleteSettings [ indexOfSettingsToEdit ] = datedAthleteSettings ; return this . save ( athleteModel ) . then ( savedAthleteModel => { return Promise . resolve ( savedAthleteModel . datedAthleteSettings ) ; } ) ; } ) ; } param { string } sinceIdentifier returns { Promise < DatedAthleteSettingsModel [ ] > } public removeSettings ( sinceIdentifier ) { return this . fetch ( ) . then ( ( athleteModel ) => { if ( _ . isNull ( sinceIdentifier ) ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_FOREVER_MUST_EXISTS , "Default forever dated athlete settings cannot be removed." ) ) ; } const indexOfSettingsToRemove = _ . findIndex ( athleteModel . datedAthleteSettings , { since : sinceIdentifier } ) ; if ( indexOfSettingsToRemove === - 1 ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_DO_NOT_EXISTS , "Dated athlete settings do not exists. You should add it instead." ) ) ; } athleteModel . datedAthleteSettings . splice ( indexOfSettingsToRemove , 1 ) ; return this . save ( athleteModel ) . then ( savedAthleteModel => { return Promise . resolve ( savedAthleteModel . datedAthleteSettings ) ; } ) ; } ) ; } param datedAthleteSettings public validate ( datedAthleteSettings ) { let hasForeverSettings = false ; let hasDuplicate = false ; const keyOccurrences = _ . countBy ( datedAthleteSettings , "since" ) ; _ . mapKeys ( keyOccurrences , ( count , key ) => { if ( key === "null" ) { hasForeverSettings = true ; } if ( count > 1 ) { hasDuplicate = true ; } return null ; } ) ; if ( ! hasForeverSettings ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_FOREVER_MUST_EXISTS , "Default forever dated athlete settings must exists." ) ) ; } if ( hasDuplicate ) { return Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_DUPLICATES , "Dated athlete settings have duplicates." ) ) ; } return Promise . resolve ( ) ; } param datedAthleteSettingsModel public validateSingle ( datedAthleteSettingsModel ) { let promise = Promise . resolve ( ) ; if ( datedAthleteSettingsModel ) { if ( ! _ . isNull ( datedAthleteSettingsModel . since ) ) { const isDateWellFormatted = ( /([0-9]{4})\-([0-9]{2})\-([0-9]{2})/gm ) . exec ( datedAthleteSettingsModel . since ) ; const onDate = new Date ( datedAthleteSettingsModel . since ) ; const isValidDate = ! isNaN ( onDate . getTime ( ) ) ; if ( ! isDateWellFormatted || ! isValidDate ) { promise = Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_INVALID_DATE , "Dated athlete settings has invalid date." ) ) ; } } } else { promise = Promise . reject ( new AppError ( AppError . DATED_ATHLETE_SETTINGS_DO_NOT_EXISTS , "Dated athlete settings do not exists." ) ) ; } return promise ; } }