diff --git a/connect/connectNotificationServer.js b/connect/connectNotificationServer.js index 51ecb71..e6ed1f4 100644 --- a/connect/connectNotificationServer.js +++ b/connect/connectNotificationServer.js @@ -361,13 +361,13 @@ if (config.ENABLE_EMAILS) { } // init database, it will clear and re-create all tables -notificationServer - .initDatabase() - .then(() => notificationServer.start()) - .catch((e) => { - console.log(e); // eslint-disable-line no-console - notificationServer.logger.error('Notification server errored out'); - }); +// notificationServer +// .initDatabase() +// .then(() => notificationServer.startKafkaConsumers()) +// .catch((e) => { +// console.log(e); // eslint-disable-line no-console +// notificationServer.logger.error('Notification server errored out'); +// }); // if no need to init database, then directly start the server: -// notificationServer.start(); +notificationServer.startKafkaConsumers(); diff --git a/connect/constants.js b/connect/constants.js index 3dff620..fd46cf8 100644 --- a/connect/constants.js +++ b/connect/constants.js @@ -11,6 +11,7 @@ module.exports = { // email service id for settings SETTINGS_EMAIL_SERVICE_ID: 'email', SETTINGS_EMAIL_BUNDLING_SERVICE_ID: 'emailBundling', + ACTIVE_USER_STATUSES: ['ACTIVE'], BUS_API_EVENT: { CONNECT: { @@ -38,7 +39,7 @@ module.exports = { LINK_CREATED: 'notifications.connect.project.linkCreated', PAUSED: 'notifications.connect.project.paused', SUBMITTED_FOR_REVIEW: 'notifications.connect.project.submittedForReview', - SPECIFICATION_MODIFIED: 'connect.action.project.product.update.spec', + SPECIFICATION_MODIFIED: 'connect.action.project.updated.spec', }, PROJECT_PLAN: { READY: 'connect.action.project.plan.ready', @@ -49,9 +50,11 @@ module.exports = { PHASE_PAYMENT_UPDATED: 'notifications.connect.project.phase.update.payment', PHASE_PROGRESS_UPDATED: 'notifications.connect.project.phase.update.progress', PHASE_SCOPE_UPDATED: 'notifications.connect.project.phase.update.scope', + PHASE_PRODUCT_SPEC_UPDATED: 'connect.action.project.product.update.spec', MILESTONE_ACTIVATED: 'connect.action.timeline.milestone.transition.active', MILESTONE_COMPLETED: 'connect.action.timeline.milestone.transition.completed', WAITING_FOR_CUSTOMER_INPUT: 'connect.action.timeline.milestone.waiting.customer', + TIMELINE_ADJUSTED: 'connect.action.timeline.adjusted', }, TOPIC: { CREATED: 'notifications.connect.project.topic.created', diff --git a/connect/events-config.js b/connect/events-config.js index cf3a5b4..142d8aa 100644 --- a/connect/events-config.js +++ b/connect/events-config.js @@ -176,6 +176,9 @@ const EVENTS = [ }, { type: BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_SCOPE_UPDATED, projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER], + }, { + type: BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_PRODUCT_SPEC_UPDATED, + projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER], }, // Timeline/Milestone activity @@ -188,6 +191,9 @@ const EVENTS = [ }, { type: BUS_API_EVENT.CONNECT.PROJECT_PLAN.WAITING_FOR_CUSTOMER_INPUT, projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER], + }, { + type: BUS_API_EVENT.CONNECT.PROJECT_PLAN.TIMELINE_ADJUSTED, + projectRoles: [PROJECT_ROLE_OWNER, PROJECT_ROLE_COPILOT, PROJECT_ROLE_MANAGER, PROJECT_ROLE_MEMBER], } ]; @@ -263,6 +269,7 @@ const EVENT_BUNDLES = { BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_PAYMENT_UPDATED, BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_PROGRESS_UPDATED, BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_SCOPE_UPDATED, + BUS_API_EVENT.CONNECT.PROJECT_PLAN.PHASE_PRODUCT_SPEC_UPDATED, BUS_API_EVENT.CONNECT.PROJECT_PLAN.MILESTONE_ACTIVATED, BUS_API_EVENT.CONNECT.PROJECT_PLAN.MILESTONE_COMPLETED, BUS_API_EVENT.CONNECT.PROJECT_PLAN.WAITING_FOR_CUSTOMER_INPUT, diff --git a/connect/notificationServices/email.js b/connect/notificationServices/email.js index 170594f..f7a20cc 100644 --- a/connect/notificationServices/email.js +++ b/connect/notificationServices/email.js @@ -14,7 +14,7 @@ const { BUS_API_EVENT, SCHEDULED_EVENT_PERIOD, SETTINGS_EMAIL_SERVICE_ID, - SETTINGS_EMAIL_BUNDLING_SERVICE_ID + ACTIVE_USER_STATUSES } = require('../constants'); const { EVENTS, EVENT_BUNDLES } = require('../events-config'); const helpers = require('../helpers'); @@ -200,12 +200,20 @@ function handler(topicName, messageJSON, notification) { } const users = yield service.getUsersById([notification.userId]); - logger.debug(`got users ${JSON.stringify(users)}`); + logger.verbose(`got users ${JSON.stringify(users)}`); - const user = users[0]; - let userEmail = user.email; + const user = users && users.length > 0 ? users[0] : null; + let userEmail = _.get(user, 'email'); if (!userEmail) { logger.error(`Email not received for user: ${user.id}`); + return; + } + const userStatus = _.get(user, 'status'); + // don't send email notification for inactive users, ideally we should not have generated + // notifications for inactive users, however, for now handling it here as safe gaurd + if (userStatus && ACTIVE_USER_STATUSES.indexOf(userStatus) === -1) { + logger.error(`Notification generated for inactive user, ignoring`); + return; } if (config.ENABLE_DEV_MODE === 'true') { userEmail = config.DEV_MODE_EMAIL; diff --git a/connect/service.js b/connect/service.js index cbecf26..850bb48 100644 --- a/connect/service.js +++ b/connect/service.js @@ -89,7 +89,7 @@ const getUsersById = (ids) => { }) .then((token) => { return request - .get(`${config.TC_API_V3_BASE_URL}/members/_search?fields=userId,email,handle,firstName,lastName,photoURL&query=${query}`) + .get(`${config.TC_API_V3_BASE_URL}/members/_search?fields=userId,email,handle,firstName,lastName,photoURL,status&query=${query}`) .set('accept', 'application/json') .set('authorization', `Bearer ${token}`) .then((res) => { diff --git a/deploy.sh b/deploy.sh index c275979..05cb923 100755 --- a/deploy.sh +++ b/deploy.sh @@ -29,7 +29,8 @@ AWS_SECRET_ACCESS_KEY=$(eval "echo \$${ENV}_AWS_SECRET_ACCESS_KEY") AWS_ACCOUNT_ID=$(eval "echo \$${ENV}_AWS_ACCOUNT_ID") AWS_REPOSITORY=$(eval "echo \$${ENV}_AWS_REPOSITORY") AWS_ECS_CLUSTER=$(eval "echo \$${ENV}_AWS_ECS_CLUSTER") -AWS_ECS_SERVICE=$(eval "echo \$${ENV}_AWS_ECS_SERVICE") +AWS_ECS_SERVICE_API=$(eval "echo \$${ENV}_AWS_ECS_SERVICE") +AWS_ECS_SERVICE_CONSUMERS=$(eval "echo \$${ENV}_AWS_ECS_SERVICE_CONSUMERS") KAFKA_CLIENT_CERT=$(eval "echo \$${ENV}_KAFKA_CLIENT_CERT") KAFKA_CLIENT_CERT_KEY=$(eval "echo \$${ENV}_KAFKA_CLIENT_CERT_KEY") @@ -99,9 +100,9 @@ deploy_cluster() { #family="nginx-api-dev-task" - make_task_def - register_definition - update_result=$(aws ecs update-service --cluster $AWS_ECS_CLUSTER --service $AWS_ECS_SERVICE --task-definition $revision ) + make_task_def $1 $2 $3 $4 + register_definition $1 + update_result=$(aws ecs update-service --cluster $AWS_ECS_CLUSTER --service $1 --task-definition $revision ) #echo $update_result result=$(echo $update_result | $JQ '.service.taskDefinition' ) echo $result @@ -120,8 +121,9 @@ make_task_def(){ "name": "%s", "image": "%s.dkr.ecr.%s.amazonaws.com/%s:%s", "essential": true, - "memory": 1536, - "cpu": 768, + "memory": 768, + "cpu": 512, + "entryPoint": ["%s", "%s", "%s"], "environment": [ { "name": "ENV", @@ -266,11 +268,11 @@ make_task_def(){ } ]' - task_def=$(printf "$task_template" $AWS_ECS_CONTAINER_NAME $AWS_ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $TAG $ENV "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID $KAFKA_URL $DATABASE_URL $AUTHSECRET $TC_API_BASE_URL $TC_API_V3_BASE_URL $TC_API_V4_BASE_URL $TC_API_V5_BASE_URL $MESSAGE_API_BASE_URL $CONNECT_URL $ENABLE_EMAILS $MENTION_EMAIL $REPLY_EMAIL_PREFIX $REPLY_EMAIL_DOMAIN $REPLY_EMAIL_FROM $DEFAULT_REPLY_EMAIL $ENABLE_DEV_MODE $DEV_MODE_EMAIL $LOG_LEVEL $VALID_ISSUERS $PORT "$API_CONTEXT_PATH" "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME "$AUTH0_PROXY_SERVER_URL" $AWS_ECS_CLUSTER $AWS_REGION $AWS_ECS_CLUSTER $ENV) + task_def=$(printf "$task_template" $1 $AWS_ACCOUNT_ID $AWS_REGION $AWS_REPOSITORY $TAG $2 $3 $4 $ENV "$KAFKA_CLIENT_CERT" "$KAFKA_CLIENT_CERT_KEY" $KAFKA_GROUP_ID $KAFKA_URL $DATABASE_URL $AUTHSECRET $TC_API_BASE_URL $TC_API_V3_BASE_URL $TC_API_V4_BASE_URL $TC_API_V5_BASE_URL $MESSAGE_API_BASE_URL $CONNECT_URL $ENABLE_EMAILS $MENTION_EMAIL $REPLY_EMAIL_PREFIX $REPLY_EMAIL_DOMAIN $REPLY_EMAIL_FROM $DEFAULT_REPLY_EMAIL $ENABLE_DEV_MODE $DEV_MODE_EMAIL $LOG_LEVEL $VALID_ISSUERS $PORT "$API_CONTEXT_PATH" "$AUTH0_URL" "$AUTH0_AUDIENCE" $AUTH0_CLIENT_ID "$AUTH0_CLIENT_SECRET" $TOKEN_CACHE_TIME "$AUTH0_PROXY_SERVER_URL" $AWS_ECS_CLUSTER $AWS_REGION $AWS_ECS_CLUSTER $ENV) } register_definition() { - if revision=$(aws ecs register-task-definition --container-definitions "$task_def" --family $family | $JQ '.taskDefinition.taskDefinitionArn'); then + if revision=$(aws ecs register-task-definition --container-definitions "$task_def" --family $1 2> /dev/null | $JQ '.taskDefinition.taskDefinitionArn'); then echo "Revision: $revision" else echo "Failed to register task definition" @@ -282,13 +284,13 @@ register_definition() { check_service_status() { counter=0 sleep 60 - servicestatus=`aws ecs describe-services --service $AWS_ECS_SERVICE --cluster $AWS_ECS_CLUSTER | $JQ '.services[].events[0].message'` + servicestatus=`aws ecs describe-services --service $1 --cluster $AWS_ECS_CLUSTER | $JQ '.services[].events[0].message'` while [[ $servicestatus != *"steady state"* ]] do echo "Current event message : $servicestatus" echo "Waiting for 30 seconds to check the service status...." sleep 30 - servicestatus=`aws ecs describe-services --service $AWS_ECS_SERVICE --cluster $AWS_ECS_CLUSTER | $JQ '.services[].events[0].message'` + servicestatus=`aws ecs describe-services --service $1 --cluster $AWS_ECS_CLUSTER | $JQ '.services[].events[0].message'` counter=`expr $counter + 1` if [[ $counter -gt $COUNTER_LIMIT ]] ; then echo "Service does not reach steady state within 10 minutes. Please check" @@ -300,5 +302,11 @@ check_service_status() { configure_aws_cli push_ecr_image -deploy_cluster -check_service_status + +deploy_cluster $AWS_ECS_SERVICE_API "npm" "run" "startAPI" + +deploy_cluster $AWS_ECS_SERVICE_CONSUMERS "npm" "run" "start" + +check_service_status $AWS_ECS_SERVICE_API + +check_service_status $AWS_ECS_SERVICE_CONSUMERS diff --git a/emails/src/partials/project-files.html b/emails/src/partials/project-files.html index 21e9456..821001c 100644 --- a/emails/src/partials/project-files.html +++ b/emails/src/partials/project-files.html @@ -60,9 +60,7 @@ - - -
View project on Connect
+ View project on Connect
diff --git a/emails/src/partials/project-links.html b/emails/src/partials/project-links.html index 6f01cc7..f70188a 100644 --- a/emails/src/partials/project-links.html +++ b/emails/src/partials/project-links.html @@ -31,9 +31,7 @@ - - -
View project on Connect
+ View project on Connect
diff --git a/emails/src/partials/project-plan.html b/emails/src/partials/project-plan.html index e4388a8..79a5d87 100644 --- a/emails/src/partials/project-plan.html +++ b/emails/src/partials/project-plan.html @@ -35,13 +35,13 @@ - {{#if [notifications.connect.project.planReady]}} + {{#if [connect.action.project.plan.ready]}} Project plan is ready for your project {{/if}} - {{#if [notifications.connect.project.plan.updated]}} + {{#if [connect.action.project.plan.updated]}} Project plan is modified for your project {{/if}} - {{#if [notifications.connect.project.progressModified]}} + {{#if [connect.action.project.updated.progress]}} Your project has made some progress {{/if}} {{#if [notifications.connect.project.phase.transition.active]}} @@ -59,13 +59,16 @@ {{#if [notifications.connect.project.phase.update.scope]}} Scope of the phase {{updatedPhase.name}} updated {{/if}} - {{#if [notifications.connect.project.phase.milestone.transition.active]}} + {{#if [connect.action.project.product.update.spec]}} + Scope of the phase {{updatedPhase.name}} updated + {{/if}} + {{#if [connect.action.timeline.milestone.transition.active]}} A milestone is activated in phase {{updatedPhase.name}} {{/if}} - {{#if [notifications.connect.project.phase.milestone.transition.completed]}} + {{#if [connect.action.timeline.milestone.transition.completed]}} A milestone is completed in phase {{updatedPhase.name}} {{/if}} - {{#if [notifications.connect.project.phase.milestone.waiting.customer]}} + {{#if [connect.action.timeline.milestone.waiting.customer]}} We are waiting for your input at a milestone in the phase {{updatedPhase.name}} {{/if}} @@ -94,9 +97,7 @@ - - -
View plan on Connect
+ View plan on Connect
diff --git a/emails/src/partials/project-specification.html b/emails/src/partials/project-specification.html index bdc81cd..e6258c6 100644 --- a/emails/src/partials/project-specification.html +++ b/emails/src/partials/project-specification.html @@ -30,9 +30,7 @@ - - -
View project on Connect
+ View project on Connect
diff --git a/emails/src/partials/project-status.html b/emails/src/partials/project-status.html index 85a3e1a..ff62ad4 100644 --- a/emails/src/partials/project-status.html +++ b/emails/src/partials/project-status.html @@ -82,9 +82,7 @@ - - -
View project on Connect
+ View project on Connect
diff --git a/emails/src/partials/project-team.html b/emails/src/partials/project-team.html index 0adb462..62c169b 100644 --- a/emails/src/partials/project-team.html +++ b/emails/src/partials/project-team.html @@ -79,9 +79,7 @@ - - -
View project on Connect
+ View project on Connect
diff --git a/emails/src/partials/topics_and_posts.html b/emails/src/partials/topics_and_posts.html index 8276e3c..bf81861 100644 --- a/emails/src/partials/topics_and_posts.html +++ b/emails/src/partials/topics_and_posts.html @@ -171,9 +171,7 @@ - - -
View post on Connect
+ View post on Connect
diff --git a/emails/src/styles/main.css b/emails/src/styles/main.css index 4efa3ea..a73295c 100644 --- a/emails/src/styles/main.css +++ b/emails/src/styles/main.css @@ -210,24 +210,16 @@ table.container, table.footer-container { background-color: #0681FF; height: 30px; } .button-row .main-child .second-child a { - height: 30px; border-radius: 4px; text-align: center; text-decoration: none; display: block; - background-color: #0681FF; } - .button-row .main-child .second-child a table { - background-color: #0681FF; - border-radius: 4px; - height: 30px; } - .button-row .main-child .second-child a table td { - height: 30px; - background-color: #0681FF; - border-radius: 4px; - vertical-align: middle; - text-align: center; - color: #FFFFFF; - font-size: 13px; } + background-color: #0681FF; + padding-top: 6px; + padding-bottom: 5px; + text-align: center; + color: #FFFFFF; + font-size: 13px; } .button-one .main-child .empty-child-one, .button-three .main-child .empty-child-one { width: 216px; } @@ -236,8 +228,6 @@ table.container, table.footer-container { width: 167px; } .button-one .main-child .second-child a, .button-three .main-child .second-child a { width: 167px; } - .button-one .main-child .second-child a table td, .button-three .main-child .second-child a table td { - width: 167px; } .button-two .main-child .empty-child-one { width: 211px; } @@ -246,8 +236,6 @@ table.container, table.footer-container { width: 178px; } .button-two .main-child .second-child a { width: 178px; } - .button-two .main-child .second-child a table td { - width: 178px; } .button-four .main-child .empty-child-one { width: 210px; } @@ -256,8 +244,6 @@ table.container, table.footer-container { width: 180px; } .button-four .main-child .second-child a { width: 180px; } - .button-four .main-child .second-child a table td { - width: 180px; } .copy-link { height: 20px; } diff --git a/emails/src/styles/partials/_base.scss b/emails/src/styles/partials/_base.scss index 8c0c3ae..a3f28e6 100644 --- a/emails/src/styles/partials/_base.scss +++ b/emails/src/styles/partials/_base.scss @@ -310,27 +310,16 @@ table.container, table.footer-container { height: 30px; a { // width: 167px; - height: 30px; border-radius: 4px; text-align: center; text-decoration: none; display: block; background-color: $button-color; - table { - background-color: $button-color; - border-radius: 4px; - height: 30px; - td { - height: 30px; - background-color: $button-color; - border-radius: 4px; - // width: 167px; - vertical-align: middle; - text-align: center; - color: $white-background; - font-size: 13px; - } - } + padding-top: 6px; + padding-bottom: 5px; + text-align: center; + color: $white-background; + font-size: 13px; } } } @@ -348,11 +337,6 @@ table.container, table.footer-container { width: 167px; a { width: 167px; - table { - td { - width: 167px; - } - } } } } @@ -367,11 +351,6 @@ table.container, table.footer-container { width: 178px; a { width: 178px; - table { - td { - width: 178px; - } - } } } } @@ -386,11 +365,6 @@ table.container, table.footer-container { width: 180px; a { width: 180px; - table { - td { - width: 180px; - } - } } } } diff --git a/index-api.js b/index-api.js new file mode 100644 index 0000000..3301805 --- /dev/null +++ b/index-api.js @@ -0,0 +1,12 @@ +const notificationServer = require('./index'); + +// // init database, it will clear and re-create all tables +// notificationServer +// .initDatabase() +// .then(() => notificationServer.startAPI()) +// .catch((e) => { +// console.log(e); // eslint-disable-line no-console +// notificationServer.logger.error('Notification API server errored out'); +// }); + +module.exports = notificationServer.startAPI(); diff --git a/index.js b/index.js index c13c164..e9e821f 100644 --- a/index.js +++ b/index.js @@ -95,15 +95,24 @@ function getAllHandlers() { } /** - * Start the notification server. + * Start the notification API server. */ -function start() { +function startAPI() { + // load app only after config is set + const app = require('./src/app'); + app.start(); +} + +/** + * Start the event bus consumer. + */ +function startKafkaConsumers() { if (_.isEmpty(handlers)) { throw new errors.ValidationError('Missing handler(s).'); } // load app only after config is set const app = require('./src/app'); - app.start(handlers, notificationServiceHandlers); + app.startKafkaConsumer(handlers, notificationServiceHandlers); } /** @@ -122,7 +131,8 @@ module.exports = { addTopicHandler, removeTopicHandler, getAllHandlers, - start, + startAPI, + startKafkaConsumers, initDatabase, addNotificationServiceHandler, diff --git a/package.json b/package.json index 01b3e5d..be1646f 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "./index.js", "scripts": { "start": "node connect/connectNotificationServer", + "startAPI": "node index-api", "lint": "eslint *.js src config test connect || true", "lint:fix": "eslint *.js --fix src config test connect || true", "postinstall": "npm run build", diff --git a/src/app.js b/src/app.js index 540983f..6243b88 100644 --- a/src/app.js +++ b/src/app.js @@ -17,7 +17,7 @@ const models = require('./models'); const Kafka = require('no-kafka'); /** - * Start Kafka consumer. + * Start Kafka consumer for event bus events. * @param {Object} handlers the handlers * @param {Array} notificationServiceHandlers list of notification service handlers */ @@ -74,6 +74,7 @@ function startKafkaConsumer(handlers, notificationServiceHandlers) { }); }); + consumer .init() .then(() => _.each(_.keys(handlers), @@ -85,11 +86,9 @@ function startKafkaConsumer(handlers, notificationServiceHandlers) { } /** - * Start the notification server. - * @param {Object} handlers the handlers - * @param {Array} notificationServiceHandlers list of notification service handlers + * Start the notifications API server. */ -function start(handlers, notificationServiceHandlers) { +function start() { const app = express(); app.set('port', config.PORT); @@ -157,19 +156,21 @@ function start(handlers, notificationServiceHandlers) { } }); - models - .init() - .then(() => { - app.listen(app.get('port'), () => { - logger.info(`Express server listening on port ${app.get('port')}`); - }); - - startKafkaConsumer(handlers, notificationServiceHandlers); - }) - .catch((err) => logger.error(err)); + // models + // .init() + // .then(() => { + // app.listen(app.get('port'), () => { + // logger.info(`Express server listening on port ${app.get('port')}`); + // }); + // }) + // .catch((err) => logger.error(err)); + app.listen(app.get('port'), () => { + logger.info(`Express server listening on port ${app.get('port')}`); + }); } // Exports module.exports = { start, + startKafkaConsumer, };