diff --git a/.env.example b/.env.example index 50d0bf5..9468085 100644 --- a/.env.example +++ b/.env.example @@ -105,7 +105,7 @@ FITBIT_SUBSCRIBER_ID=SUBSCRIBER_ID_HERE ################################################################################################# # EXPRESSION_AUTO_SYNC Frequency time that the application will sync the users data in -# background, according with the time format defined on node-cron library. +# background according to the crontab expression. # For example, the value 0 0 * * 0, means that the sync it will occurs # every sunday at 00:00. # default value: 0 0 * * 0 diff --git a/README.md b/README.md index 571be50..b0a349d 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ Application settings are defined by environment variables. To define the setting | `FITBIT_CLIENT_SECRET` | Client Secret for Fitbit Application resposible to manage user data. | `CIENT_SECRET_HERE` | | `FITBIT_CLIENT_SUBSCRIBER` | Client Subscriber code for automatically get notification from new sync data. | `CLIENT_SUBSCRIBER_HERE` | | `FITBIT_SUBSCRIBER_ID` | Customer Subscriber ID, used to manage the subscriber who will receive notification of a user resource. | `FITBIT_SUBSCRIBER_ID` | +| `EXPRESSION_AUTO_SYNC` | Defines how often the application will automatically sync user data in the background according to the crontab expression. | `0 0 * * 0` | ## Generate Certificates For development and testing environments the easiest and fastest way is to generate your own self-signed certificates. These certificates can be used to encrypt data as well as certificates signed by a CA, but users will receive a warning that the certificate is not trusted for their computer or browser. Therefore, self-signed certificates should only be used in non-production environments, that is, development and testing environments. To do this, run the `create-self-signed-certs.sh` script in the root of the repository. @@ -120,6 +121,7 @@ docker run --rm \ -e FITBIT_CLIENT_SECRET="YOUR_FITBIT_CLIENT_SECRET" \ -e FITBIT_CLIENT_SUBSCRIBER="YOUR_FITBIT_CLIENT_SUBSCRIBER" \ -e FITBIT_SUBSCRIBER_ID="SUBSCRIBER_ID_HERE" \ + -e EXPRESSION_AUTO_SYNC="0 0 * * 0" \ ocariot/ds-agent ``` If the MongoDB or RabbitMQ instance is in the host local, add the `--net=host` statement when creating the container, this will cause the docker container to communicate with its local host. @@ -132,6 +134,7 @@ docker run --rm \ -e FITBIT_CLIENT_SECRET="YOUR_FITBIT_CLIENT_SECRET" \ -e FITBIT_CLIENT_SUBSCRIBER="YOUR_FITBIT_CLIENT_SUBSCRIBER" \ -e FITBIT_SUBSCRIBER_ID="SUBSCRIBER_ID_HERE" \ + -e EXPRESSION_AUTO_SYNC="0 0 * * 0" \ ocariot/ds-agent ``` To generate your own docker image, run the following command: diff --git a/package-lock.json b/package-lock.json index 5d68a1b..a218727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "data-sync-agent", - "version": "1.2.0", + "version": "1.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -192,9 +192,9 @@ } }, "@types/chai": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.2.tgz", - "integrity": "sha512-8V2aCcPM3WLuJvJpF6N60uUvdZb7NHjpjQlLk1QmZbTP4XZET/FX0c3TJ3K7qt4L98FS1Pifa8BVofTVuJFWVA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.3.tgz", + "integrity": "sha512-VRw2xEGbll3ZiTQ4J02/hUjNqZoue1bMhoo2dgM2LXjDdyaq4q80HgBDHwpI0/VKlo4Eg+BavyQMv/NYgTetzA==", "dev": true }, "@types/connect": { @@ -261,9 +261,9 @@ "dev": true }, "@types/mongodb": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.1.tgz", - "integrity": "sha512-Va7o1fN3zeabmIJSQ6yuAWkqPvrT38HSTIi4YbVOb2UL7FJ4diXrWt+OUuuEFWAVPtF9VZV5h+7LDYdzgXWgQA==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.3.2.tgz", + "integrity": "sha512-YTTzii2ECx+4R8kzKchydG905z3gb+lzrizeC4s8SAGan7/22bDDJ6nN6r9mueJGRGrCnFo1QBAI/Wz45lETZQ==", "dev": true, "requires": { "@types/bson": "*", @@ -7400,9 +7400,9 @@ "integrity": "sha512-lK9oNNb9HAz2HJIg6+cYZszbYC/0CTm0nWi0XX2AjEbVgu43smbW2/h5zn7yAjYstPJBaYXkt5g6ABRpO/Ncfg==" }, "swagger-ui-express": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.0.tgz", - "integrity": "sha512-sCP7SxIfWr8DAK46RZXaplJIe+1h0cOFIrUw+Jx8vSstlppe7sNR8mFOyDlBwVsqAx6K2BAhKnK88Hkkj28pqw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.1.tgz", + "integrity": "sha512-PDt7A4JZYszP2/AcnsYj24u1RwVRV3/8LvCZSL6EcxKq918OBhS1e7NHqS+2woBHi0zn8HjeiJVxXDBKVxgwYw==", "requires": { "swagger-ui-dist": "^3.18.1" } diff --git a/package.json b/package.json index f5547c7..058a562 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-sync-agent", - "version": "1.2.0", + "version": "1.3.0", "description": "Service responsible for data synchronization of FitBit and CVE platform with OCARIoT platform.", "main": "dist/server.js", "scripts": { diff --git a/src/background/task/collect.fitbit.user.data.task.ts b/src/background/task/collect.fitbit.user.data.task.ts index e8ccfcb..2fe5c4c 100644 --- a/src/background/task/collect.fitbit.user.data.task.ts +++ b/src/background/task/collect.fitbit.user.data.task.ts @@ -1,54 +1,50 @@ import { inject, injectable } from 'inversify' import { Identifier } from '../../di/identifiers' import { ILogger } from '../../utils/custom.logger' -import { IConnectionDB } from '../../infrastructure/port/connection.db.interface' import { IBackgroundTask } from '../../application/port/background.task.interface' import { IFitbitDataRepository } from '../../application/port/fitbit.auth.data.repository.interface' import { IUserAuthDataRepository } from '../../application/port/user.auth.data.repository.interface' import { Query } from '../../infrastructure/repository/query/query' import { UserAuthData } from '../../application/domain/model/user.auth.data' import cron from 'node-cron' -import moment from 'moment' @injectable() export class CollectFitbitUserDataTask implements IBackgroundTask { private schedule: any constructor( - @inject(Identifier.MONGODB_CONNECTION) private readonly _mongodb: IConnectionDB, @inject(Identifier.FITBIT_DATA_REPOSITORY) private readonly _fitbitAuthDataRepo: IFitbitDataRepository, @inject(Identifier.USER_AUTH_DATA_REPOSITORY) private readonly _userAuthDataRepo: IUserAuthDataRepository, @inject(Identifier.LOGGER) private readonly _logger: ILogger ) { - this.createSchedule(`${process.env.EXPRESSION_AUTO_SYNC}`) + this.schedule = cron.schedule(`${process.env.EXPRESSION_AUTO_SYNC}`, () => this.getFitbitUsersData()) } public async run(): Promise { - this._mongodb.eventConnection.on('connected', async () => { - this.schedule.start() - }) + this.schedule.start() } - public async createSchedule(expression: string): Promise { - this.schedule = cron.schedule(expression, async () => { - console.log('running a task every minute -', moment().format('HH:mm:ss')) - this.getFitbitUsersData() - }) - } + private getFitbitUsersData(): void { + const query = new Query() + query.filters = { 'fitbit.is_valid': true } - private async getFitbitUsersData(): Promise { - return new Promise(async (resolve, reject) => { - const usersData: Array = await this._userAuthDataRepo.find(new Query()) - for await (const data of usersData) { - this._fitbitAuthDataRepo.syncFitbitUserData(data.fitbit!, data.fitbit!.last_sync!, 1, data.user_id!) - .then(() => this._logger.info(`Data from ${data.user_id} successful synchronized!`)) - .catch(err => this._logger.error(err.message)) - } - }) + this._userAuthDataRepo + .find(query) + .then((usersData: Array) => { + for (const data of usersData) { + this._fitbitAuthDataRepo + .syncFitbitUserData(data.fitbit!, data.fitbit!.last_sync!, 1, data.user_id!) + .then(() => this._logger.info(`Fitbit sync task for child ${data.user_id} finished!`)) + .catch(err => this._logger.error(err.message)) + } + }) + .catch(err => { + this._logger.error(`An error occurred while performing Fitbit sync. ${err.message}`) + }) } public stop(): Promise { - this.schedule.stop() + this.schedule.destroy() return Promise.resolve() } } diff --git a/test/unit/repositories/fitbit.data.repository.spec.ts b/test/unit/repositories/fitbit.data.repository.spec.ts index 34f9b43..2dcf114 100644 --- a/test/unit/repositories/fitbit.data.repository.spec.ts +++ b/test/unit/repositories/fitbit.data.repository.spec.ts @@ -10,7 +10,6 @@ import { DefaultEntityMock } from '../../mocks/models/default.entity.mock' import { UserAuthData } from '../../../src/application/domain/model/user.auth.data' import { assert } from 'chai' import sinon from 'sinon' -import jwt from 'jsonwebtoken' require('sinon-mongoose') @@ -192,7 +191,6 @@ describe('Repositories: FitbitDataRepository', () => { describe('syncFitbitUserData', () => { context('when token is expired', () => { it('should reject an error', () => { - console.log(generateTokenWithoutScope('none')) data.fitbit!.access_token = 'expired' sinon .mock(modelFake) @@ -292,9 +290,9 @@ describe('Repositories: FitbitDataRepository', () => { }) }) -function generateTokenWithoutScope(scope: string): any { - const payload: any = { ...DefaultEntityMock.PAYLOAD } - payload.scopes = payload.scopes === 'none' ? '' : - payload.scopes.split(' ').filter(item => item !== scope).join(' ') - return jwt.sign(payload, 'shhhhh') -} +// function generateTokenWithoutScope(scope: string): any { +// const payload: any = { ...DefaultEntityMock.PAYLOAD } +// payload.scopes = payload.scopes === 'none' ? '' : +// payload.scopes.split(' ').filter(item => item !== scope).join(' ') +// return jwt.sign(payload, 'shhhhh') +// }