Newer
Older
import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'

Hugo SUBTIL
committed
import challengeEntityData from 'db/challengeEntity.json'
import duelEntityData from 'db/duelEntity.json'
import explorationEntityData from 'db/explorationEntity.json'
import profileData from 'db/profileData'

Hugo SUBTIL
committed
import quizEntityData from 'db/quizEntity.json'

Hugo SUBTIL
committed
CHALLENGE_DOCTYPE,
DUEL_DOCTYPE,
EXPLORATION_DOCTYPE,
PROFILE_DOCTYPE,
TRIGGERS_DOCTYPE,
} from 'doctypes'
import { FluidType } from 'enums'

Hugo SUBTIL
committed
import { DateTime } from 'luxon'
import { initSchemaDoctype } from 'migrations/migration'
import { migrations } from 'migrations/migration.data'
InitSteps,
InitStepsErrors,

Hugo SUBTIL
committed
import React from 'react'
import ChallengeService from 'services/challenge.service'
import DuelService from 'services/duel.service'
import ExplorationService from 'services/exploration.service'

Hugo SUBTIL
committed
import FluidService from 'services/fluid.service'
import KonnectorStatusService from 'services/konnectorStatus.service'
import ProfileService from 'services/profile.service'
import QuizService from 'services/quiz.service'
import { getActualAnalysisDate } from 'utils/date'

Hugo SUBTIL
committed
import { hashFile } from 'utils/hash'

