1- import _ from 'lodash' ;
21import config from 'config' ;
32import moment from 'moment' ;
43import { Op } from 'sequelize' ;
54
65import models from '../../models' ;
7- import { CONNECT_NOTIFICATION_EVENT , COPILOT_OPPORTUNITY_STATUS , COPILOT_REQUEST_STATUS , TEMPLATE_IDS , USER_ROLE } from '../../constants' ;
6+ import {
7+ CONNECT_NOTIFICATION_EVENT ,
8+ COPILOT_OPPORTUNITY_STATUS ,
9+ COPILOT_REQUEST_STATUS ,
10+ TEMPLATE_IDS , USER_ROLE ,
11+ } from '../../constants' ;
812import util from '../../util' ;
913import { createEvent } from '../../services/busApi' ;
1014import { getCopilotTypeLabel } from '../../utils/copilot' ;
@@ -17,85 +21,91 @@ const resolveTransaction = (transaction, callback) => {
1721 return models . sequelize . transaction ( callback ) ;
1822} ;
1923
20- module . exports = ( req , data , existingTransaction ) => {
24+ module . exports = async ( req , data , existingTransaction ) => {
2125 const { projectId, copilotRequestId, opportunityTitle, type, startDate } = data ;
2226
23- return resolveTransaction ( existingTransaction , transaction =>
24- models . Project . findOne ( {
25- where : { id : projectId , deletedAt : { $eq : null } } ,
26- } , { transaction } )
27- . then ( ( existingProject ) => {
28- if ( ! existingProject ) {
29- const err = new Error ( `active project not found for project id ${ projectId } ` ) ;
30- err . status = 404 ;
31- throw err ;
32- }
33- return models . CopilotRequest . findByPk ( copilotRequestId , { transaction } )
34- . then ( ( existingCopilotRequest ) => {
35- if ( ! existingCopilotRequest ) {
36- const err = new Error ( `no active copilot request found for copilot request id ${ copilotRequestId } ` ) ;
37- err . status = 404 ;
38- throw err ;
39- }
40-
41- return existingCopilotRequest . update ( {
42- status : COPILOT_REQUEST_STATUS . APPROVED ,
43- } , { transaction } ) . then ( ( ) => models . CopilotOpportunity
44- . findOne ( {
45- where : {
46- projectId,
47- type : data . type ,
48- status : {
49- [ Op . in ] : [ COPILOT_OPPORTUNITY_STATUS . ACTIVE ] ,
50- }
51- } ,
52- } )
53- . then ( ( existingCopilotOpportunityOfSameType ) => {
54- if ( existingCopilotOpportunityOfSameType ) {
55- const err = new Error ( 'There\'s an active opportunity of same type already!' ) ;
56- _ . assign ( err , {
57- status : 403 ,
58- } ) ;
59- throw err ;
60- }
61- return models . CopilotOpportunity
62- . create ( data , { transaction } ) ;
63- } ) )
64- . then ( async ( opportunity ) => {
65- const roles = await util . getRolesByRoleName ( USER_ROLE . TC_COPILOT , req . log , req . id ) ;
66- const { subjects = [ ] } = await util . getRoleInfo ( roles [ 0 ] , req . log , req . id ) ;
67- const emailEventType = CONNECT_NOTIFICATION_EVENT . EXTERNAL_ACTION_EMAIL ;
68- const copilotPortalUrl = config . get ( 'copilotPortalUrl' ) ;
69- req . log . info ( "Sending emails to all copilots about new opportunity" ) ;
70-
71- const sendNotification = ( userName , recipient ) => createEvent ( emailEventType , {
72- data : {
73- user_name : userName ,
74- opportunity_details_url : `${ copilotPortalUrl } /opportunity/${ opportunity . id } ` ,
75- work_manager_url : config . get ( 'workManagerUrl' ) ,
76- opportunity_type : getCopilotTypeLabel ( type ) ,
77- opportunity_title : opportunityTitle ,
78- start_date : moment ( startDate ) . format ( "DD-MM-YYYY" ) ,
79- } ,
80- sendgrid_template_id : TEMPLATE_IDS . CREATE_REQUEST ,
81- recipients : [ recipient ] ,
82- version : 'v3' ,
83- } , req . log ) ;
84-
85- subjects . forEach ( subject => sendNotification ( subject . handle , subject . email ) ) ;
86-
87- // send email to notify via slack
88- sendNotification ( 'Copilots' , config . copilotsSlackEmail ) ;
89-
90- req . log . info ( "Finished sending emails to copilots" ) ;
91-
92- return opportunity ;
93- } )
94- . catch ( ( err ) => {
95- transaction . rollback ( ) ;
96- return Promise . reject ( err ) ;
97- } ) ;
98- } ) ;
99- } ) ,
100- ) ;
27+ return resolveTransaction ( existingTransaction , async ( transaction ) => {
28+ try {
29+ req . log . debug ( 'approveRequest: finding project' , { projectId } ) ;
30+
31+ const existingProject = await models . Project . findOne ( {
32+ where : { id : projectId , deletedAt : { $eq : null } } ,
33+ transaction,
34+ } ) ;
35+
36+ if ( ! existingProject ) {
37+ const err = new Error ( `active project not found for project id ${ projectId } ` ) ;
38+ err . status = 404 ;
39+ throw err ;
40+ }
41+
42+ const copilotRequest = await models . CopilotRequest . findByPk ( copilotRequestId , { transaction } ) ;
43+ req . log . debug ( 'approveRequest: found copilot request' , { copilotRequestId : copilotRequest . id } ) ;
44+
45+ if ( ! copilotRequest ) {
46+ const err = new Error ( `no active copilot request found for copilot request id ${ copilotRequestId } ` ) ;
47+ err . status = 404 ;
48+ throw err ;
49+ }
50+
51+ await copilotRequest . update ( { status : COPILOT_REQUEST_STATUS . APPROVED } , { transaction } ) ;
52+ req . log . debug ( 'Copilot request status updated to APPROVED' , { copilotRequestId } ) ;
53+
54+ const existingOpportunity = await models . CopilotOpportunity . findOne ( {
55+ where : {
56+ projectId,
57+ type : data . type ,
58+ status : { [ Op . in ] : [ COPILOT_OPPORTUNITY_STATUS . ACTIVE ] } ,
59+ } ,
60+ transaction,
61+ } ) ;
62+
63+ if ( existingOpportunity ) {
64+ const err = new Error ( 'There\'s an active opportunity of same type already!' ) ;
65+ err . status = 403 ;
66+ throw err ;
67+ }
68+
69+ const opportunity = await models . CopilotOpportunity . create ( data , { transaction } ) ;
70+ req . log . debug ( 'Created new copilot opportunity' , { opportunityId : opportunity . id } ) ;
71+
72+ // Send notifications
73+ try {
74+ const roles = await util . getRolesByRoleName ( USER_ROLE . TC_COPILOT , req . log , req . id ) ;
75+ req . log . debug ( 'Roles fetched' , { roles } ) ;
76+
77+ const { subjects = [ ] } = await util . getRoleInfo ( roles [ 0 ] , req . log , req . id ) ;
78+ const emailEventType = CONNECT_NOTIFICATION_EVENT . EXTERNAL_ACTION_EMAIL ;
79+ const copilotPortalUrl = config . get ( 'copilotPortalUrl' ) ;
80+ req . log . info ( 'Sending emails to all copilots about new opportunity' ) ;
81+
82+ const sendNotification = ( userName , recipient ) => createEvent ( emailEventType , {
83+ data : {
84+ user_name : userName ,
85+ opportunity_details_url : `${ copilotPortalUrl } /opportunity/${ opportunity . id } ` ,
86+ work_manager_url : config . get ( 'workManagerUrl' ) ,
87+ opportunity_type : getCopilotTypeLabel ( type ) ,
88+ opportunity_title : opportunityTitle ,
89+ start_date : moment ( startDate ) . format ( 'DD-MM-YYYY' ) ,
90+ } ,
91+ sendgrid_template_id : TEMPLATE_IDS . CREATE_REQUEST ,
92+ recipients : [ recipient ] ,
93+ version : 'v3' ,
94+ } , req . log ) ;
95+
96+ subjects . forEach ( subject => sendNotification ( subject . handle , subject . email ) ) ;
97+
98+ // send email to notify via slack
99+ sendNotification ( 'Copilots' , config . copilotsSlackEmail ) ;
100+ req . log . info ( 'Finished sending emails to copilots' ) ;
101+ } catch ( emailErr ) {
102+ req . log . error ( 'Error sending notifications' , { error : emailErr } ) ;
103+ }
104+
105+ return opportunity ;
106+ } catch ( err ) {
107+ req . log . error ( 'approveRequest failed' , { error : err } ) ;
108+ throw err ; // let outer transaction handle rollback
109+ }
110+ } ) ;
101111} ;
0 commit comments