UNPKG

zowe-cli-cics-deploy-plugin

Version:

IBM CICS Bundle generation and deployment for Zowe CLI

352 lines 13.5 kB
/* * This program and the accompanying materials are made available under the terms of the * Eclipse Public License v2.0 which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-v20.html * * SPDX-License-Identifier: EPL-2.0 * * Copyright IBM Corp, 2019 * */ "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Manifest = void 0; const { XMLParser, XMLBuilder } = require("fast-xml-parser"); const serialiser = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: "" }); /** * Class to represent a CICS Bundle manifest. * * @export * @class Manifest */ class Manifest { /** * Constructor for creating a Manifest. * * @static * @param {string} directory - The bundle directory. * @param {boolean} merge - Changes to the bundle manifest should be merged into any existing manifest. * @param {boolean} overwrite - Changes to the bundle contents should replace any existing contents. * @param {IHandlerParameters} params - The current Imperative handler parameters * @throws ImperativeError * @memberof Manifest */ constructor(directory, merge, overwrite, params) { this.MAX_BUNDLEID_LEN = 64; this.fs = require("fs"); this.path = require("path"); this.manifestAsJson = undefined; this.manifestExists = false; this.manifestAction = " create"; this.merge = merge; this.overwrite = overwrite; this.bundleDirectory = this.path.normalize(directory); this.metainfDir = this.bundleDirectory + "/META-INF"; this.manifestFile = this.metainfDir + "/cics.xml"; this.params = params; // If 'merge' is set then attempt to read any existing manifest that may // already exist. Subsequent changes will be merged with the existing // content. if (this.merge === true) { try { this.readManifest(); } catch (ex) { // Something went wrong. If it's an ENOENT response then there was // no manifest to read, so that can be ignored, otherwise propagate // the exception back to the caller. if (ex.code !== "ENOENT") { throw ex; } } } // If we've not read an existing manifest, create a new one if (this.manifestAsJson === undefined) { this.manifestAsJson = { manifest: { xmlns: "http://www.ibm.com/xmlns/prod/cics/bundle", bundleVersion: 1, bundleRelease: 0 } }; } } /** * Perform whatever validation can be done in advance of attempting to save the * manifest, thereby reducing the possibility of a failure after some of the * bundle parts have already been persisted to the file system. * * @throws ImperativeError * @memberof Manifest */ prepareForSave() { // Does the meta-inf directory already exist? if (!this.fs.existsSync(this.metainfDir)) { // we'll have to create it during the save, do we have write permission? try { this.fs.accessSync(this.bundleDirectory, this.fs.constants.W_OK); } catch (err) { throw new Error("cics-deploy requires write permission to: " + this.bundleDirectory); } return; } // Do we have write permission to the META-INF dir? try { this.fs.accessSync(this.metainfDir, this.fs.constants.W_OK); } catch (err) { throw new Error("cics-deploy requires write permission to: " + this.metainfDir); } // Does a manifest file already exist? if (this.fs.existsSync(this.manifestFile)) { if (this.overwrite === false) { throw new Error("A bundle manifest file already exists. Specify --overwrite to replace it, or --merge to merge changes into it."); } if (this.merge) { this.manifestAction = " merge"; } else { this.manifestAction = " overwrite"; } // Do we have write permission to the manifest? try { this.fs.accessSync(this.manifestFile, this.fs.constants.W_OK); } catch (err) { throw new Error("cics-deploy requires write permission to: " + this.manifestFile); } } } /** * Save the Manifest file. * * @throws ImperativeError * @memberof Manifest */ save() { // Create the META-INF directory if it doesn't already exist if (!this.fs.existsSync(this.metainfDir)) { try { this.logCreation(this.metainfDir); this.fs.mkdirSync(this.metainfDir); } catch (err) { throw new Error("An error occurred attempting to create '" + this.metainfDir + "': " + err.message); } } // Write the cics.xml manifest try { this.logCreation(this.manifestFile, this.manifestAction); this.fs.writeFileSync(this.manifestFile, this.getXML(), "utf8"); } catch (err) { throw new Error("An error occurred attempting to write manifest file '" + this.manifestFile + "': " + err.message); } this.manifestExists = true; } /** * Validates that a manifest file exists. If the manifest has not yet been saved then it is * invalid. * * @returns {object} * @throws ImperativeError * @memberof Manifest */ validate() { if (!this.manifestExists) { throw new Error("No bundle manifest file found: " + this.manifestFile); } } /** * Returns the Manifest contents as a JSON Object. * * @returns {object} * @throws ImperativeError * @memberof Manifest */ getJson() { return this.manifestAsJson; } /** * Returns the Manifest contents as XML text. * * @returns {string} * @throws ImperativeError * @memberof Manifest */ getXML() { return serialiser.parse(this.manifestAsJson) + "\n"; } /** * Returns the Bundle's identity (id) value. * * @returns {string} * @throws ImperativeError * @memberof Manifest */ getBundleId() { return this.manifestAsJson.manifest.id; } /** * Set the Bundle's identity (id) value. * @param {string} id - The identiry value for the Bundle. * @throws ImperativeError * @memberof Manifest */ setBundleId(id) { if (id === undefined) { throw new Error("BundleId not set."); } const mangledId = this.mangle(id, this.MAX_BUNDLEID_LEN); this.manifestAsJson.manifest.id = mangledId; } /** * Returns the Bundle's version number. * * @returns {string} * @throws ImperativeError * @memberof Manifest */ getBundleVersion() { if (this.manifestAsJson.manifest.bundleMajorVer === undefined) { this.manifestAsJson.manifest.bundleMajorVer = 1; } if (this.manifestAsJson.manifest.bundleMinorVer === undefined) { this.manifestAsJson.manifest.bundleMinorVer = 0; } if (this.manifestAsJson.manifest.bundleMicroVer === undefined) { this.manifestAsJson.manifest.bundleMicroVer = 0; } return this.manifestAsJson.manifest.bundleMajorVer + "." + this.manifestAsJson.manifest.bundleMinorVer + "." + this.manifestAsJson.manifest.bundleMicroVer; } /** * Set the Bundle's version number. * @param {number} majorVersion - The major version number. * @param {number} minorVersion - The minor version number. * @param {number} microVersion - The micro version number. * @throws ImperativeError * @memberof Manifest */ setBundleVersion(majorVersion, minorVersion, microVersion) { if (Number.isInteger(majorVersion) && majorVersion > 0) { this.manifestAsJson.manifest.bundleMajorVer = majorVersion; } else { throw new Error("Invalid Bundle major version specified: " + majorVersion + "."); } if (Number.isInteger(minorVersion) && minorVersion >= 0) { this.manifestAsJson.manifest.bundleMinorVer = minorVersion; } else { throw new Error("Invalid Bundle minor version specified: " + minorVersion + "."); } if (Number.isInteger(microVersion) && microVersion >= 0) { this.manifestAsJson.manifest.bundleMicroVer = microVersion; } else { throw new Error("Invalid Bundle micro version specified: " + microVersion + "."); } } /** * Add a resource definition to the Manifest. If a part of the same name and type * already exists then it is replaced with the new part data. * * @param {BundlePart} bundlePart - the BundlePart to add. * @throws ImperativeError * @memberof Manifest */ addDefinition(bundlePart) { const definition = bundlePart.getPartData(); // Ensure that the definitions array is usable this.prepareDefinitionsArray(); // Search the array for a definition with the same name and // type as the one we're trying to add, if found then update // it with the new path. let i; for (i = 0; i < this.manifestAsJson.manifest.define.length; i++) { const candidate = this.manifestAsJson.manifest.define[i]; if (candidate.name === definition.name && candidate.type === definition.type) { candidate.path = definition.path; return; } } // If we've not replaced an existing item in the list, add // the new item to the end. this.manifestAsJson.manifest.define.push(definition); } /** * Does the bundle contain any resource definitions of the * specified type? * * @param {String} resourceType - the resource type * @throws ImperativeError * @memberof Manifest */ containsDefinitionsOfType(resourceType) { // Ensure that the definitions array is usable this.prepareDefinitionsArray(); // Search the array for a definition with the nominated type let i; for (i = 0; i < this.manifestAsJson.manifest.define.length; i++) { const candidate = this.manifestAsJson.manifest.define[i]; if (candidate.type === resourceType) { return true; } } return false; } prepareDefinitionsArray() { // Create the array of definitions if it doesn't already exist if (this.manifestAsJson.manifest.define === undefined) { this.manifestAsJson.manifest.define = []; } // If what already exists is an object (as may happen when appending // to an existing XML manifest with only one entry) convert it to an array; if (Array.isArray(this.manifestAsJson.manifest.define) === false) { const obj = this.manifestAsJson.manifest.define; this.manifestAsJson.manifest.define = []; this.manifestAsJson.manifest.define.push(obj); } } // Read an existing manifest file into an object readManifest() { // read the manifest from the file system const xmltext = this.fs.readFileSync(this.manifestFile, "utf8"); try { // Reading the file worked, so convert the contents into a JSON Object this.manifestAsJson = XMLParser.parse(xmltext, { ignoreAttributes: false, attributeNamePrefix: "", trimValues: true }); } catch (exception) { throw new Error("Parsing error occurred reading a CICS manifest file: " + exception.message); } // Validate that the manifest we've read is usable. if (this.manifestAsJson.manifest === undefined) { throw new Error("Existing CICS Manifest file found with unparsable content: " + JSON.stringify(this.manifestAsJson)); } // Now check the namespace if (this.manifestAsJson.manifest.xmlns !== "http://www.ibm.com/xmlns/prod/cics/bundle") { throw new Error("Existing CICS Manifest file found with unexpected namespace: " + this.manifestAsJson.manifest.xmlns + " ."); } this.manifestExists = true; } // Function for mangaling a string for inclusion in the manifest mangle(text, maxLength) { // replace underscore with hyphen const text2 = text.replace(/_/g, "-"); // Convert all unsupported characters to X const text3 = text2.replace(/[^0-9,^A-Z,^a-z\-]/g, "X"); // Now truncate the string const text4 = text3.substring(0, maxLength); return text4; } logCreation(file, action) { if (action === undefined) { action = " create"; } if (this.params !== undefined) { this.params.response.console.log(action + " : " + this.path.relative(this.bundleDirectory, file)); } } } exports.Manifest = Manifest; //# sourceMappingURL=Manifest.js.map