Skip to content
Snippets Groups Projects
initialization.service.ts 24.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • Bastien DUMONT's avatar
    Bastien DUMONT committed
    import * as Sentry from '@sentry/react'
    
    import { Client, Q, QueryDefinition, QueryResult } from 'cozy-client'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import logger from 'cozy-logger'
    
    import challengeEntityData from 'db/challengeEntity.json'
    import duelEntityData from 'db/duelEntity.json'
    import explorationEntityData from 'db/explorationEntity.json'
    
    import profileData from 'db/profileData'
    
    import quizEntityData from 'db/quizEntity.json'
    
    import {
    
      CHALLENGE_DOCTYPE,
      DUEL_DOCTYPE,
      EXPLORATION_DOCTYPE,
      PROFILE_DOCTYPE,
    
      QUIZ_DOCTYPE,
    
    } from 'doctypes'
    import { FluidType } from 'enums'
    
    import { initSchemaDoctype } from 'migrations/migration'
    import { migrations } from 'migrations/migration.data'
    
    import {
      Dataload,
      FluidStatus,
    
      InitSteps,
      InitStepsErrors,
    
      Profile,
    
      ProfileType,
    
      UserChallenge,
    } from 'models'
    
    import React from 'react'
    import ChallengeService from 'services/challenge.service'
    
    import DuelService from 'services/duel.service'
    import ExplorationService from 'services/exploration.service'
    
    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'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import { logDuration } from 'utils/duration'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    import logApp from 'utils/logger'
    
    import ProfileEcogestureService from './profileEcogesture.service'
    import ProfileTypeEntityService from './profileTypeEntity.service'
    import TermsService from './terms.service'
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    
    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>
      >
    
        _setInitStep: React.Dispatch<React.SetStateAction<InitSteps>>,
        _setInitStepError: React.Dispatch<
    
          React.SetStateAction<InitStepsErrors | null>
        >
      ) {
    
        this._client = _client
    
        this._setInitStep = _setInitStep
        this._setInitStepError = _setInitStepError
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * Check if profil exist
    
       * If not, the profil is created and migrations are set to latest
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: profil
    
       * failure return: null
       */
      public async initProfile(): Promise<Profile | null> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const profileService = new ProfileService(this._client)
        try {
    
          this._setInitStep(InitSteps.PROFILE)
    
          const loadedProfile = await profileService.getProfile()
          if (!loadedProfile) {
            // Population with the data
            const { data: newProfile } = await this._client.create(
              PROFILE_DOCTYPE,
    
            )
            if (newProfile) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              logDuration('[Initialization] Profile created', startTime)
    
              // create schema to latest version
              await initSchemaDoctype(this._client, migrations.length)
    
            } else {
    
              this._setInitStepError(InitStepsErrors.PROFILE_ERROR)
    
              throw new Error('initProfile: Profile not created')
            }
          }
          const updatedProfile = await profileService.updateProfile({
            lastConnectionDate: DateTime.local().setZone('utc', {
              keepLocalTime: true,
            }),
          })
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] Profile loaded and updated in', startTime)
    
          return updatedProfile
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.PROFILE_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
    
          const errorMessage = `Initialization error - initProfile: :${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * Check if profileType exist
       * If not, the profileType is created
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: profileType
    
       * failure return: null
       */
      public async initProfileType(): Promise<ProfileType | null> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const profileTypeEntityService = new ProfileTypeEntityService(this._client)
        try {
    
          const loadedProfileType = await profileTypeEntityService.getProfileType()
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] ProfileType loaded', startTime)
    
          return loadedProfileType
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.PROFILETYPE_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initProfileType: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
          throw error
        }
      }
    
      public async initProfileEcogesture(): Promise<ProfileEcogesture | null> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const profileEcogestureService = new ProfileEcogestureService(this._client)
        try {
    
          const loadedProfileEcogesture =
            await profileEcogestureService.getProfileEcogesture()
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] ProfileEcogesture loaded', startTime)
    
          return loadedProfileEcogesture
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.PROFILETYPE_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initProfileEcogesture: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
      public async initFluidPrices(): Promise<void> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        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> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        this._setInitStep(InitSteps.CHALLENGES)
    
        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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Challenge entities created', startTime)
    
            return challengeHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Challenge entities updated', startTime)
    
            return challengeHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] Challenge Entity loaded', startTime)
    
          return challengeHash
        }
      }
    
      public async initDuelEntity(hash: string): Promise<string> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] UserDuel entities created', startTime)
    
            return hashDuelEntity
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] UserDuel entities updated', startTime)
    
            return hashDuelEntity
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] Duel Entity loaded', startTime)
    
          return hashDuelEntity
        }
      }
    
      public async initQuizEntity(hash: string): Promise<string> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        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'
              )
            }
    
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Quiz entities created', startTime)
    
            return quizHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Quiz entities updated', startTime)
    
            return quizHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] Quiz Entity loaded', startTime)
    
          return quizHash
        }
      }
    
      public async initExplorationEntity(hash: string): Promise<string> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Exploration entities created', startTime)
    
            return explorationHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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'
              )
            }
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Exploration entities updated', startTime)
    
            return explorationHash
          } catch (error) {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            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
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] Exploration Entity loaded', startTime)
    
          return explorationHash
        }
      }
    
    
      public async initAnalysis(profile: Profile): Promise<{
    
        monthlyAnalysisDate: DateTime
        haveSeenLastAnalysis: boolean
      }> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        try {
          const actualAnalysisDate = getActualAnalysisDate()
          if (
            profile.monthlyAnalysisDate &&
            actualAnalysisDate <= profile.monthlyAnalysisDate
          ) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration(
              '[Initialization] Analysis information from profile loaded',
              startTime
            )
    
            return {
              monthlyAnalysisDate: profile.monthlyAnalysisDate,
              haveSeenLastAnalysis: profile.haveSeenLastAnalysis,
            }
          } else {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration(
              '[Initialization] Analysis information from profile updated',
              startTime
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            )
    
            return {
              monthlyAnalysisDate: actualAnalysisDate,
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
              haveSeenLastAnalysis: profile.isFirstConnection,
    
            }
          }
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.ANALYSIS_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initAnalysis: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * Check if FluidTypes exist
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: FluidType[]
    
       * failure throw error
       */
      public async initFluidTypes(): Promise<FluidType[]> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const kss = new KonnectorStatusService(this._client)
        try {
          const fluidtypes = await kss.getKonnectorAccountStatus()
          if (fluidtypes) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Fluid Types loaded', startTime)
    
            return fluidtypes
          } else {
    
            this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
    
            throw new Error('initFluidTypes: FluidTypes not found')
          }
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logApp.error('Initialization error - : ', error)
          const errorMessage = `Initialization error - initFluidTypes: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * For each fluid get the trigger status and the last data date
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: FluidStatus[]
    
       * failure throw error
       */
      public async initFluidStatus(): Promise<FluidStatus[]> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const fs = new FluidService(this._client)
        try {
    
          this._setInitStep(InitSteps.CONSOS)
    
          const fluidStatus = await fs.getFluidStatus()
          if (fluidStatus) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] Fluid Status loaded', startTime)
    
            return fluidStatus
          } else {
    
            this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
    
            throw new Error('initFluidStatus: fluidStatus not found')
          }
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.CONSOS_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initFluidStatus: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * Build the userChallengeList
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: UserChallenge[]
    
       * failure throw error
       */
      public async initUserChallenges(
        fluidStatus: FluidStatus[]
      ): Promise<UserChallenge[]> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const challengeService = new ChallengeService(this._client)
        try {
          const userChallengeList = await challengeService.buildUserChallengeList(
            fluidStatus
          )
          if (userChallengeList) {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logDuration('[Initialization] initUserChallenges', startTime)
    
            return userChallengeList
          } else {
    
            this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
            throw new Error('initUserChallenges: userChallengeList not found')
          }
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initUserChallenges: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
      /**
    
       * Retrieve dataloads for ongoing duel
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
       * success return: UserChallenge, Dataload[]
    
       * failure throw error
       */
    
      public async initDuelProgress(userChallenge: UserChallenge): Promise<{
    
        updatedUserChallenge: UserChallenge
        dataloads: Dataload[]
      }> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        const challengeService = new ChallengeService(this._client)
        try {
    
          const { updatedUserChallenge, dataloads } =
            await challengeService.initChallengeDuelProgress(userChallenge)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          logDuration('[Initialization] initDuelProgress finished', startTime)
    
          return { updatedUserChallenge, dataloads }
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.CHALLENGES_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - : ${JSON.stringify(error)}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
          throw error
        }
      }
    
      public async initConsent(): Promise<TermsStatus> {
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        const startTime = performance.now()
    
        try {
    
          this._setInitStep(InitSteps.CONSENT)
    
          const termService = new TermsService(this._client)
          const isUpToDate = await termService.isConsentVersionUpToDate()
    
          const lastTerm = await termService.getLastTerm()
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
            logApp.info('[Initialization] Init first terms')
    
            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' }
    
        } catch (error) {
    
          this._setInitStepError(InitStepsErrors.CONSENT_ERROR)
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
          const errorMessage = `Initialization error - initConsent: ${JSON.stringify(
            error
          )}`
          logStack('error', errorMessage)
          logApp.error(errorMessage)
          Sentry.captureException(errorMessage)
    
          throw error
    
    Bastien DUMONT's avatar
    Bastien DUMONT committed
        } finally {
          logDuration('[Initialization] initConsent finished', startTime)