UNPKG

@zowe/cics-for-zowe-sdk

Version:
236 lines 11.2 kB
"use strict"; /** * 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 Contributors to the Zowe Project. * */ Object.defineProperty(exports, "__esModule", { value: true }); exports.CicsCmciRestClient = void 0; const imperative_1 = require("@zowe/imperative"); const xml2js_1 = require("xml2js"); const constants_1 = require("../constants"); const CicsCmci_messages_1 = require("../constants/CicsCmci.messages"); const CicsCmciRestError_1 = require("./CicsCmciRestError"); /** * Wrapper for invoke CICS CMCI API through the RestClient to perform common error * handling and checking and resolve promises according to generic types * @export * @class CicsCmciRestClient * @extends {RestClient} */ class CicsCmciRestClient extends imperative_1.AbstractRestClient { /** * If the API request is successful, this value should be in * api_response1 in the resultsummary object in the response */ static CMCI_SUCCESS_RESPONSE_1 = "1024"; /** * If the API request is successful, this value should be in * api_response2 in the resultsummary object in the response */ static CMCI_SUCCESS_RESPONSE_2 = "0"; /** * Convert an object to XML using the xml2js package * @param obj - the object to convert to XML * @returns {string} the string of XML generated by xml2js */ static convertObjectToXML(obj) { this.log.trace("Converting the following object to XML via xml2js:\n%s", JSON.stringify(obj, null, 2)); const builder = new xml2js_1.Builder({ headless: true }); return builder.buildObject(obj); } /** * Call the RestClient.getExpectString function assuming the response is XML, then return a promise of the JSON representation. * @static * @param {AbstractSession} session - representing connection to this api * @param {string} resource - URI for which this request should go against * @param {any} reqHeaders - headers to include in the REST request * @returns {Promise<*>} - response body content from http(s) call * @throws {ImperativeError} verifyResponseCodes fails */ static async getExpectParsedXml(session, resource, reqHeaders = [], requestOptions) { const data = await imperative_1.RestClient.getExpectString.call(imperative_1.AbstractRestClient, session, resource, reqHeaders); const apiResponse = CicsCmciRestClient.parseStringSync(data); if (requestOptions?.failOnNoData === false && !apiResponse.response.records) { const resourceName = resource.split(`${constants_1.CicsCmciConstants.CICS_SYSTEM_MANAGEMENT}/`)[1].split("/")[0].toLowerCase(); apiResponse.response.records = {}; apiResponse.response.records[resourceName] = []; } return CicsCmciRestClient.verifyResponseCodes(apiResponse, requestOptions); } /** * Call the RestClient.deleteExpectString function assuming the response is XML, then return a promise of the JSON representation. * @static * @param {AbstractSession} session - representing connection to this api * @param {string} resource - URI for which this request should go against * @param {any} reqHeaders - headers to include in the REST request * @returns {Promise<*>} - response body content from http(s) call * @throws {ImperativeError} verifyResponseCodes fails */ static async deleteExpectParsedXml(session, resource, reqHeaders = []) { const data = await imperative_1.RestClient.deleteExpectString.call(imperative_1.AbstractRestClient, session, resource, reqHeaders); const apiResponse = CicsCmciRestClient.parseStringSync(data); return CicsCmciRestClient.verifyResponseCodes(apiResponse); } /** * PUT a CMCI endpoint expecting an XML return value parsed into a JCL * Call the RestClient.putExpectString function assuming the response is XML, then return a promise of the JSON representation. * @static * @param {AbstractSession} session - representing connection to this api * @param {string} resource - URI for which this request should go against * @param {any} reqHeaders - headers to include in the REST request * @param payload - XML body to put or a javascript object to convert to XML * @returns {Promise<*>} - response body content from http(s) call * @throws {ImperativeError} verifyResponseCodes fails */ static async putExpectParsedXml(session, resource, reqHeaders = [], payload, requestOptions) { if (payload != null) { payload = CicsCmciRestClient.convertPayloadToXML(payload); } const data = await imperative_1.RestClient.putExpectString.call(imperative_1.AbstractRestClient, session, resource, reqHeaders, payload); const apiResponse = CicsCmciRestClient.parseStringSync(data); if (requestOptions?.failOnNoData === false && !apiResponse.response.records) { const resourceName = resource.split(`${constants_1.CicsCmciConstants.CICS_SYSTEM_MANAGEMENT}/`)[1].split("/")[0].toLowerCase(); apiResponse.response.records = {}; apiResponse.response.records[resourceName] = []; } return CicsCmciRestClient.verifyResponseCodes(apiResponse, requestOptions); } /** * POST a CMCI endpoint expecting a XML return value * Calls the RestClient.postExpectString function assuming the response is XML, then return a promise of the JSON representation. * @static * @param {AbstractSession} session - representing connection to this api * @param {string} resource - URI for which this request should go against * @param {any} reqHeaders - headers to include in the REST request * @param payload - XML body to put or a javascript object to convert to XML * @returns {Promise<*>} - response body content from http(s) call * @throws {ImperativeError} verifyResponseCodes fails */ static async postExpectParsedXml(session, resource, reqHeaders = [], payload) { if (payload != null) { payload = CicsCmciRestClient.convertPayloadToXML(payload); } const data = await imperative_1.RestClient.postExpectString.call(imperative_1.AbstractRestClient, session, resource, reqHeaders, payload); const apiResponse = CicsCmciRestClient.parseStringSync(data); return CicsCmciRestClient.verifyResponseCodes(apiResponse); } /** * Internal logger */ static mLogger; /** * Internal parser */ static mParser; /** * Detect if a payload is a string or an object * If it is an object, convert it to XML using xml2js * @param payload - the request body / payload * @returns {string} the XML that can be used as the request body */ static convertPayloadToXML(payload) { if (typeof payload === "string") { // if it's already a string, use it verbatim return payload; } else { // if it's an object, turn it into XML return this.convertObjectToXML(payload); } } /** * Use the Zowe logger instead of the imperative logger * @return {Logger} */ static get log() { if (this.mLogger == null) { this.mLogger = imperative_1.Logger.getAppLogger(); } return this.mLogger; } /** * Use a configured parser * @return {Parser} */ static get parser() { if (this.mParser == null) { this.mParser = new xml2js_1.Parser({ explicitArray: false, // Only create arrays if there are multiple tags with the same name mergeAttrs: true, // Do not use the attrKey, instead merge all attributes with the parent normalizeTags: true, // Guarantee that all tags are going to be lowercase }); } return this.mParser; } /** * Check that we got the expected response codes from the API after a request * @param {ICMCIApiResponse} apiResponse - the parsed response * @returns {ICMCIApiResponse} - the response if it was correct * @throws {ImperativeError} if the request failed. The error might include records that were retrieved. */ static verifyResponseCodes(apiResponse, requestOptions) { const responseCode = [`${constants_1.CicsCmciConstants.RESPONSE_1_CODES.OK}`]; if (requestOptions?.failOnNoData === false) { responseCode.push(`${constants_1.CicsCmciConstants.RESPONSE_1_CODES.NODATA}`); } // If response code is OK or NODATA (when allowed), return it if (responseCode.includes(apiResponse.response?.resultsummary?.api_response1)) { return apiResponse; } // Error code present - throw error (error will include any records retrieved)) if (requestOptions?.useCICSCmciRestError) { throw new CicsCmciRestError_1.CicsCmciRestError(CicsCmci_messages_1.CicsCmciMessages.cmciRequestFailed.message, apiResponse); } throw new imperative_1.ImperativeError({ msg: CicsCmci_messages_1.CicsCmciMessages.cmciRequestFailed.message + "\n" + imperative_1.TextUtils.prettyJson(apiResponse), }); } /** * parse an XML string with XML2js * The API is already synchronous according to their documentation but requires a callback. * This is a wrapper that doesn't require a callback. * @param {string} str - the string of XML to parse * @returns {any} the object parsed from the XML * @throws {Error} xml2js.parseString fails */ static parseStringSync(str) { let result; let error; this.parser.parseString(str, (err, r) => { error = err; result = r; }); if (error != null) { throw error; } return result; } /** * Process an error encountered in the rest client * @param {IImperativeError} original - the original error automatically built by the abstract rest client * @returns {IImperativeError} - the processed error with details added */ processError(original) { original.msg = "CICS CMCI REST API Error:\n" + original.msg; let details = original.causeErrors; try { const xmlDetails = CicsCmciRestClient.parseStringSync(details); // if we didn't get an error, make the parsed details part of the error details = imperative_1.TextUtils.prettyJson(xmlDetails, undefined, false); } catch (_e) { // if there's an error, the causeErrors text is not json this.log.debug("Encountered an error trying to parse causeErrors as XML - causeErrors is likely not JSON format"); } original.msg += "\n" + details; // add the data string which is the original error return original; } } exports.CicsCmciRestClient = CicsCmciRestClient; //# sourceMappingURL=CicsCmciRestClient.js.map