Skip to content

Commit

Permalink
Merge branch 'hotfix/1.5.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
douglasrafael committed Jan 29, 2020
2 parents e77c3cd + 33cc3b5 commit ac9a7fd
Show file tree
Hide file tree
Showing 24 changed files with 1,210 additions and 766 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Application settings are defined by environment variables. To define the setting
| `RABBITMQ_CA_PATH` | RabbitMQ CA file location. Must always be provided when using `amqps` protocol. | `.certs/rabbitmqca.crt` |
| `FITBIT_CLIENT_ID` | Client Id for Fitbit Application resposible to manage user data. | `CIENT_ID_HERE` |
| `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_CLIENT_SUBSCRIBER` | Code used by Fitbit to verify the subscriber. | `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` |

Expand Down
1,301 changes: 761 additions & 540 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "data-sync-agent",
"version": "1.5.5",
"version": "1.5.6",
"description": "Service responsible for data synchronization of FitBit and CVE platform with OCARIoT platform.",
"main": "dist/server.js",
"scripts": {
Expand Down Expand Up @@ -53,8 +53,8 @@
"all": true
},
"dependencies": {
"@ocariot/rabbitmq-client-node": "^1.4.0",
"axios": "^0.19.0",
"@ocariot/rabbitmq-client-node": "1.4.0",
"axios": "^0.19.2",
"body-parser": "^1.19.0",
"bull": "^3.12.1",
"dotenv": "^8.2.0",
Expand All @@ -65,38 +65,38 @@
"inversify-express-utils": "^6.3.2",
"jsonwebtoken": "^8.5.1",
"moment": "^2.24.0",
"mongoose": "^5.7.13",
"mongoose": "5.8.2",
"morgan": "^1.9.1",
"node-cron": "^2.0.3",
"query-strings-parser": "^2.1.3",
"reflect-metadata": "^0.1.13",
"swagger-ui-express": "^4.1.2",
"swagger-ui-express": "^4.1.3",
"winston": "^3.2.1",
"winston-daily-rotate-file": "^4.3.0"
"winston-daily-rotate-file": "^4.4.2"
},
"devDependencies": {
"@types/body-parser": "^1.17.1",
"@types/bull": "^3.10.6",
"@types/chai": "^4.2.5",
"@types/chai": "^4.2.7",
"@types/express": "^4.17.2",
"@types/helmet": "^0.0.45",
"@types/mocha": "^5.2.7",
"@types/mongoose": "^5.5.32",
"@types/mongoose": "^5.5.43",
"@types/morgan": "^1.7.37",
"@types/swagger-ui-express": "^4.1.0",
"@types/swagger-ui-express": "^4.1.1",
"chai": "^4.2.0",
"gulp": "^4.0.2",
"gulp-nodemon": "^2.4.2",
"gulp-tslint": "^8.1.4",
"gulp-typescript": "^5.0.1",
"mocha": "^6.2.2",
"nyc": "^14.1.1",
"nyc": "^15.0.0",
"sinon": "^7.5.0",
"sinon-mongoose": "^2.3.0",
"supertest": "^4.0.2",
"ts-node": "^8.5.2",
"tslint": "^5.20.1",
"typedoc": "^0.15.3",
"typescript": "^3.7.2"
"ts-node": "^8.6.2",
"tslint": "^6.0.0",
"typedoc": "^0.16.9",
"typescript": "^3.7.5"
}
}
35 changes: 17 additions & 18 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ export class App {
* @private
* @return void
*/
private async initMiddleware(): Promise<void> {
private initMiddleware(): void {
try {
await this.setupInversifyExpress()
this.setupInversifyExpress()
this.setupSwaggerUI()
this.setupErrorsHandler()
} catch (err) {
Expand All @@ -75,7 +75,7 @@ export class App {
* @private
* @return Promise<void>
*/
private async setupInversifyExpress(): Promise<void> {
private setupInversifyExpress(): void {
const inversifyExpress: InversifyExpressServer = new InversifyExpressServer(
DIContainer, null, { rootPath: '/' })

Expand Down Expand Up @@ -117,15 +117,13 @@ export class App {
*/
private setupSwaggerUI(): void {
// Middleware swagger. It should not run in the test environment.
if ((process.env.NODE_ENV || Default.NODE_ENV) !== 'test') {
const options = {
swaggerUrl: Default.SWAGGER_URI,
customCss: '.swagger-ui .topbar { display: none }',
customfavIcon: Default.LOGO_URI,
customSiteTitle: `API Reference | ${Strings.APP.TITLE}`
}
this.express.use('/v1/reference', swaggerUi.serve, swaggerUi.setup({}, options))
const options = {
swaggerUrl: Default.SWAGGER_URI,
customCss: '.swagger-ui .topbar { display: none }',
customfavIcon: Default.LOGO_URI,
customSiteTitle: `API Reference | ${Strings.APP.TITLE}`
}
this.express.use('/v1/reference', swaggerUi.serve, swaggerUi.setup({}, options))
}

/**
Expand All @@ -137,9 +135,11 @@ export class App {
private setupErrorsHandler(): void {
// Handle 404
this.express.use((req, res) => {
const errorMessage: ApiException = new ApiException(404, `${req.url} not found.`,
`Specified resource: ${req.url} was not found or does not exist.`)
res.status(HttpStatus.NOT_FOUND).send(errorMessage.toJson())
const errorMessage: ApiException = new ApiException(
404,
Strings.ERROR_MESSAGE.ENDPOINT_NOT_FOUND.replace('{0}', req.url)
)
res.status(HttpStatus.NOT_FOUND).send(errorMessage.toJSON())
})

// Handle 400, 500
Expand All @@ -149,11 +149,10 @@ export class App {
if (err && err.statusCode === HttpStatus.BAD_REQUEST) {
statusCode = HttpStatus.BAD_REQUEST
errorMessage.code = statusCode
errorMessage.message = 'Unable to process request body.'
errorMessage.description = 'Please verify that the JSON provided in'
.concat(' the request body has a valid format and try again.')
errorMessage.message = Strings.ERROR_MESSAGE.REQUEST_BODY_INVALID
errorMessage.description = Strings.ERROR_MESSAGE.REQUEST_BODY_INVALID_DESC
}
res.status(statusCode).send(errorMessage.toJson())
res.status(statusCode).send(errorMessage.toJSON())
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { IUserAuthDataRepository } from '../../port/user.auth.data.repository.in
import { Query } from '../../../infrastructure/repository/query/query'
import { IFitbitDataRepository } from '../../port/fitbit.auth.data.repository.interface'
import { UserAuthData } from '../../domain/model/user.auth.data'
import { IResourceRepository } from '../../port/resource.repository.interface'

/**
* Handler for UserDeleteEvent operation.
Expand All @@ -20,6 +21,7 @@ export const userDeleteEventHandler = async (event: any) => {
DIContainer.get<IUserAuthDataRepository>(Identifier.USER_AUTH_DATA_REPOSITORY)
const fitbitAuthDataRepo: IFitbitDataRepository =
DIContainer.get<IFitbitDataRepository>(Identifier.FITBIT_DATA_REPOSITORY)
const resourceRepo: IResourceRepository = DIContainer.get<IResourceRepository>(Identifier.RESOURCE_REPOSITORY)

try {
if (typeof event === 'string') event = JSON.parse(event)
Expand All @@ -35,21 +37,23 @@ export const userDeleteEventHandler = async (event: any) => {
const query: Query = new Query().fromJSON({ filters: { user_id: childId } })
const userAuthData: UserAuthData = await userAuthDataRepo.findOne(query)
if (userAuthData) {
const payload: any = await fitbitAuthDataRepo.getTokenPayload(userAuthData.fitbit!.access_token!)
if (payload || payload.scopes) {
const scopes: Array<string> = payload.scopes.split(' ')
if (scopes.includes('rwei')) { // Scope reference from fitbit to weight data is rwei
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'body', 'BODY')
}
if (scopes.includes('ract')) { // Scope reference from fitbit to activity data is ract
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'activities', 'ACTIVITIES')
}
if (scopes.includes('rsle')) { // Scope reference from fitbit to sleep data is rsle
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'sleep', 'SLEEP')
if (userAuthData.fitbit!.scope!) {
const payload: any = await fitbitAuthDataRepo.getTokenPayload(userAuthData.fitbit!.access_token!)
if (payload || payload.scopes) {
const scopes: Array<string> = payload.scopes.split(' ')
if (scopes.includes('rwei')) { // Scope reference from fitbit to weight data is rwei
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'body', 'BODY')
}
if (scopes.includes('ract')) { // Scope reference from fitbit to activity data is ract
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'activities', 'ACTIVITIES')
}
if (scopes.includes('rsle')) { // Scope reference from fitbit to sleep data is rsle
await fitbitAuthDataRepo.unsubscribeUserEvent(userAuthData.fitbit!, 'sleep', 'SLEEP')
}
}
}
await userAuthDataRepo.deleteByQuery(query)

await resourceRepo.deleteByQuery(query)
// 3. If got here, it's because the action was successful.
logger.info(`Action for event ${event.event_name} successfully held!`)
}
Expand Down
4 changes: 3 additions & 1 deletion src/application/port/resource.repository.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { IQuery } from './query.interface'
import { IRepository } from './repository.interface'
import { Resource } from '../domain/model/resource'

export interface IResourceRepository extends IRepository<Resource>{
export interface IResourceRepository extends IRepository<Resource> {
checkExists(query: IQuery): Promise<boolean>

deleteByQuery(query: IQuery): Promise<boolean>
}
2 changes: 1 addition & 1 deletion src/application/port/user.auth.data.service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { DataSync } from '../domain/model/data.sync'
export interface IUserAuthDataService extends IService<UserAuthData> {
getByUserId(userId: string): Promise<UserAuthData>

revokeFitbitAccessToken(userId: string): Promise<boolean>
revokeFitbitAccessToken(userId: string): Promise<void>

syncFitbitDataFromUser(userId: string): Promise<DataSync>

Expand Down
35 changes: 25 additions & 10 deletions src/application/service/user.auth.data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,36 +81,51 @@ export class UserAuthDataService implements IUserAuthDataService {
return this._userAuthDataRepo.findOne(new Query().fromJSON({ filters: { user_id: userId } }))
}

public revokeFitbitAccessToken(userId: string): Promise<boolean> {
public revokeFitbitAccessToken(userId: string): Promise<void> {
return new Promise(async (resolve, reject) => {
let authData: UserAuthData
try {
ObjectIdValidator.validate(userId)

// 1. Check if user has authorization data saved.
const authData: UserAuthData = await this._userAuthDataRepo
authData = await this._userAuthDataRepo
.findOne(new Query().fromJSON({ filters: { user_id: userId } }))
if (!authData || !authData.fitbit || !authData.fitbit.access_token) {
return resolve(false)
return resolve()
}

// 2. Unsubscribe from Fitbit events.
await this.unsubscribeFitbitEvents(authData)

// 3. Revokes Fitbit access token.

const isRevoked: boolean = await this._fitbitAuthDataRepo.revokeToken(authData.fitbit.access_token)
// 4. Remove Fitbit authorization data from local database.
if (await this._fitbitAuthDataRepo.revokeToken(authData.fitbit.access_token) &&
await this._fitbitAuthDataRepo.removeFitbitAuthData(userId)) {
// 5. Publish the Fitbit revoke event on the bus.
const isRemoved: boolean = await this._fitbitAuthDataRepo.removeFitbitAuthData(userId)

// 5. Publish the Fitbit revoke event on the bus.
if (isRevoked && isRemoved) {
this._eventBus.bus
.pubFitbitRevoke({ child_id: userId })
.then(() => this._logger.info(`Fitbit revoke event for child ${userId} successfully published!`))
.catch((err) => this._logger.error(`There was an error publishing Fitbit revoke event for child ${userId}. ${err.message}`))
return resolve(true)
} else {
return resolve(false)
}
return resolve()
} catch (err) {
return reject(err)
if (err.type) {
if (err.type === 'expired_token') {
this._fitbitAuthDataRepo
.refreshToken(userId, authData!.fitbit!.access_token!, authData!.fitbit!.refresh_token!)
.then(async newToken => {
await this.revokeFitbitAccessToken(userId)
return resolve()
}).catch(err => {
if (err.type !== 'system') this.updateTokenStatus(userId, err.type)
})
}
this.publishFitbitAuthError(err, userId)
}
return resolve()
}
})
}
Expand Down
Loading

0 comments on commit ac9a7fd

Please sign in to comment.