UNPKG

salesforce-alm

Version:

This package contains tools, and APIs, for an improved salesforce.com developer experience.

230 lines (228 loc) 10.5 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DescribeMetadataDecomposition = exports.DescribeMetadataAnnotation = void 0; const util = require("util"); const path = require("path"); const _ = require("lodash"); const xmlMetadataDocument_1 = require("../xmlMetadataDocument"); class DescribeMetadataAnnotation { } exports.DescribeMetadataAnnotation = DescribeMetadataAnnotation; /** * Decomposition strategy driven by describeMetadata results. * Each childXmlName is assumed to be a subtype for decomposition. * * For this decomposition the aggregate and decomposed documents * are all mdapi XML. The WSDL is used (while building the decomposition * configuration in the metadata registry) to interpret the contents * of the aggregate metadata document. */ class DescribeMetadataDecomposition { constructor(decompositionConfig) { this.decompositionConfig = decompositionConfig; } newDocument(metadataName, xmlAttributes) { return new xmlMetadataDocument_1.XmlMetadataDocument(metadataName, xmlAttributes); } newContainerDocument(metadataName) { return new xmlMetadataDocument_1.XmlMetadataDocument(metadataName); } newDecompositionDocument(metadataName) { return new xmlMetadataDocument_1.XmlMetadataDocument(metadataName); } newComposedDocument(metadataName) { return new xmlMetadataDocument_1.XmlMetadataDocument(metadataName); } compose(container, decompositions) { const xmlAttributes = DescribeMetadataDecomposition.getXmlAttributesFromDecomposedSource(container, decompositions); const composed = new xmlMetadataDocument_1.XmlMetadataDocument(this.decompositionConfig.metadataName, xmlAttributes); if (!util.isNullOrUndefined(container)) { const metadataElement = DescribeMetadataDecomposition.getFirstElement(container); let child = metadataElement.firstChild; while (child !== null) { const node = composed.data.importNode(child, true); composed.data.documentElement.appendChild(node); child = child.nextSibling; } } if (!util.isNullOrUndefined(decompositions)) { for (const decomposedSubtypeConfig of decompositions.keys()) { for (const decomposition of decompositions.get(decomposedSubtypeConfig)) { const fragmentDoc = decomposition.data; const fragmentElement = composed.data.createElement(decomposedSubtypeConfig.xmlFragmentName); composed.data.documentElement.appendChild(fragmentElement); let child = fragmentDoc.documentElement.firstChild; while (child !== null) { const node = composed.data.importNode(child, true); fragmentElement.appendChild(node); child = child.nextSibling; } } } } return composed; } decompose(composed, name, manifest, metadataType) { const xmlAttributes = DescribeMetadataDecomposition.getXmlAttributes(composed); let container = this.newDocument(this.decompositionConfig.metadataName, xmlAttributes); let decompositions = new Map(); let child = composed.data.documentElement.firstChild; while (child !== null) { if (child.nodeType === 1) { const decomposedSubtypeConfig = DescribeMetadataDecomposition.findDecompositionSubtypeConfig(this.decompositionConfig.decompositions, child.nodeName); if (!util.isNullOrUndefined(decomposedSubtypeConfig)) { const decomposition = new xmlMetadataDocument_1.XmlMetadataDocument(decomposedSubtypeConfig.metadataName, xmlAttributes); let elt = child.firstChild; while (elt !== null) { if (elt.nodeType === 1) { DescribeMetadataDecomposition.importDocumentElementChild(decomposition.data, elt); } elt = elt.nextSibling; } const annotation = this.getAnnotation(decomposition, decomposedSubtypeConfig); decomposition.setAnnotation(annotation); if (util.isNullOrUndefined(decompositions.get(decomposedSubtypeConfig))) { decompositions.set(decomposedSubtypeConfig, []); } decompositions.get(decomposedSubtypeConfig).push(decomposition); } else { DescribeMetadataDecomposition.importDocumentElementChild(container.data, child); } } child = child.nextSibling; } /* * An aggregate mdapi file can be sparse, but that doesn't necessarily mean anything - either for * retrieve or deploy. Unfortunately it's also possible to get a dense aggregate even when very * specific bits have been requested (eg. when, say, a custom field was requested, but also the container * object by virtue of an object label change). * So, we use the manifest to see what's been explicitly requested from the server. Anything else * is not material and should be pruned from the effective decomposition. */ container = this.pruneContainer(container, this.decompositionConfig.metadataName, name, manifest, metadataType); decompositions = this.pruneDocuments(decompositions, name, manifest); return [container, decompositions]; } isComposable() { return true; } newAnnotation() { return new DescribeMetadataAnnotation(); } getAnnotation(decomposition, subtypeConfig) { const annotation = this.newAnnotation(); annotation.name = DescribeMetadataDecomposition.getName(decomposition, subtypeConfig.metadataEntityNameElement); return annotation; } pruneDocuments(decompositions, parentName, manifest) { if (util.isNullOrUndefined(manifest)) { return decompositions; } const pruned = new Map(); decompositions.forEach((value, key) => { if (key.isAddressable) { const subtypeMembers = manifest.Package.types.reduce((members, type) => { if (type.name === key.metadataName) { return members.concat(type.members); } else { return members; } }, []); const docs = value.filter((doc) => { const annotation = doc.getAnnotation(); return (subtypeMembers.filter((subtypeMember) => subtypeMember === `${parentName}.${annotation.name}`).length > 0); }); pruned.set(key, docs); } else { pruned.set(key, value); } }); return pruned; } pruneContainer(container, containerType, name, manifest, metadataType) { if (metadataType && !metadataType.isContainerValid(container)) { return null; } if (util.isNullOrUndefined(manifest)) { return container; } const subtypeMembers = manifest.Package.types.reduce((members, type) => { // eslint-disable-next-line @typescript-eslint/no-shadow const _getCorrespondingManifestType = (containerType) => { if (containerType.endsWith('Folder')) { return containerType.replace('Folder', ''); } return containerType; }; if (type.name === _getCorrespondingManifestType(containerType)) { return members.concat(type.members); } else { return members; } }, []); // The package.xml is using forward slashes, so replace these with path separators so the matching works on Windows return subtypeMembers.some((subTypeMember) => subTypeMember.replace('/', path.sep) === name) ? container : null; } static getName(decomposition, metadataEntityNameElement) { let child = decomposition.data.documentElement.firstChild; while (child !== null) { if (child.nodeType === 1 && child.nodeName === metadataEntityNameElement) { return child.firstChild.nodeValue; } child = child.nextSibling; } return null; } static importDocumentElementChild(document, child) { const node = document.importNode(child, true); document.documentElement.appendChild(node); } static findDecompositionSubtypeConfig(decompositionDefs, tag) { for (const decompositionDef of decompositionDefs) { if (decompositionDef.xmlFragmentName === tag) { return decompositionDef; } } return null; } static getFirstElement(doc) { for (let i = 0; i < doc.data.childNodes.length; ++i) { const child = doc.data.childNodes[i]; if (child.nodeType === 1) { return child; } } return null; } static getXmlAttributes(document) { if (document) { return _.values(document.data.documentElement.attributes).map((attribute) => ({ nodeName: attribute.nodeName, nodeValue: attribute.nodeValue, })); } return []; } static getXmlAttributesFromDecomposedSource(container, decompositions) { if (container) { return DescribeMetadataDecomposition.getXmlAttributes(container); } if (decompositions.size > 0) { const firstDecomposition = Array.from(decompositions.values())[0][0]; return DescribeMetadataDecomposition.getXmlAttributes(firstDecomposition); } return []; } } exports.DescribeMetadataDecomposition = DescribeMetadataDecomposition; //# sourceMappingURL=describeMetadataDecomposition.js.map