Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update CC15 lambdas source code #67

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
254 changes: 250 additions & 4 deletions ServerlessApplication/ServiceCloudVoiceLambdas.yaml

Large diffs are not rendered by default.

7 changes: 6 additions & 1 deletion authKeysSSMUtil/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ function generatePrivatePublicKeyPair(requestDetails) {
];
const expiresIn = requestDetails.ExpiresIn;

const pems = selfsigned.generate(attrs, { days: expiresIn });
var pems = selfsigned.generate(attrs, {
keySize: 2048, // the size for the private key in bits (default: 1024)
days: expiresIn, // how long till expiry of the signed certificate (default: 365)
algorithm: "RS256",
extensions: [{ name: "basicConstraints", cA: true }], // certificate extensions array
});
return pems;
}

Expand Down
82 changes: 82 additions & 0 deletions contactDataSync/SCVLoggingUtil.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/**
* JS logging utils class used in SCV project
*
*/

/** Logging Level:
* INFO, // For generally useful information to log
* WARN, // For anything that can potentially cause application oddities
* ERROR, // For any error which is fatal to the operation
* DEBUG, // Only when you would be "debugging" the code and trying to find one part of a function specifically
*/

const winston = require("winston");

const logger = winston.createLogger({
level: process.env.LOG_LEVEL || "info",
format: winston.format.combine(
winston.format.timestamp({ format: "YYYY-MM-DD HH:mm:ss" }),
winston.format.prettyPrint(),
winston.format.json()
),
transports: [new winston.transports.Console()],
});

const noContext = "NO_CONTEXT";
const noMessage = "NO_MESSAGE";

/**
* Create a log line with level of INFO
* @param logLine Object containing message, context, evenType and context to log
*/
function info(logLine) {
const logline = buildLog(logLine);
logger.info(logline);
}

/**
* Create a log line with level of DEBUG
* @param logLine Object containing message, context, evenType and context to log
*/
function debug(logLine) {
const logline = buildLog(logLine);
logger.debug(logline);
}

/**
* Create a log line with level of WARN
* @param logLine Object containing message, context, evenType and context to log
*/
function warn(logLine) {
const logline = buildLog(logLine);
logger.warn(logline);
}

/**
* Create a log line with level of ERROR
* @param logLine Object containing message, context, evenType and context to log
*/
function error(logLine) {
const logline = buildLog(logLine);
logger.error(logline);
}

/**
* Create a log line with level of ERROR
* @param logLine Object containing message, context, evenType and context to log
*/
function buildLog(logLine) {
const log = {
context: logLine.context || noContext,
message: logLine.message || noMessage,
category: "ContactDataSync",
};
return log;
}

module.exports = {
error,
warn,
info,
debug,
};
165 changes: 165 additions & 0 deletions contactDataSync/awsUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
const aws = require("aws-sdk");
const SCVLoggingUtil = require("./SCVLoggingUtil");
const lambda = new aws.Lambda();
const connect = new aws.Connect();
const s3 = new aws.S3();
const participantMap = {
["CUSTOMER"]: ["END_USER"],
["AGENT"]: ["VIRTUAL_AGENT"],
};

/**
* Gets the transcripts from the contactlens object and returns the transcripts in a format that can be used for the SCV
* @param clObject - ContactLens object from the contactlens s3 file
* @param contactIdRelatedRecordMap - Map of contactIds to related record Ids
@returns - Conversation transcript entries
*/
async function getTranscript(clObject, contactIdRelatedRecordMap) {
const describeContactParams = {
ContactId: clObject.CustomerMetadata.ContactId,
InstanceId: clObject.CustomerMetadata.InstanceId,
};
const describeContactResponse = await getAgentTimestamp(
describeContactParams
);
const connectedToAgentTimestamp =
describeContactResponse.Contact.AgentInfo.ConnectedToAgentTimestamp;
SCVLoggingUtil.debug({
message: "Agent connected timestamp.",
context: { payload: { connectedToAgentTimestamp } },
});
const date = new Date(connectedToAgentTimestamp);
const conversationEntries = [];
const transcripts = clObject.Transcript;
for (let i = 0; i < transcripts.length; i++) {
const transcript = transcripts[i];
const id = i.toString();
const clientSentTimestamp = date.setMilliseconds(
date.getMilliseconds() + Number(transcript.BeginOffsetMillis)
);
const subject =
clObject.CustomerMetadata.ContactId +
participantMap[transcript.ParticipantId];
const entryTranscriptTemplate = {
type: "conversationEntry",
payload: {
conversationId: clObject.CustomerMetadata.ContactId,
id,
clientSentTimestamp,
clientDuration: 1,
messageText: transcript.Content,
sender: {
appType: "TELEPHONY_INTEGRATION",
subject,
role: transcript.ParticipantId,
},
relatedRecords:
contactIdRelatedRecordMap[clObject.CustomerMetadata.ContactId],
},
};
conversationEntries.push(entryTranscriptTemplate);
}
return conversationEntries;
}

