gads
Version:
An unofficial JS client library for the SOAP-based DFP Ads API
204 lines • 8.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const util_1 = require("../common/util");
const soap_1 = require("soap");
const wsdlToMetaServiceTranslator_1 = require("./wsdlToMetaServiceTranslator");
const xmlToJsTranslator_1 = require("./xmlToJsTranslator");
/* tslint:disable:no-any */
class SoapClientFactory {
constructor() {
this.xmlToJsTranslator = new xmlToJsTranslator_1.XmlToJsTranslator();
this.wsdlToMetaServiceTranslator = new wsdlToMetaServiceTranslator_1.WsdlToMetaServiceTranslator();
}
getSoapClient(wsdlUrl, config) {
return this.createSoapClient(wsdlUrl, config)
.then((client) => {
return this.xmlToJsTranslator.translate(client.wsdl.xml)
.then(wsdl => this.wsdlToMetaServiceTranslator.translate(wsdl))
.then(metaService => {
for (const methodName in metaService.methods) {
if (metaService.methods.hasOwnProperty(methodName)) {
client[methodName] = this.wrapSoapClientMethod(client, methodName, metaService, config);
}
}
})
.then(() => client);
});
}
createSoapClient(wsdlUrl, config) {
return new Promise((resolve, reject) => {
// Create options
const options = {
attributesKey: '$attr',
// Whether to cache WSDL files or request them every time
disableCache: !config.cache,
// HTTP headers to be sent on WSDL requests
wsdl_headers: { connection: 'keep-alive' },
// Options for the request module on WSDL requests
wsdl_options: Object.assign({
timeout: 3600,
gzip: config.enableCompression
}, config.proxy ? config.proxy.getProxyInfo() : {})
};
// Create client
soap_1.createClient(wsdlUrl, options, (err, client) => {
err ? reject(err) : resolve(client);
});
})
.then(client => {
this.addLogging(client, config);
return client;
});
}
wrapSoapClientMethod(client, methodName, metaService, config) {
const method = client[methodName];
const metaArg = metaService.interfaces[metaService.methods[methodName].inputs[0].type];
return (arg, cb) => {
// Add HTTP headers
const promise = new Promise((resolve, reject) => {
config.headerHandler.generateHttpHeaders((err, headers) => {
err ? reject(err) : resolve(headers);
});
})
.then(httpHeaders => {
for (const key in httpHeaders) {
if (httpHeaders.hasOwnProperty(key)) {
client.addHttpHeader(key, httpHeaders[key]);
}
}
})
// Add SOAP headers
.then(() => new Promise((resolve, reject) => {
config.headerHandler.generateSoapHeaders((err, headers) => {
err ? reject(err) : resolve(headers);
});
}))
.then(soapHeaders => {
client.clearSoapHeaders();
for (const header of soapHeaders) {
client.addSoapHeader(header.value, header.name, header.namespace, header.xmlns);
}
})
// Fix request's property order since it matters to SOAP server
.then(() => this.sanitizeSoapClientMethodArg(arg, metaArg, metaService))
// Perform a SOAP call
.then(arg => new Promise((resolve, reject) => {
const options = {
gzip: config.enableCompression,
time: true,
postProcess: (xml) => {
return xml.replace(' xsi:nil="true"', '');
}
};
method.call(client, arg, (err, res //, rawRes: string, soapHeader: any, rawReq: string
) => {
err ? reject(err) : resolve(res);
}, Object.assign(options, config.proxy ? config.proxy.getProxyInfo() : {}));
}));
return util_1.callbackOrPromise(cb, promise);
};
}
flattenInterfaceProperties(metaInterface, metaService) {
const props = {};
// Set super properties first
for (const interfaceName of metaInterface.extends) {
const superInterface = metaService.interfaces[interfaceName];
const superProps = this.flattenInterfaceProperties(superInterface, metaService);
Object.assign(props, superProps);
}
// Set properties
return Object.assign(props, metaInterface.properties);
}
sanitizeSoapClientMethodArg(arg, metaArg, metaService) {
const out = {};
const stack = [{ arg, metaArg, out }];
// For each arg in the stack
do {
const { arg, metaArg, out } = stack.pop();
const props = this.flattenInterfaceProperties(metaArg, metaService);
// Set correct property order for output
for (const name in props) {
if (props.hasOwnProperty(name)) {
out[name] = undefined;
}
}
// For each property in arg
for (const key in arg) {
if (arg.hasOwnProperty(key)) {
// Push null and unknown properties verbatim
if (arg[key] == null || !out.hasOwnProperty(key)) {
out[key] = arg[key];
continue;
}
// Push properties verbatim that are not interfaces
const prop = props[key];
const metaArg = metaService.interfaces[prop.type];
if (!metaArg) {
out[key] = arg[key];
continue;
}
// If array or object
if (prop.isArray && arg[key] instanceof Array) {
const outs = out[key] = [];
for (const v of arg[key]) {
const out = {};
outs.push(out);
stack.push({ arg: v, metaArg, out });
}
continue;
}
// If Object
stack.push({ arg: arg[key], metaArg, out: out[key] = {} });
}
}
} while (stack.length > 0);
return out;
}
addLogging(client, config) {
client.on('request', (_, eid) => {
if (config.logger) {
const msg = `[${(new Date()).toISOString()}] REQUEST id=${eid}\n`;
config.logger.log(msg);
}
});
client.on('soapError', (_, eid) => {
if (config.logger) {
const msg = `[${(new Date()).toISOString()}] ERROR id=${eid}\n`;
config.logger.error(msg);
}
});
client.on('response', (body, res, eid) => {
if (!config.logger) {
return;
}
const request = res.request;
// Print header
let msg = `[${(new Date()).toISOString()}] RESPONSE id=${eid}`;
msg = msg + ` responseTime=${res.elapsedTime}\n`;
msg = msg + `${request.method} ${res.statusCode}`;
msg = msg + ` (${res.statusMessage}) ${res.url}\n\n`;
// Print request
for (const header in request.headers) {
if (!request.headers.hasOwnProperty(header)) {
continue;
}
let val = request.headers[header];
if (header.toLowerCase() === 'authorization') {
val = 'REDACTED';
}
msg = msg + `${header}: ${val}\n`;
}
msg = msg + `\n${request.body}\n\n`;
// Print response
for (const header in res.headers) {
if (res.headers.hasOwnProperty(header)) {
msg = msg + `${header}: ${res.headers[header]}\n`;
}
}
msg = msg + `\n${body}\n`;
config.logger.log(msg);
});
}
}
exports.SoapClientFactory = SoapClientFactory;
//# sourceMappingURL=soapClientFactory.js.map