diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index 3f8cea74ad51eb810ebf5fefb466d04263e1f1ad..37f76f5ce7b573821657f6e46d8fff4ff7f4f805 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -1,4 +1,10 @@ -import React, { useState, useEffect, ComponentType, ReactNode } from 'react' +import React, { + useState, + useEffect, + ComponentType, + ReactNode, + Dispatch, +} from 'react' import { useClient } from 'cozy-client' import classNames from 'classnames' import { useDispatch } from 'react-redux' @@ -8,14 +14,19 @@ import { toggleChallengeActionNotification, toggleChallengeDuelNotification, setFluidStatus, + GlobalActionTypes, } from 'store/global/global.actions' -import { updateProfile } from 'store/profile/profile.actions' +import { + ProfileActionTypes, + updateProfile, +} from 'store/profile/profile.actions' import { setUserChallengeList, setChallengeConsumption, updateUserChallengeList, + ChallengeActionTypes, } from 'store/challenge/challenge.actions' -import { setSelectedDate } from 'store/chart/chart.actions' +import { ChartActionTypes, setSelectedDate } from 'store/chart/chart.actions' import InitializationService from 'services/initialization.service' import './splashRoot.scss' import { UserChallengeState } from 'enum/userChallenge.enum' @@ -54,7 +65,12 @@ const SplashRoot = ({ splashStart: false, }) const [error, setError] = useState<Error | null>(null) - const dispatch = useDispatch() + const dispatch: Dispatch< + | ChallengeActionTypes + | ChartActionTypes + | GlobalActionTypes + | ProfileActionTypes + > = useDispatch() useEffect(() => { let timeoutSplash: NodeJS.Timeout @@ -76,58 +92,31 @@ const SplashRoot = ({ // Init index await initializationService.initIndex() // Init profile and update ecogestures, challenges, analysis - let profile = await initializationService.initProfile() + const profile = await initializationService.initProfile() if (subscribed && profile) { setValidExploration(UserExplorationID.EXPLORATION007) - const resultEcogesture = await initializationService.initEcogesture( - profile.ecogestureHash - ) - if ( - subscribed && - resultEcogesture.result && - resultEcogesture.profile - ) { - profile = resultEcogesture.profile - } - const resultDuel = await initializationService.initDuelEntity( - profile.duelHash - ) - if (subscribed && resultDuel.result && resultDuel.profile) { - profile = resultDuel.profile - } - const resultQuiz = await initializationService.initQuizEntity( - profile.quizHash - ) - if (subscribed && resultQuiz.result && resultQuiz.profile) { - profile = resultQuiz.profile - } - const resultExploration = await initializationService.initExplorationEntity( - profile.explorationHash - ) - if ( - subscribed && - resultExploration.result && - resultExploration.profile - ) { - profile = resultExploration.profile - } - - const resultChallengeEntity = await initializationService.initChallengeEntity( - profile.challengeHash - ) - if ( - subscribed && - resultChallengeEntity.result && - resultChallengeEntity.profile - ) { - profile = resultChallengeEntity.profile - } - const resultAnalysis = await initializationService.initAnalysis( - profile - ) - if (subscribed && resultAnalysis.result && resultAnalysis.profile) { - profile = resultAnalysis.profile - } + const [ + ecogestureHash, + duelHash, + quizHash, + challengeHash, + explorationHash, + analysisResult, + ] = await Promise.all([ + initializationService.initEcogesture(profile.ecogestureHash), + initializationService.initDuelEntity(profile.duelHash), + initializationService.initQuizEntity(profile.quizHash), + initializationService.initExplorationEntity(profile.challengeHash), + initializationService.initChallengeEntity(profile.explorationHash), + initializationService.initAnalysis(profile), + ]) + profile.ecogestureHash = ecogestureHash + profile.duelHash = duelHash + profile.quizHash = quizHash + profile.challengeHash = challengeHash + profile.explorationHash = explorationHash + profile.monthlyAnalysisDate = analysisResult.monthlyAnalysisDate + profile.haveSeenLastAnalysis = analysisResult.haveSeenLastAnalysis dispatch(updateProfile(profile)) dispatch(toggleAnalysisNotification(!profile.haveSeenLastAnalysis)) } diff --git a/src/services/initialization.service.spec.ts b/src/services/initialization.service.spec.ts index cfbf464fb5a1fc70dc371fb8fcc8ec54f20453ea..2fac83cb747aed3397f29ba12b9401b4768a2139 100644 --- a/src/services/initialization.service.spec.ts +++ b/src/services/initialization.service.spec.ts @@ -3,16 +3,25 @@ import { DateTime } from 'luxon' import { UserChallenge } from 'models' import InitializationService from './initialization.service' import mockClient from '../../tests/__mocks__/client' -import { ecogesturesData } from '../../tests/__mocks__/ecogesturesData.mock' -import { profileData } from '../../tests/__mocks__/profile.mock' -import { fluidStatusData } from '../../tests/__mocks__/fluidStatusData.mock' + import ecogestureData from 'db/ecogestureData.json' +import challengeEntityData from 'db/challengeEntity.json' +import duelEntityData from 'db/duelEntity.json' +import quizEntityData from 'db/quizEntity.json' +import explorationEntityData from 'db/explorationEntity.json' import { hashFile } from 'utils/hash' import { getActualAnalysisDate } from 'utils/date' import { FluidType } from 'enum/fluid.enum' +import { ecogesturesData } from '../../tests/__mocks__/ecogesturesData.mock' +import { profileData } from '../../tests/__mocks__/profile.mock' +import { fluidStatusData } from '../../tests/__mocks__/fluidStatusData.mock' import { userChallengeData } from '../../tests/__mocks__/userChallengeData.mock' import { graphData } from '../../tests/__mocks__/datachartData.mock' +import { allChallengeEntityData } from '../../tests/__mocks__/challengeEntity.mock' +import { allDuelEntity } from '../../tests/__mocks__/duelData.mock' +import { allQuizEntities } from '../../tests/__mocks__/quizData.mock' +import { allExplorationEntities } from '../../tests/__mocks__/explorationData.mock' const mockCreateIndexKonnector = jest.fn() jest.mock('./konnector.service', () => { @@ -54,41 +63,86 @@ jest.mock('./ecogesture.service', () => { }) }) -const mockGetKonnectorAccountStatus = jest.fn() -jest.mock('./konnectorStatus.service', () => { +const mockGetAllChallengeEntities = jest.fn() +const mockDeleteAllChallengeEntities = jest.fn() +const mockBuildUserChallengeList = jest.fn() +const mockGetUserChallengeDataload = jest.fn() +const mockUserChallengeUpdateFlag = jest.fn() +jest.mock('./challenge.service', () => { return jest.fn(() => { return { - getKonnectorAccountStatus: mockGetKonnectorAccountStatus, + getAllChallengeEntities: mockGetAllChallengeEntities, + deleteAllChallengeEntities: mockDeleteAllChallengeEntities, + buildUserChallengeList: mockBuildUserChallengeList, + getUserChallengeDataload: mockGetUserChallengeDataload, + updateUserChallenge: mockUserChallengeUpdateFlag, } }) }) -const mockGetFluidStatus = jest.fn() -jest.mock('./fluid.service', () => { +const mockGetAllDuelEntities = jest.fn() +const mockDeleteAllDuelEntities = jest.fn() +jest.mock('./duel.service', () => { return jest.fn(() => { return { - getFluidStatus: mockGetFluidStatus, + getAllDuelEntities: mockGetAllDuelEntities, + deleteAllDuelEntities: mockDeleteAllDuelEntities, } }) }) -const mockBuildUserChallengeList = jest.fn() -const mockGetUserChallengeDataload = jest.fn() -const mockUserChallengeUpdateFlag = jest.fn() -jest.mock('./challenge.service', () => { +const mockGetAllQuizEntities = jest.fn() +const mockDeleteAllQuizEntities = jest.fn() +jest.mock('./quiz.service', () => { return jest.fn(() => { return { - buildUserChallengeList: mockBuildUserChallengeList, - getUserChallengeDataload: mockGetUserChallengeDataload, - updateUserChallenge: mockUserChallengeUpdateFlag, + getAllQuizEntities: mockGetAllQuizEntities, + deleteAllQuizEntities: mockDeleteAllQuizEntities, + } + }) +}) + +const mockGetAllExplorationEntities = jest.fn() +const mockDeleteAllExplorationEntities = jest.fn() +jest.mock('./exploration.service', () => { + return jest.fn(() => { + return { + getAllExplorationEntities: mockGetAllExplorationEntities, + deleteAllExplorationEntities: mockDeleteAllExplorationEntities, + } + }) +}) + +const mockGetKonnectorAccountStatus = jest.fn() +jest.mock('./konnectorStatus.service', () => { + return jest.fn(() => { + return { + getKonnectorAccountStatus: mockGetKonnectorAccountStatus, + } + }) +}) + +const mockGetFluidStatus = jest.fn() +jest.mock('./fluid.service', () => { + return jest.fn(() => { + return { + getFluidStatus: mockGetFluidStatus, } }) }) describe('Initialization service', () => { const initializationService = new InitializationService(mockClient) + beforeEach(() => { + mockClient.query.mockClear() + mockClient.create.mockClear() + }) describe('initIndex method', () => { + beforeEach(() => { + mockCreateIndexKonnector.mockClear() + mockCreateIndexAccount.mockClear() + }) it('shoud return true when all indexes created', async () => { const mockQueryResult: QueryResult<boolean> = { data: true, @@ -101,7 +155,6 @@ describe('Initialization service', () => { mockCreateIndexAccount.mockResolvedValueOnce(mockQueryResult) await expect(initializationService.initIndex()).resolves.toBe(true) }) - it('shoud throw error when an index is not created', async () => { const mockQueryResult: QueryResult<boolean> = { data: true, @@ -119,6 +172,10 @@ describe('Initialization service', () => { }) describe('initProfile method', () => { + beforeEach(() => { + mockGetProfile.mockClear() + mockUpdateProfile.mockClear() + }) it('shoud return the profil when existing', async () => { mockGetProfile.mockResolvedValueOnce(profileData) mockUpdateProfile.mockResolvedValueOnce(profileData) @@ -126,7 +183,6 @@ describe('Initialization service', () => { profileData ) }) - it('shoud create and return the profil when no existing', async () => { const mockQueryResult: QueryResult<boolean> = { data: true, @@ -141,7 +197,6 @@ describe('Initialization service', () => { profileData ) }) - it('shoud throw error when the profile is not created', async () => { const mockQueryResult: QueryResult<null> = { data: null, @@ -155,12 +210,10 @@ describe('Initialization service', () => { new Error('initProfile: Profile not created') ) }) - it('shoud throw error when the profile could not be fetched', () => { mockGetProfile.mockRejectedValueOnce(new Error()) expect(initializationService.initProfile()).rejects.toEqual(new Error()) }) - it('shoud throw error when the profile failed to be created', () => { mockGetProfile.mockResolvedValueOnce(null) mockClient.create.mockRejectedValueOnce(new Error()) @@ -169,14 +222,18 @@ describe('Initialization service', () => { }) describe('initEcoGesture method', () => { - it('shoud return true and profile = null when ecogestures hash is already up to date', async () => { - mockGetAllEcogestures.mockResolvedValue(ecogestureData) - await expect( - initializationService.initEcogesture(hashFile(ecogestureData)) - ).resolves.toEqual({ result: true, profile: null }) + beforeEach(() => { + mockGetAllEcogestures.mockClear() + mockDeleteAllEcogestures.mockClear() }) - - it('shoud return true and ecogestures when ecogestures are created', async () => { + it('shoud return hash when ecogestures hash is already up to date', async () => { + mockGetAllEcogestures.mockResolvedValueOnce(ecogestureData) + const hash = hashFile(ecogestureData) + await expect(initializationService.initEcogesture(hash)).resolves.toEqual( + hash + ) + }) + it('shoud return hash when ecogestures are created', async () => { mockGetAllEcogestures .mockResolvedValueOnce(null) .mockResolvedValueOnce(ecogestureData) @@ -187,16 +244,11 @@ describe('Initialization service', () => { skip: 0, } mockClient.create.mockResolvedValue(mockQueryResult) - const mockProfile = { - ...profileData, - ecogestureHash: hashFile(ecogestureData), - } - mockUpdateProfile.mockResolvedValueOnce(mockProfile) - await expect( - initializationService.initEcogesture(hashFile(ecogestureData)) - ).resolves.toEqual({ result: true, profile: mockProfile }) + const hash = hashFile(ecogestureData) + await expect(initializationService.initEcogesture(hash)).resolves.toEqual( + hash + ) }) - it('shoud throw an error when ecogestures should be created and created ecogestures number does not match', async () => { mockGetAllEcogestures .mockResolvedValueOnce(null) @@ -216,28 +268,14 @@ describe('Initialization service', () => { ) ) }) - - it('shoud throw an error when ecogestures should be created and updateProfile failed', async () => { - mockGetAllEcogestures - .mockResolvedValueOnce(null) - .mockResolvedValueOnce(ecogestureData) - mockUpdateProfile.mockResolvedValueOnce(null) - await expect( - initializationService.initEcogesture(hashFile(ecogestureData)) - ).rejects.toThrow(new Error('initEcogesture: Profile not updated')) - }) - - it('shoud throw an error when ecogestures should be created and challenge creation failed', async () => { - mockGetAllEcogestures - .mockResolvedValueOnce(null) - .mockResolvedValueOnce(ecogestureData) - mockUpdateProfile.mockRejectedValueOnce(new Error()) + it('shoud throw an error when ecogestures should be created and creation failed', async () => { + mockGetAllEcogestures.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) await expect( initializationService.initEcogesture(hashFile(ecogestureData)) ).rejects.toThrow(new Error()) }) - - it('shoud return true and profil when ecogestures are updated', async () => { + it('shoud return hash when ecogestures are updated', async () => { mockGetAllEcogestures .mockResolvedValueOnce(ecogestureData) .mockResolvedValueOnce(ecogestureData) @@ -249,17 +287,10 @@ describe('Initialization service', () => { skip: 0, } mockClient.create.mockResolvedValue(mockQueryResult) - const mockProfile = { - ...profileData, - ecogestureHash: hashFile(ecogestureData), - } - mockUpdateProfile.mockResolvedValueOnce(mockProfile) - await expect(initializationService.initEcogesture('')).resolves.toEqual({ - result: true, - profile: mockProfile, - }) + await expect(initializationService.initEcogesture('')).resolves.toEqual( + hashFile(ecogestureData) + ) }) - it('shoud throw an error when ecogestures should be updated and created ecogestures number does not match', async () => { mockGetAllEcogestures .mockResolvedValueOnce(ecogestureData) @@ -278,94 +309,468 @@ describe('Initialization service', () => { ) ) }) - - it('shoud throw an error when ecogestures should be updated and updateProfile failed', async () => { + it('shoud throw an error when ecogestures should be updated and ecogestures creation failed', async () => { mockGetAllEcogestures .mockResolvedValueOnce(ecogestureData) .mockResolvedValueOnce(ecogestureData) mockDeleteAllEcogestures.mockResolvedValue(true) - mockUpdateProfile.mockResolvedValueOnce(null) - await expect(initializationService.initEcogesture('')).rejects.toThrow( - new Error('initEcogesture: Profile not updated') + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initEcogesture('')).rejects.toThrow( + new Error() ) }) + }) - it('shoud throw an error when ecogestures should be updated and ecogestures creation failed', async () => { - mockGetAllEcogestures - .mockResolvedValueOnce(ecogestureData) - .mockResolvedValueOnce(ecogestureData) - mockDeleteAllEcogestures.mockResolvedValue(true) - mockUpdateProfile.mockRejectedValueOnce(new Error()) - expect(initializationService.initEcogesture('')).rejects.toThrow( + describe('initChallengeEntity method', () => { + beforeEach(() => { + mockGetAllChallengeEntities.mockClear() + mockDeleteAllChallengeEntities.mockClear() + }) + it('shoud return hash when challenges hash is already up to date', async () => { + mockGetAllChallengeEntities.mockResolvedValueOnce(challengeEntityData) + const hash = hashFile(challengeEntityData) + await expect( + initializationService.initChallengeEntity(hash) + ).resolves.toEqual(hash) + }) + it('shoud return hash when challenge entities are created', async () => { + mockGetAllChallengeEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(challengeEntityData) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + const hash = hashFile(challengeEntityData) + await expect( + initializationService.initChallengeEntity(hash) + ).resolves.toEqual(hash) + }) + it('shoud throw an error when challenge entities should be created and created challenge entities number does not match', async () => { + mockGetAllChallengeEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(allChallengeEntityData) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initChallengeEntity(hashFile(challengeEntityData)) + ).rejects.toThrow( + new Error( + 'initChallengeEntity: Created challenge entities does not match' + ) + ) + }) + it('shoud throw an error when challenge entities should be created and creation failed', async () => { + mockGetAllChallengeEntities.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) + await expect( + initializationService.initChallengeEntity(hashFile(challengeEntityData)) + ).rejects.toThrow(new Error()) + }) + it('shoud return hash when challenge entities are updated', async () => { + mockGetAllChallengeEntities + .mockResolvedValueOnce(challengeEntityData) + .mockResolvedValueOnce(challengeEntityData) + mockDeleteAllChallengeEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initChallengeEntity('') + ).resolves.toEqual(hashFile(challengeEntityData)) + }) + it('shoud throw an error when challenge entities should be updated and created challenge entities number does not match', async () => { + mockGetAllChallengeEntities + .mockResolvedValueOnce(challengeEntityData) + .mockResolvedValueOnce(allChallengeEntityData) + mockDeleteAllChallengeEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initChallengeEntity('') + ).rejects.toThrow( + new Error( + 'initChallengeEntity: Created challenge entities does not match' + ) + ) + }) + it('shoud throw an error when challenge entities should be updated and challenge entities creation failed', async () => { + mockGetAllChallengeEntities + .mockResolvedValueOnce(challengeEntityData) + .mockResolvedValueOnce(challengeEntityData) + mockDeleteAllChallengeEntities.mockResolvedValue(true) + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initChallengeEntity('')).rejects.toThrow( new Error() ) }) }) - describe('initAnalysis method', () => { - it('should return true and profile when analysis is up to date', async () => { - const mockProfile = { - ...profileData, - monthlyAnalysisDate: getActualAnalysisDate(), + describe('initDuelEntity method', () => { + beforeEach(() => { + mockGetAllDuelEntities.mockClear() + mockDeleteAllDuelEntities.mockClear() + }) + it('shoud return hash when duel hash is already up to date', async () => { + mockGetAllDuelEntities.mockResolvedValueOnce(duelEntityData) + const hash = hashFile(duelEntityData) + await expect(initializationService.initDuelEntity(hash)).resolves.toEqual( + hash + ) + }) + it('shoud return hash when duel entities are created', async () => { + mockGetAllDuelEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(duelEntityData) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + const hash = hashFile(duelEntityData) + await expect(initializationService.initDuelEntity(hash)).resolves.toEqual( + hash + ) + }) + it('shoud throw an error when duel entities should be created and created duel entities number does not match', async () => { + mockGetAllDuelEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(allDuelEntity) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, } + mockClient.create.mockResolvedValue(mockQueryResult) await expect( - initializationService.initAnalysis(mockProfile) - ).resolves.toEqual({ - result: true, - profile: mockProfile, - }) + initializationService.initDuelEntity(hashFile(duelEntityData)) + ).rejects.toThrow( + new Error('initDuelEntity: Created duel entities does not match') + ) + }) + it('shoud throw an error when duel entities should be created and creation failed', async () => { + mockGetAllDuelEntities.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) + await expect( + initializationService.initDuelEntity(hashFile(duelEntityData)) + ).rejects.toThrow(new Error()) + }) + it('shoud return hash when duel entities are updated', async () => { + mockGetAllDuelEntities + .mockResolvedValueOnce(duelEntityData) + .mockResolvedValueOnce(duelEntityData) + mockDeleteAllDuelEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initDuelEntity('')).resolves.toEqual( + hashFile(duelEntityData) + ) + }) + it('shoud throw an error when duel entities should be updated and created duel entities number does not match', async () => { + mockGetAllDuelEntities + .mockResolvedValueOnce(duelEntityData) + .mockResolvedValueOnce(allDuelEntity) + mockDeleteAllDuelEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initDuelEntity('')).rejects.toThrow( + new Error('initDuelEntity: Created duel entities does not match') + ) }) + it('shoud throw an error when duel entities should be updated and duel entities creation failed', async () => { + mockGetAllDuelEntities + .mockResolvedValueOnce(duelEntityData) + .mockResolvedValueOnce(duelEntityData) + mockDeleteAllDuelEntities.mockResolvedValue(true) + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initDuelEntity('')).rejects.toThrow( + new Error() + ) + }) + }) - it('should return true and updated profile when analysis is not up to date', async () => { - const mockProfile = { - ...profileData, - monthlyAnalysisDate: DateTime.fromISO('2000-10-02T00:00:00.000Z', { - zone: 'utc', - }), - haveSeenLastAnalysis: true, + describe('initQuizEntity method', () => { + beforeEach(() => { + mockGetAllQuizEntities.mockClear() + mockDeleteAllQuizEntities.mockClear() + }) + it('shoud return hash when quiz hash is already up to date', async () => { + mockGetAllQuizEntities.mockResolvedValueOnce(quizEntityData) + const hash = hashFile(quizEntityData) + await expect(initializationService.initQuizEntity(hash)).resolves.toEqual( + hash + ) + }) + it('shoud return hash when quiz entities are created', async () => { + mockGetAllQuizEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(quizEntityData) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + const hash = hashFile(quizEntityData) + await expect(initializationService.initQuizEntity(hash)).resolves.toEqual( + hash + ) + }) + it('shoud throw an error when quiz entities should be created and created quiz entities number does not match', async () => { + mockGetAllQuizEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(allQuizEntities) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initQuizEntity(hashFile(quizEntityData)) + ).rejects.toThrow( + new Error('initQuizEntity: Created quiz entities does not match') + ) + }) + it('shoud throw an error when quiz entities should be created and creation failed', async () => { + mockGetAllQuizEntities.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) + await expect( + initializationService.initQuizEntity(hashFile(quizEntityData)) + ).rejects.toThrow(new Error()) + }) + it('shoud return hash when quiz entities are updated', async () => { + mockGetAllQuizEntities + .mockResolvedValueOnce(quizEntityData) + .mockResolvedValueOnce(quizEntityData) + mockDeleteAllQuizEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initQuizEntity('')).resolves.toEqual( + hashFile(quizEntityData) + ) + }) + it('shoud throw an error when quiz entities should be updated and created quiz entities number does not match', async () => { + mockGetAllQuizEntities + .mockResolvedValueOnce(quizEntityData) + .mockResolvedValueOnce(allQuizEntities) + mockDeleteAllQuizEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initQuizEntity('')).rejects.toThrow( + new Error('initQuizEntity: Created quiz entities does not match') + ) + }) + it('shoud throw an error when quiz entities should be updated and quiz entities creation failed', async () => { + mockGetAllQuizEntities + .mockResolvedValueOnce(quizEntityData) + .mockResolvedValueOnce(quizEntityData) + mockDeleteAllQuizEntities.mockResolvedValue(true) + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initQuizEntity('')).rejects.toThrow( + new Error() + ) + }) + }) + + describe('initExplorationEntity method', () => { + beforeEach(() => { + mockGetAllExplorationEntities.mockClear() + mockDeleteAllExplorationEntities.mockClear() + }) + it('shoud return hash when explorations hash is already up to date', async () => { + mockGetAllExplorationEntities.mockResolvedValueOnce(explorationEntityData) + const hash = hashFile(explorationEntityData) + await expect( + initializationService.initExplorationEntity(hash) + ).resolves.toEqual(hash) + }) + it('shoud return hash when exploration entities are created', async () => { + mockGetAllExplorationEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(explorationEntityData) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + const hash = hashFile(explorationEntityData) + await expect( + initializationService.initExplorationEntity(hash) + ).resolves.toEqual(hash) + }) + it('shoud throw an error when exploration entities should be created and created exploration entities number does not match', async () => { + mockGetAllExplorationEntities + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(allExplorationEntities) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initExplorationEntity( + hashFile(explorationEntityData) + ) + ).rejects.toThrow( + new Error( + 'initExplorationEntity: Created exploration entities does not match' + ) + ) + }) + it('shoud throw an error when exploration entities should be created and creation failed', async () => { + mockGetAllExplorationEntities.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) + await expect( + initializationService.initExplorationEntity( + hashFile(explorationEntityData) + ) + ).rejects.toThrow(new Error()) + }) + it('shoud return hash when exploration entities are updated', async () => { + mockGetAllExplorationEntities + .mockResolvedValueOnce(explorationEntityData) + .mockResolvedValueOnce(explorationEntityData) + mockDeleteAllExplorationEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, } - const updatedProfile = { + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initExplorationEntity('') + ).resolves.toEqual(hashFile(explorationEntityData)) + }) + it('shoud throw an error when exploration entities should be updated and created exploration entities number does not match', async () => { + mockGetAllExplorationEntities + .mockResolvedValueOnce(explorationEntityData) + .mockResolvedValueOnce(allExplorationEntities) + mockDeleteAllExplorationEntities.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initExplorationEntity('') + ).rejects.toThrow( + new Error( + 'initExplorationEntity: Created exploration entities does not match' + ) + ) + }) + it('shoud throw an error when exploration entities should be updated and exploration entities creation failed', async () => { + mockGetAllExplorationEntities + .mockResolvedValueOnce(explorationEntityData) + .mockResolvedValueOnce(explorationEntityData) + mockDeleteAllExplorationEntities.mockResolvedValue(true) + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initExplorationEntity('')).rejects.toThrow( + new Error() + ) + }) + }) + + describe('initAnalysis method', () => { + it('should return monthlyAnalysisDate and haveSeenLastAnalysis when analysis is up to date', async () => { + const mockProfile = { ...profileData, monthlyAnalysisDate: getActualAnalysisDate(), - haveSeenLastAnalysis: false, } - mockUpdateProfile.mockResolvedValueOnce(updatedProfile) await expect( initializationService.initAnalysis(mockProfile) ).resolves.toEqual({ - result: true, - profile: updatedProfile, + monthlyAnalysisDate: getActualAnalysisDate(), + haveSeenLastAnalysis: mockProfile.haveSeenLastAnalysis, }) }) - - it('should throw error when analysis is not up to date and profile is not updated', async () => { + it('should return updated monthlyAnalysisDate and haveSeenLastAnalysis=true when analysis is not up to date and isFirstConnection', async () => { const mockProfile = { ...profileData, monthlyAnalysisDate: DateTime.fromISO('2000-10-02T00:00:00.000Z', { zone: 'utc', }), + haveSeenLastAnalysis: true, } - mockUpdateProfile.mockResolvedValueOnce(null) await expect( initializationService.initAnalysis(mockProfile) - ).rejects.toThrow(new Error('initAnalysis: Profile not updated')) + ).resolves.toEqual({ + monthlyAnalysisDate: getActualAnalysisDate(), + haveSeenLastAnalysis: true, + }) }) - - it('should throw error when analysis is not up to date and update profile failed', async () => { + it('should return updated monthlyAnalysisDate and haveSeenLastAnalysis=false when analysis is not up to date and isFirstConnection is false', async () => { const mockProfile = { ...profileData, + isFirstConnection: false, monthlyAnalysisDate: DateTime.fromISO('2000-10-02T00:00:00.000Z', { zone: 'utc', }), + haveSeenLastAnalysis: true, } - mockUpdateProfile.mockRejectedValueOnce(new Error()) await expect( initializationService.initAnalysis(mockProfile) - ).rejects.toThrow(new Error()) + ).resolves.toEqual({ + monthlyAnalysisDate: getActualAnalysisDate(), + haveSeenLastAnalysis: false, + }) }) }) describe('initFluidType method', () => { + beforeEach(() => { + mockGetKonnectorAccountStatus.mockClear() + }) it('shoud return all fluid types', async () => { mockGetKonnectorAccountStatus.mockResolvedValueOnce([ FluidType.ELECTRICITY, @@ -378,14 +783,12 @@ describe('Initialization service', () => { FluidType.GAS, ]) }) - it('shoud throw an error when null is retrieved as fluid types', async () => { mockGetKonnectorAccountStatus.mockResolvedValueOnce(null) await expect(initializationService.initFluidTypes()).rejects.toThrow( new Error('initFluidTypes: FluidTypes not found') ) }) - it('shoud throw an error when it fails to retrieve the fluid types', async () => { mockGetKonnectorAccountStatus.mockRejectedValueOnce(new Error()) await expect(initializationService.initFluidTypes()).rejects.toThrow( @@ -395,20 +798,21 @@ describe('Initialization service', () => { }) describe('initFluidStatus method', () => { + beforeEach(() => { + mockGetFluidStatus.mockClear() + }) it('shoud return all fluids type', async () => { mockGetFluidStatus.mockResolvedValueOnce(fluidStatusData) await expect(initializationService.initFluidStatus()).resolves.toEqual( fluidStatusData ) }) - it('shoud throw an error when null is retrieved as status', async () => { mockGetFluidStatus.mockResolvedValueOnce(null) await expect(initializationService.initFluidStatus()).rejects.toThrow( new Error('initFluidStatus: fluidStatus not found') ) }) - it('shoud throw an error when it fails to retrieve the status', async () => { mockGetFluidStatus.mockRejectedValueOnce(new Error()) await expect(initializationService.initFluidStatus()).rejects.toThrow( @@ -418,32 +822,38 @@ describe('Initialization service', () => { }) describe('initUserChallenges method', () => { + beforeEach(() => { + mockBuildUserChallengeList.mockClear() + }) it('shoud return all userChallenges', async () => { mockBuildUserChallengeList.mockResolvedValueOnce(userChallengeData) - await expect(initializationService.initUserChallenges()).resolves.toEqual( - userChallengeData - ) + await expect( + initializationService.initUserChallenges([]) + ).resolves.toEqual(userChallengeData) }) - it('shoud throw an error when null is retrieved as status', async () => { mockBuildUserChallengeList.mockResolvedValueOnce(null) - await expect(initializationService.initUserChallenges()).rejects.toThrow( + await expect( + initializationService.initUserChallenges([]) + ).rejects.toThrow( new Error('initUserChallenges: userChallengeList not found') ) }) - it('shoud throw an error when it fails to retrieve the status', async () => { mockBuildUserChallengeList.mockRejectedValueOnce(new Error()) - await expect(initializationService.initUserChallenges()).rejects.toThrow( - new Error() - ) + await expect( + initializationService.initUserChallenges([]) + ).rejects.toThrow(new Error()) }) }) describe('initDuelProgress method', () => { + beforeEach(() => { + mockGetUserChallengeDataload.mockClear() + mockUserChallengeUpdateFlag.mockClear() + }) it('shoud return updatedUserChallenge and dataload ', async () => { mockGetUserChallengeDataload.mockResolvedValueOnce(graphData.actualData) - const expectedUpdatedUserChallenge: UserChallenge = { ...userChallengeData[0], duel: { @@ -462,7 +872,6 @@ describe('Initialization service', () => { initializationService.initDuelProgress(userChallengeData[0]) ).resolves.toEqual(expectedResult) }) - it('shoud throw an error when it fails to retrieve the status', async () => { mockGetUserChallengeDataload.mockRejectedValueOnce(new Error()) await expect( diff --git a/src/services/initialization.service.ts b/src/services/initialization.service.ts index 261f928612dba3a7cbd3e98a0ec384ecf5df41bb..ec3330a2d8591174b247b2e0353997d647a58f43 100644 --- a/src/services/initialization.service.ts +++ b/src/services/initialization.service.ts @@ -1,845 +1,703 @@ -import { Client, Q, QueryDefinition } from 'cozy-client' -import { - ECOGESTURE_DOCTYPE, - PROFILE_DOCTYPE, - EGL_DAY_DOCTYPE, - EGL_MONTH_DOCTYPE, - EGL_YEAR_DOCTYPE, - ENEDIS_DAY_DOCTYPE, - ENEDIS_MINUTE_DOCTYPE, - ENEDIS_MONTH_DOCTYPE, - ENEDIS_YEAR_DOCTYPE, - GRDF_DAY_DOCTYPE, - GRDF_MONTH_DOCTYPE, - GRDF_YEAR_DOCTYPE, - CHALLENGE_DOCTYPE, - DUEL_DOCTYPE, - QUIZ_DOCTYPE, - EXPLORATION_DOCTYPE, -} from 'doctypes' - -import { FluidType } from 'enum/fluid.enum' -import { Dataload, FluidStatus, Profile, UserChallenge } from 'models' - -import EcogestureService from 'services/ecogesture.service' -import ChallengeService from 'services/challenge.service' -import ecogestureData from 'db/ecogestureData.json' -import challengeEntityData from 'db/challengeEntity.json' -import duelEntityData from 'db/duelEntity.json' -import quizEntityData from 'db/quizEntity.json' -import explorationEntityData from 'db/explorationEntity.json' - -import ProfileService from 'services/profile.service' -import profileData from 'db/profileData.json' -import KonnectorStatusService from 'services/konnectorStatus.service' -import KonnectorService from 'services/konnector.service' -import AccountService from 'services/account.service' -import FluidService from 'services/fluid.service' -import DuelService from 'services/duel.service' -import QuizService from 'services/quiz.service' -import ExplorationService from 'services/exploration.service' - -import { hashFile } from 'utils/hash' -import { getActualAnalysisDate } from 'utils/date' -import { TimeStep } from 'enum/timeStep.enum' -import ConsumptionDataManager from './consumption.service' -import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' -import { getRoundFloat } from 'utils/math' -import { DateTime } from 'luxon' - -export default class InitializationService { - private readonly _client: Client - - constructor(_client: Client) { - this._client = _client - } - - /* - * Call a query with where clause to create the index if not exist - */ - private async createIndex( - doctype: string, - timestep: TimeStep - ): Promise<object> { - const getMongoSelector = () => { - switch (timestep) { - case TimeStep.YEAR: - return { - year: { - $lte: 9999, - }, - } - case TimeStep.MONTH: - return { - year: { - $lte: 9999, - }, - month: { - $lte: 12, - }, - } - case TimeStep.DAY: - case TimeStep.HALF_AN_HOUR: - return { - year: { - $lte: 9999, - }, - month: { - $lte: 12, - }, - day: { - $lte: 31, - }, - } - default: - return {} - } - } - const query: QueryDefinition = Q(doctype) - .where(getMongoSelector()) - .limitBy(1) - return await this._client.query(query) - } - - /* - * create index for each Doctype - * sucess return: true - * failure throw error - */ - public async initIndex(): Promise<boolean> { - try { - const accountService = new AccountService(this._client) - const konnectorService = new KonnectorService(this._client) - await Promise.all([ - this.createIndex(EGL_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(EGL_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(EGL_DAY_DOCTYPE, TimeStep.DAY), - this.createIndex(ENEDIS_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(ENEDIS_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(ENEDIS_DAY_DOCTYPE, TimeStep.DAY), - this.createIndex(ENEDIS_MINUTE_DOCTYPE, TimeStep.HALF_AN_HOUR), - this.createIndex(GRDF_YEAR_DOCTYPE, TimeStep.YEAR), - this.createIndex(GRDF_MONTH_DOCTYPE, TimeStep.MONTH), - this.createIndex(GRDF_DAY_DOCTYPE, TimeStep.DAY), - konnectorService.createIndexKonnector(), - accountService.createIndexAccount(), - ]) - console.log( - '%c Initialization: Indexes created', - 'background: #222; color: white' - ) - return true - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - /* - * Check if profil exist - * If not, the profil is created - * sucess return: profil - * 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, - profileData[0] - ) - if (newProfile) { - console.log( - '%c Initialization: Profile created', - 'background: #222; color: white' - ) - } else { - throw new Error('initProfile: Profile not created') - } - } else { - console.log( - '%c Initialization: Profile loaded', - 'background: #222; color: white' - ) - // TODO ensure that actual profile has all needed attributes - } - const updatedProfile = await profileService.updateProfile({ - lastConnectionDate: DateTime.local().setZone('utc', { - keepLocalTime: true, - }), - }) - return updatedProfile - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - public async initEcogesture( - hash: string - ): Promise<{ result: boolean; profile: Profile | null }> { - const hashEcogestureType = hashFile(ecogestureData) - const ecogestureService = new EcogestureService(this._client) - const profileService = new ProfileService(this._client) - - // Populate data if none ecogesture exists - const loadedEcogestures = await ecogestureService.getAllEcogestures() - if ( - !loadedEcogestures || - (loadedEcogestures && loadedEcogestures.length === 0) - ) { - // Populate the doctype with data - try { - for (let i = 0; i <= ecogestureData.length - 1; i++) { - await this._client.create(ECOGESTURE_DOCTYPE, ecogestureData[i]) - } - // Check of created document based on count - const checkCount = await ecogestureService.getAllEcogestures() - if ( - !checkCount || - (checkCount && checkCount.length !== ecogestureData.length) - ) { - throw new Error( - 'initEcogesture: Created ecogesture type entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - ecogestureHash: hashEcogestureType, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Ecogesture created', - 'background: #222; color: white' - ) - return { result: true, profile: updatedProfile } - } else { - throw new Error('initEcogesture: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - // Update if the hash is not the same as the one from profile - if (hash !== hashEcogestureType) { - // Update the doctype - try { - // Deletion of all documents - await ecogestureService.deleteAllEcogestures() - // Population with the data - await Promise.all( - ecogestureData.map(async ecogesture => { - await this._client.create(ECOGESTURE_DOCTYPE, ecogesture) - }) - ) - // Check of created document based on count - const checkCount = await ecogestureService.getAllEcogestures() - if ( - !checkCount || - (checkCount && checkCount.length !== ecogestureData.length) - ) { - throw new Error( - 'initEcogesture: Created ecogesture type entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - ecogestureHash: hashEcogestureType, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Ecogesture updated', - 'background: #222; color: white' - ) - return { result: true, profile: updatedProfile } - } else { - throw new Error('initEcogesture: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } else { - // Doctype already up to date - console.log( - '%c Initialization: Ecogesture loaded', - 'background: #222; color: white' - ) - return { result: true, profile: null } - } - } - - public async initChallengeEntity( - hash: string - ): Promise<{ - result: boolean - profile: Profile | null - }> { - const challengeHash = hashFile(challengeEntityData) - const challengeService = new ChallengeService(this._client) - const profileService = new ProfileService(this._client) - - // Populate data if none challengeEntity exists - const loadedChallengeEntity = await challengeService.getAllChallengeEntities() - if ( - !loadedChallengeEntity || - (loadedChallengeEntity && loadedChallengeEntity.length === 0) - ) { - // Populate the doctype with data - try { - for (let i = 0; i <= challengeEntityData.length - 1; i++) { - await this._client.create(CHALLENGE_DOCTYPE, challengeEntityData[i]) - } - // Check of created document - const checkCount = await challengeService.getAllChallengeEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== challengeEntityData.length) - ) { - throw new Error( - 'initChallengeEntity: Created challenge type entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - challengeHash: challengeHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Challenge entities created', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initChallengeEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - 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 - await Promise.all( - challengeEntityData.map(async challengeEntity => { - await this._client.create(CHALLENGE_DOCTYPE, challengeEntity) - }) - ) - // Check of created document - const checkCount = await challengeService.getAllChallengeEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== challengeEntityData.length) - ) { - throw new Error( - 'initChallengeEntity: Created challenge entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - challengeHash: challengeHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Challenge entities updated', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initChallengeEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } else { - // Doctype already up to date - console.log( - '%c Initialization: Challenge Entity loaded', - 'background: #222; color: white' - ) - return { result: true, profile: null } - } - } - - public async initDuelEntity( - hash: string - ): Promise<{ - result: boolean - profile: Profile | null - }> { - const hashDuelEntity = hashFile(duelEntityData) - const duelService = new DuelService(this._client) - const profileService = new ProfileService(this._client) - - // Populate data if none DuelEntity exists - const loadedDuelTypes = await duelService.getAllDuelEntities() - if (!loadedDuelTypes || (loadedDuelTypes && loadedDuelTypes.length === 0)) { - // Populate the doctype with data - try { - for (let i = 0; i <= duelEntityData.length - 1; i++) { - await this._client.create(DUEL_DOCTYPE, duelEntityData[i]) - } - // Check of created document - const checkCount = await duelService.getAllDuelEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== duelEntityData.length) - ) { - throw new Error( - 'initDuelEntity: Created duel entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - duelHash: hashDuelEntity, - }) - if (updatedProfile) { - console.log( - '%c Initialization: UserDuel entities created', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initDuelEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - 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 - await Promise.all( - duelEntityData.map(async duelEntity => { - await this._client.create(DUEL_DOCTYPE, duelEntity) - }) - ) - // Check of created document - const checkCount = await duelService.getAllDuelEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== duelEntityData.length) - ) { - throw new Error( - 'initChallengeEntity: Created duel entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - duelHash: hashDuelEntity, - }) - if (updatedProfile) { - console.log( - '%c Initialization: UserDuel entities updated', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initDuelEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } else { - // Doctype already up to date - console.log( - '%c Initialization: Challenge Entity loaded', - 'background: #222; color: white' - ) - return { result: true, profile: null } - } - } - - public async initQuizEntity( - hash: string - ): Promise<{ - result: boolean - profile: Profile | null - }> { - const quizHash = hashFile(quizEntityData) - const quizService = new QuizService(this._client) - const profileService = new ProfileService(this._client) - - // Populate data if none quizEntity exists - const loadedQuizEntity = await quizService.getAllQuizEntities() - if ( - !loadedQuizEntity || - (loadedQuizEntity && loadedQuizEntity.length === 0) - ) { - // Populate the doctype with data - try { - for (let i = 0; i <= quizEntityData.length - 1; i++) { - await this._client.create(QUIZ_DOCTYPE, quizEntityData[i]) - } - // Check of created document - const checkCount = await quizService.getAllQuizEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== quizEntityData.length) - ) { - throw new Error( - 'initQuizEntity: Created quiz type entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - quizHash: quizHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Quiz entities created', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initQuizEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - 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 - await Promise.all( - quizEntityData.map(async quizEntity => { - await this._client.create(QUIZ_DOCTYPE, quizEntity) - }) - ) - // Check of created document - const checkCount = await quizService.getAllQuizEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== quizEntityData.length) - ) { - throw new Error( - 'initQuizEntity: Created quiz entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - quizHash: quizHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Quiz entities updated', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initQuizEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } else { - // Doctype already up to date - console.log( - '%c Initialization: Quiz Entity loaded', - 'background: #222; color: white' - ) - return { result: true, profile: null } - } - } - - public async initExplorationEntity( - hash: string - ): Promise<{ - result: boolean - profile: Profile | null - }> { - const explorationHash = hashFile(explorationEntityData) - const explorationService = new ExplorationService(this._client) - const profileService = new ProfileService(this._client) - - // Populate data if none explorationEntity exists - const loadedExplorationEntity = await explorationService.getAllExplorationEntities() - if ( - !loadedExplorationEntity || - (loadedExplorationEntity && loadedExplorationEntity.length === 0) - ) { - // Populate the doctype with data - try { - for (let i = 0; i <= explorationEntityData.length - 1; i++) { - await this._client.create( - EXPLORATION_DOCTYPE, - explorationEntityData[i] - ) - } - // Check of created document - const checkCount = await explorationService.getAllExplorationEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== explorationEntityData.length) - ) { - throw new Error( - 'initExplorationEntity: Created exploration type entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - explorationHash: explorationHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Exploration entities created', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initExplorationEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - 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 - await Promise.all( - explorationEntityData.map(async explorationEntity => { - await this._client.create(EXPLORATION_DOCTYPE, explorationEntity) - }) - ) - // Check of created document - const checkCount = await explorationService.getAllExplorationEntities() - if ( - !checkCount || - (checkCount && checkCount.length !== explorationEntityData.length) - ) { - throw new Error( - 'initExplorationEntity: Created exploration entities does not match' - ) - } - // Update profil with the hash - const updatedProfile = await profileService.updateProfile({ - explorationHash: explorationHash, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Exploration entities updated', - 'background: #222; color: white' - ) - return { - result: true, - profile: updatedProfile, - } - } else { - throw new Error('initExplorationEntity: Profile not updated') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } else { - // Doctype already up to date - console.log( - '%c Initialization: Exploration Entity loaded', - 'background: #222; color: white' - ) - return { result: true, profile: null } - } - } - - public async initAnalysis( - profile: Profile - ): Promise<{ - result: boolean - profile: Profile | null - }> { - try { - const actualAnalysisDate = getActualAnalysisDate() - if ( - profile.monthlyAnalysisDate && - actualAnalysisDate <= profile.monthlyAnalysisDate - ) { - return { result: true, profile: profile } - } else { - const profileService = new ProfileService(this._client) - const updatedProfile = await profileService.updateProfile({ - monthlyAnalysisDate: actualAnalysisDate, - haveSeenLastAnalysis: profile.isFirstConnection ? true : false, - }) - if (updatedProfile) { - console.log( - '%c Initialization: Analysis information from profile updated', - 'background: #222; color: white' - ) - return { result: true, profile: updatedProfile } - } else { - throw new Error('initAnalysis: Profile not updated') - } - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - /* - * Check if FluidTypes exist - * sucess return: FluidType[] - * failure throw error - */ - public async initFluidTypes(): Promise<FluidType[]> { - const kss = new KonnectorStatusService(this._client) - try { - const fluidtypes = await kss.getKonnectorAccountStatus() - if (fluidtypes) { - console.log( - '%c Initialization: Fluid Types loaded', - 'background: #222; color: white' - ) - return fluidtypes - } else { - throw new Error('initFluidTypes: FluidTypes not found') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - /* - * For each fluid get the trigger status and the last data date - * sucess return: FluidStatus[] - * failure throw error - */ - public async initFluidStatus(): Promise<FluidStatus[]> { - const fs = new FluidService(this._client) - try { - const fluidStatus = await fs.getFluidStatus() - if (fluidStatus) { - console.log( - '%c Initialization: Fluid Status loaded', - 'background: #222; color: white' - ) - return fluidStatus - } else { - throw new Error('initFluidStatus: fluidStatus not found') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - /* - * Build the userChallengeList - * sucess return: UserChallenge[] - * 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) { - console.log( - '%c Initialization: Challenges loaded', - 'background: #222; color: white' - ) - return userChallengeList - } else { - throw new Error('initUserChallenges: userChallengeList not found') - } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } - - /* - * Retrieve dataloads for ongoing duel - * sucess return: UserChallenge, Dataload[] - * failure throw error - */ - public async initDuelProgress( - userChallenge: UserChallenge - ): Promise<{ - updatedUserChallenge: UserChallenge - dataloads: Dataload[] - }> { - const challengeService = new ChallengeService(this._client) - const consumptionService = new ConsumptionDataManager(this._client) - try { - const dataloads: Dataload[] = await challengeService.getUserChallengeDataload( - userChallenge - ) - const userConsumption: number = getRoundFloat( - consumptionService.calculatePerformanceIndicatorValue(dataloads) - ) - const _userChallenge: UserChallenge = { - ...userChallenge, - duel: { - ...userChallenge.duel, - userConsumption: userConsumption, - }, - } - const updatedUserChallenge: UserChallenge = await challengeService.updateUserChallenge( - _userChallenge, - UserChallengeUpdateFlag.DUEL_CONSUMPTION - ) - return { updatedUserChallenge, dataloads } - } catch (error) { - console.log('Initialization error: ', error) - throw error - } - } -} +import { Client, Q, QueryDefinition } from 'cozy-client' +import { + ECOGESTURE_DOCTYPE, + PROFILE_DOCTYPE, + EGL_DAY_DOCTYPE, + EGL_MONTH_DOCTYPE, + EGL_YEAR_DOCTYPE, + ENEDIS_DAY_DOCTYPE, + ENEDIS_MINUTE_DOCTYPE, + ENEDIS_MONTH_DOCTYPE, + ENEDIS_YEAR_DOCTYPE, + GRDF_DAY_DOCTYPE, + GRDF_MONTH_DOCTYPE, + GRDF_YEAR_DOCTYPE, + CHALLENGE_DOCTYPE, + DUEL_DOCTYPE, + QUIZ_DOCTYPE, + EXPLORATION_DOCTYPE, +} from 'doctypes' + +import { FluidType } from 'enum/fluid.enum' +import { Dataload, FluidStatus, Profile, UserChallenge } from 'models' + +import EcogestureService from 'services/ecogesture.service' +import ChallengeService from 'services/challenge.service' +import ecogestureData from 'db/ecogestureData.json' +import challengeEntityData from 'db/challengeEntity.json' +import duelEntityData from 'db/duelEntity.json' +import quizEntityData from 'db/quizEntity.json' +import explorationEntityData from 'db/explorationEntity.json' + +import ProfileService from 'services/profile.service' +import profileData from 'db/profileData.json' +import KonnectorStatusService from 'services/konnectorStatus.service' +import KonnectorService from 'services/konnector.service' +import AccountService from 'services/account.service' +import FluidService from 'services/fluid.service' +import DuelService from 'services/duel.service' +import QuizService from 'services/quiz.service' +import ExplorationService from 'services/exploration.service' + +import { hashFile } from 'utils/hash' +import { getActualAnalysisDate } from 'utils/date' +import { TimeStep } from 'enum/timeStep.enum' +import ConsumptionDataManager from './consumption.service' +import { UserChallengeUpdateFlag } from 'enum/userChallenge.enum' +import { getRoundFloat } from 'utils/math' +import { DateTime } from 'luxon' + +export default class InitializationService { + private readonly _client: Client + + constructor(_client: Client) { + this._client = _client + } + + /* + * Call a query with where clause to create the index if not exist + */ + private async createIndex( + doctype: string, + timestep: TimeStep + ): Promise<object> { + const getMongoSelector = () => { + switch (timestep) { + case TimeStep.YEAR: + return { + year: { + $lte: 9999, + }, + } + case TimeStep.MONTH: + return { + year: { + $lte: 9999, + }, + month: { + $lte: 12, + }, + } + case TimeStep.DAY: + case TimeStep.HALF_AN_HOUR: + return { + year: { + $lte: 9999, + }, + month: { + $lte: 12, + }, + day: { + $lte: 31, + }, + } + default: + return {} + } + } + const query: QueryDefinition = Q(doctype) + .where(getMongoSelector()) + .limitBy(1) + return await this._client.query(query) + } + + /* + * create index for each Doctype + * sucess return: true + * failure throw error + */ + public async initIndex(): Promise<boolean> { + try { + const accountService = new AccountService(this._client) + const konnectorService = new KonnectorService(this._client) + await Promise.all([ + this.createIndex(EGL_YEAR_DOCTYPE, TimeStep.YEAR), + this.createIndex(EGL_MONTH_DOCTYPE, TimeStep.MONTH), + this.createIndex(EGL_DAY_DOCTYPE, TimeStep.DAY), + this.createIndex(ENEDIS_YEAR_DOCTYPE, TimeStep.YEAR), + this.createIndex(ENEDIS_MONTH_DOCTYPE, TimeStep.MONTH), + this.createIndex(ENEDIS_DAY_DOCTYPE, TimeStep.DAY), + this.createIndex(ENEDIS_MINUTE_DOCTYPE, TimeStep.HALF_AN_HOUR), + this.createIndex(GRDF_YEAR_DOCTYPE, TimeStep.YEAR), + this.createIndex(GRDF_MONTH_DOCTYPE, TimeStep.MONTH), + this.createIndex(GRDF_DAY_DOCTYPE, TimeStep.DAY), + konnectorService.createIndexKonnector(), + accountService.createIndexAccount(), + ]) + console.log( + '%c Initialization: Indexes created', + 'background: #222; color: white' + ) + return true + } catch (error) { + console.log('Initialization error - initIndex: ', error) + throw error + } + } + + /* + * Check if profil exist + * If not, the profil is created + * sucess return: profil + * 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, + profileData[0] + ) + if (newProfile) { + console.log( + '%c Initialization: Profile created', + 'background: #222; color: white' + ) + } else { + throw new Error('initProfile: Profile not created') + } + } else { + console.log( + '%c Initialization: Profile loaded', + 'background: #222; color: white' + ) + } + const updatedProfile = await profileService.updateProfile({ + lastConnectionDate: DateTime.local().setZone('utc', { + keepLocalTime: true, + }), + }) + return updatedProfile + } catch (error) { + console.log('Initialization error - initProfile: ', error) + throw error + } + } + + public async initEcogesture(hash: string): Promise<string> { + const hashEcogestureType = hashFile(ecogestureData) + const ecogestureService = new EcogestureService(this._client) + // Populate data if none ecogesture exists + const loadedEcogestures = await ecogestureService.getAllEcogestures() + if ( + !loadedEcogestures || + (loadedEcogestures && loadedEcogestures.length === 0) + ) { + // Populate the doctype with data + try { + for (let i = 0; i <= ecogestureData.length - 1; i++) { + await this._client.create(ECOGESTURE_DOCTYPE, ecogestureData[i]) + } + // Check of created document based on count + const checkCount = await ecogestureService.getAllEcogestures() + if ( + !checkCount || + (checkCount && checkCount.length !== ecogestureData.length) + ) { + throw new Error( + 'initEcogesture: Created ecogesture type entities does not match' + ) + } + console.log( + '%c Initialization: Ecogesture created', + 'background: #222; color: white' + ) + return hashEcogestureType + } catch (error) { + console.log('Initialization error - initEcogesture: ', error) + throw error + } + } + + // Update if the hash is not the same as the one from profile + if (hash !== hashEcogestureType) { + // Update the doctype + try { + // Deletion of all documents + await ecogestureService.deleteAllEcogestures() + // Population with the data + await Promise.all( + ecogestureData.map(async ecogesture => { + await this._client.create(ECOGESTURE_DOCTYPE, ecogesture) + }) + ) + // Check of created document based on count + const checkCount = await ecogestureService.getAllEcogestures() + if ( + !checkCount || + (checkCount && checkCount.length !== ecogestureData.length) + ) { + throw new Error( + 'initEcogesture: Created ecogesture type entities does not match' + ) + } + console.log( + '%c Initialization: Ecogesture updated', + 'background: #222; color: white' + ) + return hashEcogestureType + } catch (error) { + console.log('Initialization error - initEcogesture: ', error) + throw error + } + } else { + // Doctype already up to date + console.log( + '%c Initialization: Ecogesture loaded', + 'background: #222; color: white' + ) + return hashEcogestureType + } + } + + 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 && loadedChallengeEntity.length === 0) + ) { + // Populate the doctype with data + try { + for (let i = 0; i <= challengeEntityData.length - 1; i++) { + await this._client.create(CHALLENGE_DOCTYPE, challengeEntityData[i]) + } + + // Check of created document + const checkCount = await challengeService.getAllChallengeEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== challengeEntityData.length) + ) { + throw new Error( + 'initChallengeEntity: Created challenge entities does not match' + ) + } + console.log( + '%c Initialization: Challenge entities created', + 'background: #222; color: white' + ) + return challengeHash + } catch (error) { + console.log('Initialization error - initChallengeEntity: ', error) + 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 + await Promise.all( + challengeEntityData.map(async challengeEntity => { + await this._client.create(CHALLENGE_DOCTYPE, challengeEntity) + }) + ) + // Check of created document + const checkCount = await challengeService.getAllChallengeEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== challengeEntityData.length) + ) { + throw new Error( + 'initChallengeEntity: Created challenge entities does not match' + ) + } + console.log( + '%c Initialization: Challenge entities updated', + 'background: #222; color: white' + ) + return challengeHash + } catch (error) { + console.log('Initialization error - initChallengeEntity: ', error) + throw error + } + } else { + // Doctype already up to date + console.log( + '%c Initialization: Challenge Entity loaded', + 'background: #222; color: white' + ) + 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 && loadedDuelTypes.length === 0)) { + // Populate the doctype with data + try { + for (let i = 0; i <= duelEntityData.length - 1; i++) { + await this._client.create(DUEL_DOCTYPE, duelEntityData[i]) + } + // Check of created document + const checkCount = await duelService.getAllDuelEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== duelEntityData.length) + ) { + throw new Error( + 'initDuelEntity: Created duel entities does not match' + ) + } + console.log( + '%c Initialization: UserDuel entities created', + 'background: #222; color: white' + ) + return hashDuelEntity + } catch (error) { + console.log('Initialization error - initDuelEntity: ', error) + 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 + await Promise.all( + duelEntityData.map(async duelEntity => { + await this._client.create(DUEL_DOCTYPE, duelEntity) + }) + ) + // Check of created document + const checkCount = await duelService.getAllDuelEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== duelEntityData.length) + ) { + throw new Error( + 'initDuelEntity: Created duel entities does not match' + ) + } + console.log( + '%c Initialization: UserDuel entities updated', + 'background: #222; color: white' + ) + return hashDuelEntity + } catch (error) { + console.log('Initialization error - initDuelEntity: ', error) + throw error + } + } else { + // Doctype already up to date + console.log( + '%c Initialization: Challenge Entity loaded', + 'background: #222; color: white' + ) + 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 && loadedQuizEntity.length === 0) + ) { + // Populate the doctype with data + try { + for (let i = 0; i <= quizEntityData.length - 1; i++) { + await this._client.create(QUIZ_DOCTYPE, quizEntityData[i]) + } + // Check of created document + const checkCount = await quizService.getAllQuizEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== quizEntityData.length) + ) { + throw new Error( + 'initQuizEntity: Created quiz entities does not match' + ) + } + + console.log( + '%c Initialization: Quiz entities created', + 'background: #222; color: white' + ) + return quizHash + } catch (error) { + console.log('Initialization error - initQuizEntity: ', error) + 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 + await Promise.all( + quizEntityData.map(async quizEntity => { + await this._client.create(QUIZ_DOCTYPE, quizEntity) + }) + ) + // Check of created document + const checkCount = await quizService.getAllQuizEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== quizEntityData.length) + ) { + throw new Error( + 'initQuizEntity: Created quiz entities does not match' + ) + } + console.log( + '%c Initialization: Quiz entities updated', + 'background: #222; color: white' + ) + return quizHash + } catch (error) { + console.log('Initialization error - initQuizEntity: ', error) + throw error + } + } else { + // Doctype already up to date + console.log( + '%c Initialization: Quiz Entity loaded', + 'background: #222; color: white' + ) + 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 && loadedExplorationEntity.length === 0) + ) { + // Populate the doctype with data + try { + for (let i = 0; i <= explorationEntityData.length - 1; i++) { + await this._client.create( + EXPLORATION_DOCTYPE, + explorationEntityData[i] + ) + } + // Check of created document + const checkCount = await explorationService.getAllExplorationEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== explorationEntityData.length) + ) { + throw new Error( + 'initExplorationEntity: Created exploration entities does not match' + ) + } + console.log( + '%c Initialization: Exploration entities created', + 'background: #222; color: white' + ) + return explorationHash + } catch (error) { + console.log('Initialization error - initExplorationEntity: ', error) + 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 + await Promise.all( + explorationEntityData.map(async explorationEntity => { + await this._client.create(EXPLORATION_DOCTYPE, explorationEntity) + }) + ) + // Check of created document + const checkCount = await explorationService.getAllExplorationEntities() + if ( + !checkCount || + (checkCount && checkCount.length !== explorationEntityData.length) + ) { + throw new Error( + 'initExplorationEntity: Created exploration entities does not match' + ) + } + console.log( + '%c Initialization: Exploration entities updated', + 'background: #222; color: white' + ) + return explorationHash + } catch (error) { + console.log('Initialization error - initExplorationEntity: ', error) + throw error + } + } else { + // Doctype already up to date + console.log( + '%c Initialization: Exploration Entity loaded', + 'background: #222; color: white' + ) + return explorationHash + } + } + + public async initAnalysis( + profile: Profile + ): Promise<{ + monthlyAnalysisDate: DateTime + haveSeenLastAnalysis: boolean + }> { + try { + const actualAnalysisDate = getActualAnalysisDate() + if ( + profile.monthlyAnalysisDate && + actualAnalysisDate <= profile.monthlyAnalysisDate + ) { + return { + monthlyAnalysisDate: profile.monthlyAnalysisDate, + haveSeenLastAnalysis: profile.haveSeenLastAnalysis, + } + } else { + console.log( + '%c Initialization: Analysis information from profile updated', + 'background: #222; color: white' + ) + return { + monthlyAnalysisDate: actualAnalysisDate, + haveSeenLastAnalysis: profile.isFirstConnection ? true : false, + } + } + } catch (error) { + console.log('Initialization error - initAnalysis: ', error) + throw error + } + } + + /* + * Check if FluidTypes exist + * sucess return: FluidType[] + * failure throw error + */ + public async initFluidTypes(): Promise<FluidType[]> { + const kss = new KonnectorStatusService(this._client) + try { + const fluidtypes = await kss.getKonnectorAccountStatus() + if (fluidtypes) { + console.log( + '%c Initialization: Fluid Types loaded', + 'background: #222; color: white' + ) + return fluidtypes + } else { + throw new Error('initFluidTypes: FluidTypes not found') + } + } catch (error) { + console.log('Initialization error - initFluidTypes: ', error) + throw error + } + } + + /* + * For each fluid get the trigger status and the last data date + * sucess return: FluidStatus[] + * failure throw error + */ + public async initFluidStatus(): Promise<FluidStatus[]> { + const fs = new FluidService(this._client) + try { + const fluidStatus = await fs.getFluidStatus() + if (fluidStatus) { + console.log( + '%c Initialization: Fluid Status loaded', + 'background: #222; color: white' + ) + return fluidStatus + } else { + throw new Error('initFluidStatus: fluidStatus not found') + } + } catch (error) { + console.log('Initialization error - initFluidStatus: ', error) + throw error + } + } + + /* + * Build the userChallengeList + * sucess return: UserChallenge[] + * 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) { + console.log( + '%c Initialization: Challenges loaded', + 'background: #222; color: white' + ) + return userChallengeList + } else { + throw new Error('initUserChallenges: userChallengeList not found') + } + } catch (error) { + console.log('Initialization error - initUserChallenges: ', error) + throw error + } + } + + /* + * Retrieve dataloads for ongoing duel + * sucess return: UserChallenge, Dataload[] + * failure throw error + */ + public async initDuelProgress( + userChallenge: UserChallenge + ): Promise<{ + updatedUserChallenge: UserChallenge + dataloads: Dataload[] + }> { + const challengeService = new ChallengeService(this._client) + const consumptionService = new ConsumptionDataManager(this._client) + try { + const dataloads: Dataload[] = await challengeService.getUserChallengeDataload( + userChallenge + ) + const userConsumption: number = getRoundFloat( + consumptionService.calculatePerformanceIndicatorValue(dataloads) + ) + const _userChallenge: UserChallenge = { + ...userChallenge, + duel: { + ...userChallenge.duel, + userConsumption: userConsumption, + }, + } + const updatedUserChallenge: UserChallenge = await challengeService.updateUserChallenge( + _userChallenge, + UserChallengeUpdateFlag.DUEL_CONSUMPTION + ) + return { updatedUserChallenge, dataloads } + } catch (error) { + console.log('Initialization error: ', error) + throw error + } + } +} diff --git a/src/store/profile/profile.actions.ts b/src/store/profile/profile.actions.ts index a8bc3635b347a2017e89f46fb040631153c39f16..eb8ef46cf11cb28f11cb5c9d90a023f11f6d6e8f 100644 --- a/src/store/profile/profile.actions.ts +++ b/src/store/profile/profile.actions.ts @@ -1,36 +1,37 @@ -import { Client } from 'cozy-client' -import { Profile } from 'models' -import ProfileService from 'services/profile.service' -import { Dispatch } from 'react' -import { AppStore } from 'store' - -export const UPDATE_PROFILE = 'UPDATE_PROFILE' -export const SET_FIRST_CONNECTION = 'SET_FIRST_CONNECTION' - -interface UpdateProfile { - type: typeof UPDATE_PROFILE - payload?: Profile -} - -export type ProfileActionTypes = UpdateProfile - -export function updateProfileSuccess(updatedProfile: Profile): UpdateProfile { - return { - type: UPDATE_PROFILE, - payload: updatedProfile, - } -} - -export function updateProfile(upd: Partial<Profile>) { - return async ( - dispatch: Dispatch<UpdateProfile>, - getState: () => AppStore, - { client }: { client: Client } - ) => { - const profileService = new ProfileService(client) - const updatedProfile = await profileService.updateProfile(upd) - if (updatedProfile) { - dispatch(updateProfileSuccess(updatedProfile)) - } - } -} +import { Client } from 'cozy-client' +import { Profile } from 'models' +import ProfileService from 'services/profile.service' +import { Dispatch } from 'react' +import { AppStore } from 'store' + +export const UPDATE_PROFILE = 'UPDATE_PROFILE' +export const SET_FIRST_CONNECTION = 'SET_FIRST_CONNECTION' + +export interface UpdateProfile { + type: typeof UPDATE_PROFILE + payload?: Profile +} + +export type ProfileActionTypes = UpdateProfile + +export function updateProfileSuccess(updatedProfile: Profile): UpdateProfile { + return { + type: UPDATE_PROFILE, + payload: updatedProfile, + } +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function updateProfile(upd: Partial<Profile>): any { + return async ( + dispatch: Dispatch<UpdateProfile>, + getState: () => AppStore, + { client }: { client: Client } + ) => { + const profileService = new ProfileService(client) + const updatedProfile = await profileService.updateProfile(upd) + if (updatedProfile) { + dispatch(updateProfileSuccess(updatedProfile)) + } + } +}