Skip to content

Commit

Permalink
Merge branch 'hotfix/1.3.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffsampaio committed Nov 30, 2020
2 parents ddaa6a1 + 5720fd1 commit c912c49
Show file tree
Hide file tree
Showing 35 changed files with 1,870 additions and 744 deletions.
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,14 @@ SMTP_PASS=YOUR_SMTP_PASS
# SENDER_NAME: Sender of emails that will be sent by the service.
SENDER_NAME=YOUR_SENDER_NAME

# EMAIL_TEMPLATES_PATH Email templates path.
# If there are no customized email templates, the variable does not need
# to be configured, so the default templates will be used.
#
# If it exists, the configured path must have at least 3 directories with
# the template files: reset-password, updated-password and welcome.
EMAIL_TEMPLATES_PATH=YOUR_EMAIL_TEMPLATES_PATH

#################################################################################################
##################################### MONGO DATABASE SETUP #####################################
#################################################################################################
Expand Down
1,286 changes: 582 additions & 704 deletions package-lock.json

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "notification",
"version": "1.3.1",
"version": "1.3.2",
"description": "Microservice for sending messages: email, sms or push. In this version only the sending of email will be available.",
"main": "dist/server.js",
"scripts": {
Expand Down Expand Up @@ -56,13 +56,13 @@
"amqp-client-node": "^1.0.10",
"body-parser": "^1.19.0",
"dotenv": "^8.2.0",
"email-templates": "^7.2.0",
"email-templates": "^8.0.1",
"express": "^4.17.1",
"firebase-admin": "^9.4.1",
"helmet": "^3.23.3",
"inversify": "^5.0.1",
"inversify-express-utils": "^6.3.2",
"mongoose": "^5.10.15",
"mongoose": "^5.10.19",
"morgan": "^1.10.0",
"multer": "^1.4.2",
"nodemailer": "^6.4.16",
Expand Down
1 change: 1 addition & 0 deletions src/application/domain/model/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export class Address implements IJSONSerializable, IJSONDeserializable<Address>
}

public fromJSON(json: any): Address {
if (!json) return this
if (JsonUtils.isJsonString(json)) {
json = JSON.parse(json)
}
Expand Down
1 change: 1 addition & 0 deletions src/application/domain/model/attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export class Attachment implements IJSONSerializable, IJSONDeserializable<Attach
}

public fromJSON(json: any): Attachment {
if (!json) return this
if (JsonUtils.isJsonString(json)) {
json = JSON.parse(json)
}
Expand Down
1 change: 1 addition & 0 deletions src/application/domain/model/email.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ export class Email extends Entity implements IJSONSerializable, IJSONDeserializa
}

public fromJSON(json: any): Email {
if (!json) return this
if (JsonUtils.isJsonString(json)) {
json = JSON.parse(json)
}
Expand Down
7 changes: 7 additions & 0 deletions src/application/domain/model/user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IJSONSerializable } from '../utils/json.serializable.interface'
import { Entity } from './entity'
import { IJSONDeserializable } from '../utils/json.deserializable.interface'
import { JsonUtils } from '../utils/json.utils'

export class User extends Entity implements IJSONSerializable, IJSONDeserializable<User> {
private _type?: string
Expand All @@ -18,6 +19,12 @@ export class User extends Entity implements IJSONSerializable, IJSONDeserializab
}

public fromJSON(json: any): User {
if (!json) return this

if (typeof json === 'string' && JsonUtils.isJsonString(json)) {
json = JSON.parse(json)
}

if (json.id) super.id = json.id
if (json.type) this.type = json.type
return this
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidationException } from '../exception/validation.exception'
import { EmailToValidator } from './email.to.validator'
import { Strings } from '../../../utils/strings'

