diff --git a/Web Services/LibUserCreate/LibUserCreate.js b/Web Services/LibUserCreate/LibUserCreate.js index fbeb29a..e78671e 100644 --- a/Web Services/LibUserCreate/LibUserCreate.js +++ b/Web Services/LibUserCreate/LibUserCreate.js @@ -1,812 +1,770 @@ -var logger = require('../log'); -var Q = require('q'); -var moment = require('moment'); -var momentTz = require('moment-timezone'); - +const logger = require('../log'); +const momentTz = require('moment-timezone'); module.exports.getCredentials = function () { var options = {}; options.customerAlias = "CUSTOMERALIAS"; options.databaseAlias = "DATABASEALIAS"; options.userId = "USERID"; options.password = "PASSWORD"; - options.clientId = 'DEVELOPERKEY'; - options.clientSecret = 'DEVELOPERSECRET'; + options.clientId = "DEVELOPERKEY"; + options.clientSecret = "DEVELOPERSECRET"; return options; }; - -module.exports.main = function (ffCollection, vvClient, response) { - /*Script Name: LibUserCreate - Customer: Visual Vault - Purpose: This process will allow a user to be created with various potential options. - Those options will be turned on or off depending on what is passed to the NodeJS process. - NOTE: If the user is found to exist already, whether enabled or disabled, this script will simply return that info. No other actions will be taken. - IMPORTANT CONFIGURATION NOTE: A query must be configured in Default queries (NOT in form data queries) that reflects the below: +module.exports.main = async function (ffCollection, vvClient, response) { + /* + Script Name: LibUserCreate + Customer: Visual Vault + Purpose: This process will allow a user to be created with various potential options. + Those options will be turned on or off depending on what is passed to the NodeJS process. + NOTE: If the user is found to exist already, whether enabled or disabled, this script will simply return that info. No other actions will be taken. + Pre-conditions: A query must be configured in Default queries (NOT in form data queries) that reflects the below: SELECT UsUserID, UsId, UsFirstName, UsMiddleInit, UsLastName, UsEmailAddress, UsSiteID, UsEnabled FROM dbo.Users - Parameters: The following represent variables passed into the function from an array: - User Id - (String, Required) - Email Address - (String, Required) - Site Name - (String, Required) - Group List - (String, Required) String of groups separated by commas. May be an empty string if no groups are desired. - First Name - (String, Optional) - Middle Initial - (String, Optional) - Last Name - (String, Optional) - Password - (String, Optional) If blank or not passed in, random password will be generated - Folder Path - (String, Optional) If blank or not passed in, a folder will not be created. - Folder Permissions - (String, Optional unless folder path was provided and config variable forceSecurities set to true.) Pass in 'Viewer', 'Editor', or 'Owner' - IMPORTANT NOTE: This feature should not be used without discussion. Assigning user-based security to folders can cause performance issues. - 'Viewer' - The created user account will be assigned viewer permissions to the created folder - 'Editor' - The created user account will be assigned editor permissions to the created folder - 'Owner' - The created user account will be assigned owner permissions to the created folder - Send Email - (String, Required) Pass in 'Standard', 'Custom', or 'Both'. - 'Standard' will send only the VV-generated username and password email. - 'Custom' allows Email Subject and Email Body to be passed in. - 'Both' will send both the VV-generated email and the custom email passed in. - Email Subject - (String, Required when Send Email is Custom or Both) Subject of the username and password email - Email Body - (String, Required when Send Email is Custom or Both) Body of username and password email. - When Send Email is 'Custom', [Username] and [Password] must be included in the email body. - Related Records - (Array of Strings, optional) The Form IDs of any records that the Communication Log should be related to. - Other Fields - (Object, optional) This is an object of other field names with field values to update on the Communications Log. - Example format: - { - name: 'Other Fields', value: { - "Individual ID": indivId, - "Record ID": formId + Parameters: The following represent variables passed into the function from an array: + User Id - (String, Required) + Email Address - (String, Required) + Site Name - (String, Required) + Group List - (String, Required) String of groups separated by commas. May be an empty string if no groups are desired. + First Name - (String, Optional) + Middle Initial - (String, Optional) + Last Name - (String, Optional) + Password - (String, Optional) If blank or not passed in, random password will be generated + Folder Path - (String, Optional) If blank or not passed in, a folder will not be created. + Folder Permissions - (String, Optional unless folder path was provided and config variable forceSecurities set to true.) Pass in 'Viewer', 'Editor', or 'Owner' + IMPORTANT NOTE: This feature should not be used without discussion. Assigning user-based security to folders can cause performance issues. + 'Viewer' - The created user account will be assigned viewer permissions to the created folder + 'Editor' - The created user account will be assigned editor permissions to the created folder + 'Owner' - The created user account will be assigned owner permissions to the created folder + Send Email - (String, Required) Pass in 'Standard', 'Custom', or 'Both'. + 'Standard' will send only the VV-generated username and password email. + 'Custom' allows Email Subject and Email Body to be passed in. + 'Both' will send both the VV-generated email and the custom email passed in. + Email Subject - (String, Required when Send Email is Custom or Both) Subject of the username and password email + Email Body - (String, Required when Send Email is Custom or Both) Body of username and password email. + When Send Email is 'Custom', [Username] and [Password] must be included in the email body. + Related Records - (Array of Strings, optional) The Form IDs of any records that the Communication Log should be related to. + Other Fields - (Object, optional) This is an object of other field names with field values to update on the Communications Log. + Example format: + { + name: 'Other Fields', value: { + "Individual ID": indivId, + "Record ID": formId + } } - } - - Return Array: The following represents the array of information returned to the calling function. This is a standardized response. - Any item in the array at points 2 or above can be used to return multiple items of information. - 0 - Status: Success, Minor Error, Error - //Minor Error represents any errors that occurred after the user account was created. - //If updating form record fields to reflect an "account created" status, update the fields when Success or Minor Error returned. - 1 - Message - //On Error response, "User Exists" and "User Disabled" messages should be handled specifically. - 2 - User GUID - Psuedo code: - 1. Validate passed in parameters - 2. Search for the user ID provided to see if the user already exists. Use a custom query to return disabled users too. - a. If user already exists, process will end and notify user of the duplicate. UsID and SiteID are returned. - 3. Search the Site Name passed in to determine if it exists. - a. If site already exists then it will save the SiteID pass that on. - b. If site does not exist then it will create a site by running postSite - 4. If groups were passed in, check if the groups exist using getGroups. - a. If any of the groups do not exist, process will end and notify user of the error. - 5. Start of user creation logic. If a random password needs to be generated, do it now. - 6. Create the user account by calling postUsers. - a. If this step is completed successfully, any handled errors occurring later in the code are pushed into the error array and returned as minor errors. - Once the user has been created, want to be sure the user GUID and site ID are passed back so user creation is reflected on the form record. - 7. If groups were passed in, add the user to groups using addUserToGroup. - 8. If a custom email needs to be sent to the created user, send it using postEmails. - 9. If a communications log needs to be created to reflect the welcome email, create it with postForms. - a. Relate the created communication log to each Form ID in the relateToRecords array. - 10. If user has entered a folder path, determine if a folder exists in the destination location, to prevent duplication. Call getFolders. - a. If the folder was not found, create the folder suing postFolderByPath. - b. Add security permission for the user on that folder. - 11. If all steps above completed successfully, check if any errors were logged in the error array. - a. If minor errors occurred, return ‘Minor Error’ with details. - b. If no errors, return ‘Success’ - Date of Dev: 12/4/2018 - Last Rev Date: 06/17/2020 - Revision Notes: - 12/04/2018 - Alex Rhee: Initial creation of the business process. - 12/18/2018 - Alex Rhee: Code reorganized and rewritten to follow promise chaining examples. Still missing folder provisioning. - 12/20/2018 - Alex Rhee: Script is now fully functional and adding folder securities works. Need to now clean up code and test further. - 1/2/19 - Alex Rhee: Script has been cleaned up, commented, bug tested. - 1/18/19 - Alex Rhee: Made sure all API calls are being measured by Resp.meta.status === 200. - 09/25/2019 - Removed uppercase I and lowercase L at customer request. - 12/19/2019 - Kendra Austin: Script rewrite. Add hyphen (-) to user ID chars, add configuration to use or not use Comm Log, send only one custom email. - 01/08/2020 - Kendra Austin: Switch out getUsers to a custom query. Allows disabled users to be returned so better error handling. - 01/16/2020 - Kendra Austin: Add Other Fields to parameters for Comm Log creation. Make comments about folder security. - 06/17/2020 - Kendra Austin: Do not use $ in passwords. $& is a .replace() method shortcut. - */ - - logger.info('Start of the process UserCreate at ' + Date()); - - //---------------CONFIG OPTIONS--------------- - //Possible characters for password. Do not include $. - var passwordChars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789!@#%^&*()_+"; - - //Allowed characters for User ID - var userNameChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.@+-"; - - var PasswordLength = 8; //Password length when a random password is generated - var minPasswordLength = 5; //Minimum length for password when one is passed in to be used. - var SysChangePass = 'true'; //Require user to change password on first login. Set to 'true' for required; set to 'false' for not required. - var createCommLog = true; //Dictates whether a communication log is created to reflect the custom welcome email sent to the user. Passwords are redacted. - var commLogTemplateID = 'Communications Log'; //When createCommLog is true, this is the template name of the Communications Log. Used to post form. - var userQueryName = 'User Lookup'; //The name of the custom query in Default queries (NOT in form data queries) - var timeZone = 'America/Phoenix'; //Set the local time zone here. Used when posting Communications Logs - - /* Reference List of Moment Timezones: - Pacific Time: America/Los_Angeles - Arizona Time: America/Phoenix - Central Time: America/Chicago - Eastern Time: America/New_York + Return Array: The following represents the array of information returned to the calling function. This is a standardized response. + Any item in the array at points 2 or above can be used to return multiple items of information. + 0 - Status: Success, Minor Error, Error + //Minor Error represents any errors that occurred after the user account was created. + //If updating form record fields to reflect an "account created" status, update the fields when Success or Minor Error returned. + 1 - Message + //On Error response, "User Exists" and "User Disabled" messages should be handled specifically. + 2 - User GUID + Psuedo code: + 1. Validate passed in parameters + 2. Search for the user ID provided to see if the user already exists. Use a custom query to return disabled users too. + a. If user already exists, process will end and notify user of the duplicate. UsID and SiteID are returned. + 3. Search the Site Name passed in to determine if it exists. + a. If site already exists then it will save the SiteID pass that on. + b. If site does not exist then it will create a site by running postSite + 4. If groups were passed in, check if the groups exist using getGroups. + a. If any of the groups do not exist, process will end and notify user of the error. + 5. Start of user creation logic. If a random password needs to be generated, do it now. + 6. Create the user account by calling postUsers. + a. If this step is completed successfully, any handled errors occurring later in the code are pushed into the error array and returned as minor errors. + Once the user has been created, want to be sure the user GUID and site ID are passed back so user creation is reflected on the form record. + 7. If groups were passed in, add the user to groups using addUserToGroup. + 8. If a custom email needs to be sent to the created user, send it using postEmails. + 9. If a communications log needs to be created to reflect the welcome email, create it with postForms. + a. Relate the created communication log to each Form ID in the relateToRecords array. + 10. If user has entered a folder path, determine if a folder exists in the destination location, to prevent duplication. Call getFolders. + a. If the folder was not found, create the folder suing postFolderByPath. + b. Add security permission for the user on that folder. + 11. If all steps above completed successfully, check if any errors were logged in the error array. + a. If minor errors occurred, return ‘Minor Error’ with details. + b. If no errors, return ‘Success’ + Date of Dev: 12/4/2018 + Last Rev Date: 06/17/2020 + Revision Notes: + 12/04/2018 - Alex Rhee: Initial creation of the business process. + 12/18/2018 - Alex Rhee: Code reorganized and rewritten to follow promise chaining examples. Still missing folder provisioning. + 12/20/2018 - Alex Rhee: Script is now fully functional and adding folder securities works. Need to now clean up code and test further. + 1/2/19 - Alex Rhee: Script has been cleaned up, commented, bug tested. + 1/18/19 - Alex Rhee: Made sure all API calls are being measured by Resp.meta.status === 200. + 09/25/2019 - Removed uppercase I and lowercase L at customer request. + 12/19/2019 - Kendra Austin: Script rewrite. Add hyphen (-) to user ID chars, add configuration to use or not use Comm Log, send only one custom email. + 01/08/2020 - Kendra Austin: Switch out getUsers to a custom query. Allows disabled users to be returned so better error handling. + 01/16/2020 - Kendra Austin: Add Other Fields to parameters for Comm Log creation. Make comments about folder security. + 06/17/2020 - Kendra Austin: Do not use $ in passwords. $& is a .replace() method shortcut. + 08/03/2022 - Franco Petosa Ayala - General Refactor. + Remove library q and moment as are no longer used. + Fix the handle response of vvClient.library.getFolders as it returns an obj instead of an array. + 01/06/2023 - Franco Petosa Ayala - Fix password min-length validation + Fix getGroups helper function error messages handler */ - + logger.info("Start of the process SCRIPT NAME HERE at " + Date()); + /* -------------------------------------------------------------------------- */ + /* Response and error handling variables */ + /* -------------------------------------------------------------------------- */ + // Response array + const outputCollection = []; + // Array for capturing error messages that may occur during the process + const errorLog = []; + /* -------------------------------------------------------------------------- */ + /* Configurable Variables */ + /* -------------------------------------------------------------------------- */ + const getUserDataQuery = 'UserCreateQueryForLib' + const minPasswordLength = 5; + const PasswordLength = 8; + const passwordChars = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789!@#%^&*()_+"; //forceSecurities: KEEP THIS FALSE UNLESS GIVEN EXPLICIT PERMISSION. //Force the user to input security permissions when passing in folder paths. //If set to true an error will be sent back if the user passes in a folder but no folder security or an invalid folder security. //If false and not passed in, folder is created without security. - var forceSecurities = false; - - //------------------END CONFIG OPTIONS---------------- - - //Passed-in Parameters - //Create variables for the values the user inputs when creating their User - var userID = ffCollection.getFormFieldByName('User Id'); - var siteName = ffCollection.getFormFieldByName('Site Name'); - var firstName = ffCollection.getFormFieldByName('First Name'); - var middleInitial = ffCollection.getFormFieldByName('Middle Initial'); - var lastName = ffCollection.getFormFieldByName('Last Name'); - var emailAddress = ffCollection.getFormFieldByName('Email Address'); - var password = ffCollection.getFormFieldByName('Password'); - var groupList = ffCollection.getFormFieldByName('Group List'); - - //Admin options for User Creation - var sendEmail = ffCollection.getFormFieldByName('Send Email'); - var relateToRecords = ffCollection.getFormFieldByName('Related Records'); - var otherFields = ffCollection.getFormFieldByName('Other Fields'); - var folderPath = ffCollection.getFormFieldByName('Folder Path'); - var securityLevel = ffCollection.getFormFieldByName('Folder Permissions'); - - //Email information coming from client side or intermediary node script - var subjectField = ffCollection.getFormFieldByName('Email Subject'); - var bodyField = ffCollection.getFormFieldByName('Email Body'); - - //Script Variables - var errors = []; //Used to hold errors as they are found, to return together. - var createFolder = false; //Used to measure whether folder attributes were passed in and a folder should be created. - var siteID = ''; //Used to store the site ID that the user wil be assigned to. - var siteExists = false; //Used to determine if the site exists or if it needs to be created. - var userGUID = ''; //Used to store the user GUID to be returned to the calling function. - var groupsPassed = false; //Used to determine if groups were passed in to be assigned to the user. - var groupIdArray = []; //Used to hold the IDs of all groups the user should be added to. - var bodyWithoutPassword = ''; //Used to hold the version of the welcome email with the username in it but password redacted. - var folderId = ''; //Used to hold the folder ID, stored to apply security permissions. - var userPassword = ''; //Used to hold the user's password. - - //Initialization of the return object - var returnObj = []; - - /******************************************************* - * HELPER FUNCTIONS * - ********************************************************/ - //Character validation function, used for usernames and passwords - var characterValidation = function (phrase, type) { - var errorList = ''; - var charList = ''; - - if (type == 'password') { - charList = passwordChars; - } - else if (type == 'username') { - charList = userNameChars; - } - else { - return ['Error', 'Invalid type']; - } - - for (i = 0; i < phrase.length; i++) { - if (charList.indexOf(phrase[i]) < 0) { - errorList += phrase[i] + ' is an invalid character for ' + type + 's. '; + const forceSecurities = false; + const SysChangePass = 'true'; //Require user to change password on first login. Set to 'true' for required; set to 'false' for not required. + const createCommLog = true; //Dictates whether a communication log is created to reflect the custom welcome email sent to the user. Passwords are redacted. + const timeZone = 'America/Phoenix'; //Set the local time zone here. Used when posting Communications Logs + const commLogTemplateID = 'Communications Log'; //When createCommLog is true, this is the template name of the Communications Log. Used to post form. + /* -------------------------------------------------------------------------- */ + /* Script 'Global' Variables */ + /* -------------------------------------------------------------------------- */ + let siteID = ''; //Used to store the site ID that the user wil be assigned to. + const groupObjListToAdd = []; //Used to hold the IDs of all groups the user should be added to. + let folderId = ''; //Used to hold the folder ID, stored to apply security permissions. + /* -------------------------------------------------------------------------- */ + /* Helper Functions */ + /* -------------------------------------------------------------------------- */ + function getFieldValueByName(fieldName, isRequired = true) { + /* + Check if a field was passed in the request and get its value + Parameters: + fieldName: The name of the field to be checked + isRequired: If the field is required or not + */ + let resp = ''; + try { + // Tries to get the field from the passed in arguments + const field = ffCollection.getFormFieldByName(fieldName); + if (!field && isRequired) { + throw new Error(`The ${fieldName} parameter was not supplied.`); + } else if (field) { + // If the field was found, get its value + let fieldValue = field.value ? field.value : null; + if (typeof fieldValue === "string") { + // Remove any leading or trailing spaces + fieldValue = fieldValue.trim(); + } + if (fieldValue) { + // Sets the field value to the response + resp = fieldValue; + } else if (isRequired) { + // If the field is required and has no value, throw an error + throw new Error(`The ${fieldName} parameter was not supplied.`); + } } + } catch (error) { + // If an error was thrown, add it to the error log + errorLog.push(error.message); } - - if (errorList != '') { - return ['Error', errorList]; - } - else { - return ['Success']; - } + return resp; } - - var arrayTrimmer = function (array) { - arrayHolder = []; - for (i = 0; i < array.length; i++) { - arrayHolder.push(array[i].trim()); - } - return arrayHolder; + function LogInfo(message, isSuccess, vvClientRes = null){ + /* + Add message to the info logg and in case it is an minor error add it to the errorlog array + Parameters: + message: string value that represents the message sent to the logg + isSuccess: boolean value that determinates if the message has to be added to the errorlog array + vvClientRes: JSON response from a vvClient API method + */ + logger.info(message); + if(!isSuccess){ + errorLog.push(message); + } + return vvClientRes } - - var randomPassword = function () { - var text = ""; - - for (var i = 0; i < PasswordLength; i++) - text += passwordChars.charAt(Math.floor(Math.random() * passwordChars.length)); - - return text; - }; - - /******************************************************* - * MAIN FUNCTION * - ********************************************************/ - - - //Start the promise chain - var result = Q.resolve(); - - return result.then(function () { - //Validate passed in fields - //User Id - if (!userID || !userID.value) { - errors.push("The User Id parameter was not supplied."); - } - else { - userID = userID.value; - - //Validate that the user ID provided includes only accepted characters - var userIdValidation = characterValidation(userID, 'username'); - if (userIdValidation[0] == 'Error') { - errors.push(userIdValidation[1]); + function parseRes(vvClientRes) { + /* + Generic JSON parsing function + Parameters: + vvClientRes: JSON response from a vvClient API method + */ + try { + // Parses the response in case it's a JSON string + const jsObject = JSON.parse(vvClientRes); + // Handle non-exception-throwing cases: + if (jsObject && typeof jsObject === "object") { + vvClientRes = jsObject; } + } catch (e) { + // If an error occurs, it's because the resp is already a JS object and doesn't need to be parsed } - - //Email Address - if (!emailAddress || !emailAddress.value) { - errors.push("The Email Address parameter was not supplied."); - } - else { - emailAddress = emailAddress.value; - - //Validate that the email address is a valid email - var emailReg = new RegExp('\\b[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b'); - if (!emailReg.test(emailAddress)) { - errors.push("The Email Address provided is not a valid email format."); + return vvClientRes; + } + function checkMetaAndStatus(vvClientRes, ignoreStatusCode = 999) { + /* + Checks that the meta property of a vvClient API response object has the expected status code + Parameters: + vvClientRes: Parsed response object from a vvClient API method + ignoreStatusCode: An integer status code for which no error should be thrown. If you're using checkData(), make sure to pass the same param as well. + */ + if (!vvClientRes.meta) { + throw new Error(); + } + const status = vvClientRes.meta.status; + if (status != 200 && status != 201 && status != ignoreStatusCode && status != 400) { + throw new Error(status); + } + return vvClientRes; + } + function checkDataPropertyExists(vvClientRes, ignoreStatusCode = 999) { + /* + Checks that the data property of a vvClient API response object exists + Parameters: + res: Parsed response object from the API call + ignoreStatusCode: An integer status code for which no error should be thrown. If you're using checkMeta(), make sure to pass the same param as well. + */ + const status = vvClientRes.meta.status; + if (status != ignoreStatusCode) { + // If the data property doesn't exist, throw an error + if (!vvClientRes.data) { + throw new Error('checkDataPropertyExists'); } } - - //Site Name - if (!siteName || !siteName.value) { - errors.push("The Site Name parameter was not supplied."); - } - else { - siteName = siteName.value; - } - - //Group List. Allow an empty string. - if (!groupList || (!groupList.value && groupList.value.trim() != '')) { - errors.push("The Group List parameter was not supplied."); - } - else { - groupList = groupList.value; - - if (groupList.trim() == '') { - groupsPassed = false; + return vvClientRes; + } + function checkDataIsNotEmpty(vvClientRes, ignoreStatusCode = 999) { + /* + Checks that the data property of a vvClient API response object is not empty + Parameters: + res: Parsed response object from the API call + ignoreStatusCode: An integer status code for which no error should be thrown. If you're using checkMeta(), make sure to pass the same param as well. + */ + const status = vvClientRes.meta.status; + if (status != ignoreStatusCode) { + const dataIsArray = Array.isArray(vvClientRes.data); + const dataIsObject = typeof vvClientRes.data === "object"; + const isEmptyArray = dataIsArray && vvClientRes.data.length == 0; + const isEmptyObject = dataIsObject && Object.keys(vvClientRes.data).length == 0; + // If the data is empty, throw an error + if (isEmptyArray || isEmptyObject) { + throw new Error('checkDataIsNotEmpty'); } - else { - groupsPassed = true; + // If it is a Web Service response, check that the first value is not an Error status + if (dataIsArray) { + const firstValue = vvClientRes.data[0]; + if (firstValue == "Error") { + throw new Error('checkDataIsNotEmpty'); + } } } - - //Send Email - if (!sendEmail || !sendEmail.value) { - errors.push("The Send Email parameter was not supplied."); - } - else { - sendEmail = sendEmail.value; - - //Validate that Send Email is an appropriate value. - if (sendEmail != 'Standard' && sendEmail != 'Custom' && sendEmail != 'Both') { - errors.push("The Send Email parameter must be 'Standard', 'Custom', or 'Both'."); + return vvClientRes; + } + function verifySingleData(vvClientRes){ + /* + Verify data returned is not duplicated + Parameters: + vvClientRes: Parsed response object from the API call + */ + if(vvClientRes.data.length > 1){ + throw new Error('verifySingleData'); + } + return vvClientRes + } + function checkPasswordIsValid(password){ + /* + This function verifies the passed in password is valid. + Firstly checks if every digit that the password contains are valid + Secondly the password length must be at least of 5 digits long + Parameters: + Password: string value that represents the passed in passoword + */ + //Verify the digits that the password contains are all valid + for (let i = 0; i < password.length; i++) { + if(!passwordChars.includes(password[i])){ + errorLog.push(`${password[i]} is an invalid character for passwords. `); } } - - //Validate conditionally required inputs - //First Name - if (!firstName || !firstName.value) { - //Not required. Set to empty string to avoid undefined errors. - firstName = ''; - } - else { - firstName = firstName.value; - } - - //Middle Initial - if (!middleInitial || !middleInitial.value) { - //Not required. Set to empty string to avoid undefined errors. - middleInitial = ''; - } - else { - middleInitial = middleInitial.value; - } - - //Last Name - if (!lastName || !lastName.value) { - //Not required. Set to empty string to avoid undefined errors. - lastName = ''; - } - else { - lastName = lastName.value; - } - - //Password - if (!password || !password.value || password.value.trim() == '') { - //Not required. Set to empty string to avoid undefined errors. Will generate random password for user. - password = ''; - } - else { - password = password.value; - - //Validate that the password is a valid length. - if (password.length < minPasswordLength) { - errors.push('The password must be at least ' + minPasswordLength + ' long. '); - } - - //Validate that the password provided includes only accepted characters - var passwordValidation = characterValidation(password, 'password'); - if (passwordValidation[0] == 'Error') { - errors.push(passwordValidation[1]); + //Verify the password is at least 5 digits long + if(password.length < minPasswordLength){ + errorLog.push(`The password must be at least ${minPasswordLength} digits long. `); + } + } + function checkUserIDisValid(userID){ + /* + Verify the passed in userID contains only valid digits + Parameters: + userID: string value that represents the passed in user ID + */ + //Valid digits for UserID + const userNameChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.@+-"; + //Verify the digits that the userID contains are all valid + for (let i = 0; i < userID.length; i++) { + if(!userNameChars.includes(userID[i])){ + errorLog.push(`${userID[i]} is an invalid character for userID. `); } } - - //Folder Path - if (!folderPath || !folderPath.value || folderPath.value.trim() == '') { - //Not required. Set to empty string to avoid undefined errors. - folderPath = ''; - createFolder = false; - } - else { - folderPath = folderPath.value; - createFolder = true; - } - - //Folder Permissions - if (createFolder) { - if (!securityLevel || !securityLevel.value || securityLevel.value.trim() == '') { - if (forceSecurities) { - errors.push('The Folder Permissions parameter was not supplied.'); + } + function checkEmailAddressIsValid(emailAddress){ + /* + This function verifies if the passed in Email Address is valid + Parameters: + emailAddress: string value that represents the passed in email address + */ + const emailReg = new RegExp('\\b[A-Za-z0-9._%+-]+\@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}\\b'); + const isEmailValid = emailReg.test(emailAddress); + if (!isEmailValid) { + errorLog.push("The Email Address provided is not a valid email format."); + } + } + function getUserData(userID){ + /* + Call getCustomQueryResultsByName to get the user data from VV with the passed in user ID + Parameters: + UserID: string value that represents the passed in User ID + */ + const queryParams = { filter: `[UsUserID] = '${userID}'`}; + return vvClient.customQuery + .getCustomQueryResultsByName(getUserDataQuery, queryParams) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => verifySingleData(res)) + .then((res) => LogInfo(`Searched for existing users with ID ${userID}. None found. Continuing the process.`, true, res)) + .catch((error) =>{ + switch (error.message) { + case '404': + throw new Error(`The custom query to find users was not found. Please ensure that a query named ${getUserDataQuery} has been configured in the default queries area.`); + case 'verifySingleData': + throw new Error(`More than one user was found for ID: ${userID}. This is an invalid state. Please notify a system administrator.`); + default: + throw new Error(`There was an error when searching for the user ${userID}.`); } - else { - //Not required. Set to empty string to avoid undefined errors. - securityLevel = ''; + }) + } + function getSiteData(siteName){ + /* + Call getSites to get the site data from VV with the passed in site name + Parameters: + siteName: string value that represents the passed in site name + */ + const objectParam = { + q: `name eq '${siteName}'`, + fields: 'id, name' + } + return vvClient.sites + .getSites(objectParam) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .catch(() => {throw new Error(`The call to search for the site name ${siteName} returned with an error.`);}) + } + function createSite(siteName){ + /* + Call postSites to create the site on VV with the passed in site name + Parameters: + siteName: string value that represents the passed in site name + */ + const newSiteData = { + name: siteName, + description: siteName, + } + return vvClient.sites + .postSites(null, newSiteData) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .then((res) => LogInfo('Site created successfully.',true, res)) + .catch(() => { throw new Error(`The call to create a site with name ${siteName} returned with an error.`); }) + } + function getGroups(){ + /* + Call getGroups to get all the existing groups on VV + Parameters: + -No parameters required to run the function + */ + const getGroupsParams = { + q: '', + fields: 'id,name,description' + } + return vvClient.groups + .getGroups(getGroupsParams) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .catch((error) => { + const { message } = error; + if(message === 'checkDataIsNotEmpty')throw new Error('No user permission groups were found to exist in the system. Please contact a system administrator.'); + else throw new Error('The call to get existing group names returned with an error.'); + }); + } + function generatePassword(){ + /* + Build up the password randomly + Parameters: + -No parameters required to run the function + */ + let generatedPassword = ''; + for (let i = 0; i < PasswordLength; i++) { + generatedPassword += passwordChars.charAt(Math.floor(Math.random() * passwordChars.length)); + } + return generatedPassword + } + function createUser(newUserObject,siteID){ + /* + Call postUsers to create a new user with the passed in data + Parameters: + newUserObject: obj value that contains data for the new user creation + siteID: string value that represents the id of the site + */ + return vvClient.users. + postUsers({}, newUserObject, siteID) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .catch(() => {throw new Error('The call to create the user returned with an error.')}) + } + function addNewUserToGroups(groupObjListToAdd, newUserGUID){ + /* + Call addUserToGroup for each group passed in to add the user + Parameters: + groupObjListToAdd: obj array that contains the data of each passed in group + newUserGUID: string value that represents the user GUID + Note:For better performance handle the asynchronos operations all at once + using the promise method Promise.allSettled. If an error occurs the process will not stop. + */ + return Promise.allSettled(groupObjListToAdd.map(group => { + return vvClient.groups + .addUserToGroup({}, group.id, newUserGUID) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .catch(() => LogInfo(`Error adding user to group ${group.name}.`,false)) + })) + } + function sendEmailToUser(PostEmailData){ + /* + Call postEmails to send an email + Parameters: + PostEmailData: obj contains the required data to send the email + */ + return vvClient.email + .postEmails('', PostEmailData) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .then((res) => LogInfo("Welcome email sent successfully.", true, res)) + .catch(() => LogInfo('User has been created, but the welcome email was not sent successfully.',false)) + } + function createCommunicationLog(targetFields,commLogTemplateID){ + /* + Call postForms to create an instance of Communication Log template + Parameters: + targetFields: obj that contains data of the fields required to be completed + commLogTemplateID. string value that represents the id of the form template + */ + return vvClient.forms + .postForms(null, targetFields, commLogTemplateID) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .catch((error) => LogInfo(`Call to post Communications Log form returned with an error. The server returned a status of ${error.message}`, false)) + } + function relateForms(communicationLogRevisionID,relateToRecords){ + /* + Call relateFormByDocId to relate any record the Communication Log should be related to + Parameters: + communicationLogRevisionID: string value that represents the form record id to be related to other form records + relateToRecords. array of string values that represents the form records that has to be related to the Communication Log record + */ + return Promise.allSettled(relateToRecords.map(relatedForm =>{ + return vvClient.forms + .relateFormByDocId(communicationLogRevisionID, relatedForm) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res,200)) + .then((res) => LogInfo(`Communications Log related to form ${relatedForm} successfully.`, true, res)) + .catch(() => LogInfo(`Call to relate the Communications Log to form ${relatedForm} returned with an error.`,false)) + })) + } + function getFolder(folderPath){ + /* + Call getFolders to get the folder data with the passed in folder path + Parameters: + folderPath: string value that represents the passed in folder path + */ + const folderObjParams = {folderPath : folderPath} + return vvClient.library + .getFolders(folderObjParams) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res, 403)) + .then((res) => checkDataPropertyExists(res,403)) + .then((res) => { + if(res.meta.status == 403){ + LogInfo(`The call to find a folder at ${folderPath} returned with a 403. Assuming no duplicates and continuing the process.`, true); + }else{ + LogInfo(`The call to find a folder at ${folderPath} returned with no duplicates. Continuing the process.`, true); } + return res + }) + .catch(() => LogInfo(`The call to find a folder at ${folderPath} returned with an error.`, false)) + } + function createFolder(folderPath){ + /* + Call postFolderByPath to create a folder with the passed in folder path + Parameters: + folderPath: string value that represents the passed in folder path + */ + return vvClient.library + .postFolderByPath(null, {}, folderPath) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .then((res) => LogInfo('Folder created succesfully', true, res)) + .catch(() => LogInfo(`User was created but call to create folder at ${folderPath} returned with an error.`, false)) + } + function addPermissionsToFolder(folderPermissions,folderId,newUserGUID){ + /* + Call putFolderSecurityMember to give folder permissions to the requested user + Parameters: + folderPermissions: string value that represents the folder permission (viewer, editor, owner) + folderId: string value that represents the folder id + newUserGUID: string value that represents the user GUID that will be assigned the folder permissions + */ + const memberType = vvClient.constants.securityMemberType['User']; + const role = vvClient.constants.securityRoles[folderPermissions]; + const cascadeChanges = true; + return vvClient.library + .putFolderSecurityMember(folderId, newUserGUID, memberType, role, cascadeChanges) + .then((res) => parseRes(res)) + .then((res) => checkMetaAndStatus(res)) + .then((res) => checkDataPropertyExists(res)) + .then((res) => checkDataIsNotEmpty(res)) + .then((res) => LogInfo('Security permissions successfully added to folder', true, res)) + .catch(() => LogInfo('The folder was created, but an error was returned when attempting to add user security permissions to the folder.', false)) + } + /* -------------------------------------------------------------------------- */ + /* MAIN CODE */ + /* -------------------------------------------------------------------------- */ + try { + // GET THE VALUES OF THE FIELDS + //PASSED IN DATA FOR USER CREATION + const userID = getFieldValueByName('User Id'); + const emailAddress = getFieldValueByName('Email Address'); + const siteName = getFieldValueByName('Site Name'); + let groupList = getFieldValueByName('Group List',false); + const firstName = getFieldValueByName('First Name',false); + const middleInitial = getFieldValueByName('Middle Initial',false); + const lastName = getFieldValueByName('Last Name',false); + let password = getFieldValueByName('Password',false); + //ADMIN DATA FOR USER CONFIG CREATION + let relateToRecords = getFieldValueByName('Related Records',false); + let otherFields = getFieldValueByName('Other Fields',false); + const folderPath = getFieldValueByName('Folder Path',false); + const folderPermissions = getFieldValueByName('Folder Permissions',false); + //PASSED IN DATA FOR SENDING EMAIL + const sendEmail = getFieldValueByName('Send Email'); + const emailBodyField = getFieldValueByName('Email Body',false); + const emailSubjectField = getFieldValueByName('Email Subject',false); + // VERIFY PASSED IN VALID USER ID + if(userID){ + checkUserIDisValid(userID); + } + // VERIFY PASSED IN VALID PASSWORD + if(password){ + checkPasswordIsValid(password); + } + //VERIFY PASSED IN VALID EMAIL ADDRESS + if(emailAddress){ + checkEmailAddressIsValid(emailAddress); + } + //VERIFY SEND EMAIL HAS A VALID VALUE + if (sendEmail != 'Standard' && sendEmail != 'Custom' && sendEmail != 'Both') { + errorLog.push("The Send Email parameter must be 'Standard', 'Custom', or 'Both'."); + } + //VERIFY EMAIL SUBJECT AND BODY ARE VALID + if(sendEmail == 'Custom' || sendEmail == 'Both'){ + if (!emailSubjectField) { + errorLog.push("The Email Subject parameter was not supplied."); } - else { - securityLevel = securityLevel.value; - - if (securityLevel != 'Viewer' && securityLevel != 'Editor' && securityLevel != 'Owner') { - errors.push('The Folder Permissions parameter must be Viewer, Editor, or Owner.'); + if (!emailBodyField) { + errorLog.push("The Email Body parameter was not supplied."); + }else if (sendEmail == 'Custom') { + if (!emailBodyField.includes('[Username]') || !emailBodyField.includes('[Password]')) { + errorLog.push('The Email Body must include [Username] and [Password].'); } } } - else { - //Set to empty string to avoid undefined errors. - securityLevel = ''; - } - - //Email Subject and Email Body - if (sendEmail == 'Custom' || sendEmail == 'Both') { - //Email Subject - if (!subjectField || !subjectField.value) { - errors.push("The Email Subject parameter was not supplied."); - } - else { - subjectField = subjectField.value; - } - - //Email Body - if (!bodyField || !bodyField.value) { - errors.push("The Email Body parameter was not supplied."); + //VERIFY PASSED IN FOLDER PERMISSIONS ARE VALID + if(folderPath && !folderPermissions && forceSecurities){ + errorLog.push('The Folder Permissions parameter was not supplied.'); + }else if(folderPath && folderPermissions){ + if (folderPermissions != 'Viewer' && folderPermissions != 'Editor' && folderPermissions != 'Owner') { + errorLog.push('The Folder Permissions parameter must be Viewer, Editor, or Owner.'); } - else { - bodyField = bodyField.value; - - //If only the custom email is being sent, make sure it includes the username and password - if (sendEmail == 'Custom') { - if (bodyField.indexOf('[Username]') == -1 || bodyField.indexOf('[Password]') == -1) { - errors.push('The Email Body must include [Username] and [Password].'); - } - } - } - } - else { - //Set values to empty strings to avoid undefined errors. - subjectField = ''; - bodyField = ''; } - - //Related Records - if (!relateToRecords || !relateToRecords.value) { + //VERIFY PASSED IN RELATE RECORDS IS VALID + if(!relateToRecords){ //Not required. Set to empty array to avoid undefined errors. relateToRecords = []; + }else if(!Array.isArray(relateToRecords)){ + errorLog.push('The Related Records parameter must be an array when it is provided.'); } - else { - relateToRecords = relateToRecords.value; - - if (Array.isArray(relateToRecords) == false) { - errors.push('The Related Records parameter must be an array when it is provided.'); - } - } - - //Other Fields - if (!otherFields || !otherFields.value) { - //Not required. Set to empty object to avoid undefined errors. + //VERIFY PASSED IN OTHER FIELDS IS VALID + if(!otherFields){ otherFields = {}; } - else { - otherFields = otherFields.value; - } - - //Return all validation errors at once. - if (errors.length > 0) { - throw new Error(errors); - } - }) - .then(function () { - //Search for the user ID provided to see if the user already exists - var userQueryParams = { filter: "[UsUserID] = '" + userID + "'" } - - return vvClient.customQuery.getCustomQueryResultsByName(userQueryName, userQueryParams).then(function (userRes) { - var userData = JSON.parse(userRes); - if (userData.meta.status == 200) { - if (userData.data.length == 1) { - //Exactly one user found. Save all user info for later use. - var userInfo = userData.data[0]; - userGUID = userInfo.usId; - returnObj[2] = userGUID; - - //Measure the found user is enabled or disabled. Send a correct error. - var userEnabled = userInfo.usEnabled; //This is 0 or 1 - if (userEnabled == 1) { - throw new Error('User Exists'); - } - else if (userEnabled == 0) { - throw new Error('User Disabled'); - } - else { - throw new Error('A duplicate user was found, but the process was unable to determine if the user account is currently enabled or disabled. ' - + 'Please try again or contact a system administrator.'); - } - } - else if (userData.data.length == 0) { - //No user found. This is good. - logger.info('Searched for existing users with ID ' + userID + '. None found. Continuing the process.'); - } - else { - //Many users found. Invalid state. - throw new Error('More than one user was found for ID: ' + userID + '. This is an invalid state. Please notify a system administrator.'); - } - } - else if (userData.meta.status == 404) { - throw new Error('The custom query to find users was not found. Please ensure that a query named ' + userQueryName + ' has been configured in the default queries area.'); - } - else { - throw new Error('There was an error when searching for the user ' + userID + '.'); - } - }) - }) - .then(function () { - //Search the Site Name passed in to determine if it exists. - var siteSearchObject = {}; - siteSearchObject.q = "name eq '" + siteName + "'"; - siteSearchObject.fields = "id,name"; - - return vvClient.sites.getSites(siteSearchObject).then(function (siteResp) { - var siteData = JSON.parse(siteResp); - if (siteData.meta.status === 200) { - if (siteData.data.length === 0) { - //Need to create the site. - siteExists = false; - } - else if (siteData.data.length === 1) { - logger.info('The site exists. Continuing the process.'); - //Found exactly one site. Store the ID and move on. - siteID = siteData.data[0].id; - - //Do not need to create the site. - siteExists = true; - } - else { - //Found more than one matching site. This is not a valid state. - throw new Error('The call to search for the site name ' + siteName + ' returned with more than one result. This is an invalid state. Please contact a system administrator.'); - } - } - else { - throw new Error('The call to search for the site name ' + siteName + ' returned with an error.'); - } - }) - }) - .then(function () { - //If needed, create the site. - if (!siteExists) { - var newSiteData = {}; - newSiteData.name = siteName; - newSiteData.description = siteName; - - return vvClient.sites.postSites(null, newSiteData).then(function (postSiteResp) { - if (postSiteResp.meta.status === 200) { - logger.info('Site created successfully.'); - siteID = postSiteResp.data.id; - } - else { - throw new Error('The call to create a site with name ' + siteName + ' returned with an error.'); - } - }); - } - }) - .then(function () { - if (groupsPassed) { - //Check if the groups passed in by the user exist. - var groupParam = {}; - groupParam.q = ""; - groupParam.fields = 'id,name,description'; - - //Function to get all system groups - return vvClient.groups.getGroups(groupParam).then(function (groupResp) { - var groupData = JSON.parse(groupResp); - if (groupData.meta.status === 200) { - if (groupData.data.length > 0) { - //Groups were found. Process results. - //Array to hold all the group names that the user has input. - var groupArrayUntrimmed = groupList.split(","); - var groupArray = arrayTrimmer(groupArrayUntrimmed); - - //Need to ensure that all items in groupArray exist in existingGroups; extract group IDs. - var groupErrors = ''; - groupArray.forEach(function (groupName) { - var group = groupData.data.find(x => x.name === groupName); - if (group != undefined) { - //Found it - groupIdArray.push(group); - } - else { - groupErrors += 'The group ' + groupName + ' does not exist. '; - } - }); - - //If any groups do not exist, stop the process. Otherwise continue. - if (groupErrors != '') { - throw new Error('At least one group was not found to exist. ' + groupErrors); - } - else { - logger.info('All groups were found to exist. Continuing the process.'); - } - } - else { - throw new Error('No user permission groups were found to exist in the system. Please contact a system administrator.'); - } - } - else { - throw new Error('The call to get existing group names returned with an error.'); - } - }); + //IF ERRORS OCCURRED RETURN ALL AT ONCE + if(errorLog.length > 0){ + throw new Error(errorLog); + } + //START PROCESS + //GET USER DATA WITH PASSED IN USER ID + const getUserDataRes = await getUserData(userID); + const userData = getUserDataRes.data + //IF DATA IS RETURN, IT MEANS AN USER WITH THE PASSED IN ID ALREADY EXIST AND PROCESS MUST STOP THROWING AN ERROR + if(userData.length == 1){ + const isUserEnabled = userData[0].usEnabled + if(isUserEnabled == 1){ + throw new Error('User Exists'); + }else if(isUserEnabled == 0){ + throw new Error('User Disabled'); + }else{ + throw new Error(`A duplicate user was found, but the process was unable to determine if the user account is currently enabled or disabled. Please try again or contact a system administrator.`); } - }) - .then(function () { - //Starting user creation logic. If a random password needs to be generated, do it now. - if (password == '') { - //Generate a random password - userPassword = randomPassword(); - } - else { - //Use the password that was passed in. - userPassword = password; - } - - var newUserObject = {}; - newUserObject.userid = userID; - newUserObject.firstName = firstName; - newUserObject.middleInitial = middleInitial; - newUserObject.lastName = lastName; - newUserObject.emailaddress = emailAddress; - newUserObject.password = userPassword; - newUserObject.mustChangePassword = SysChangePass; - - if (sendEmail != 'Custom') { - newUserObject.sendEmail = 'true'; - } - else { - newUserObject.sendEmail = 'false'; - } - - var userParams = {}; - - return vvClient.users.postUsers(userParams, newUserObject, siteID).then(function (userResp) { - if (userResp.meta.status === 200) { - //User created. Store the user GUID for passing back to the calling function. - //At this point, future processes should return Success or Minor Errors. Not throwing any more errors because the user has been created. - userGUID = userResp.data.id; - } - else { - throw new Error('The call to create the user returned with an error.'); + } + //GET SITE DATA WITH THE PASSED IN SITE NAME. + const getSiteDataResp = await getSiteData(siteName) + const siteData = getSiteDataResp.data + if(siteData.length == 0){ + //NO DATA FOUND, IT IS NECESSARY TO CREATE THE SITE + const createSiteResp = await createSite(siteName) + siteID = createSiteResp.data.id + }else if(siteData.length == 1){ + //THE SITE ALREADY EXIST, NO NEED TO CREATE IT + siteID = siteData[0].id; + }else{ + //FOUND MORE THAN ONE SITE. INVALID STATE + throw new Error(`The call to search for the site name ${siteName} returned with more than one result. This is an invalid state. Please contact a system administrator.`); + } + //ONLY IF GROUP LIST IS PASSED IN + if(groupList){ + //BUILD UP THE ARRAY OF GROUPS + groupList = groupList.split(','); + groupList = groupList.map( group => group.trim()); + //GET THE EXISTING GROUPS ON VV + const getGroupsRes = await getGroups(); + const existingGroupList = getGroupsRes.data; + //VERIFY ALL PASSED IN GROUPS EXIST ON VV + groupList.forEach( passedInGroupName => { + const groupObj = existingGroupList.find( existingGroup => existingGroup.name == passedInGroupName) + if(groupObj){ + groupObjListToAdd.push(groupObj) + }else{ + errorLog.push(`The group ${passedInGroupName} does not exist. `) } }); - }) - .then(function () { - //If groups were passed in, add the user to groups - if (groupsPassed) { - var addGroupProcess = Q.resolve(); - - groupIdArray.forEach(function (groupItem) { - addGroupProcess = addGroupProcess.then(function () { - return vvClient.groups.addUserToGroup({}, groupItem.id, userGUID).then(function (addResp) { - var addData = JSON.parse(addResp); - if (addData.meta.status !== 201) { - //Continuing the process if an error occurs; will measure this at the end of the process. - logger.info('Error adding user to group ' + groupItem.name + '.'); - errors.push('Error adding user to group ' + groupItem.name + '.'); - } - }); - }); - }); - - return addGroupProcess; - } - }) - .then(function () { - //If an email needs to be sent to the created user, send it now - if (sendEmail == 'Custom' || sendEmail == 'Both') { - var emailData = {}; - emailData.recipients = emailAddress; - emailData.subject = subjectField; - - //Replace Username and Password tokens. - bodyWithoutPassword = bodyField.replace('[Username]', userID); - emailData.body = bodyWithoutPassword.replace('[Password]', userPassword); - - var emailParams = ''; - - return vvClient.email.postEmails(emailParams, emailData).then(function (emailResp) { - if (emailResp.meta.status === 201 && emailResp.data.success == true) { - logger.info("Welcome email sent successfully."); - } - else { - logger.info('User has been created, but the welcome email was not sent successfully.'); - errors.push('User has been created, but the welcome email was not sent successfully.'); - } - }); + //IF ONLY ONE GROUP FROM THE PASSED IN LIST TURNS OUT TO BE INVALID, STOP PROCESS AND THROW AN ERROR + if(errorLog.length > 0){ + throw new Error(`At least one group was not found to exist. ${errorLog}`); + }else{ + logger.info('All groups were found to exist. Continuing the process.'); } - }) - .then(function () { - //If a communications log needs to be created to reflect the welcome email, send it now. - if (createCommLog && sendEmail != 'Standard') { - - var sendDate = momentTz().tz(timeZone).format('L'); - var sendTime = momentTz().tz(timeZone).format('LT'); - var localTime = sendDate + " " + sendTime; - - var targetFields = {}; - targetFields['Communication Type'] = 'Email'; - targetFields['Email Type'] = 'Immediate Send'; - targetFields['Email Recipients'] = emailAddress; - targetFields['Subject'] = subjectField; - targetFields['Email Body'] = bodyWithoutPassword; - targetFields['Scheduled Date'] = localTime; - targetFields['Communication Date'] = localTime; - targetFields['Approved'] = 'Yes'; - targetFields['Communication Sent'] = 'Yes'; - - //Load additional fields - for (var property1 in otherFields) { - targetFields[property1] = otherFields[property1]; - } - - return vvClient.forms.postForms(null, targetFields, commLogTemplateID).then(function (postResp) { - if (postResp.meta.status === 201 || postResp.meta.status === 200) { - var commLogRevisionId = postResp.data.revisionId; - - if (relateToRecords.length > 0) { - //Relate the created communication log to each Form ID in the relateToRecords array. - var relateProcessResult = Q.resolve(); - - relateToRecords.forEach(function (relatedForm) { - relateProcessResult = relateProcessResult.then(function () { - return vvClient.forms.relateFormByDocId(commLogRevisionId, relatedForm).then(function (relateResp) { - var relatedResp = JSON.parse(relateResp); - if (relatedResp.meta.status === 200 || relatedResp.meta.status === 404) { - logger.info("Communications Log related to form " + relatedForm + " successfully."); - } - else { - logger.info("Call to relate the Communications Log to form " + relatedForm + " returned with an error."); - errors.push("Call to relate the Communications Log to form " + relatedForm + " returned with an error."); - } - }); - }); - }); - - return relateProcessResult; - } - } - else { - errors.push("Call to post Communications Log form returned with an error. The server returned a status of " + postResp.meta.status); - } - }) - } - }) - .then(function () { - //If user has entered a folder path, then the system will search for the folder and create a new folder if it does not already exist. - if (createFolder) { - //Determine if a folder exists in the destination location, to prevent duplication - logger.info("Finding folder: " + folderPath); - - var FolderParams = {}; - FolderParams.folderPath = folderPath; - - return vvClient.library.getFolders(FolderParams).then(function (folderResp) { - var folderData = JSON.parse(folderResp); - if (folderData.meta.status === 200) { - if (folderData.data.length > 0) { - //Folder found. Log the ID but do not create the folder. - folderId = folderData.data[0].id; - } - else { - logger.info("The call to find a folder at " + folderPath + " returned with no duplicates. Continuing the process."); - } - } - else if (folderData.meta.status === 403) { - logger.info("The call to find a folder at " + folderPath + " returned with a 403. Assuming no duplicates and continuing the process."); - } - else { - logger.info("The call to find a folder at " + folderPath + " returned with an error."); - errors.push("The call to find a folder at " + folderPath + " returned with an error."); - } - }); - } - }) - .then(function () { - //If the folder was not found in the previous step, create the folder - if (createFolder && folderId == '') { - var folderData = {}; - return vvClient.library.postFolderByPath(null, folderData, folderPath).then(function (createFolderResp) { - if (createFolderResp.meta.status === 200) { - logger.info("Folder created successfully."); - folderId = createFolderResp.data.id; - } - else { - errors.push("User was created but call to create folder at " + folderPath + " returned with an error."); - } - }); - } - }) - .then(function () { - //The system will then take the security permissions that the user has entered and will add that security permission for the user on that folder. - if (securityLevel != '' && folderId != '') { - var memType = 'User'; - memType = vvClient.constants.securityMemberType[memType]; - var role = vvClient.constants.securityRoles[securityLevel] - var cascadeChanges = true; - - return vvClient.library.putFolderSecurityMember(folderId, userGUID, memType, role, cascadeChanges).then(function (folderSecurityResp) { - if (folderSecurityResp.meta.status === 200) { - logger.info('Security permissions successfully added to folder'); - } - else { - errors.push('The folder was created, but an error was returned when attempting to add user security permissions to the folder.'); - } - }); + } + //START THE USER CREATION PROCESS + //IF PASSWORD IS NOT PASSED IN MUST BE GENERATED + if(!password){ + password = generatePassword() + } + const newUserObject = {}; + newUserObject.userid = userID; + newUserObject.firstName = firstName; + newUserObject.middleInitial = middleInitial; + newUserObject.lastName = lastName; + newUserObject.emailaddress = emailAddress; + newUserObject.password = password; + newUserObject.mustChangePassword = SysChangePass; + newUserObject.sendEmail = sendEmail != 'Custom' ? 'true' : 'false'; + //CREATE THE NEW USER WITH VALID PASSED IN DATA + const createUserResp = await createUser(newUserObject,siteID); + const newUserGUID = createUserResp.data.id + //FROM NOW ON IF AN ERROR OCCURS WILL BE CONSIDERED MINOR + //IF GROUP LIST IS PASSED IN + if(groupList){ + await addNewUserToGroups(groupObjListToAdd, newUserGUID) + } + //SEND EMAIL ONLY IF IT IS REQUIRED + if (sendEmail == 'Custom' || sendEmail == 'Both'){ + const PostEmailData = {}; + PostEmailData.recipients = emailAddress; + PostEmailData.subject = emailSubjectField; + PostEmailData.body = emailBodyField.replace('[Username]', userID).replace('[Password]', password); + await sendEmailToUser(PostEmailData); + } + //CREATE COMMUNICATION LOG IF REQUESTED + if (createCommLog && sendEmail != 'Standard'){ + const sendDate = momentTz().tz(timeZone).format('L'); + const sendTime = momentTz().tz(timeZone).format('LT'); + const localTime = `${sendDate} ${sendTime}`; + const targetFields = {}; + targetFields['Communication Type'] = 'Email'; + targetFields['Email Type'] = 'Immediate Send'; + targetFields['Email Recipients'] = emailAddress; + targetFields['Subject'] = emailSubjectField + targetFields['Email Body'] = emailBodyField.replace('[Username]', userID); + targetFields['Scheduled Date'] = localTime; + targetFields['Communication Date'] = localTime; + targetFields['Approved'] = 'Yes'; + targetFields['Communication Sent'] = 'Yes'; + //LOAD ADDITIONAL FIELDS + for (let property1 in otherFields) { + targetFields[property1] = otherFields[property1]; } - }) - .then(function () { - //Handle any minor errors. - if (errors.length > 0) { - returnObj[0] = 'Minor Error'; - returnObj[1] = ''; - errors.forEach(function (errorLog) { - returnObj[1] += errorLog + '
'; - }); + const createCommunicationLogRes = await createCommunicationLog(targetFields,commLogTemplateID); + const communicationLogRevisionID = createCommunicationLogRes.data.revisionId; + //RELATE FORMS IF REQUESTED + if(relateToRecords.length > 0){ + await relateForms(communicationLogRevisionID,relateToRecords); } - else { - returnObj[0] = 'Success'; - returnObj[1] = 'All actions completed successfully.'; + } + if(folderPath){ + logger.info(`Finding folder: ${folderPath}`); + //VERIFY THE PASSED IN FOLDER EXIST TO AVOID DUPLICATED FOLDERS. IF NOT CREATE IT + const getFoldersRes = await getFolder(folderPath); + const folderData = getFoldersRes.data; + //IF DATA IS RETURNED, THE FOLDER ALREADY EXIST. IF NOT IT NEEDS TO BE CREATED + if(folderData){ + folderId = folderData.id + }else{ + const createFolderRes = await createFolder(folderPath); + folderId = createFolderRes.data.id; } - - //Always pass back the user GUID, since user was created. - returnObj[2] = userGUID; - return response.json(returnObj); - }) - .catch(function (err) { - logger.info(JSON.stringify(err)); - - returnObj[0] = 'Error'; - - if (err && err.message) { - returnObj[1] = err.message; - } else { - returnObj[1] = "An unhandled error has occurred. The message returned was: " + err; + //ADD THE FOLDER PERMISSIONS IF IT IS REQUESTED AND THE FOLDER ID IS PRESENT + if(folderPermissions && folderId){ + await addPermissionsToFolder(folderPermissions,folderId,newUserGUID); } - - return response.json(returnObj); - }) -} + } + //VERIFY IF MINOR ERRORS OCCURED + const areMinorErros = errorLog.length > 0 ? true : false + // BUILD THE SUCCESS RESPONSE ARRAY + outputCollection[0] = areMinorErros ? 'Minor Error' : "Success"; // Don´t change this + outputCollection[1] = areMinorErros ? errorLog.join('
') : "All actions completed successfully."; + outputCollection[2] = newUserGUID; + } catch (error) { + logger.info("Error encountered" + error); + // BUILD THE ERROR RESPONSE ARRAY + outputCollection[0] = "Error"; // Don´t change this + if (error.message) { + outputCollection[1] = error.message; + } else { + outputCollection[1] = `An Unhandled error has occurred. The message returned was: ${error}`; + } + } finally { + // SEND THE RESPONSE + response.json(200, outputCollection); + } +}; \ No newline at end of file