Skip to content

Commit

Permalink
Merge branch 'notification' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffsampaio committed Apr 15, 2020
2 parents 9e2526d + 137149c commit ef6f880
Show file tree
Hide file tree
Showing 24 changed files with 677 additions and 57 deletions.
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
language: node_js
node_js:
- "8"
- "9"
- "10"
- "11"
- "12"
- "13"
env:
- NODE_ENV=test RABBITMQ_URI=amqp://guest:[email protected]:5672 MONGODB_URI_TEST=mongodb://127.0.0.1:27017/ocariot-account-test
dist: xenial
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM node:10.16.3
FROM node:12.13.1

# Create app directory
RUN mkdir -p /usr/src/ac
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,10 @@
"all": true
},
"dependencies": {
"@ocariot/rabbitmq-client-node": "1.5.1",
"@ocariot/rabbitmq-client-node": "1.6.1",
"bcryptjs": "^2.4.3",
"body-parser": "^1.19.0",
"cron": "1.8.2",
"dotenv": "^8.2.0",
"express": "^4.17.1",
"fs-extra": "^9.0.0",
Expand Down
13 changes: 1 addition & 12 deletions src/application/domain/model/child.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export class Child extends User implements IJSONSerializable, IJSONDeserializabl
private _age_calc_date?: string // Date the age was registered.
private _last_sync?: Date // Last synchronization time according to the UTC.
private _fitbit_status?: string // Fitbit status value.
private _cve_status?: string // CVE status value.

constructor() {
super()
Expand All @@ -42,7 +41,6 @@ export class Child extends User implements IJSONSerializable, IJSONDeserializabl
'notifications:create'
]
this.fitbit_status = 'none'
this.cve_status = 'none'
}

get gender(): string | undefined {
Expand Down Expand Up @@ -85,14 +83,6 @@ export class Child extends User implements IJSONSerializable, IJSONDeserializabl
this._fitbit_status = value
}

get cve_status(): string | undefined {
return this._cve_status
}

set cve_status(value: string | undefined) {
this._cve_status = value
}

public convertDatetimeString(value: string): Date {
DatetimeValidator.validate(value)
return new Date(value)
Expand Down Expand Up @@ -125,8 +115,7 @@ export class Child extends User implements IJSONSerializable, IJSONDeserializabl
age: this.age,
age_calc_date: this.age_calc_date,
last_sync: this.last_sync,
fitbit_status: this.fitbit_status,
cve_status: this.cve_status
fitbit_status: this.fitbit_status
}
}
}
Expand Down
1 change: 0 additions & 1 deletion src/application/domain/model/children.group.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ export class ChildrenGroup extends Entity implements IJSONSerializable, IJSONDes
if (item.last_sync !== undefined) child.last_sync = item.last_sync
if (item.last_login !== undefined) child.last_login = item.last_login
child.fitbit_status = item.fitbit_status
child.cve_status = item.cve_status
return child
})
else this.children = json.children
Expand Down
10 changes: 10 additions & 0 deletions src/application/port/child.repository.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,14 @@ export interface IChildRepository extends IRepository<Child> {
* @throws {ValidationException | RepositoryException}
*/
updateLastSync(childId: string, lastSync: Date): Promise<Child>

/**
* Returns the children who had their data synchronized in a range of days (up to N days ago).
*
* @param numberOfDays Number of days used to search for children who had their data synchronized in a range of days
* (up to {numberOfDays} ago).
* @return {Promise<Array<Child>>}
* @throws {RepositoryException}
*/
findInactiveChildren(numberOfDays: number): Promise<Array<Child>>
}
11 changes: 10 additions & 1 deletion src/background/background.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@ import fs from 'fs'
import { IEventBus } from '../infrastructure/port/eventbus.interface'
import { Default } from '../utils/default'
import { ILogger } from '../utils/custom.logger'
import { IChildRepository } from '../application/port/child.repository.interface'
import { NotificationTask } from './task/notification.task'

