UNPKG

@basetime/bldr-sfmc

Version:

CLI application for SFMC Development workflow and package deployment

575 lines (486 loc) 15.5 kB
const { v4: uuidv4 } = require('uuid'); const contextMap = require('./contextMap'); const getFiles = require('node-recursive-directory'); const LocalFile = require('./Blueprint/LocalFile'); const localFiles = new LocalFile(); module.exports.assignObject = (obj) => Object.assign({}, obj); module.exports.uniqueArray = (arr, key) => arr.filter((v, i, a) => a.findIndex((v2) => v2[key] === v[key]) === i); module.exports.getParentFolderFromArray = (folders, parentId) => folders.filter((folder) => folder.id === parentId); module.exports.splitDateFromISO = (dateStr) => dateStr.substring(0, dateStr.indexOf('T')); module.exports.guid = () => uuidv4(); module.exports.filePathDetails = async (filePath) => { const ctx = await this.ctx(filePath); const folderArr = filePath.split('/'); const fileName = folderArr.pop(); const folderName = folderArr.slice(-1).pop(); const folderPath = folderArr.join('/'); let projectPath = filePath.substring(filePath.indexOf(ctx.root)); projectPath = projectPath.split('/'); projectPath.pop(); projectPath = projectPath.join('/'); // filename.html // rootFolder/Subdirectory/assetFolder // assetFolder // rootFolder/Subdirectory/assetFolder/filename.html return { fileName, folderPath, folderName, projectPath, context: ctx, }; }; module.exports.getAllFiles = async () => { // Get the root directory for the project being worked on const dirPath = await localFiles._getRootPath(contextMap); // Get the current working directory that the [add] command was triggered const cwdPath = process.cwd(); // Identify the context for request const contexts = contextMap.map((ctx) => localFiles._fileExists(`./${ctx.root}`) && ctx.root).filter(Boolean); // Store all complete file paths for files in CWD and subdirectories let ctxFiles = new Array(); // if dir is root folder if (dirPath === './') { // iterate all contexts and add files for (const c in contexts) { ctxFiles.push(...(await getFiles(`./${contexts[c]}`))); } } else { // get files from current working directory and subdirectories ctxFiles.push(...(await getFiles(`${cwdPath}`))); } return ctxFiles; }; module.exports.ctx = (filePath) => { const ctxFilter = contextMap.map((ctx) => filePath.includes(`/${ctx.root}/`) && ctx); return ctxFilter.filter(Boolean)[0]; }; /** * * @param {string} status * @returns */ module.exports.automationStatus = (status) => { let statusText; switch (status) { case -1: statusText = 'Error'; break; case 0: statusText = 'BuildingError'; break; case 1: statusText = 'Building'; break; case 2: statusText = 'Ready'; break; case 3: statusText = 'Running'; break; case 4: statusText = 'Paused'; break; case 5: statusText = 'Stopped'; break; case 6: statusText = 'Scheduled'; break; case 7: statusText = 'Awaiting'; break; case 8: statusText = 'InactiveTrigger'; break; } return statusText; }; /** * Method to format API response from SFMC into minimum required POST/PUT JSON objects * Updates Category object with full folder paths * Gathers additional data for Image assets * * @param {object} results from API Request * @param {object} folderPaths category object * @returns {object} Array of formatted asset payloads */ module.exports.formatContentBuilderDataForFile = async (bldr, results, folderPaths) => { if (!Array.isArray(results)) { results = [results]; } const formatted = new Array(); for (const r in results) { const asset = results[r]; // Generate new bldrId for asset const bldrId = this.guid(); const searchPath = folderPaths.find(({ id }) => id === asset.category.id); const folderPath = searchPath ? searchPath.folderPath : ''; // Create JSON structure for new asset post let post = {}; post.id = asset.id; post.bldrId = bldrId; post.name = asset.name; post.assetType = asset.assetType; post.category = asset.category; post.category.folderPath = folderPath; if (asset.content) { post.content = asset.content; } if (asset.meta) { post.meta = asset.meta; } if (asset.slots) { post.slots = asset.slots; } if (asset.views) { post.views = asset.views; } if (asset.assetType.displayName === 'Image') { post.name = asset.name.indexOf('.') === -1 ? asset.name : asset.name.substring(0, asset.name.indexOf('.')); post.publishedURL = asset.fileProperties.publishedURL; post.file = await bldr.asset.getImageFile(asset.id); } formatted.push(post); } return formatted; }; /** * * @param {string} activityType * @returns */ module.exports.identifyAutomationStudioActivityObjectTypeId = (activityType) => { let out; switch (activityType) { case 'queries': out = { objectTypeId: 300, api: 'queries', name: 'queryactivity', objectIdKey: 'queryDefinitionId', folder: 'Automation Studio/Query', }; break; case 'scripts': out = { objectTypeId: 423, api: 'scripts', name: 'ssjsactivity', objectIdKey: 'ssjsActivityId', folder: 'Automation Studio/Scripts', }; break; case 'imports': out = { objectTypeId: 43, api: 'imports', name: 'importactivity', objectIdKey: 'importDefinitionId', folder: 'Automation Studio/File Imports', }; break; case 'filetransfers': out = { objectTypeId: 53, api: 'filetransfers', name: 'transferactivity', objectIdKey: 'id', folder: 'Automation Studio/File Transfers', }; break; case 'filters': out = { objectTypeId: 303, api: 'filters', name: 'filteractivity', objectIdKey: 'filterActivityId', folder: 'Automation Studio/Filters', }; break; case 'dataextracts': out = { objectTypeId: 73, api: 'dataextracts', name: 'dataextractactivity', objectIdKey: 'dataExtractDefinitionId', folder: 'Automation Studio/Extracts', }; break; case 'EmailSendDefinition': out = { objectTypeId: 42, api: 'EmailSendDefinition', name: 'userinitiatedsend', objectIdKey: 'ObjectID', folder: 'Automation Studio/User-Initiated', }; break; default: } return out; }; /** * * @param {number} objectTypeId * @returns */ module.exports.identifyAutomationStudioActivityType = (objectTypeId) => { let out; switch (objectTypeId) { case 300: out = { api: 'queries', name: 'queryactivity', objectIdKey: 'queryDefinitionId', folder: 'Automation Studio/Query', }; break; case 423: out = { api: 'scripts', name: 'ssjsactivity', objectIdKey: 'ssjsActivityId', folder: 'Automation Studio/Scripts', }; break; case 43: out = { api: 'imports', name: 'importactivity', objectIdKey: 'importDefinitionId', folder: 'Automation Studio/File Imports', }; break; case 53: out = { api: 'filetransfers', name: 'transferactivity', objectIdKey: 'id', folder: 'Automation Studio/File Transfers', }; break; case 303: out = { api: 'filters', name: 'filteractivity', objectIdKey: 'filterActivityId', folder: 'Automation Studio/Filters', }; break; case 73: out = { api: 'dataextracts', name: 'dataextractactivity', objectIdKey: 'dataExtractDefinitionId', folder: 'Automation Studio/Extracts', }; break; case 42: out = { api: 'EmailSendDefinition', name: 'userinitiatedsend', objectIdKey: 'ObjectID', folder: 'Automation Studio/User-Initiated', }; break; default: } return out; }; module.exports.createAPIConfig = async (config, template = true) => { const configTemplate = config || { client_id: '', client_secret: '', authentication_uri: '', parentMID: '', }; const dirPath = await localFiles._getRootPath(contextMap); localFiles.createFile(`${dirPath}/.sfmc.config.json`, JSON.stringify(configTemplate, null, 2)); if (template) { localFiles.createFile(`${dirPath}/template.sfmc.config.json`, JSON.stringify(configTemplate, null, 2)); } localFiles.append(`${dirPath}/.gitignore`, `\n#sfmc config \n.sfmc.config.json`); }; module.exports.scrubConfig = async (content) => { const dirPath = await localFiles._getRootPath(contextMap); if (localFiles._fileExists(`${dirPath}/.sfmc.config.json`)) { const config = await localFiles._getSFMCConfig(dirPath); for (const c in config) { const key = c; const value = config[c]; if (content.match(value)) { content = content.replace(value, `{{${key}}}`); } } } return content; }; module.exports.replaceConfig = async (content) => { const dirPath = await localFiles._getRootPath(contextMap); if (localFiles._fileExists(`${dirPath}/.sfmc.config.json`)) { const config = await localFiles._getSFMCConfig(dirPath); for (const c in config) { const key = c; const value = config[c]; if (content.match(key)) { content = content.replace(`{{${key}}}`, value); } } } return content; }; module.exports.deployCheckConfig = async () => { let preventDeployment = false; const dirPath = await localFiles._getRootPath(contextMap); if (localFiles._fileExists(`${dirPath}/.sfmc.config.json`)) { const config = await localFiles._getSFMCConfig(dirPath); for (const c in config) { const key = c; const value = config[c]; if (value === '') { console.log(`Please configure ${key} in .sfmc.config.json`); preventDeployment = true; } } } return preventDeployment; }; module.exports.updateAssetContent = (asset, content) => { const assetType = asset.assetType.name; switch (assetType) { case 'webpage': case 'htmlemail': asset.views.html.content = content; break; case 'codesnippetblock': case 'htmlblock': case 'jscoderesource': asset.content = content; break; case 'textonlyemail': asset.views.text.content = content; break; case 'queryactivity': asset.queryText = content; break; case 'ssjsactivity': asset.script = content; break; default: content = null; } return asset; }; module.exports.getAssetContent = (asset) => { const assetType = (asset && asset.assetType && asset.assetType.name) || 'null'; let content; switch (assetType) { case 'webpage': case 'htmlemail': content = asset.views.html.content; break; case 'codesnippetblock': case 'htmlblock': case 'jscoderesource': content = asset.content; break; case 'textonlyemail': content = asset.views.text.content; break; case 'queryactivity': content = asset.queryText; break; case 'ssjsactivity': content = asset.script; break; default: content = JSON.stringify(asset); } return content; }; function capitalizeFirstLetter(string) { return string.charAt(0).toUpperCase() + string.slice(1); } function lowercaseFirstLetter(string) { return string.charAt(0).toLowerCase() + string.slice(1); } module.exports.capitalizeKeys = (obj) => { let objectOut = obj; var key, upKey; for (key in objectOut) { if (Object.prototype.hasOwnProperty.call(objectOut, key)) { upKey = capitalizeFirstLetter(key); if (upKey !== key) { objectOut[upKey] = objectOut[key]; delete objectOut[key]; } // recurse if (typeof objectOut[upKey] === 'object') { this.capitalizeKeys(objectOut[upKey]); } } } return objectOut; }; module.exports.lowercaseKeys = (obj) => { let objectOut = obj; var key, keyDown; for (key in objectOut) { if (Object.prototype.hasOwnProperty.call(objectOut, key)) { keyDown = lowercaseFirstLetter(key); if (keyDown !== key) { objectOut[keyDown] = objectOut[key]; delete objectOut[key]; } // recurse if (typeof objectOut[keyDown] === 'object') { this.lowercaseKeys(objectOut[keyDown]); } } } return objectOut; }; /** * Automation Studio Object Reference * Salesforce Send "objectTypeId": 771 Fire Event "objectTypeId": 749 Guided Send "objectTypeId": 42 Wait Activity "objectTypeId": 467 Verification Activity "objectTypeId": 1000 Refresh Group "objectTypeId": 45 Data Factory Utility "objectTypeId": 425 Send SMS "objectTypeId": 725 Import Mobile Contacts "objectTypeId": 726 Refresh Mobile Filtered List "objectTypeId": 724 Send GroupConnect "objectTypeId": 783 Report Definition "objectTypeId": 84 Send Push "objectTypeId": 736 //DONE// ---Query Activity--- "objectTypeId": 300 ---Script Activity--- "objectTypeId": 423 ---Import File--- "objectTypeId": 43 ---File Transfer--- "objectTypeId": 53 ---Filter Activity--- "objectTypeId": 303 ---Data Extract--- "objectTypeId": 73 */