diff --git a/lib/metadataTypes/Asset.js b/lib/metadataTypes/Asset.js index f6847f5d3..d376c5b4d 100644 --- a/lib/metadataTypes/Asset.js +++ b/lib/metadataTypes/Asset.js @@ -259,7 +259,7 @@ class Asset extends MetadataType { // assets can link to other assets (via template, content block reference and SSJS/AMPscript) and deployment would fail if we did not sort this here metadataMap = await this._getUpsertOrder(metadataMap, deployDir); } - return super.upsert(metadataMap, deployDir); + return super.upsert(metadataMap, deployDir, true); } /** diff --git a/lib/metadataTypes/MetadataType.js b/lib/metadataTypes/MetadataType.js index 1d6d97358..16a5468fc 100644 --- a/lib/metadataTypes/MetadataType.js +++ b/lib/metadataTypes/MetadataType.js @@ -710,12 +710,14 @@ class MetadataType { * * @param {MetadataTypeMap} metadataMap metadata mapped by their keyField * @param {string} deployDir directory where deploy metadata are saved + * @param {boolean} [runCreatesSequentially] when a type has self-dependencies creates need to run one at a time and created keys need to be cached to ensure following creates/updates have thoses keys available * @returns {Promise.} keyField => metadata map */ - static async upsert(metadataMap, deployDir) { + static async upsert(metadataMap, deployDir, runCreatesSequentially = false) { const orignalMetadataMap = structuredClone(metadataMap); const metadataToUpdate = []; const metadataToCreate = []; + let createResults = []; let filteredByPreDeploy = 0; for (const metadataKey in metadataMap) { let hasError = false; @@ -744,13 +746,25 @@ class MetadataType { if (deployableMetadata) { metadataMap[metadataKey] = deployableMetadata; // create normalizedKey off of whats in the json rather than from "metadataKey" because preDeployTasks might have altered something (type asset) - await this.createOrUpdate( + const action = await this.createOrUpdate( metadataMap, metadataKey, hasError, metadataToUpdate, metadataToCreate ); + if (runCreatesSequentially && action === 'create') { + // handle creates sequentially here becasue we might have interdepencies + const result = await this.create(metadataMap[metadataKey], deployDir); + if (result) { + createResults.push(result); + + // make this newly created item available in cache for other itmes that might reference it + const newObject = {}; + newObject[metadataKey] = metadataMap[metadataKey]; + cache.mergeMetadata(this.definition.type, newObject); + } + } } else { filteredByPreDeploy++; } @@ -758,37 +772,39 @@ class MetadataType { Util.logger.errorStack(ex, `Upserting ${this.definition.type} failed`); } } - const createLimit = pLimit(10); - const createResults = ( - await Promise.all( - metadataToCreate - .filter((r) => r !== undefined && r !== null) - .map((metadataEntry) => - createLimit(() => this.create(metadataEntry, deployDir)) - ) - ) - ).filter((r) => r !== undefined && r !== null); + if (!runCreatesSequentially) { + const createLimit = pLimit(10); + createResults = ( + await Promise.all( + metadataToCreate + .filter((r) => r !== undefined && r !== null) + .map((metadataEntry) => + createLimit(() => this.create(metadataEntry, deployDir)) + ) + ) + ).filter((r) => r !== undefined && r !== null); + } - if (Util.OPTIONS.noUpdate && metadataToUpdate.length > 0) { + let updateResults = []; + if (Util.OPTIONS.noUpdate && metadataToUpdate.length) { Util.logger.info( ` ☇ skipping update of ${metadataToUpdate.length} ${this.definition.type}${metadataToUpdate.length == 1 ? '' : 's'}: --noUpdate flag is set` ); + } else if (metadataToUpdate.length) { + const updateLimit = pLimit(10); + updateResults = ( + await Promise.all( + metadataToUpdate + .filter((r) => r !== undefined && r !== null) + .map((metadataEntry) => + updateLimit(() => + this.update(metadataEntry.after, metadataEntry.before) + ) + ) + ) + ).filter((r) => r !== undefined && r !== null); } - const updateLimit = pLimit(10); - const updateResults = Util.OPTIONS.noUpdate - ? [] - : ( - await Promise.all( - metadataToUpdate - .filter((r) => r !== undefined && r !== null) - .map((metadataEntry) => - updateLimit(() => - this.update(metadataEntry.after, metadataEntry.before) - ) - ) - ) - ).filter((r) => r !== undefined && r !== null); // Logging Util.logger.info( `${this.definition.type} upsert: ${createResults.length} of ${metadataToCreate.length} created / ${updateResults.length} of ${metadataToUpdate.length} updated` +