diff --git a/src/components/Splash/SplashRoot.tsx b/src/components/Splash/SplashRoot.tsx index c1305efcb2f20b33b525a0a8bed4dc0d5bcf6024..12719870f0514736462054edce1161d25f721eaf 100644 --- a/src/components/Splash/SplashRoot.tsx +++ b/src/components/Splash/SplashRoot.tsx @@ -135,6 +135,7 @@ const SplashRoot = ({ setValidExploration(UserExplorationID.EXPLORATION007) const [ ecogestureHash, + fluidPricesHash, duelHash, quizHash, challengeHash, @@ -142,6 +143,7 @@ const SplashRoot = ({ analysisResult, ] = await Promise.all([ initializationService.initEcogesture(profile.ecogestureHash), + initializationService.initFluidPrices(profile.fluidPricesHash), initializationService.initDuelEntity(profile.duelHash), initializationService.initQuizEntity(profile.quizHash), initializationService.initExplorationEntity(profile.challengeHash), @@ -149,6 +151,7 @@ const SplashRoot = ({ initializationService.initAnalysis(profile), ]) profile.ecogestureHash = ecogestureHash + profile.fluidPricesHash = fluidPricesHash profile.duelHash = duelHash profile.quizHash = quizHash profile.challengeHash = challengeHash diff --git a/src/db/profileData.json b/src/db/profileData.json index bbd3db0efaa3bf6dc0e26b4e0a8dbd227e1fa0e5..27e8408d285fc5f4fab68729f99cdcdd9fab3fe1 100644 --- a/src/db/profileData.json +++ b/src/db/profileData.json @@ -5,6 +5,7 @@ "mailToken": "", "duelHash": "", "quizHash": "", + "fluidPricesHash": "", "isFirstConnection": true, "lastConnectionDate": "0000-01-01T00:00:00.000Z", "haveSeenLastAnalysis": true, @@ -13,6 +14,7 @@ "sendConsumptionAlert": false, "waterDailyConsumptionLimit": 0, "isProfileTypeCompleted": false, + "isProfileEcogestureCompleted": false, "onboarding": { "isWelcomeSeen": false }, diff --git a/src/migrations/migration.data.ts b/src/migrations/migration.data.ts index 014a1a104c81a6f43bf29d9679aed3f96ea62e6e..a99bd4e99eb31bb9d9ebb7b60e9c745cc1038df1 100644 --- a/src/migrations/migration.data.ts +++ b/src/migrations/migration.data.ts @@ -284,20 +284,12 @@ export const migrations: Migration[] = [ baseSchemaVersion: 8, targetSchemaVersion: 9, appVersion: '1.6.0', - description: 'Init new doctype fluidPrices', + description: 'Init new doctype fluidPrices --deprecated--', releaseNotes: null, docTypes: FLUIDPRICES_DOCTYPE, isCreate: true, run: async (_client: Client, docs: any[]): Promise<any> => { - if (docs.length > 0) { - console.log('collection already exists, delete all') - docs.forEach(async doc => { - await _client.destroy(doc) - }) - } - for (const fluidPrice of fluidsPricesData) { - await _client.create(FLUIDPRICES_DOCTYPE, fluidPrice) - } + return null }, }, { @@ -502,22 +494,12 @@ export const migrations: Migration[] = [ baseSchemaVersion: 17, targetSchemaVersion: 18, appVersion: '1.7.0', - description: 'Init new fluidPrices for water', + description: 'Init new fluidPrices for water -- deprecated --', releaseNotes: null, docTypes: FLUIDPRICES_DOCTYPE, run: async (_client: Client, docs: any[]): Promise<any> => { - const waterPricesData: any[] = fluidsPricesData.filter(fluidPriceData => { - return fluidPriceData.fluidType === FluidType.WATER - }) - const createWaterPricesData = waterPricesData.map( - (waterPriceData: any) => { - waterPriceData.createAction = true - waterPriceData.doctype = FLUIDPRICES_DOCTYPE - return waterPriceData - } - ) - return createWaterPricesData + return null }, }, { diff --git a/src/models/profile.model.ts b/src/models/profile.model.ts index 5f17419398b7ae090182b69fb60ada24eab9052a..3073cdd0e0d4b82db6bb977153fb7567e70ba1e6 100644 --- a/src/models/profile.model.ts +++ b/src/models/profile.model.ts @@ -11,6 +11,7 @@ export interface ProfileEntity { duelHash: string quizHash: string explorationHash: string + fluidPricesHash: string isFirstConnection: boolean lastConnectionDate: string haveSeenLastAnalysis: boolean diff --git a/src/services/fluidsPrices.service.ts b/src/services/fluidsPrices.service.ts index 2304a2f51321eec5319cf2303014ec02f0eaa422..9fc5be5abca5accffab8024797ec4054f44e53f4 100644 --- a/src/services/fluidsPrices.service.ts +++ b/src/services/fluidsPrices.service.ts @@ -94,8 +94,8 @@ export default class FluidPricesService { public async deleteAllFluidsPrices(): Promise<boolean> { const fluidsPrices = await this.getAllPrices() try { - for (let index = 0; index < fluidsPrices.length; index++) { - await this._client.destroy(fluidsPrices[index]) + for (const price of fluidsPrices) { + await this._client.destroy(price) } return true } catch (error) { diff --git a/src/services/initialization.service.spec.ts b/src/services/initialization.service.spec.ts index a79575e9f92b65cae32a8b509c4f6588fbed99cd..ea766400e86cbc57ca569cb0db43f60fd1852d8d 100644 --- a/src/services/initialization.service.spec.ts +++ b/src/services/initialization.service.spec.ts @@ -9,6 +9,7 @@ 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 fluidPrices from 'db/fluidPrices.json' import { hashFile } from 'utils/hash' import { getActualAnalysisDate } from 'utils/date' @@ -22,6 +23,7 @@ import { allChallengeEntityData } from '../../tests/__mocks__/challengeEntity.mo import { allDuelEntity } from '../../tests/__mocks__/duelData.mock' import { allQuizEntities } from '../../tests/__mocks__/quizData.mock' import { allExplorationEntities } from '../../tests/__mocks__/explorationData.mock' +import { fluidPrices as allFluidPrices } from '../../tests/__mocks__/fluidPrice.mock' import { mockOutdatedTerm, mockUpToDateTerm, @@ -84,6 +86,17 @@ jest.mock('./challenge.service', () => { }) }) +const mockGetAllPrices = jest.fn() +const mockDeleteAllFluidsPrices = jest.fn() +jest.mock('./fluidsPrices.service', () => { + return jest.fn(() => { + return { + getAllPrices: mockGetAllPrices, + deleteAllFluidsPrices: mockDeleteAllFluidsPrices, + } + }) +}) + const mockGetAllDuelEntities = jest.fn() const mockDeleteAllDuelEntities = jest.fn() jest.mock('./duel.service', () => { @@ -340,6 +353,106 @@ describe('Initialization service', () => { }) }) + describe('initFluidPrices method', () => { + beforeEach(() => { + mockGetAllPrices.mockClear() + mockDeleteAllFluidsPrices.mockClear() + }) + it('should return hash when fluidPrices hash is already up to date', async () => { + mockGetAllPrices.mockResolvedValueOnce(fluidPrices) + const hash = hashFile(fluidPrices) + await expect( + initializationService.initFluidPrices(hash) + ).resolves.toEqual(hash) + }) + it('should return hash when fluidPrices are created', async () => { + mockGetAllPrices + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(fluidPrices) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + const hash = hashFile(fluidPrices) + await expect( + initializationService.initFluidPrices(hash) + ).resolves.toEqual(hash) + }) + it('should throw an error when fluidPrices entities should be created and created fluidPrices entities number does not match', async () => { + mockGetAllPrices + .mockResolvedValueOnce(null) + .mockResolvedValueOnce(allFluidPrices) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect( + initializationService.initFluidPrices(hashFile(fluidPrices)) + ).rejects.toThrow( + new Error( + 'initFluidPrices: Created fluidPrices type entities does not match' + ) + ) + }) + it('should throw an error when fluidPrices should be created and creation failed', async () => { + mockGetAllPrices.mockResolvedValueOnce(null) + mockClient.create.mockRejectedValue(new Error()) + await expect( + initializationService.initChallengeEntity(hashFile(fluidPrices)) + ).rejects.toThrow(new Error()) + }) + it('should return hash when fluidPrices are updated', async () => { + mockGetAllPrices + .mockResolvedValueOnce(fluidPrices) + .mockResolvedValueOnce(fluidPrices) + mockDeleteAllFluidsPrices.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initFluidPrices('')).resolves.toEqual( + hashFile(fluidPrices) + ) + }) + it('should throw an error when fluidPrices should be updated and fluidPrices entities number does not match', async () => { + mockGetAllPrices + .mockResolvedValueOnce('') + .mockResolvedValueOnce(allFluidPrices) + mockDeleteAllFluidsPrices.mockResolvedValue(true) + const mockQueryResult: QueryResult<boolean> = { + data: true, + bookmark: '', + next: false, + skip: 0, + } + mockClient.create.mockResolvedValue(mockQueryResult) + await expect(initializationService.initFluidPrices('')).rejects.toThrow( + new Error( + 'initFluidPrices: Created fluidPrices type entities does not match' + ) + ) + }) + it('should throw an error whenfluidPrices should be updated and fluidPrices entities creation failed', async () => { + mockGetAllPrices + .mockResolvedValueOnce(fluidPrices) + .mockResolvedValueOnce(fluidPrices) + mockDeleteAllFluidsPrices.mockResolvedValue(true) + mockClient.create.mockRejectedValueOnce(new Error()) + expect(initializationService.initFluidPrices('')).rejects.toThrow( + new Error() + ) + }) + }) + describe('initChallengeEntity method', () => { beforeEach(() => { mockGetAllChallengeEntities.mockClear() diff --git a/src/services/initialization.service.ts b/src/services/initialization.service.ts index 923c9836d95a51d89a0bd91de10f9785cc8724f5..6c606a1ae7da8d663185b90d9ae56916856a768b 100644 --- a/src/services/initialization.service.ts +++ b/src/services/initialization.service.ts @@ -16,6 +16,7 @@ import { DUEL_DOCTYPE, QUIZ_DOCTYPE, EXPLORATION_DOCTYPE, + FLUIDPRICES_DOCTYPE, } from 'doctypes' import { FluidType } from 'enum/fluid.enum' @@ -35,6 +36,7 @@ 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 fluidPrices from 'db/fluidPrices.json' import ProfileService from 'services/profile.service' import profileData from 'db/profileData.json' @@ -58,6 +60,7 @@ import TermsService from './terms.service' import log from 'utils/logger' import { ProfileEcogesture } from 'models/profileEcogesture.model' import ProfileEcogestureService from './profileEcogesture.service' +import FluidPricesService from './fluidsPrices.service' export default class InitializationService { private readonly _client: Client @@ -287,6 +290,69 @@ export default class InitializationService { } } + public async initFluidPrices(hash: string): Promise<string> { + const hashFluidPricesType = hashFile(fluidPrices) + const fpService = new FluidPricesService(this._client) + // Populate data if none ecogesture exists + const loadedPrices = await fpService.getAllPrices() + if (!loadedPrices || (loadedPrices && loadedPrices.length === 0)) { + // Populate the doctype with data + + try { + for (const price of fluidPrices) { + await this._client.create(FLUIDPRICES_DOCTYPE, price) + } + // Check of created document based on count + const checkCount = await fpService.getAllPrices() + if ( + !checkCount || + (checkCount && checkCount.length !== fluidPrices.length) + ) { + throw new Error( + 'initFluidPrices: Created fluidPrices type entities does not match' + ) + } + log.info('[Initialization] FluidPrices list created') + return hashFluidPricesType + } catch (error) { + log.error('Initialization error - initFluidPrices: ', error) + throw error + } + } + // Update if the hash is not the same as the one from profile + if (hash !== hashFluidPricesType) { + // Update the doctype + try { + // Deletion of all documents + await fpService.deleteAllFluidsPrices() + // Population with the data + for (const price of fluidPrices) { + await this._client.create(FLUIDPRICES_DOCTYPE, price) + } + // Check of created document based on count + const checkCount = await fpService.getAllPrices() + + if ( + !checkCount || + (checkCount && checkCount.length !== fluidPrices.length) + ) { + throw new Error( + 'initFluidPrices: Created fluidPrices type entities does not match' + ) + } + log.info('[Initialization] FluidPrices updated') + return hashFluidPricesType + } catch (error) { + log.error('Initialization error - initFluidPrices: ', error) + throw error + } + } else { + // Doctype already up to date + log.info('[Initialization] FluidPrices already up-to-date') + return hashFluidPricesType + } + } + public async initChallengeEntity(hash: string): Promise<string> { const challengeHash = hashFile(challengeEntityData) const challengeService = new ChallengeService(this._client) diff --git a/tests/__mocks__/profile.mock.ts b/tests/__mocks__/profile.mock.ts index aaee9099c64830813737693e0113150e2419adf6..57ea3c6099ffa237f363c284d2109dd395f8024d 100644 --- a/tests/__mocks__/profile.mock.ts +++ b/tests/__mocks__/profile.mock.ts @@ -6,6 +6,7 @@ export const profileData: Profile = { _rev: '16-57473da4fc26315247c217083175dfa0', id: '4d9403218ef13e65b2e3a8ad1700bc41', ecogestureHash: '9798a0aaccb47cff906fc4931a2eff5f9371dd8b', + fluidPricesHash: '9798a0aaccb47cff906fc4931a2eff5f9371dd8a', challengeHash: '1136feb6185c7643e071d14180c0e95782aa4ba3', duelHash: '1136feb6185c7643e071d14180c0e95782aa4ba3', quizHash: '1136feb6185c7643e071d14180c0e95782aa4ba3',