UNPKG

ibm-igc-extensions

Version:

Re-usable functions for extending IBM Information Governance Catalog (i.e. OpenIGC)

277 lines (255 loc) 9.32 kB
/*** * Copyright 2016 IBM Corp. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ "use strict"; const xmldom = require('xmldom'); const xpath = require('xpath'); const _ = require('underscore'); const regexRemove = /[^\w \(\),.{}\[\]\:\/\-\#]/g; /** * AssetHandler class -- for handling IGC Flow Documents (XML) with the purpose of creating or updating asset instances * @example * // create an XML flow document with a new asset instance * var igcext = require('ibm-igc-extensions'); * var ah = new igcext.AssetHandler(); * ah.addAsset('$MyBundle-ClassName', 'AssetInstanceName', '123', { * "short_description": "This is a short description of my asset", * "$newField": "This is the value for a field that only exists in this bundle (and class)" * }); */ class AssetHandler { constructor() { this._xmlOriginal = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<doc xmlns=\"http://www.ibm.com/iis/flow-doc\">\n <assets>\n </assets>\n</doc>"; this._doc = new xmldom.DOMParser().parseFromString(this._xmlOriginal); this._select = xpath.useNamespaces({"flowdoc": "http://www.ibm.com/iis/flow-doc"}); } /** * Parses an XML flow document * * @function * @param {string} xml */ parseXML(xml) { this._xmlOriginal = xml; this._doc = new xmldom.DOMParser().parseFromString(xml); } /** * @private */ _getElementsByContext(expression, context) { return this._select(expression, context); } _getElementByContext(expression, context) { return this._getElementsByContext(expression, context)[0]; } getElements(expression) { return this._getElementsByContext(expression, this._doc); } getElement(expression) { return this.getElements(expression)[0]; } /** * Gets the name of an asset * * @function * @param {Asset} asset * @returns {string} */ getAssetName(asset) { return asset.getAttribute("repr"); } /** * Gets the RID of an asset * * @function * @param {Asset} asset * @returns {string} */ getAssetRID(asset) { return asset.getAttribute("externalID"); } /** * @private */ getAssetByClass(className) { return this.getElement("/flowdoc:doc/flowdoc:assets/flowdoc:asset[@class='" + className + "']"); } /** * Gets an asset by its unique flow XML ID (not RID) * * @function * @param {string} id * @returns {Asset} */ getAssetById(id) { return this.getElement("/flowdoc:doc/flowdoc:assets/flowdoc:asset[@ID='" + id + "']"); } /** * Gets the name of an asset based on its unique flow XML ID (not RID) * * @function * @param {string} id * @returns {string} */ getAssetNameById(id) { return this.getAssetName(this.getAssetById(id)); } /** * Gets the ID of the parent (reference) of the provided asset * * @function * @param {Asset} asset * @returns {string} */ getParentAssetId(asset) { return this._getElementByContext("flowdoc:reference", asset).getAttribute("assetIDs"); } /** * Gets the identity string (externalID) for the provided database table * * @function * @see module:ibm-igc-lineage~FlowHandler#getParentAssetId * @param {string} tblName - the name of the database table * @param {string} schemaId - the ID of the parent database schema * @returns {string} */ getTableIdentity(tblName, schemaId) { const eSchema = this.getAssetById(schemaId); const schemaName = this.getAssetName(eSchema); const dcnId = this.getParentAssetId(eSchema); const eDCN = this.getAssetById(dcnId); const dcnName = this.getAssetName(eDCN); const creationTool = this._getElementByContext("flowdoc:attribute[@name='creationTool']", eDCN).getAttribute("value"); const hostId = this.getParentAssetId(eDCN); const eHost = this.getAssetById(hostId); const hostName = this.getAssetName(eHost); return "_ngo:table:" + "_ngo:db:" + hostName.toLowerCase() + "::" + dcnName.toLowerCase() + "::" + creationTool.toLowerCase() + "::" + schemaName.toLowerCase() + "::" + tblName.toLowerCase(); } /** * Gets the identity string (externalID) for the provided database column * * @function * @see module:ibm-igc-lineage~FlowHandler#getParentAssetId * @param {string} colName - the name of the database column * @param {string} tableId - the ID of the parent database table * @returns {string} */ getColumnIdentity(colName, tableId) { const eTable = this.getAssetById(tableId); const tableName = this.getAssetName(eTable); const schemaId = this.getParentAssetId(eTable); const tableIdentity = this.getTableIdentity(tableName, schemaId); return "_ngo:" + colName.toLowerCase() + tableIdentity.replace("_ngo:table:", "::"); } /** * Gets the database column identity string (externalID) from an existing database table identity string * * @function * @see module:ibm-igc-lineage~FlowHandler#getTableIdentity * @param {string} colName - the name of the database column * @param {string} tableIdentity - the identity string (externalID) of the parent database table * @returns {string} */ getColumnIdentityFromTableIdentity(colName, tableIdentity) { return "_ngo:" + colName.toLowerCase() + tableIdentity.replace("_ngo:table:", "::"); } /** * Adds an asset to the flow XML * * @function * @param {string} className - the classname of the data type of the asset (e.g. ASCLModel.DatabaseField) * @param {string} name - the name of the asset * @param {string} xmlId - the unique ID of the asset within the XML flow document * @param {Object} objAttrs - the attributes to set on this asset * @param {string} [parentType] - the classname of the asset's parent data type (e.g. ASCLModel.DatabaseTable) * @param {string} [parentId] - the unique ID of the asset's parent within the XML flow document */ addAsset(className, name, xmlId, objAttrs, parentType, parentId) { const eAsset = this._doc.createElement("asset"); eAsset.setAttribute("class", className); eAsset.setAttribute("repr", name); eAsset.setAttribute("ID", xmlId); const eAttr = this._doc.createElement("attribute"); eAttr.setAttribute("name", "name"); eAttr.setAttribute("value", name); eAsset.appendChild(eAttr); const aAttrKeys = Object.keys(objAttrs); for (let j = 0; j < aAttrKeys.length; j++) { const key = aAttrKeys[j]; if (objAttrs.hasOwnProperty(key) && key !== 'name') { const eAttr = this._doc.createElement("attribute"); eAttr.setAttribute("name", key); let val = objAttrs[key]; if (Array.isArray(val)) { for (let k = 0; k < val.length; k++) { const singleVal = val[k]; const txt = this._doc.createTextNode(_.escape(singleVal)); const eSingleVal = this._doc.createElement("multiValueItem"); eSingleVal.appendChild(txt); eAttr.appendChild(eSingleVal); } } else if (typeof val === 'string') { val = val.replace(regexRemove, ' '); eAttr.setAttribute("value", val); } else { eAttr.setAttribute("value", val); } eAsset.appendChild(eAttr); } } if (parentType) { const eRef = this._doc.createElement("reference"); eRef.setAttribute("name", parentType); eRef.setAttribute("assetIDs", parentId); eAsset.appendChild(eRef); } this._doc.getElementsByTagName("assets").item(0).appendChild(eAsset); } /** * Adds an import action to the flow XML (to actually create the assets) * * @function * @param {string[]} completeAssetIDs - an array of asset IDs that should be created & replaced (if they already exist) * @param {string[]} [partialAssetIDs] - an array of asset IDs that should be created if they do not exist; otherwise should be updated (i.e. children added) */ addImportAction(completeAssetIDs, partialAssetIDs) { const eImportAction = this._doc.createElement("importAction"); eImportAction.setAttribute("completeAssetIDs", completeAssetIDs.join(' ')); if (partialAssetIDs !== null && partialAssetIDs.length > 0) { eImportAction.setAttribute("partialAssetIDs", partialAssetIDs.join(' ')); } this._doc.getElementsByTagName("doc").item(0).appendChild(eImportAction); } /** * Retrieves the flow XML, including any modifications that have been made (added assets) * * @function * @see module:ibm-igc-extensions~AssetHandler#addAsset * @returns {string} the full XML of the flow document */ getCustomisedXML() { return new xmldom.XMLSerializer().serializeToString(this._doc); } } module.exports = AssetHandler;