@injectable()
export class BackgroundService {
private _notificationTask: IBackgroundTask = new NotificationTask(
this._eventBus, DIContainer.get<IChildRepository>(Identifier.CHILD_REPOSITORY), this._logger,
Default.NUMBER_OF_DAYS, Default.EXPRESSION_AUTO_NOTIFICATION
)

constructor(
@inject(Identifier.MONGODB_CONNECTION) private readonly _mongodb: IDatabase,
Expand Down Expand Up @@ -56,6 +62,9 @@ export class BackgroundService {
// All resource provider
this._providerTask.run()

// Notification task
this._notificationTask.run()

/**
* Create keys fot JWT
*/
Expand All @@ -68,7 +77,7 @@ export class BackgroundService {
public async stopServices(): Promise<void> {
try {
await this._mongodb.dispose()
await this._subscribeTask.stop()
await this._notificationTask.stop()
} catch (err) {
return Promise.reject(new Error(`Error stopping MongoDB! ${err.message}`))
}
Expand Down
91 changes: 91 additions & 0 deletions src/background/task/notification.task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { IBackgroundTask } from '../../application/port/background.task.interface'
import { inject, injectable } from 'inversify'
import { Identifier } from '../../di/identifiers'
import { IEventBus } from '../../infrastructure/port/eventbus.interface'
import { ILogger } from '../../utils/custom.logger'
import cron from 'cron'
import { IChildRepository } from '../../application/port/child.repository.interface'
import { Child } from '../../application/domain/model/child'

@injectable()
export class NotificationTask implements IBackgroundTask {
private job: any

constructor(
@inject(Identifier.RABBITMQ_EVENT_BUS) private readonly _eventBus: IEventBus,
@inject(Identifier.CHILD_REPOSITORY) private readonly _childRepository: IChildRepository,
@inject(Identifier.LOGGER) private readonly _logger: ILogger,
private readonly numberOfDays: number,
private readonly expression_auto_notification?: string
) {
}

public run(): void {
try {
if (this.expression_auto_notification) {
this.job = new cron.CronJob(`${this.expression_auto_notification}`, () => this.checkInactivity())
this.job.start()
} else this.checkInactivity()

this._logger.debug('Notification task started successfully!')
} catch (err) {
this._logger.error(`An error occurred initializing the Notification task. ${err.message}`)
}
}

public stop(): Promise<void> {
if (this.expression_auto_notification) this.job.stop()
return this._eventBus.dispose()
}

private sendNotification(children: Array<Child>): void {
for (const child of children) {
let notification

try {
notification = this.buildNotification(child)
} catch (err) {
this._logger.error(`An error occurred while trying to build the notification. ${err.message}`)
}

if (notification) {
this._eventBus.bus
.pubSendNotification(notification)
.then(() => {
this._logger.info('\'monitoring:miss_child_data\' notification sent')
})
.catch(err => {
this._logger.error(`An error occurred while trying to send a notification about the Child with ID: `
.concat(`${child.id}. ${err.message}`))
})
}
}
}

private buildNotification(child: Child): any {
let calc_days_since = this.numberOfDays

if (child.last_sync) {
const now = new Date()
const last_sync: Date = child.last_sync
const diff = Math.abs(now.getTime() - last_sync.getTime())
calc_days_since = Math.trunc(diff / (1000 * 60 * 60 * 24))
}

return {
notification_type: 'monitoring:miss_child_data',
id: child.id,
days_since: calc_days_since
}
}

private checkInactivity(): void {
this._childRepository.findInactiveChildren(this.numberOfDays)
.then(result => {
if (result.length) this.sendNotification(result)
})
.catch(err => {
this._logger.error(`An error occurred while trying to retrieve Child data. ${err.message}`)
})
}
}
4 changes: 4 additions & 0 deletions src/di/di.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ import { IDatabase } from '../infrastructure/port/database.interface'
import { RabbitMQ } from '../infrastructure/eventbus/rabbitmq/rabbitmq'
import { SubscribeEventBusTask } from '../background/task/subscribe.event.bus.task'
import { ProviderEventBusTask } from '../background/task/provider.event.bus.task'
import { NotificationTask } from '../background/task/notification.task'

export class IoC {
private readonly _container: Container
Expand Down Expand Up @@ -237,6 +238,9 @@ export class IoC {
this.container
.bind<IBackgroundTask>(Identifier.PROVIDER_EVENT_BUS_TASK)
.to(ProviderEventBusTask).inRequestScope()
this.container
.bind<IBackgroundTask>(Identifier.NOTIFICATION_TASK)
.to(NotificationTask).inRequestScope()

// Log
this.container.bind<ILogger>(Identifier.LOGGER).to(CustomLogger).inSingletonScope()
Expand Down
1 change: 1 addition & 0 deletions src/di/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export abstract class Identifier {
public static readonly SUB_EVENT_BUS_TASK: any = Symbol.for('SubscribeEventBusTask')
public static readonly PROVIDER_EVENT_BUS_TASK: any = Symbol.for('ProviderEventBusTask')
public static readonly GENERATE_JWT_KEYS_TASK: any = Symbol.for('GenerateJwtKeysTask')
public static readonly NOTIFICATION_TASK: any = Symbol.for('NotificationTask')

// Log
public static readonly LOGGER: any = Symbol.for('CustomLogger')
Expand Down
5 changes: 0 additions & 5 deletions src/infrastructure/database/schema/user.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ const userSchema = new Mongoose.Schema({
readonly: true,
default: 'none'
},
cve_status: {
type: String,
readonly: true,
default: 'none'
},
scopes: [{ type: String }] // Scope that signal the types of access the user has.
},
{
Expand Down
1 change: 0 additions & 1 deletion src/infrastructure/entity/child.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ export class ChildEntity extends UserEntity {
public age_calc_date?: string // Date the age was registered.
public last_sync?: Date // Last synchronization time according to the UTC.
public fitbit_status?: string // Fitbit status value.
public cve_status?: string // CVE status value.
}
2 changes: 0 additions & 2 deletions src/infrastructure/entity/mapper/child.entity.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export class ChildEntityMapper implements IEntityMapper<Child, ChildEntity> {
if (item.last_login) result.last_login = item.last_login
if (item.last_sync) result.last_sync = item.last_sync
if (item.fitbit_status) result.fitbit_status = item.fitbit_status
if (item.cve_status) result.cve_status = item.cve_status
if (item.scopes) result.scopes = item.scopes

return result
Expand Down Expand Up @@ -66,7 +65,6 @@ export class ChildEntityMapper implements IEntityMapper<Child, ChildEntity> {
if (json.last_login !== undefined) result.last_login = json.last_login
if (json.last_sync !== undefined) result.last_sync = json.last_sync
if (json.fitbit_status !== undefined) result.fitbit_status = json.fitbit_status
if (json.cve_status !== undefined) result.cve_status = json.cve_status
if (json.scopes !== undefined) result.scopes = json.scopes

return result
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ export class ChildrenGroupEntityMapper implements IEntityMapper<ChildrenGroup, C
if (item.last_sync !== undefined) child.last_sync = item.last_sync
if (item.last_login !== undefined) child.last_login = item.last_login
child.fitbit_status = item.fitbit_status
child.cve_status = item.cve_status
return child
})
}
Expand Down
1 change: 0 additions & 1 deletion src/infrastructure/entity/mapper/family.entity.mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export class FamilyEntityMapper implements IEntityMapper<Family, FamilyEntity> {
if (item.last_sync !== undefined) child.last_sync = item.last_sync
if (item.last_login !== undefined) child.last_login = item.last_login
child.fitbit_status = item.fitbit_status
child.cve_status = item.cve_status
return child
})
}
Expand Down
23 changes: 23 additions & 0 deletions src/infrastructure/repository/child.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,27 @@ export class ChildRepository extends BaseRepository<Child, ChildEntity> implemen
.catch(err => reject(this.mongoDBErrorListener(err)))
})
}

/**
* Returns the children who had their data synchronized in a range of days (up to N days ago).
*
* @param numberOfDays Number of days used to search for children who had their data synchronized in a range of days
* (up to {numberOfDays} ago).
* @return {Promise<Array<Child>>}
* @throws {RepositoryException}
*/
public findInactiveChildren(numberOfDays: number): Promise<Array<Child>> {
// Sets the date object to be used in the search
const searchDate: Date = new Date(new Date().getTime() - ((1000 * 60 * 60 * 24) * numberOfDays))

// Sets the query and search
const query: IQuery = new Query()
query.pagination.limit = Number.MAX_SAFE_INTEGER
query.filters = {
type: UserType.CHILD,
$or: [ { last_sync: { $lt: searchDate.toISOString() } }, { last_sync: { $exists: false } } ]
}

return super.find(query)
}
}
15 changes: 15 additions & 0 deletions src/utils/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,21 @@ export abstract class Default {
public static readonly SSL_CERT_PATH: string = '.certs/server.crt'
public static readonly RABBITMQ_CA_PATH: string = '.certs/ca.crt'

/**
* The frequency of time that the application will check, in the background, the need to send one or more
* notifications, according to the cron expression.
* For example, the value 0 0 9 * * *, means that the check it will occurs every day at 09:00:00.
*
* Cron ranges:
*
* Seconds Minutes Hours Day of Month Months Day of Week
* 0-59 0-59 0-23 1-31 0-11 (Jan-Dec) 0-6 (Sun-Sat)
*/
public static readonly EXPRESSION_AUTO_NOTIFICATION: string = '0 0 9 * * *'

// The number of days to be used as a parameter for checking the need to send one or more notifications.
public static readonly NUMBER_OF_DAYS: number = 7

/**
* User scopes
*/
Expand Down
Loading

0 comments on commit ef6f880

Please sign in to comment.