UNPKG

mcdev

Version:

Accenture Salesforce Marketing Cloud DevTools

306 lines (289 loc) 12.8 kB
import { Util } from './util.js'; /** * @typedef {import('../../types/mcdev.d.js').AuthObject} AuthObject * @typedef {import('../../types/mcdev.d.js').BuObject} BuObject * @typedef {import('../../types/mcdev.d.js').Cache} Cache * @typedef {import('../../types/mcdev.d.js').CodeExtract} CodeExtract * @typedef {import('../../types/mcdev.d.js').CodeExtractItem} CodeExtractItem * @typedef {import('../../types/mcdev.d.js').DeltaPkgItem} DeltaPkgItem * @typedef {import('../../types/mcdev.d.js').Mcdevrc} Mcdevrc * @typedef {import('../../types/mcdev.d.js').MetadataTypeItem} MetadataTypeItem * @typedef {import('../../types/mcdev.d.js').MetadataTypeItemDiff} MetadataTypeItemDiff * @typedef {import('../../types/mcdev.d.js').MetadataTypeItemObj} MetadataTypeItemObj * @typedef {import('../../types/mcdev.d.js').MetadataTypeMap} MetadataTypeMap * @typedef {import('../../types/mcdev.d.js').MetadataTypeMapObj} MetadataTypeMapObj * @typedef {import('../../types/mcdev.d.js').MultiMetadataTypeList} MultiMetadataTypeList * @typedef {import('../../types/mcdev.d.js').MultiMetadataTypeMap} MultiMetadataTypeMap * @typedef {import('../../types/mcdev.d.js').SoapRequestParams} SoapRequestParams * @typedef {import('../../types/mcdev.d.js').TemplateMap} TemplateMap * @typedef {import('../../types/mcdev.d.js').TypeKeyCombo} TypeKeyCombo * * @typedef {import('../../types/mcdev.d.js').ListItem} ListItem * @typedef {import('../../types/mcdev.d.js').ListMap} ListMap */ /** @type {Cache} */ const dataStore = {}; let currentMID = null; export default { /** * Method to setup buObject * NOTE: in future this may need to restore, rather than wipe the cache * * @param {BuObject} buObject for current Business unit * @returns {void} */ initCache: (buObject) => { if (buObject?.mid) { currentMID = buObject.mid; dataStore[currentMID] = {}; // If the EID is not setup, also do this for required types (ie. Folders) if (!dataStore[buObject.eid]) { dataStore[buObject.eid] = {}; } } else { throw new Error('Business Unit (buObject) used when initialzing cache was missing MID'); } }, /** * return entire cache for current MID * * @returns {MultiMetadataTypeMap} cache for one Business Unit */ getCache: () => dataStore[currentMID], /* eslint-disable unicorn/no-array-for-each */ /** * clean cache for one BU if mid provided, otherwise whole cache * * @param {number} [mid] limit clearing to provided business unit MID * @param {string} [type] optionally limit clearing to specific metadata type; only used if mid is provided * @returns {void} */ clearCache: (mid, type) => mid ? Object.keys(dataStore[mid]).forEach((key) => { if (!type || type === key) { delete dataStore[mid][key]; } }) : Object.keys(dataStore).forEach((key) => delete dataStore[key]), /* eslint-enable unicorn/no-array-for-each */ /** * return a specific item from cache * * @param {string} type of Metadata to retrieve from cache * @param {string} key of the specific metadata * @returns {MetadataTypeItem} cached metadata item */ getByKey: (type, key) => dataStore[currentMID]?.[type]?.[key], /** * override cache for given metadata type with new data * * @param {string} type of Metadata to retrieve from cache * @param {MetadataTypeMap} metadataMap map to be set * @returns {void} */ setMetadata: (type, metadataMap) => { dataStore[currentMID][type] = metadataMap; }, /** * merges entire metadata type with existing cache * * @param {string} type of Metadata to retrieve from cache * @param {MetadataTypeMap} metadataMap map to be merged * @param {number} [overrideMID] which should be used for merging * @returns {void} */ mergeMetadata: (type, metadataMap, overrideMID) => { // ensure cache exists for type dataStore[currentMID][type] ||= {}; // if overrideMID is provided, create a copy of current MID cache if (overrideMID) { // ! needs to be verified if this is actually needed. When discovering an issue with this method actually overriting metadataMap, this copy-logic was present and i did not want to break things dataStore[overrideMID][type] = Object.assign({}, dataStore[currentMID][type]); } // merge metadataMap into existing cache Object.assign(dataStore[overrideMID || currentMID][type] || {}, metadataMap); }, /** * standardized method for getting data from cache. * * @param {string} metadataType metadata type ie. query * @param {string|number|boolean} searchValue unique identifier of metadata being looked for * @param {string} searchField field name (key in object) which contains the unique identifer * @param {string} returnField field which should be returned * @param {number} [overrideMID] ignore currentMID and use alternative (for example parent MID) * @param {boolean} caseInsensitive optional; if true, search is case insensitive * @returns {string} value of specified field. Error is thrown if not found */ searchForField( metadataType, searchValue, searchField, returnField, overrideMID, caseInsensitive = false ) { if (caseInsensitive) { searchValue = (searchValue + '').toLowerCase(); } for (const key in dataStore[overrideMID || currentMID]?.[metadataType]) { if ( (caseInsensitive && ( Util.resolveObjPath( searchField, dataStore[overrideMID || currentMID][metadataType][key] ) + '' ).toLowerCase() == searchValue) || (!caseInsensitive && Util.resolveObjPath( searchField, dataStore[overrideMID || currentMID][metadataType][key] ) == searchValue) ) { try { if ( Util.resolveObjPath( returnField, dataStore[overrideMID || currentMID][metadataType][key] ) ) { return Util.resolveObjPath( returnField, dataStore[overrideMID || currentMID][metadataType][key] ); } else { throw new Error(); // eslint-disable-line unicorn/error-message } } catch { throw new Error( `${metadataType} with ${searchField} '${searchValue}' does not have field '${returnField}'` ); } } } throw new Error( `Dependent ${metadataType} with ${searchField}='${searchValue}' was not found on your BU` ); }, /** * helper for setFolderId * * @param {string} r__folder_Path folder path value * @param {number} [overrideMID] ignore currentMID and use alternative (for example parent MID) * @param {boolean} allowOtherBu getting folder from other BU; FALSE for folder parent search * @returns {number} folder ID */ getFolderId(r__folder_Path, overrideMID, allowOtherBu = true) { const folder = this.getFolderByPath(r__folder_Path, overrideMID, allowOtherBu); return folder?.ID; }, /** * helper for setFolderId * * @param {string} r__folder_Path folder path value * @param {number} [overrideMID] ignore currentMID and use alternative (for example parent MID) * @param {boolean} allowOtherBu getting folder from other BU; FALSE for folder parent search * @returns {object} folder item */ getFolderByPath(r__folder_Path, overrideMID, allowOtherBu = true) { if (!r__folder_Path) { throw new Error('r__folder_Path not set'); } /** @type {ListMap} */ const folderMap = dataStore[overrideMID || currentMID]?.['folder']; if (!folderMap) { throw new Error('No folders found in cache'); } const folderPath_lower = r__folder_Path.toLowerCase(); const potentialFolders = []; for (const folder of Object.values(folderMap)) { if (!folder?.Path) { continue; // skip folders without Path } if (folder.Path.toLowerCase() === folderPath_lower) { if (folder?.Client?.ID === (overrideMID || currentMID)) { return folder; } else if (allowOtherBu) { potentialFolders.push(folder); } } } if (potentialFolders.length >= 1) { return potentialFolders[0]; } else { throw new Error(`No folders found with path ${r__folder_Path}`); } }, /** * standardized method for getting data from cache - adapted for special case of lists * ! keeping this in util/cache.js rather than in metadataTypes/List.js to avoid potential circular dependencies * * @param {string} searchValue unique identifier of metadata being looked for * @param {'ObjectID'|'ID'|'CustomerKey'} searchField ObjectID:string(uuid), ID:numeric, CustomerKey:string(name + folder ID) * @returns {string} unique folderPath/ListName combo of list */ getListPathName(searchValue, searchField) { const returnField1 = 'r__folder_Path'; const returnField2 = 'ListName'; for (const key in dataStore[currentMID]['list']) { if (dataStore[currentMID]['list'][key][searchField] === searchValue) { try { if ( dataStore[currentMID]['list'][key][returnField1] && dataStore[currentMID]['list'][key][returnField2] ) { return ( dataStore[currentMID]['list'][key][returnField1] + '/' + dataStore[currentMID]['list'][key][returnField2] ); } else { throw new Error(); // eslint-disable-line unicorn/error-message } } catch { throw new Error( `${'list'} with ${searchField}='${searchValue}' does not have the fields ${returnField1} and ${returnField2}` ); } } } throw new Error( `Dependent list with ${searchField}='${searchValue}' was not found on your BU` ); }, /** * standardized method for getting data from cache - adapted for special case of lists * ! keeping this in util/cache.js rather than in metadataTypes/List.js to avoid potential circular dependencies * * @param {string} listPathName folderPath/ListName combo of list * @param {'ObjectID'|'ID'|'CustomerKey'|'ListName'} returnField ObjectID:string(uuid), ID:numeric, CustomerKey:string(name + folder ID) * @returns {string} unique ObjectId of list */ getListObjectId(listPathName, returnField) { const folderPathArr = listPathName.split('/'); const listName = folderPathArr.pop(); const folderPath = folderPathArr.join('/'); for (const key in dataStore[currentMID]['list']) { if ( dataStore[currentMID]['list'][key].ListName === listName && dataStore[currentMID]['list'][key].r__folder_Path === folderPath ) { try { if (dataStore[currentMID]['list'][key][returnField]) { return dataStore[currentMID]['list'][key][returnField]; } else { throw new Error(); // eslint-disable-line unicorn/error-message } } catch { throw new Error( `${'list'} with ListName='${listName}' and r__folder_Path='${folderPath}' does not have field '${returnField}'` ); } } } throw new Error( `Dependent list with ListName='${listName}' and r__folder_Path='${folderPath}' was not found on your BU` ); }, };