/*
Gets Contact Lens files from S3
@param bucketName - S3 Bucket name containing contact lens files
@param contactIds - List of contact ids to get contact lens files from S3
@param instanceId - Connect instanceId
@returns List of Contact Lens files
*/
async function getContactLensS3Path(bucketName, eventPayload, instanceId) {
let filePaths = [];
const contactIds = eventPayload.map((x) => x.contactId);
const contactLensFilePathPrefixDate = [];
for (let i = 0; i < contactIds.length; i++) {
const contactId = contactIds[i];
const matched = contactLensFilePathPrefixDate.filter((s) =>
s.includes(contactId)
);
if (matched.length > 0) {
filePaths = filePaths.concat(matched);
SCVLoggingUtil.info({
message: "Found contact id in the exisiting S3 prefix.",
context: { contactId: contactId },
});
} else {
const describeContactParams = {
ContactId: contactId,
InstanceId: instanceId,
};
const describeContactResponse = await getAgentTimestamp(
describeContactParams
);
const connectedToAgentTimestamp =
describeContactResponse.Contact.AgentInfo.ConnectedToAgentTimestamp;
const date = new Date(connectedToAgentTimestamp);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, "0");
const day = String(date.getDate()).padStart(2, "0");
const prefixDate = year + "/" + month + "/" + day;

const s3Params = {
Bucket: bucketName,
Prefix: "Analysis/Voice/" + prefixDate + "/",
};
const s3Data = await s3.listObjectsV2(s3Params).promise();

const filteredFiles = s3Data.Contents.filter((s3file) => {
return s3file.Key.includes(contactId);
});
if (filteredFiles.length === 0) {
SCVLoggingUtil.warn({
message: `Did not find contact lens json file in the S3 bucket:${s3Params.Bucket} with prefix ${s3Params.Prefix} for contactId:${contactId}.`,
context: { contactId },
});
}
const contactLensFilePaths = filteredFiles.map((file) => file.Key);
// add key to contactLensS3Keys
s3Data.Contents.forEach((s3file) => {
contactLensFilePathPrefixDate.push(s3file.Key);
});
filePaths = filePaths.concat(contactLensFilePaths);
}
}
return filePaths;
}

async function getAgentTimestamp(describeContactParams) {
try {
const describeContactResponse = await connect
.describeContact(describeContactParams)
.promise();
SCVLoggingUtil.debug({
message:
"Successfully fetched the result from the describeContact API call.",
context: describeContactResponse,
});
return describeContactResponse;
} catch (err) {
const message = `Error fetching information for ContactId:${describeContactParams.ContactId} and InstanceId:${describeContactParams.InstanceId}. Check if ContactId is valid or retry request by removing it.`;
SCVLoggingUtil.error({
category: [],
message,
context: err,
});
throw new Error(message);
}
}

async function getS3Object(params) {
return s3.getObject(params).promise();
}

async function invokeLambdaFunction(params) {
return lambda.invoke(params).promise();
}

module.exports = {
getAgentTimestamp,
getContactLensS3Path,
getTranscript,
getS3Object,
invokeLambdaFunction,
};
8 changes: 8 additions & 0 deletions contactDataSync/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
orgId: process.env.SALESFORCE_ORG_ID,
connectInstanceId: process.env.CONNECT_INSTANCE_ID,
callCenterApiName: process.env.CALL_CENTER_API_NAME,
maxContactIds: process.env.MAX_CONTACT_IDS,
invokeSfRestApiArn: process.env.INVOKE_SALESFORCE_REST_API_ARN,
batchSize: process.env.BATCH_SIZE,
};
20 changes: 20 additions & 0 deletions contactDataSync/fetchUploadIdsStatus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
const SCVLoggingUtil = require("./SCVLoggingUtil");
const sfRestApi = require("./sfRestApi");

async function processFetchUploadIdsStatus(event) {
const uploadIds = event.uploadIds.join();
SCVLoggingUtil.debug({
message: `Payload for connect api`,
context: { uploadIds },
});
const result = await sfRestApi.invokeSfRestApiFetchUploadIdsStatus(uploadIds);
SCVLoggingUtil.info({
message: `FetchUploadIdsStatus result`,
context: { payload: result },
});
return result;
}

module.exports = {
processFetchUploadIdsStatus,
};
Loading