diff --git a/__tests__/unit/handlers/add-note-to-case.test.js b/__tests__/unit/handlers/add-note-to-case.test.js index 6a8f266f0e..fcdf9cb777 100644 --- a/__tests__/unit/handlers/add-note-to-case.test.js +++ b/__tests__/unit/handlers/add-note-to-case.test.js @@ -13,16 +13,20 @@ const app = require('../../../src/core/app'); const lambda = require('../../../src/handlers/add-note-to-case'); +const ErrorMessages = require('../../../src/constants/messages/error'); +const SuccessMessages = require('../../../src/constants/messages/success'); + describe('Test for add-note-to-case', function() { test.each([ - [1, 'cid', 'test1', 'John Doe', { data: { records: [{ id: 1 }] } }, { data: { record: { id: 1 } } }, true], - [1, 'cid', 'test2', '', { data: { records: [{ id: 1 }] } }, { data: { record: { id: 1 } } }, true], - [1, 'cid', 'test3', 'John Doe', { data: { records: [{ id: 1 }] } }, { data: {} }, false], - [1, 'cid', 'test4', 'John Doe', { data: { records: [] } }, { data: { record: { id: 1 } } }, false], - [1, 'cid', 'test5', 'John Doe', { data: { records: [] } }, { data: {} }, false] + [1, 'cid', 'test1', 'John Doe', { data: { records: [{ id: 1 }] } }, { data: { record: { id: 1 } } }, true, SuccessMessages.LAMBDA_FUNCTION_SUCCESS], + [1, 'cid', 'test2', null, { data: { records: [{ id: 1 }] } }, { data: { record: { id: 1 } } }, true, SuccessMessages.LAMBDA_FUNCTION_SUCCESS], + [1, 'cid', 'test3', 'John Doe', { data: { records: [{ id: 1 }] } }, { data: {} }, false, ErrorMessages.ERROR_NOTE_CREATE_FAILED], + [1, 'cid', 'test4', 'John Doe', { data: { records: [] } }, { data: { record: { id: 1 } } }, false, 'Unable to match any Case records'], + [1, 'cid', 'test5', 'John Doe', { data: { records: [] } }, { data: {} }, false, 'Unable to match any Case records'], + [1, 'cid', 'test6', 'John Doe', { data: { records: [{ id: 1 }, { id: 2 }] } }, { data: {} }, false, 'Matched multiple Case records'] ])( 'Verify correct response whether or not case is returned by API and note is added', - async (number, cid, noteDescription, contactName, apiResponse1, apiResponse2, matched) => { + async (number, cid, noteDescription, contactName, apiResponse1, apiResponse2, matched, body) => { process.env = { sugarUrl: 'https://cs-829.msqa.sugarcrm.com' }; @@ -45,7 +49,8 @@ describe('Test for add-note-to-case', function() { let expected = { statusCode: matched ? 200 : 500, - caseId: matched ? 1 : '' + caseId: matched ? 1 : '', + body: body }; expect(result).toEqual(expected); }); diff --git a/__tests__/unit/handlers/case-status.test.js b/__tests__/unit/handlers/case-status.test.js index 8f20f25e82..3b5a7a41cb 100644 --- a/__tests__/unit/handlers/case-status.test.js +++ b/__tests__/unit/handlers/case-status.test.js @@ -13,6 +13,8 @@ const app = require('../../../src/core/app'); const lambda = require('../../../src/handlers/case-status'); +const SuccessMessages = require('../../../src/constants/messages/success'); + describe('Test for case-status', function() { test.each([ [1, 'cid', { data: { records: [{ id: 1, status: 'good' }] } }, true], @@ -32,13 +34,17 @@ describe('Test for case-status', function() { mockApi.mockReturnValue(apiResponse); app.api.call = mockApi; let result = await lambda.handler(evt); - let expected = matched ? { - statusCode: 200, - caseId: 1, - caseStatus: 'good' - } : { - statusCode: 404 - }; + let expected = matched + ? { + statusCode: 200, + caseId: 1, + caseStatus: 'good', + body: SuccessMessages.LAMBDA_FUNCTION_SUCCESS + } + : { + statusCode: 404, + body: 'Unable to match exactly one Case record' + }; expect(result).toEqual(expected); }); }); diff --git a/__tests__/unit/handlers/create-case.test.js b/__tests__/unit/handlers/create-case.test.js index 79528c2039..65ef586a3c 100644 --- a/__tests__/unit/handlers/create-case.test.js +++ b/__tests__/unit/handlers/create-case.test.js @@ -11,6 +11,8 @@ // Import all functions from createCase.js const lambda = require('../../../src/handlers/create-case.js'); +// Defined Constants +const SuccessMessages = require('../../../src/constants/messages/success'); // Mock bean.js jest.mock('../../../src/core/bean.js', () => () => ({ @@ -46,7 +48,8 @@ describe('Test for create-case', function() { const expectedResult = { statusCode: 200, caseId: 1, - caseNumber: 1 + caseNumber: 1, + body: SuccessMessages.LAMBDA_FUNCTION_SUCCESS }; // Compare the result with the expected result expect(result).toEqual(expectedResult); diff --git a/__tests__/unit/utils/string-utils.test.js b/__tests__/unit/utils/string-utils.test.js new file mode 100644 index 0000000000..a4edf9c349 --- /dev/null +++ b/__tests__/unit/utils/string-utils.test.js @@ -0,0 +1,39 @@ +/* + * Your installation or use of this SugarCRM file is subject to the applicable + * terms available at + * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/. + * If you do not agree to all of the applicable terms or do not have the + * authority to bind the entity as an authorized representative, then do not + * install or use this SugarCRM file. + * + * Copyright (C) SugarCRM Inc. All rights reserved. + */ +const strUtils = require('../../../src/utils/string-utils.js'); + +describe('generateMessage', function() { + test.each([ + { + template: 'Single ${} test', + filler: 'dog', + expected: 'Single dog test' + }, + { + template: 'Multiples ${} ${} test', + filler: ['dog', 'cat'], + expected: 'Multiples dog cat test' + }, + { + template: 'Mismatched ${} ${} ${} too few fillers', + filler: ['dog', 'cat'], + expected: 'Mismatched dog cat ${} too few fillers' + }, + { + template: 'Mismatch ${} too many fillers', + filler: ['dog', 'cat'], + expected: 'Mismatch dog too many fillers' + } + ])('should fill the template string as expected', (values) => { + let actual = strUtils.generateMessage(values.template, values.filler); + expect(actual).toEqual(values.expected); + }); +}); diff --git a/package.json b/package.json index 58b4d03677..3c52458661 100755 --- a/package.json +++ b/package.json @@ -21,6 +21,6 @@ }, "scripts": { "test": "jest --watchAll", - "lint": "eslint ./src ./__tests__" + "lint": "eslint --ext .js src/* __tests__/unit/* --fix" } } diff --git a/src/constants/messages/error.js b/src/constants/messages/error.js index 41a070eae5..a323137b4b 100644 --- a/src/constants/messages/error.js +++ b/src/constants/messages/error.js @@ -9,9 +9,15 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ -module.exports = { +module.exports = { /** * Error messages */ - ERROR_MULTIPLE_CALL_RECORDS_IN_RESPONSE: 'Unable to match to exactly one Call record' + ERROR_CANNOT_CREATE_CASE: 'Unable to create Case in SugarCRM', + ERROR_NOTE_CREATE_FAILED: 'Unable to link Note to Case in SugarCRM', + + TPL_CANNOT_MATCH_RECORD: 'Unable to match exactly one ${} record', + TPL_MISSING_REQUIRED_PARAMETERS: 'Missing required parameters: ${}', + TPL_MULTIPLE_RECORDS_MATCHED: 'Matched multiple ${} records', + TPL_NO_RECORDS_MATCHED: 'Unable to match any ${} records' }; diff --git a/src/constants/messages/success.js b/src/constants/messages/success.js new file mode 100644 index 0000000000..201581c232 --- /dev/null +++ b/src/constants/messages/success.js @@ -0,0 +1,17 @@ +/* + * Your installation or use of this SugarCRM file is subject to the applicable + * terms available at + * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/. + * If you do not agree to all of the applicable terms or do not have the + * authority to bind the entity as an authorized representative, then do not + * install or use this SugarCRM file. + * + * Copyright (C) SugarCRM Inc. All rights reserved. + */ + +module.exports = { + /** + * Success messages + */ + LAMBDA_FUNCTION_SUCCESS: 'Lambda Function Completed Successfully' +}; diff --git a/src/constants/url.js b/src/constants/url.js index 05899e9f7e..d08b9e63d6 100644 --- a/src/constants/url.js +++ b/src/constants/url.js @@ -9,9 +9,9 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ -module.exports = { +module.exports = { /** * URL Partials */ - HTTPS_PREFIX: 'https://', + HTTPS_PREFIX: 'https://' }; diff --git a/src/core/api.js b/src/core/api.js index bd4635283d..f7aff27fd6 100644 --- a/src/core/api.js +++ b/src/core/api.js @@ -12,6 +12,7 @@ const axios = require('axios'); const { HttpStatus } = require('../constants/http-status.js'); +const loggerUtils = require('../utils/logger-utils'); const { Secrets } = require('../utils/aws/secrets'); const methodToRequest = { @@ -61,7 +62,9 @@ module.exports = () => { request.params = params; } response = await axios(request); + if (response.data) { + loggerUtils.logSugarApiResponse(response.data); return { status: HttpStatus.ok, data: response.data diff --git a/src/core/data.js b/src/core/data.js index f82c7f4260..5859f419f8 100644 --- a/src/core/data.js +++ b/src/core/data.js @@ -9,7 +9,7 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ -var Bean = require('./bean.js'); +let Bean = require('./bean.js'); module.exports = { createBean: (module, data) => { diff --git a/src/handlers/add-note-to-case.js b/src/handlers/add-note-to-case.js index 22bbdef5af..d7fc281268 100644 --- a/src/handlers/add-note-to-case.js +++ b/src/handlers/add-note-to-case.js @@ -14,13 +14,16 @@ */ const app = require('../core/app.js'); /** - * Util class to log JSOn to Cloudwatch + * Util classes to write logs to Cloudwatch */ const loggerUtils = require('../utils/logger-utils'); +const strUtils = require('../utils/string-utils'); /** - * HTTP status codes used in this function + * Defined Constants */ const { HttpStatus } = require('../constants/http-status'); +const ErrorMessages = require('../constants/messages/error'); +const SuccessMessages = require('../constants/messages/success'); /** * Sugar Instance URL */ @@ -32,27 +35,44 @@ const baseUrl = process.env.sugarUrl; * @param {Object} event */ const addNoteToCaseHandler = async (event) => { + loggerUtils.logContactFlowEvent(event); + + let statusCode = HttpStatus.error; + let caseId = ''; + let noteName = ''; + let body = ''; + // input from the contact flow provided by the user const caseNumber = event.Details.Parameters.caseNumber || ''; const contactId = event.Details.Parameters.contactId || ''; + if (!caseNumber || !contactId) { - return { - statusCode: HttpStatus.preconditionFailed - }; + let filler = caseNumber ? '' : 'caseNumber '; + filler += contactId ? '' : 'contactId'; + body = strUtils.generateMessage(ErrorMessages.TPL_MISSING_REQUIRED_PARAMETERS, filler); + return loggerUtils.logReturnValue({ + statusCode: HttpStatus.preconditionFailed, + caseId: caseId, + body: body + }); } + const noteDescription = event.Details.Parameters.noteDescription; const contactName = event.Details.Parameters.contactName; // Use the given case number to get the relavant case id const filterUrl = encodeURI(`${baseUrl}/rest/v11_10/Contact/${contactId}/Cases?filter[0][case_number]=${caseNumber}&fields=id`); const idResponse = await app.api.call('read', filterUrl, null, null); - const caseBean = idResponse.data.records[0]; - - loggerUtils.logSugarApiResponse(idResponse); - let statusCode = HttpStatus.error; - let caseId = ''; - let noteName = '' + if (idResponse.data.records.length > 1) { + body = strUtils.generateMessage(ErrorMessages.TPL_MULTIPLE_RECORDS_MATCHED, 'Case'); + return loggerUtils.logReturnValue({ + statusCode: statusCode, + caseId: caseId, + body: body + }); + } + const caseBean = idResponse.data.records[0]; // if contact name is empty then default it to 'customer' if (contactName === '' || contactName === undefined) { @@ -70,26 +90,29 @@ const addNoteToCaseHandler = async (event) => { 'description': noteDescription, 'name': noteName }; - + // add note to the case const filterUrl = encodeURI(`${baseUrl}/rest/v11_10/Cases/${caseBean.id}/link/notes`); const noteResponse = await app.api.call('create', filterUrl, null, notePayload); - - loggerUtils.logSugarApiResponse(noteResponse); - const updateCaseBean = noteResponse.data.record; - + // if the case was successfully updated if (updateCaseBean && updateCaseBean.id !== '') { statusCode = HttpStatus.ok; caseId = updateCaseBean.id; + body = SuccessMessages.LAMBDA_FUNCTION_SUCCESS; + } else { + body = ErrorMessages.ERROR_NOTE_CREATE_FAILED; } + } else { + body = strUtils.generateMessage(ErrorMessages.TPL_NO_RECORDS_MATCHED, 'Case'); } - return { + return loggerUtils.logReturnValue({ statusCode: statusCode, - caseId: caseId - }; + caseId: caseId, + body: body + }); }; exports.handler = addNoteToCaseHandler; diff --git a/src/handlers/call-recording.js b/src/handlers/call-recording.js index c5ee9f2d21..87112ab3fd 100644 --- a/src/handlers/call-recording.js +++ b/src/handlers/call-recording.js @@ -14,24 +14,26 @@ const app = require('../core/app.js'); /** * Utils */ +const callRecordingUtils = require('../utils/sugar/call-record-utils'); const loggerUtils = require('../utils/logger-utils'); const s3Utils = require('../utils/aws/s3-utils'); -const callRecordingUtils = require('../utils/sugar/call-record-utils'); +const stringUtils = require('../utils/string-utils.js'); /** * Defined constants */ -const ErrorMessages = require('../constants/messages/error'); const CallsConstants = require('../constants/sugar-modules/calls'); +const ErrorMessages = require('../constants/messages/error'); const { HttpStatus } = require('../constants/http-status.js'); /** - * Function to update a call record's call recording URL when the + * Function to update a call record's call recording URL when the * audio file is ready * * @param {Object} event the S3 trigger event */ const handler = async (event) => { + // Log S3 Event to cloudwatch for debugging loggerUtils.logS3Event(event); let objectKey = s3Utils.getObjectKeyFromS3Event(event); @@ -39,10 +41,10 @@ const handler = async (event) => { let callRecord = await callRecordingUtils.getCallRecord(contactId); if (!callRecord) { - return { + return loggerUtils.logReturnValue({ status: HttpStatus.preconditionFailed, - body: ErrorMessages.ERROR_MULTIPLE_CALL_RECORDS_IN_RESPONSE - }; + body: stringUtils.generateMessage(ErrorMessages.TPL_CANNOT_MATCH_RECORD, 'Call') + }); } let callRecordingUrl = callRecordingUtils.buildCallRecordingUrl(contactId); @@ -50,7 +52,7 @@ const handler = async (event) => { let callBean = app.data.createBean('Calls', callRecord); callBean.set(CallsConstants.CALLS_CALL_RECORDING_URL, callRecordingUrl); - return await callBean.save(); + return loggerUtils.logReturnValue(await callBean.save()); }; exports.handler = handler; diff --git a/src/handlers/case-status.js b/src/handlers/case-status.js index a6d52575fb..2797d374ad 100644 --- a/src/handlers/case-status.js +++ b/src/handlers/case-status.js @@ -14,13 +14,20 @@ */ const app = require('../core/app.js'); /** - * HTTP status codes used in this function + * Defined constants */ const { HttpStatus } = require('../constants/http-status'); +const ErrorMessages = require('../constants/messages/error'); +const SuccessMessages = require('../constants/messages/success'); /** * Sugar Instance URL */ const baseUrl = process.env.sugarUrl; +/** + * Util class to log JSON to Cloudwatch + */ +const loggerUtils = require('../utils/logger-utils'); +const stringUtils = require('../utils/string-utils.js'); /** * Lambda function to get case status based on case number sent via Contact Flow's Invoke Lambda @@ -28,15 +35,16 @@ const baseUrl = process.env.sugarUrl; * @param {Object} event */ const handler = async (event) => { + loggerUtils.logContactFlowEvent(event); const queryParams = { fields: 'status' }; const caseNumber = event.Details.Parameters.caseNumber || ''; const contactId = event.Details.Parameters.contactId || ''; if (!caseNumber || !contactId) { - return { + return loggerUtils.logReturnValue({ statusCode: HttpStatus.preconditionFailed - }; + }); } const filterUrl = encodeURI(`${baseUrl}/rest/v11_10/Contact/${contactId}/Cases?filter[0][case_number]=${caseNumber}&fields=id,status`); const response = await app.api.call('read', filterUrl, null, queryParams); @@ -44,15 +52,17 @@ const handler = async (event) => { const caseBean = response.data.records[0]; if (caseBean) { - return { + return loggerUtils.logReturnValue({ statusCode: HttpStatus.ok, caseId: caseBean.id, - caseStatus: caseBean.status - }; + caseStatus: caseBean.status, + body: SuccessMessages.LAMBDA_FUNCTION_SUCCESS + }); } else { - return { - statusCode: HttpStatus.notFound - }; + return loggerUtils.logReturnValue({ + statusCode: HttpStatus.notFound, + body: stringUtils.generateMessage(ErrorMessages.TPL_CANNOT_MATCH_RECORD, 'Case') + }); } }; diff --git a/src/handlers/create-case.js b/src/handlers/create-case.js index e99eaa76e6..cb55553c0a 100755 --- a/src/handlers/create-case.js +++ b/src/handlers/create-case.js @@ -13,9 +13,18 @@ * A Lambda function that creates a case. */ const app = require('../core/app.js'); + +/** + * Defined constants + */ const { HttpStatus } = require('../constants/http-status'); +const ErrorMessages = require('../constants/messages/error'); +const SuccessMessages = require('../constants/messages/success'); +const loggerUtils = require('../utils/logger-utils.js'); exports.createCaseHandler = async (event) => { + // Log Contact Flow Event to cloudwatch for debugging + loggerUtils.logContactFlowEvent(event); const params = { name: 'Case from ' + (event.Details.Parameters.contactName || 'Customer'), description: event.Details.Parameters.caseDescription || '', @@ -33,14 +42,16 @@ exports.createCaseHandler = async (event) => { await bean.save(); if (bean.get('id')) { - return { + return loggerUtils.logReturnValue({ statusCode: HttpStatus.ok, caseId: bean.get('id'), - caseNumber: bean.get('case_number') - }; + caseNumber: bean.get('case_number'), + body: SuccessMessages.LAMBDA_FUNCTION_SUCCESS + }); } else { - return { - statusCode: HttpStatus.error - }; + return loggerUtils.logReturnValue({ + statusCode: HttpStatus.error, + body: ErrorMessages.ERROR_CANNOT_CREATE_CASE + }); } }; diff --git a/src/handlers/lex-get-string.js b/src/handlers/lex-get-string.js index e21b199132..55476157cf 100644 --- a/src/handlers/lex-get-string.js +++ b/src/handlers/lex-get-string.js @@ -9,6 +9,8 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ +const loggerUtils = require('../utils/logger-utils'); + /** * Close dialog with the customer, reporting fulfillmentState Fulfilled * @param sessionAttributes @@ -22,10 +24,10 @@ function close(sessionAttributes, fulfillmentState, message) { type: 'Close', fulfillmentState, message - }, + } }; } - + /** * Events * @param intentRequest @@ -33,10 +35,10 @@ function close(sessionAttributes, fulfillmentState, message) { */ function dispatch(intentRequest, callback) { const sessionAttributes = intentRequest.sessionAttributes; - + // assign the whole user input to sessionAttributes.string sessionAttributes.string = intentRequest.inputTranscript; - + callback( close( sessionAttributes, @@ -44,12 +46,12 @@ function dispatch(intentRequest, callback) { { 'contentType': 'PlainText', 'content': 'You entered: ' + sessionAttributes.string - + } ) ); } - + /** * Main handler * @param event @@ -57,6 +59,8 @@ function dispatch(intentRequest, callback) { * @param callback */ exports.handler = (event, context, callback) => { + // Log Lex Event to cloudwatch for debugging + loggerUtils.logLexEvent(event); try { dispatch(event, (response) => { @@ -66,4 +70,3 @@ exports.handler = (event, context, callback) => { callback(err); } }; - diff --git a/src/handlers/save-transcript.js b/src/handlers/save-transcript.js index f4a47239ec..207a2fb408 100644 --- a/src/handlers/save-transcript.js +++ b/src/handlers/save-transcript.js @@ -18,6 +18,7 @@ const s3Utils = require('../utils/aws/s3-utils'); const utils = require('../utils/utils'); const loggerUtils = require('../utils/logger-utils'); const callRecordingUtils = require('../utils/sugar/call-record-utils'); +const strUtils = require('../utils/string-utils'); /** * Defined constants @@ -38,7 +39,7 @@ const handler = async (event) => { // Fetch and process transcript from s3 const transcript = await s3Utils.getJsonFromS3Event(event); - console.log('Fetched transcript: \n', transcript); + loggerUtils.logJson('Fetched transcript', transcript); const processedTranscript = utils.processTranscript(transcript); // Fetch related call record from Sugar @@ -48,17 +49,17 @@ const handler = async (event) => { // If our response doesn't match exactly one call, the conditions for this function haven't // been met so return 412 if (!callRecord) { - return { + return loggerUtils.logReturnValue({ status: HttpStatus.preconditionFailed, - body: ErrorMessages.ERROR_MULTIPLE_CALL_RECORDS_IN_RESPONSE - }; + body: strUtils.generateMessage(ErrorMessages.TPL_CANNOT_MATCH_RECORD, 'Call') + }); } // Update call record with readable transcript, return the result let callBean = app.data.createBean('Calls', callRecord); callBean.set(CallsConstants.CALLS_TRANSCRIPT, processedTranscript); - return await callBean.save(); + return loggerUtils.logReturnValue(await callBean.save()); }; exports.handler = handler; diff --git a/src/handlers/start-chat.js b/src/handlers/start-chat.js index 46d472686b..dcf9559e39 100644 --- a/src/handlers/start-chat.js +++ b/src/handlers/start-chat.js @@ -9,9 +9,11 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ let { startChatContact, buildResponseFailed, buildSuccessfulResponse } = require('../utils/aws/chat-utils'); +let loggerUtils = require('../utils/logger-utils'); exports.handler = (event, context, callback) => { - console.log('Received event: ' + JSON.stringify(event)); + // Log incoming API gateway event to cloudwatch for debugging + loggerUtils.logAPIGatewayEvent(event); let body = JSON.parse(event['body']); startChatContact(body).then((startChatResult) => { diff --git a/src/utils/aws/s3-utils.js b/src/utils/aws/s3-utils.js index ff9cc5d6c5..2cf3edbea4 100644 --- a/src/utils/aws/s3-utils.js +++ b/src/utils/aws/s3-utils.js @@ -9,6 +9,7 @@ * Copyright (C) SugarCRM Inc. All rights reserved. */ const AWS = require('aws-sdk'); +const loggerUtils = require('../logger-utils'); /** * Get JSON uploaded to S3 based on the S3 trigger event @@ -25,13 +26,12 @@ async function getJsonFromS3Event(event) { Bucket: srcBucket, Key: srcKey }; - console.log('fetching s3 bucket object\n', params); + loggerUtils.logJson('Fetching from S3 bucket:', params); // Fetch object form S3 const s3Object = await s3.getObject(params, function(err, data) { if (err) { - console.log('error happened'); - console.log(err, err.stack); + console.warn(err, err.stack); } else { return JSON.stringify(data.Body.toString('utf-8')); } diff --git a/src/utils/logger-utils.js b/src/utils/logger-utils.js index 443e2f693c..4bc6c21fca 100644 --- a/src/utils/logger-utils.js +++ b/src/utils/logger-utils.js @@ -12,32 +12,74 @@ /** * Log the JSON to AWS Cloudwatch * - * @param {string} title - * @param {Object} json + * @param {string} title + * @param {Object} json */ -function logJson (title, json) { +function logJson(title, json) { console.log(`${title}:\n`, JSON.stringify(json)); } - + +/** + * Log API Gateway event for debugging + * + * @param {Object} event + */ +function logAPIGatewayEvent(event) { + logJson('API Gateway Event', event); +} + +/** + * Log the Contact Flow event for debugging + * + * @param {Object} event + */ +function logContactFlowEvent(event) { + logJson('Contact Flow Event', event); +} + +/** + * Log the Amazon Lex event for deubbing + * + * @param {Object} event + */ +function logLexEvent(event) { + logJson('Lex Event', event); +} + +/** + * Log value returned from Lambda function for debugging + * + * @param {Object} returnValue + */ +function logReturnValue(returnValue) { + logJson('Lambda Return Value', returnValue); + return returnValue; +} + /** * Log the S3 event for debugging * - * @param {Object} event + * @param {Object} event */ -function logS3Event (event) { +function logS3Event(event) { logJson('S3 Event', event); } /** * Log the Sugar API response for debugging * - * @param {string} response + * @param {string} response */ -function logSugarApiResponse (response) { +function logSugarApiResponse(response) { logJson('Sugar API Response', response); } -module.exports = { +module.exports = { + logAPIGatewayEvent, + logContactFlowEvent, + logJson, + logLexEvent, + logReturnValue, logS3Event, logSugarApiResponse }; diff --git a/src/utils/string-utils.js b/src/utils/string-utils.js new file mode 100644 index 0000000000..e1f6f85eb4 --- /dev/null +++ b/src/utils/string-utils.js @@ -0,0 +1,34 @@ +/* + * Your installation or use of this SugarCRM file is subject to the applicable + * terms available at + * http://support.sugarcrm.com/Resources/Master_Subscription_Agreements/. + * If you do not agree to all of the applicable terms or do not have the + * authority to bind the entity as an authorized representative, then do not + * install or use this SugarCRM file. + * + * Copyright (C) SugarCRM Inc. All rights reserved. + */ +const _ = require('underscore'); + +module.exports = { + /** + * Provides rudimentary string interpolation. Replaces '${}' placeholders + * in template strings with provided fillers. It is the responsibility of + * the calling function to ensure the number of fillers is appropriate + * for the template string provided + * + * @param {String} template - Template string with placeholders + * @param {String|Array} filler - String or array of strings to fill placeholders + * @return Template string with placeholders filled with provided filler(s) + */ + generateMessage: (template, filler) => { + if (Array.isArray(filler)) { + _.each(filler, function(item) { + template = template.replace('${}', item); + }); + } else { + template = template.replace('${}', filler); + } + return template.trim(); + } +}; diff --git a/src/utils/sugar/call-record-utils.js b/src/utils/sugar/call-record-utils.js index bc646d7174..4150ce1ceb 100644 --- a/src/utils/sugar/call-record-utils.js +++ b/src/utils/sugar/call-record-utils.js @@ -14,71 +14,78 @@ const app = require('../../core/app'); /** * Utils */ -const loggerUtils = require('../logger-utils'); +const strUtils = require('../string-utils'); /** * Defined Constants */ const CallsConstants = require('../../constants/sugar-modules/calls'); +const ErrorMessages = require('../../constants/messages/error'); const UrlConstants = require('../../constants/url'); - /** +/** * Return a single call record, false otherwise * * @param {string} contactId the AWS Connect contact ID */ -async function getCallRecord (contactId) { - let url = encodeURI(`Calls?filter[0][${CallsConstants.CALLS_AWS_CONTACT_ID}]=${contactId}`); - url = app.api.buildUrl(url); - const response = await app.api.call('read', url); - loggerUtils.logSugarApiResponse(response); +async function getCallRecord(contactId) { + let url = encodeURI(`Calls?filter[0][${CallsConstants.CALLS_AWS_CONTACT_ID}]=${contactId}`); + url = app.api.buildUrl(url); + const response = await app.api.call('read', url); + + // return false if the response is invalid or no records were returned + if (!response.data || !Array.isArray(response.data.records) || response.data.records.length === 0) { + console.warn(strUtils.generateMessage(ErrorMessages.TPL_NO_RECORDS_MATCHED, 'Call')); + return false; + } - // return false if the response is invalid or more than one record was returned - if (!response.data || !Array.isArray(response.data.records) || response.data.records.length !== 1) { - return false; - } + // return false if multiple call records were returned + if (response.data.length > 1) { + console.warn(strUtils.generateMessage(ErrorMessages.TPL_MULTIPLE_RECORDS_MATCHED, 'Call')); + return false; + } - // return a single call record - return response.data.records[0]; + // return a single call record + return response.data.records[0]; } /** * Build the call recording URL based on an AWS Connect URL and not S3. Appropriate * AWS Connect user permissions are required to access the URL * - * @param {string} contactId + * @param {string} contactId */ -function buildCallRecordingUrl (contactId) { - let connectInstance = process.env.awsConnectInstance; - let connectDomain = process.env.awsConnectDomain; - let callRecordingPartialUrl = process.env.callRecordingPartialUrl; +function buildCallRecordingUrl(contactId) { + let connectInstance = process.env.awsConnectInstance; + let connectDomain = process.env.awsConnectDomain; + let callRecordingPartialUrl = process.env.callRecordingPartialUrl; - // return empty if contact ID is not defined - if (!contactId) { - console.warn('Call Recording URL Warning: Unable to construct a URL without AWS Connect contact ID'); - return ''; - } + // return empty if contact ID is not defined + if (!contactId) { + console.warn('Call Recording URL Warning: Unable to construct a URL without AWS Connect contact ID'); + return ''; + } - // return empty if one or more of the required parameters is not defined - if (!connectInstance || !connectDomain || !callRecordingPartialUrl) { - console.warn('Call Recording URL Warning: Unable to construct a URL with missing required parameter(s). ' + + // return empty if one or more of the required parameters is not defined + if (!connectInstance || !connectDomain || !callRecordingPartialUrl) { + console.warn('Call Recording URL Warning: Unable to construct a URL with missing required parameter(s). ' + 'Please check the following parameters: AwsConnectInstance, AWSConnectDomain, CallRecordingPartialURL'); - return ''; - } + return ''; + } - let encodedConnectInstance = encodeURIComponent(connectInstance); - let encodedContactId = encodeURIComponent(contactId); + let encodedConnectInstance = encodeURIComponent(connectInstance); + let encodedContactId = encodeURIComponent(contactId); - connectDomain = connectDomain.trim(); - callRecordingPartialUrl = callRecordingPartialUrl.trim(); + connectDomain = connectDomain.trim(); + callRecordingPartialUrl = callRecordingPartialUrl.trim(); - let url = UrlConstants.HTTPS_PREFIX + encodedConnectInstance + '.' + connectDomain + + let url = UrlConstants.HTTPS_PREFIX + encodedConnectInstance + '.' + connectDomain + callRecordingPartialUrl + encodedContactId; - return url; + return url; } -module.exports = { - getCallRecord, - buildCallRecordingUrl +module.exports = { + getCallRecord, + buildCallRecordingUrl }; diff --git a/src/utils/utils.js b/src/utils/utils.js index 17fc9666c8..d96811a708 100644 --- a/src/utils/utils.js +++ b/src/utils/utils.js @@ -23,4 +23,6 @@ function processTranscript(transcript) { return processedTranscript.trim(); } -module.exports = { processTranscript }; +module.exports = { + processTranscript +};