text: "use strict";
  /*
   * Copyright (c) 2011 Vinay Pulim <vinay@milewise.com>
   * MIT Licensed
   */
  var __extends = (this && this.__extends) || (function () {
      var extendStatics = function (d, b) {
          extendStatics = Object.setPrototypeOf ||
              ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
              function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
          return extendStatics(d, b);
      };
      return function (d, b) {
          extendStatics(d, b);
          function __() { this.constructor = d; }
          d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
      };
  })();
  exports.__esModule = true;
  var assert = require("assert");
  var BluebirdPromise = require("bluebird");
  var concatStream = require("concat-stream");
  var debugBuilder = require("debug");
  var events_1 = require("events");
  var _ = require("lodash");
  var uuid_1 = require("uuid");
  var http_1 = require("./http");
  var utils_1 = require("./utils");
  var debug = debugBuilder('node-soap');
  var nonIdentifierChars = /[^a-z$_0-9]/i;
  var Client = /** @class */ (function (_super) {
      __extends(Client, _super);
      function Client(wsdl, endpoint, options) {
          var _this_1 = _super.call(this) || this;
          options = options || {};
          _this_1.wsdl = wsdl;
          _this_1._initializeOptions(options);
          _this_1._initializeServices(endpoint);
          _this_1.httpClient = options.httpClient || new http_1.HttpClient(options);
          var promiseOptions = { multiArgs: true };
          if (options.overridePromiseSuffix) {
              promiseOptions.suffix = options.overridePromiseSuffix;
          }
          BluebirdPromise.promisifyAll(_this_1, promiseOptions);
          return _this_1;
      }
      /** add soapHeader to soap:Header node */
      Client.prototype.addSoapHeader = function (soapHeader, name, namespace, xmlns) {
          if (!this.soapHeaders) {
              this.soapHeaders = [];
          }
          soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
          return this.soapHeaders.push(soapHeader) - 1;
      };
      Client.prototype.changeSoapHeader = function (index, soapHeader, name, namespace, xmlns) {
          if (!this.soapHeaders) {
              this.soapHeaders = [];
          }
          soapHeader = this._processSoapHeader(soapHeader, name, namespace, xmlns);
          this.soapHeaders[index] = soapHeader;
      };
      /** return all defined headers */
      Client.prototype.getSoapHeaders = function () {
          return this.soapHeaders;
      };
      /** remove all defined headers */
      Client.prototype.clearSoapHeaders = function () {
          this.soapHeaders = null;
      };
      Client.prototype.addHttpHeader = function (name, value) {
          if (!this.httpHeaders) {
              this.httpHeaders = {};
          }
          this.httpHeaders[name] = value;
      };
      Client.prototype.getHttpHeaders = function () {
          return this.httpHeaders;
      };
      Client.prototype.clearHttpHeaders = function () {
          this.httpHeaders = null;
      };
      Client.prototype.addBodyAttribute = function (bodyAttribute, name, namespace, xmlns) {
          if (!this.bodyAttributes) {
              this.bodyAttributes = [];
          }
          if (typeof bodyAttribute === 'object') {
              var composition_1 = '';
              Object.getOwnPropertyNames(bodyAttribute).forEach(function (prop, idx, array) {
                  composition_1 += ' ' + prop + '="' + bodyAttribute[prop] + '"';
              });
              bodyAttribute = composition_1;
          }
          if (bodyAttribute.substr(0, 1) !== ' ') {
              bodyAttribute = ' ' + bodyAttribute;
          }
          this.bodyAttributes.push(bodyAttribute);
      };
      Client.prototype.getBodyAttributes = function () {
          return this.bodyAttributes;
      };
      Client.prototype.clearBodyAttributes = function () {
          this.bodyAttributes = null;
      };
      /** overwrite the SOAP service endpoint address */
      Client.prototype.setEndpoint = function (endpoint) {
          this.endpoint = endpoint;
          this._initializeServices(endpoint);
      };
      /** description of services, ports and methods as a JavaScript object */
      Client.prototype.describe = function () {
          return this.wsdl.describeServices();
      };
      /** use the specified security protocol */
      Client.prototype.setSecurity = function (security) {
          this.security = security;
      };
      Client.prototype.setSOAPAction = function (SOAPAction) {
          this.SOAPAction = SOAPAction;
      };
      Client.prototype._initializeServices = function (endpoint) {
          var definitions = this.wsdl.definitions;
          var services = definitions.services;
          for (var name_1 in services) {
              this[name_1] = this._defineService(services[name_1], endpoint);
          }
      };
      Client.prototype._initializeOptions = function (options) {
          this.streamAllowed = options.stream;
          this.normalizeNames = options.normalizeNames;
          this.wsdl.options.attributesKey = options.attributesKey || 'attributes';
          this.wsdl.options.envelopeKey = options.envelopeKey || 'soap';
          this.wsdl.options.preserveWhitespace = !!options.preserveWhitespace;
          var igNs = options.ignoredNamespaces;
          if (igNs !== undefined && typeof igNs === 'object') {
              if ('override' in igNs) {
                  if (igNs.override === true) {
                      if (igNs.namespaces !== undefined) {
                          this.wsdl.options.ignoredNamespaces = igNs.namespaces;
                      }
                  }
              }
          }
          if (options.overrideRootElement !== undefined) {
              this.wsdl.options.overrideRootElement = options.overrideRootElement;
          }
          this.wsdl.options.forceSoap12Headers = !!options.forceSoap12Headers;
      };
      Client.prototype._defineService = function (service, endpoint) {
          var ports = service.ports;
          var def = {};
          for (var name_2 in ports) {
              def[name_2] = this._definePort(ports[name_2], endpoint ? endpoint : ports[name_2].location);
          }
          return def;
      };
      Client.prototype._definePort = function (port, endpoint) {
          var location = endpoint;
          var binding = port.binding;
          var methods = binding.methods;
          var def = {};
          for (var name_3 in methods) {
              def[name_3] = this._defineMethod(methods[name_3], location);
              var methodName = this.normalizeNames ? name_3.replace(nonIdentifierChars, '_') : name_3;
              this[methodName] = def[name_3];
          }
          return def;
      };
      Client.prototype._defineMethod = function (method, location) {
          var _this_1 = this;
          var temp;
          return function (args, callback, options, extraHeaders) {
              if (typeof args === 'function') {
                  callback = args;
                  args = {};
              }
              else if (typeof options === 'function') {
                  temp = callback;
                  callback = options;
                  options = temp;
              }
              else if (typeof extraHeaders === 'function') {
                  temp = callback;
                  callback = extraHeaders;
                  extraHeaders = options;
                  options = temp;
              }
              _this_1._invoke(method, args, location, function (error, result, rawResponse, soapHeader, rawRequest) {
                  callback(error, result, rawResponse, soapHeader, rawRequest);
              }, options, extraHeaders);
          };
      };
      Client.prototype._processSoapHeader = function (soapHeader, name, namespace, xmlns) {
          switch (typeof soapHeader) {
              case 'object':
                  return this.wsdl.objectToXML(soapHeader, name, namespace, xmlns, true);
              case 'function':
                  var _this_2 = this;
                  // arrow function does not support arguments variable
                  // tslint:disable-next-line
                  return function () {
                      var result = soapHeader.apply(null, arguments);
                      if (typeof result === 'object') {
                          return _this_2.wsdl.objectToXML(result, name, namespace, xmlns, true);
                      }
                      else {
                          return result;
                      }
                  };
              default:
                  return soapHeader;
          }
      };
      Client.prototype._invoke = function (method, args, location, callback, options, extraHeaders) {
          var _this_1 = this;
          var name = method.$name;
          var input = method.input;
          var output = method.output;
          var style = method.style;
          var defs = this.wsdl.definitions;
          var envelopeKey = this.wsdl.options.envelopeKey;
          var ns = defs.$targetNamespace;
          var encoding = '';
          var message = '';
          var xml = null;
          var req;
          var soapAction;
          var alias = utils_1.findPrefix(defs.xmlns, ns);
          var headers = {
              'Content-Type': 'text/xml; charset=utf-8'
          };
          var xmlnsSoap = 'xmlns:' + envelopeKey + '="http://schemas.xmlsoap.org/soap/envelope/"';
          var finish = function (obj, body, response) {
              var result;
              if (!output) {
                  // one-way, no output expected
                  return callback(null, null, body, obj.Header, xml);
              }
              // If it's not HTML and Soap Body is empty
              if (!obj.html && !obj.Body) {
                  if (response.statusCode >= 400) {
                      var error = new Error('Error http status codes');
                      error.response = response;
                      error.body = body;
                      _this_1.emit('soapError', error, eid);
                      return callback(error, obj, body, obj.Header);
                  }
                  return callback(null, obj, body, obj.Header);
              }
              if (typeof obj.Body !== 'object') {
                  var error = new Error('Cannot parse response');
                  error.response = response;
                  error.body = body;
                  return callback(error, obj, body, undefined, xml);
              }
              result = obj.Body[output.$name];
              // RPC/literal response body may contain elements with added suffixes I.E.
              // 'Response', or 'Output', or 'Out'
              // This doesn't necessarily equal the ouput message name. See WSDL 1.1 Section 2.4.5
              if (!result) {
                  result = obj.Body[output.$name.replace(/(?:Out(?:put)?|Response)$/, '')];
              }
              if (!result) {
                  ['Response', 'Out', 'Output'].forEach(function (term) {
                      if (obj.Body.hasOwnProperty(name + term)) {
                          return result = obj.Body[name + term];
                      }
                  });
              }
              callback(null, result, body, obj.Header, xml);
          };
          var parseSync = function (body, response) {
              var obj;
              try {
                  obj = _this_1.wsdl.xmlToObject(body);
              }
              catch (error) {
                  //  When the output element cannot be looked up in the wsdl and the body is JSON
                  //  instead of sending the error, we pass the body in the response.
                  if (!output || !output.$lookupTypes) {
                      debug('Response element is not present. Unable to convert response xml to json.');
                      //  If the response is JSON then return it as-is.
                      var json = _.isObject(body) ? body : tryJSONparse(body);
                      if (json) {
                          return callback(null, response, json, undefined, xml);
                      }
                  }
                  error.response = response;
                  error.body = body;
                  _this_1.emit('soapError', error, eid);
                  return callback(error, response, body, undefined, xml);
              }
              return finish(obj, body, response);
          };
          if (this.SOAPAction) {
              soapAction = this.SOAPAction;
          }
          else if (method.soapAction !== undefined && method.soapAction !== null) {
              soapAction = method.soapAction;
          }
          else {
              soapAction = ((ns.lastIndexOf('/') !== ns.length - 1) ? ns + '/' : ns) + name;
          }
          if (this.wsdl.options.forceSoap12Headers) {
              headers['Content-Type'] = "application/soap+xml; charset=utf-8; action=\"" + soapAction + "\"";
              xmlnsSoap = 'xmlns:' + envelopeKey + '="http://www.w3.org/2003/05/soap-envelope"';
          }
          else {
              headers.SOAPAction = '"' + soapAction + '"';
          }
          options = options || {};
          // Add extra headers
          if (this.httpHeaders === null) {
              headers = {};
          }
          else {
              for (var header in this.httpHeaders) {
                  headers[header] = this.httpHeaders[header];
              }
              for (var attr in extraHeaders) {
                  headers[attr] = extraHeaders[attr];
              }
          }
          // Allow the security object to add headers
          if (this.security && this.security.addHeaders) {
              this.security.addHeaders(headers);
          }
          if (this.security && this.security.addOptions) {
              this.security.addOptions(options);
          }
          if ((style === 'rpc') && ((input.parts || input.name === 'element') || args === null)) {
              assert.ok(!style || style === 'rpc', 'invalid message definition for document style binding');
              message = this.wsdl.objectToRpcXML(name, args, alias, ns, (input.name !== 'element'));
              (method.inputSoap === 'encoded') && (encoding = 'soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" ');
          }
          else {
              assert.ok(!style || style === 'document', 'invalid message definition for rpc style binding');
              // pass `input.$lookupType` if `input.$type` could not be found
              message = this.wsdl.objectToDocumentXML(input.$name, args, input.targetNSAlias, input.targetNamespace, (input.$type || input.$lookupType));
          }
          var decodedHeaders;
          if (this.soapHeaders) {
              decodedHeaders = this.soapHeaders.map(function (header) {
                  if (typeof header === 'function') {
                      return header(method, location, soapAction, args);
                  }
                  else {
                      return header;
                  }
              }).join(' ');
          }
          xml = '<?xml version="1.0" encoding="utf-8"?>' +
              '<' + envelopeKey + ':Envelope ' +
              xmlnsSoap + ' ' +
              'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' +
              encoding +
              this.wsdl.xmlnsInEnvelope + '>' +
              ((decodedHeaders || this.security) ?
                  ('<' + envelopeKey + ':Header' + (this.wsdl.xmlnsInHeader ? (' ' + this.wsdl.xmlnsInHeader) : '') + '>' +
                      (decodedHeaders ? decodedHeaders : '') +
                      (this.security && !this.security.postProcess ? this.security.toXML() : '') +
                      '</' + envelopeKey + ':Header>')
                  :
                      '') +
              '<' + envelopeKey + ':Body' +
              (this.bodyAttributes ? this.bodyAttributes.join(' ') : '') +
              (this.security && this.security.postProcess ? ' Id="_0"' : '') +
              '>' +
              message +
              '</' + envelopeKey + ':Body>' +
              '</' + envelopeKey + ':Envelope>';
          if (this.security && this.security.postProcess) {
              xml = this.security.postProcess(xml, envelopeKey);
          }
          if (options && options.postProcess) {
              xml = options.postProcess(xml);
          }
          this.lastMessage = message;
          this.lastRequest = xml;
          this.lastEndpoint = location;
          var eid = options.exchangeId || uuid_1.v4();
          this.emit('message', message, eid);
          this.emit('request', xml, eid);
          var tryJSONparse = function (body) {
              try {
                  return JSON.parse(body);
              }
              catch (err) {
                  return undefined;
              }
          };
          if (this.streamAllowed && typeof this.httpClient.requestStream === 'function') {
              callback = _.once(callback);
              var startTime_1 = Date.now();
              req = this.httpClient.requestStream(location, xml, headers, options, this);
              this.lastRequestHeaders = req.headers;
              var onError_1 = function (err) {
                  _this_1.lastResponse = null;
                  _this_1.lastResponseHeaders = null;
                  _this_1.lastElapsedTime = null;
                  _this_1.emit('response', null, null, eid);
                  callback(err, undefined, undefined, undefined, xml);
              };
              req.on('error', onError_1);
              req.on('response', function (response) {
                  response.on('error', onError_1);
                  // When the output element cannot be looked up in the wsdl, play it safe and
                  // don't stream
                  if (response.statusCode !== 200 || !output || !output.$lookupTypes) {
                      response.pipe(concatStream({ encoding: 'string' }, function (body) {
                          _this_1.lastResponse = body;
                          _this_1.lastResponseHeaders = response && response.headers;
                          _this_1.lastElapsedTime = Date.now() - startTime_1;
                          _this_1.emit('response', body, response, eid);
                          return parseSync(body, response);
                      }));
                      return;
                  }
                  _this_1.wsdl.xmlToObject(response, function (error, obj) {
                      _this_1.lastResponse = response;
                      _this_1.lastResponseHeaders = response && response.headers;
                      _this_1.lastElapsedTime = Date.now() - startTime_1;
                      _this_1.emit('response', '<stream>', response, eid);
                      if (error) {
                          error.response = response;
                          error.body = '<stream>';
                          _this_1.emit('soapError', error, eid);
                          return callback(error, response, undefined, undefined, xml);
                      }
                      return finish(obj, '<stream>', response);
                  });
              });
              return;
          }
          req = this.httpClient.request(location, xml, function (err, response, body) {
              _this_1.lastResponse = body;
              _this_1.lastResponseHeaders = response && response.headers;
              _this_1.lastElapsedTime = response && response.elapsedTime;
              _this_1.emit('response', body, response, eid);
              if (err) {
                  callback(err, undefined, undefined, undefined, xml);
              }
              else {
                  return parseSync(body, response);
              }
          }, headers, options, this);
          // Added mostly for testability, but possibly useful for debugging
          if (req && req.headers && !options.ntlm) { // fixes an issue when req or req.headers is undefined, doesn't apply to ntlm requests
              this.lastRequestHeaders = req.headers;
          }
      };
      return Client;
  }(events_1.EventEmitter));
  exports.Client = Client;
  //# sourceMappingURL=client.js.map