@zowe/cics-for-zowe-sdk
Version:
Zowe SDK for IBM CICS Transaction Server
236 lines • 11.2 kB
JavaScript
;
/**
* 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