export class EmailPilotStudyDataValidator {
public static validate(email: any): void | ValidationException {
Expand All @@ -15,7 +16,7 @@ export class EmailPilotStudyDataValidator {

if (!email.attachments) fields.push('attachments')
else if (!email.attachments.length) {
throw new ValidationException('At least one file is required in attachments')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.EMPTY_ATTACHMENTS)
} else {
for (const item of email.attachments) {
if (!item.filename) fields.push('attachments.filename')
Expand All @@ -25,8 +26,10 @@ export class EmailPilotStudyDataValidator {
}

if (fields.length > 0) {
throw new ValidationException('Required fields were not provided...',
'Email validation: '.concat(fields.join(', ')).concat(' is required!'))
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidationException } from '../exception/validation.exception'
import { EmailToValidator } from './email.to.validator'
import { Strings } from '../../../utils/strings'

export class EmailResetPasswordValidator {
public static validate(email: any): void | ValidationException {
Expand All @@ -12,8 +13,10 @@ export class EmailResetPasswordValidator {
if (!email.action_url) fields.push('action_url')

if (fields.length > 0) {
throw new ValidationException('Required fields were not provided...',
'Email validation: '.concat(fields.join(', ')).concat(' is required!'))
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
26 changes: 12 additions & 14 deletions src/application/domain/validator/email.send.validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ValidationException } from '../exception/validation.exception'
import { Email } from '../model/email'
import { EmailValidator } from './email.validator'
import { Strings } from '../../../utils/strings'

export class EmailSendValidator {
public static validate(email: Email): void | ValidationException {
Expand All @@ -10,19 +11,17 @@ export class EmailSendValidator {
if (!email.subject) fields.push('subject')
if (email.reply) {
if (!email.reply.email) {
throw new ValidationException('The reply field requires that the object have email!')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_REPLY_EMAIL)
}
EmailValidator.validate(email.reply.email)
}
if (!email.to) fields.push('to')
if (!email.to.length) {
throw new ValidationException('The to field requires at least one recipient with ' +
'and an email address!')
else if (!email.to.length) {
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.EMPTY_TO)
} else {
for (const item of email.to) {
if (!item.email) {
throw new ValidationException('The to field requires an array of recipients ' +
'with a email address!.')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_TO_EMAIL)
} else {
EmailValidator.validate(item.email)
}
Expand All @@ -31,8 +30,7 @@ export class EmailSendValidator {
if (email.cc && email.cc.length) {
for (const item of email.cc) {
if (!item.email) {
throw new ValidationException('The cc field requires an array of recipients ' +
' with a email address.')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_CC_EMAIL)
} else {
EmailValidator.validate(item.email)
}
Expand All @@ -41,8 +39,7 @@ export class EmailSendValidator {
if (email.bcc && email.bcc.length) {
for (const item of email.bcc) {
if (!item.email) {
throw new ValidationException('The bcc field requires an array of recipients ' +
'with a email address.')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_BCC_EMAIL)
} else {
EmailValidator.validate(item.email)
}
Expand All @@ -51,15 +48,16 @@ export class EmailSendValidator {
if (email.attachments && email.attachments.length) {
for (const item of email.attachments) {
if (!item.path) {
throw new ValidationException('The attachment field requires a variety of ' +
'attachments with at least the file path or URL.')
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_ATTACHMENTS_PATH)
}
}
}

if (fields.length > 0) {
throw new ValidationException('Required fields were not provided...',
'Email validation: '.concat(fields.join(', ')).concat(' is required!'))
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
13 changes: 9 additions & 4 deletions src/application/domain/validator/email.to.validator.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { ValidationException } from '../exception/validation.exception'
import { EmailValidator } from './email.validator'
import { Strings } from '../../../utils/strings'

export class EmailToValidator {
public static validate(email: any): void | ValidationException {
const INVALID_TO: string = 'The "to" field is not in valid format!'

if (!email.name) {
throw new ValidationException(INVALID_TO, 'The name attribute is required.')
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.INVALID_TO,
Strings.ERROR_MESSAGE.VALIDATE.INVALID_TO_DESC.replace('{0}', 'name')
)
}
if (!email.email) {
throw new ValidationException(INVALID_TO, 'The email attribute is required.')
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.INVALID_TO,
Strings.ERROR_MESSAGE.VALIDATE.INVALID_TO_DESC.replace('{0}', 'email')
)
}
EmailValidator.validate(email.email)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidationException } from '../exception/validation.exception'
import { EmailToValidator } from './email.to.validator'
import { Strings } from '../../../utils/strings'

export class EmailUpdatePasswordValidator {
public static validate(email: any): void | ValidationException {
Expand All @@ -10,8 +11,10 @@ export class EmailUpdatePasswordValidator {
else EmailToValidator.validate(email.to)

if (fields.length > 0) {
throw new ValidationException('Required fields were not provided...',
'Email validation: '.concat(fields.join(', ')).concat(' is required!'))
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
3 changes: 2 additions & 1 deletion src/application/domain/validator/email.validator.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { ValidationException } from '../exception/validation.exception'
import { Strings } from '../../../utils/strings'

export class EmailValidator {
public static validate(email: string | undefined): void | ValidationException {
if (!email || !(/^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/).test(email)) {
throw new ValidationException(`Email "${email}" does not have a valid format!`)
throw new ValidationException(Strings.ERROR_MESSAGE.VALIDATE.INVALID_EMAIL.replace('{0}', email))
}
}
}
7 changes: 5 additions & 2 deletions src/application/domain/validator/email.welcome.validator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ValidationException } from '../exception/validation.exception'
import { EmailToValidator } from './email.to.validator'
import { Strings } from '../../../utils/strings'

export class EmailWelcomeValidator {
public static validate(email: any): void | ValidationException {
Expand All @@ -12,8 +13,10 @@ export class EmailWelcomeValidator {
if (!email.action_url) fields.push('action_url')

if (fields.length > 0) {
throw new ValidationException('Required fields were not provided...',
'Email validation: '.concat(fields.join(', ')).concat(' is required!'))
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
9 changes: 6 additions & 3 deletions src/application/domain/validator/user.validator.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { User } from '../model/user'
import { ValidationException } from '../exception/validation.exception'
import { ObjectIdValidator } from './object.id.validator'
import { Strings } from '../../../utils/strings'

export class UserValidator {
public static validate(item: User): void | ValidationException {
Expand All @@ -10,9 +11,11 @@ export class UserValidator {
else ObjectIdValidator.validate(item.id)
if (!item.type) fields.push('type')

if (fields.length) {
throw new ValidationException('Required fields were not provided...',
'User validation: '.concat(fields.join(', ')).concat(' required!'))
if (fields.length > 0) {
throw new ValidationException(
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS,
Strings.ERROR_MESSAGE.VALIDATE.REQUIRED_FIELDS_DESC.replace('{0}', fields.join(', '))
)
}
}
}
18 changes: 18 additions & 0 deletions src/infrastructure/repository/email.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,24 @@ export class EmailRepository extends BaseRepository<Email, EmailEntity> implemen
}

private getEmailTemplateInstance(): any {
const emailTemplatesPath = process.env.EMAIL_TEMPLATES_PATH
if (emailTemplatesPath) {
try {
const data: any = fs.readdirSync(emailTemplatesPath)
if (data && data.find(item => item === 'reset-password') && data.find(item => item === 'updated-password')
&& data.find(item => item === 'welcome')) {
return new Template({
transport: this.smtpTransport,
send: true,
preview: false,
views: { root: path.resolve(emailTemplatesPath) }
})
}
} catch (err) {
this.logger.error(`The custom templates could not be accessed successfully, so the default will be used. `
.concat(err.message))
}
}
return new Template({
transport: this.smtpTransport,
send: true,
Expand Down
6 changes: 6 additions & 0 deletions src/infrastructure/repository/push.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ export class PushRepository extends BaseRepository<Push, PushEntity> implements
Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN.replace(': {0}', recipient ? `: ${recipient}` : '.'),
Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN_DESC)
},
'messaging/invalid-argument': () => {
return new FirebaseClientException(
HttpStatus.UNAUTHORIZED,
Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN.replace(': {0}', recipient ? `: ${recipient}` : '.'),
Strings.FIREBASE_ADMIN_ERROR.INVALID_TOKEN_DESC)
},
'messaging/registration-token-not-registered': () => {
return new FirebaseClientException(
HttpStatus.UNAUTHORIZED,
Expand Down
12 changes: 11 additions & 1 deletion src/utils/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ export abstract class Strings {
AT_LEAST_ONE_RECIPIENT: 'At least one recipient is required.',
AT_LEAST_ONE_RECIPIENT_DESC: 'Please enter at least one user id for direct notifications or at least one topic name for topic notifications.',
USER_HAS_NO_PUSH_TOKEN: 'Some user ids do not have saved push tokens for any type of client: {0}.',
USER_HAS_NO_PUSH_TOKEN_DESC: 'Please submit a valid user id and try again.'
USER_HAS_NO_PUSH_TOKEN_DESC: 'Please submit a valid user id and try again.',
INVALID_EMAIL: 'Email "{0}" does not have a valid format!',
INVALID_TO: 'The "to" field is not in valid format!',
INVALID_TO_DESC: 'The {0} attribute is required.',
EMPTY_ATTACHMENTS: 'At least one file is required in attachments',
EMPTY_TO: 'The to field requires at least one recipient with an email address!',
REQUIRED_REPLY_EMAIL: 'The reply field requires that the object have email!',
REQUIRED_TO_EMAIL: 'The to field requires an array of recipients with a email address!',
REQUIRED_CC_EMAIL: 'The cc field requires an array of recipients with a email address!',
REQUIRED_BCC_EMAIL: 'The bcc field requires an array of recipients with a email address!',
REQUIRED_ATTACHMENTS_PATH: 'The attachment field requires a variety of attachments with at least the file path or URL.'
}
}

Expand Down
10 changes: 10 additions & 0 deletions test/mocks/generator.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ export class GeneratorMock {
return randS
}

public static generateUserType(): string {
const userTypes = {
0: 'admin',
1: 'health_professional',
2: 'patient'
}

return userTypes[Math.floor((Math.random() * 3))] // 0-2
}

public static async generateEmailTemp(email): Promise<any> {
return new Promise<any>((resolve, reject) => {
const options = {
Expand Down
Loading

0 comments on commit c912c49

Please sign in to comment.