Hugo SUBTIL
committed
import ProfileEcogestureService from './profileEcogesture.service'
import ProfileTypeEntityService from './profileTypeEntity.service'
import TermsService from './terms.service'
const logStack = logger.namespace('initializationService')
export default class InitializationService {
private readonly _client: Client
private readonly _setInitStep: React.Dispatch<React.SetStateAction<InitSteps>>
private readonly _setInitStepError: React.Dispatch<
React.SetStateAction<InitStepsErrors | null>
>
constructor(
_client: Client,
_setInitStep: React.Dispatch<React.SetStateAction<InitSteps>>,
_setInitStepError: React.Dispatch<
React.SetStateAction<InitStepsErrors | null>
>
) {
this._setInitStep = _setInitStep
this._setInitStepError = _setInitStepError
* If not, the profil is created and migrations are set to latest
* failure return: null
*/
public async initProfile(): Promise<Profile | null> {
const profileService = new ProfileService(this._client)
try {
const loadedProfile = await profileService.getProfile()
if (!loadedProfile) {
// Population with the data
const { data: newProfile } = await this._client.create(
PROFILE_DOCTYPE,
logDuration('[Initialization] Profile created', startTime)
// create schema to latest version
await initSchemaDoctype(this._client, migrations.length)
this._setInitStepError(InitStepsErrors.PROFILE_ERROR)
throw new Error('initProfile: Profile not created')
}
}
const updatedProfile = await profileService.updateProfile({
lastConnectionDate: DateTime.local().setZone('utc', {
keepLocalTime: true,
}),
})
logDuration('[Initialization] Profile loaded and updated in', startTime)
this._setInitStepError(InitStepsErrors.PROFILE_ERROR)
const errorMessage = `Initialization error - initProfile: :${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
* Check if profileType exist
* If not, the profileType is created
* failure return: null
*/
public async initProfileType(): Promise<ProfileType | null> {
const profileTypeEntityService = new ProfileTypeEntityService(this._client)
try {
const loadedProfileType = await profileTypeEntityService.getProfileType()
logDuration('[Initialization] ProfileType loaded', startTime)
return loadedProfileType
} catch (error) {
this._setInitStepError(InitStepsErrors.PROFILETYPE_ERROR)
const errorMessage = `Initialization error - initProfileType: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
public async initProfileEcogesture(): Promise<ProfileEcogesture | null> {
const profileEcogestureService = new ProfileEcogestureService(this._client)
try {
const loadedProfileEcogesture =
await profileEcogestureService.getProfileEcogesture()
logDuration('[Initialization] ProfileEcogesture loaded', startTime)
return loadedProfileEcogesture
} catch (error) {
this._setInitStepError(InitStepsErrors.PROFILETYPE_ERROR)
const errorMessage = `Initialization error - initProfileEcogesture: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
public async initFluidPrices(): Promise<void> {
logDuration('[Initialization] Launching fluidPrices service', startTime)
const triggerQuery: QueryDefinition = Q(TRIGGERS_DOCTYPE).where({
'message.name': 'fluidsPrices',
})
const {
data: [trigger],
}: QueryResult<Array<Trigger>> = await this._client.query(triggerQuery)
if (trigger?._id) {
this._client
.getStackClient()
.fetchJSON('POST', `/jobs/triggers/${trigger._id}/launch`)
logDuration(
'[Initialization] fluidPrices service launched successfully',
startTime
)
logDuration(
'[Initialization] FluidPrices service trigger not found',
startTime
)
public async initChallengeEntity(hash: string): Promise<string> {
const challengeHash = hashFile(challengeEntityData)
const challengeService = new ChallengeService(this._client)
// Populate data if none challengeEntity exists
const loadedChallengeEntity =
await challengeService.getAllChallengeEntities()
if (!loadedChallengeEntity || loadedChallengeEntity?.length === 0) {
// Populate the doctype with data
try {
for (const challengeEntity of challengeEntityData) {
await this._client.create(CHALLENGE_DOCTYPE, challengeEntity)
}
// Check of created document
const checkCount = await challengeService.getAllChallengeEntities()
if (!checkCount || checkCount?.length !== challengeEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initChallengeEntity: Created challenge entities does not match'
)
}
logDuration('[Initialization] Challenge entities created', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initChallengeEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
}
// Update if the hash is not the same as the one from profile
if (hash !== challengeHash) {
// Update the doctype
try {
// Deletion of all documents
await challengeService.deleteAllChallengeEntities()
// Population with the data
for (const challengeEntity of challengeEntityData) {
await this._client.create(CHALLENGE_DOCTYPE, challengeEntity)
}
// Check of created document
const checkCount = await challengeService.getAllChallengeEntities()
if (!checkCount || checkCount?.length !== challengeEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initChallengeEntity: Created challenge entities does not match'
)
}
logDuration('[Initialization] Challenge entities updated', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initChallengeEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
} else {
// Doctype already up to date
logDuration('[Initialization] Challenge Entity loaded', startTime)
return challengeHash
}
}
public async initDuelEntity(hash: string): Promise<string> {
const hashDuelEntity = hashFile(duelEntityData)
const duelService = new DuelService(this._client)
// Populate data if none DuelEntity exists
const loadedDuelTypes = await duelService.getAllDuelEntities()
if (!loadedDuelTypes || loadedDuelTypes?.length === 0) {
// Populate the doctype with data
try {
for (const duelEntity of duelEntityData) {
await this._client.create(DUEL_DOCTYPE, duelEntity)
}
// Check of created document
const checkCount = await duelService.getAllDuelEntities()
if (!checkCount || checkCount?.length !== duelEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initDuelEntity: Created duel entities does not match'
)
}
logDuration('[Initialization] UserDuel entities created', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initDuelEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
}
// Update if the hash is not the same as the one from profile
if (hash !== hashDuelEntity) {
// Update the doctype
try {
// Deletion of all documents
await duelService.deleteAllDuelEntities()
// Population with the data
for (const duelEntity of duelEntityData) {
await this._client.create(DUEL_DOCTYPE, duelEntity)
}
// Check of created document
const checkCount = await duelService.getAllDuelEntities()
if (!checkCount || checkCount?.length !== duelEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initDuelEntity: Created duel entities does not match'
)
}
logDuration('[Initialization] UserDuel entities updated', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initDuelEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
} else {
// Doctype already up to date
logDuration('[Initialization] Duel Entity loaded', startTime)
return hashDuelEntity
}
}
public async initQuizEntity(hash: string): Promise<string> {
const quizHash = hashFile(quizEntityData)
const quizService = new QuizService(this._client)
// Populate data if none quizEntity exists
const loadedQuizEntity = await quizService.getAllQuizEntities()
if (!loadedQuizEntity || loadedQuizEntity?.length === 0) {
// Populate the doctype with data
try {
for (const quizEntity of quizEntityData) {
await this._client.create(QUIZ_DOCTYPE, quizEntity)
}
// Check of created document
const checkCount = await quizService.getAllQuizEntities()
if (!checkCount || checkCount?.length !== quizEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initQuizEntity: Created quiz entities does not match'
)
}
logDuration('[Initialization] Quiz entities created', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initQuizEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
}
// Update if the hash is not the same as the one from profile
if (hash !== quizHash) {
// Update the doctype
try {
// Deletion of all documents
await quizService.deleteAllQuizEntities()
// Population with the data
for (const quizEntity of quizEntityData) {
await this._client.create(QUIZ_DOCTYPE, quizEntity)
}
// Check of created document
const checkCount = await quizService.getAllQuizEntities()
if (!checkCount || checkCount?.length !== quizEntityData.length) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initQuizEntity: Created quiz entities does not match'
)
}
logDuration('[Initialization] Quiz entities updated', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initQuizEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
} else {
// Doctype already up to date
logDuration('[Initialization] Quiz Entity loaded', startTime)
return quizHash
}
}
public async initExplorationEntity(hash: string): Promise<string> {
const explorationHash = hashFile(explorationEntityData)
const explorationService = new ExplorationService(this._client)
// Populate data if none explorationEntity exists
const loadedExplorationEntity =
await explorationService.getAllExplorationEntities()
if (!loadedExplorationEntity || loadedExplorationEntity?.length === 0) {
// Populate the doctype with data
try {
for (const explorationEntity of explorationEntityData) {
await this._client.create(EXPLORATION_DOCTYPE, explorationEntity)
}
// Check of created document
const checkCount = await explorationService.getAllExplorationEntities()
if (
!checkCount ||
checkCount?.length !== explorationEntityData.length
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initExplorationEntity: Created exploration entities does not match'
)
}
logDuration('[Initialization] Exploration entities created', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initExplorationEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
}
// Update if the hash is not the same as the one from profile
if (hash !== explorationHash) {
// Update the doctype
try {
// Deletion of all documents
await explorationService.deleteAllExplorationEntities()
// Population with the data
for (const explorationEntity of explorationEntityData) {
await this._client.create(EXPLORATION_DOCTYPE, explorationEntity)
}
// Check of created document
const checkCount = await explorationService.getAllExplorationEntities()
if (
!checkCount ||
checkCount?.length !== explorationEntityData.length
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error(
'initExplorationEntity: Created exploration entities does not match'
)
}
logDuration('[Initialization] Exploration entities updated', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initExplorationEntity: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
throw error
}
} else {
// Doctype already up to date
logDuration('[Initialization] Exploration Entity loaded', startTime)
public async initAnalysis(profile: Profile): Promise<{
monthlyAnalysisDate: DateTime
haveSeenLastAnalysis: boolean
}> {
try {
const actualAnalysisDate = getActualAnalysisDate()
if (
profile.monthlyAnalysisDate &&
actualAnalysisDate <= profile.monthlyAnalysisDate
) {
logDuration(
'[Initialization] Analysis information from profile loaded',
startTime
)
return {
monthlyAnalysisDate: profile.monthlyAnalysisDate,
haveSeenLastAnalysis: profile.haveSeenLastAnalysis,
}
} else {
logDuration(
'[Initialization] Analysis information from profile updated',
startTime
return {
monthlyAnalysisDate: actualAnalysisDate,
this._setInitStepError(InitStepsErrors.ANALYSIS_ERROR)
const errorMessage = `Initialization error - initAnalysis: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
* failure throw error
*/
public async initFluidTypes(): Promise<FluidType[]> {
const kss = new KonnectorStatusService(this._client)
try {
const fluidtypes = await kss.getKonnectorAccountStatus()
if (fluidtypes) {
logDuration('[Initialization] Fluid Types loaded', startTime)
this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
throw new Error('initFluidTypes: FluidTypes not found')
}
} catch (error) {
this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
logApp.error('Initialization error - : ', error)
const errorMessage = `Initialization error - initFluidTypes: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
* For each fluid get the trigger status and the last data date
* failure throw error
*/
public async initFluidStatus(): Promise<FluidStatus[]> {
const fs = new FluidService(this._client)
try {
const fluidStatus = await fs.getFluidStatus()
if (fluidStatus) {
logDuration('[Initialization] Fluid Status loaded', startTime)
this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
throw new Error('initFluidStatus: fluidStatus not found')
}
} catch (error) {
this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
const errorMessage = `Initialization error - initFluidStatus: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
* failure throw error
*/
public async initUserChallenges(
fluidStatus: FluidStatus[]
): Promise<UserChallenge[]> {
const challengeService = new ChallengeService(this._client)
try {
const userChallengeList = await challengeService.buildUserChallengeList(
fluidStatus
)
if (userChallengeList) {
logDuration('[Initialization] initUserChallenges', startTime)
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
throw new Error('initUserChallenges: userChallengeList not found')
}
} catch (error) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - initUserChallenges: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
public async initDuelProgress(userChallenge: UserChallenge): Promise<{
updatedUserChallenge: UserChallenge
dataloads: Dataload[]
}> {
const challengeService = new ChallengeService(this._client)
try {
const { updatedUserChallenge, dataloads } =
await challengeService.initChallengeDuelProgress(userChallenge)
logDuration('[Initialization] initDuelProgress finished', startTime)
return { updatedUserChallenge, dataloads }
} catch (error) {
this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
const errorMessage = `Initialization error - : ${JSON.stringify(error)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
Yoan VALLET
committed
public async initConsent(): Promise<TermsStatus> {
const termService = new TermsService(this._client)
const isUpToDate = await termService.isConsentVersionUpToDate()
const lastTerm = await termService.getLastTerm()
return { accepted: false, versionType: 'init' }
}
if (isUpToDate) {
const isLastConsentValidated = await termService.isLastTermValidated()
if (isLastConsentValidated) {
logApp.info(
'[Initialization] Last Consent successfully loaded and valid'
)
return { accepted: true, versionType: 'init' }
}
logApp.info('[Initialization] Consent not up-to-date')
return { accepted: false, versionType: 'init' }
}
const versionType = await termService.getTermsVersionType()
if (versionType === 'minor') {
logApp.info('[Initialization] Minor Terms update detected')
return { accepted: false, versionType: 'minor' }
logApp.info('[Initialization] Major Terms update detected')
return { accepted: false, versionType: 'major' }
this._setInitStepError(InitStepsErrors.CONSENT_ERROR)
const errorMessage = `Initialization error - initConsent: ${JSON.stringify(
error
)}`
logStack('error', errorMessage)
logApp.error(errorMessage)
Sentry.captureException(errorMessage)
} finally {
logDuration('[Initialization] initConsent finished', startTime)