From 506f351d36e0446af1e3a7f4e25fdfb13ce1abe7 Mon Sep 17 00:00:00 2001 From: Henry Tao Date: Fri, 28 Aug 2020 14:34:34 -0400 Subject: [PATCH] Consume new endpoint to return last 14 days keys after onboarding --- .env | 3 +-- .../BackendService/BackendService.spec.ts | 25 ++++++++++++++++++- src/services/BackendService/BackendService.ts | 12 ++++++--- .../ExposureNotificationService.spec.ts | 8 +++--- .../ExposureNotificationService.ts | 25 +++++++++++-------- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/.env b/.env index 1d054a53..c24c1cc8 100644 --- a/.env +++ b/.env @@ -6,8 +6,7 @@ APP_VERSION_NAME=1.0 SUBMIT_URL=https://submission.covidshield.app RETRIEVE_URL=https://retrieval.covidshield.app HMAC_KEY=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -REGION=302 -MCC_CODE=302; +REGION=302; TRANSMISSION_RISK_LEVEL=2 MINIMUM_FETCH_INTERVAL=15 diff --git a/src/services/BackendService/BackendService.spec.ts b/src/services/BackendService/BackendService.spec.ts index 94c81a32..495ee3fd 100644 --- a/src/services/BackendService/BackendService.spec.ts +++ b/src/services/BackendService/BackendService.spec.ts @@ -1,3 +1,4 @@ +import {downloadDiagnosisKeysFile} from '../../bridge/CovidShield'; import {TemporaryExposureKey} from '../../bridge/ExposureNotification'; import {BackendService} from './BackendService'; @@ -89,7 +90,7 @@ describe('BackendService', () => { describe('reportDiagnosisKeys', () => { it('returns last 14 keys if there is more than 14', async () => { - const backendService = new BackendService('http://localhost', 'https://localhost', 'mock', 0); + const backendService = new BackendService('http://localhost', 'https://localhost', 'mock', 302); const keys = generateRandomKeys(20); await backendService.reportDiagnosisKeys( @@ -115,4 +116,26 @@ describe('BackendService', () => { }); }); }); + + describe('retrieveDiagnosisKeys', () => { + it('returns keys file for set period', async () => { + const backendService = new BackendService('http://localhost', 'https://localhost', 'mock', 302); + + await backendService.retrieveDiagnosisKeys(18457); + + expect(downloadDiagnosisKeysFile).toHaveBeenCalledWith( + 'http://localhost/retrieve/302/18457/c4d9820c20f7073e47f54cc1bd24475fb98c8ab9ffc0ea81dded3f8ebfb48b67', + ); + }); + + it('returns keys file for 14 days if period is 0', async () => { + const backendService = new BackendService('http://localhost', 'https://localhost', 'mock', 302); + + await backendService.retrieveDiagnosisKeys(0); + + expect(downloadDiagnosisKeysFile).toHaveBeenCalledWith( + 'http://localhost/retrieve/302/00000/ca365ad512568f4292953403590b398e3f1336efcb4f1acedb20926c35408bd9', + ); + }); + }); }); diff --git a/src/services/BackendService/BackendService.ts b/src/services/BackendService/BackendService.ts index 56ce2bc9..3b4f1759 100644 --- a/src/services/BackendService/BackendService.ts +++ b/src/services/BackendService/BackendService.ts @@ -6,7 +6,7 @@ import {TemporaryExposureKey} from 'bridge/ExposureNotification'; import nacl from 'tweetnacl'; import {getRandomBytes, downloadDiagnosisKeysFile} from 'bridge/CovidShield'; import {blobFetch} from 'shared/fetch'; -import {TRANSMISSION_RISK_LEVEL, REGION} from 'env'; +import {TRANSMISSION_RISK_LEVEL} from 'env'; import {captureMessage} from 'shared/log'; import {covidshield} from './covidshield'; @@ -15,6 +15,9 @@ import {BackendInterface, SubmissionKeySet} from './types'; const MAX_UPLOAD_KEYS = 14; const FETCH_HEADERS = {headers: {'Cache-Control': 'no-store'}}; +// See https://github.com/cds-snc/covid-shield-server/pull/176 +const LAST_14_DAYS_PERIOD = '00000'; + export class BackendService implements BackendInterface { retrieveUrl: string; submitUrl: string; @@ -29,15 +32,16 @@ export class BackendService implements BackendInterface { } async retrieveDiagnosisKeys(period: number) { - const message = `${this.region}:${period}:${Math.floor(Date.now() / 1000 / 3600)}`; + const periodStr = `${period > 0 ? period : LAST_14_DAYS_PERIOD}`; + const message = `${this.region}:${periodStr}:${Math.floor(Date.now() / 1000 / 3600)}`; const hmac = hmac256(message, encHex.parse(this.hmacKey)).toString(encHex); - const url = `${this.retrieveUrl}/retrieve/${this.region}/${period}/${hmac}`; + const url = `${this.retrieveUrl}/retrieve/${this.region}/${periodStr}/${hmac}`; captureMessage('retrieveDiagnosisKeys', {period, url}); return downloadDiagnosisKeysFile(url); } async getExposureConfiguration() { - const url = `${this.retrieveUrl}/exposure-configuration/${REGION}.json`; + const url = `${this.retrieveUrl}/exposure-configuration/${this.region}.json`; captureMessage('getExposureConfiguration', {url}); return (await fetch(url, FETCH_HEADERS)).json(); } diff --git a/src/services/ExposureNotificationService/ExposureNotificationService.spec.ts b/src/services/ExposureNotificationService/ExposureNotificationService.spec.ts index 7f1624ea..0d000dad 100644 --- a/src/services/ExposureNotificationService/ExposureNotificationService.spec.ts +++ b/src/services/ExposureNotificationService/ExposureNotificationService.spec.ts @@ -82,7 +82,7 @@ describe('ExposureNotificationService', () => { .mockImplementation((args: any) => new OriginalDate(args)); await service.updateExposureStatus(); - expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(14); + expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(1); }); it('backfills the right amount of keys for current day', async () => { @@ -98,7 +98,7 @@ describe('ExposureNotificationService', () => { }, }); await service.updateExposureStatus(); - expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(0); + expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(1); server.retrieveDiagnosisKeys.mockClear(); @@ -109,7 +109,7 @@ describe('ExposureNotificationService', () => { }, }); await service.updateExposureStatus(); - expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(1); + expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(2); server.retrieveDiagnosisKeys.mockClear(); @@ -120,7 +120,7 @@ describe('ExposureNotificationService', () => { }, }); await service.updateExposureStatus(); - expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(2); + expect(server.retrieveDiagnosisKeys).toHaveBeenCalledTimes(3); }); it('serializes status update', async () => { diff --git a/src/services/ExposureNotificationService/ExposureNotificationService.ts b/src/services/ExposureNotificationService/ExposureNotificationService.ts index a4d5706e..0423b9ea 100644 --- a/src/services/ExposureNotificationService/ExposureNotificationService.ts +++ b/src/services/ExposureNotificationService/ExposureNotificationService.ts @@ -1,4 +1,3 @@ -import {Platform} from 'react-native'; import ExposureNotification, { ExposureSummary, Status as SystemStatus, @@ -263,10 +262,20 @@ export class ExposureNotificationService { ): AsyncGenerator<{keysFileUrl: string; period: number} | null> { const runningDate = new Date(); - const lastCheckedPeriod = - _lastCheckedPeriod || periodSinceEpoch(addDays(runningDate, -EXPOSURE_NOTIFICATION_CYCLE), HOURS_PER_PERIOD); let runningPeriod = periodSinceEpoch(runningDate, HOURS_PER_PERIOD); + if (!_lastCheckedPeriod) { + try { + const keysFileUrl = await this.backendInterface.retrieveDiagnosisKeys(0); + yield {keysFileUrl, period: runningPeriod}; + } catch (error) { + captureException('Error while downloading batch files', error); + } + return; + } + + const lastCheckedPeriod = Math.max(_lastCheckedPeriod - 1, runningPeriod - EXPOSURE_NOTIFICATION_CYCLE); + while (runningPeriod > lastCheckedPeriod) { try { const keysFileUrl = await this.backendInterface.retrieveDiagnosisKeys(runningPeriod); @@ -339,12 +348,7 @@ export class ExposureNotificationService { if (!value) continue; const {keysFileUrl, period} = value; keysFileUrls.push(keysFileUrl); - - // Temporarily disable persisting lastCheckPeriod on Android - // Ref https://github.com/cds-snc/covid-shield-mobile/issues/453 - if (Platform.OS !== 'android') { - lastCheckedPeriod = Math.max(lastCheckedPeriod || 0, period); - } + lastCheckedPeriod = Math.max(lastCheckedPeriod || 0, period); } captureMessage('performExposureStatusUpdate', { @@ -365,11 +369,12 @@ export class ExposureNotificationService { lastCheckedPeriod, ); } + return finalize({}, lastCheckedPeriod); } catch (error) { captureException('performExposureStatusUpdate', error); } - return finalize({}, lastCheckedPeriod); + return finalize(); } private async processPendingExposureSummary() {