diagnostic-channel-publishers
Version:
A collection of pre-built module patches that enable existing npm modules to publish diagnostic data
304 lines • 13.1 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.enable = exports.postgres = exports.postgres6 = void 0;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
var diagnostic_channel_1 = require("diagnostic-channel");
var events_1 = require("events");
var publisherName = "postgres";
function postgres6PatchFunction(originalPg, originalPgPath) {
var originalClientQuery = originalPg.Client.prototype.query;
var diagnosticOriginalFunc = "__diagnosticOriginalFunc";
// wherever the callback is passed, find it, save it, and remove it from the call
// to the the original .query() function
originalPg.Client.prototype.query = function query(config, values, callback) {
var data = {
query: {},
database: {
host: this.connectionParameters.host,
port: this.connectionParameters.port
},
result: null,
error: null,
duration: 0,
time: new Date()
};
var start = process.hrtime();
var queryResult;
function patchCallback(cb) {
if (cb && cb[diagnosticOriginalFunc]) {
cb = cb[diagnosticOriginalFunc];
}
var trackingCallback = diagnostic_channel_1.channel.bindToContext(function (err, res) {
var end = process.hrtime(start);
data.result = res && { rowCount: res.rowCount, command: res.command };
data.error = err;
data.duration = Math.ceil((end[0] * 1e3) + (end[1] / 1e6));
diagnostic_channel_1.channel.publish(publisherName, data);
// emulate weird internal behavior in pg@6
// on success, the callback is called *before* query events are emitted
// on failure, the callback is called *instead of* the query emitting events
// with no events, that means no promises (since the promise is resolved/rejected in an event handler)
// since we are always inserting ourselves as a callback, we have to restore the original
// behavior if the user didn't provide one themselves
if (err) {
if (cb) {
return cb.apply(this, arguments);
}
else if (queryResult && queryResult instanceof events_1.EventEmitter) {
queryResult.emit("error", err);
}
}
else if (cb) {
cb.apply(this, arguments);
}
});
try {
Object.defineProperty(trackingCallback, diagnosticOriginalFunc, { value: cb });
return trackingCallback;
}
catch (e) {
// this should never happen, but bailout in case it does
return cb;
}
}
// this function takes too many variations of arguments.
// this patches any provided callback or creates a new callback if one wasn't provided.
// since the callback is always called (if provided) in addition to always having a Promisified
// EventEmitter returned (well, sometimes -- see above), its safe to insert a callback if none was given
try {
if (typeof config === "string") {
if (values instanceof Array) {
data.query.preparable = {
text: config,
args: values
};
callback = patchCallback(callback);
}
else {
data.query.text = config;
// pg v6 will, for some reason, accept both
// client.query("...", undefined, () => {...})
// **and**
// client.query("...", () => {...});
// Internally, precedence is given to the callback argument
if (callback) {
callback = patchCallback(callback);
}
else {
values = patchCallback(values);
}
}
}
else {
if (typeof config.name === "string") {
data.query.plan = config.name;
}
else if (config.values instanceof Array) {
data.query.preparable = {
text: config.text,
args: config.values
};
}
else {
data.query.text = config.text;
}
if (callback) {
callback = patchCallback(callback);
}
else if (values) {
values = patchCallback(values);
}
else {
config.callback = patchCallback(config.callback);
}
}
}
catch (e) {
// if our logic here throws, bail out and just let pg do its thing
return originalClientQuery.apply(this, arguments);
}
arguments[0] = config;
arguments[1] = values;
arguments[2] = callback;
arguments.length = (arguments.length > 3) ? arguments.length : 3;
queryResult = originalClientQuery.apply(this, arguments);
return queryResult;
};
return originalPg;
}
function postgresLatestPatchFunction(originalPg, originalPgPath) {
var originalClientQuery = originalPg.Client.prototype.query;
var diagnosticOriginalFunc = "__diagnosticOriginalFunc";
// wherever the callback is passed, find it, save it, and remove it from the call
// to the the original .query() function
originalPg.Client.prototype.query = function query(config, values, callback) {
var _this = this;
var _a, _b;
var callbackProvided = !!callback; // Starting in pg@7.x+, Promise is returned only if !callbackProvided
var data = {
query: {},
database: {
host: this.connectionParameters.host,
port: this.connectionParameters.port
},
result: null,
error: null,
duration: 0,
time: new Date()
};
var queryResult;
var start = process.hrtime();
function patchCallback(cb) {
if (cb && cb[diagnosticOriginalFunc]) {
cb = cb[diagnosticOriginalFunc];
}
var trackingCallback = diagnostic_channel_1.channel.bindToContext(function (err, res) {
var end = process.hrtime(start);
data.result = res && { rowCount: res.rowCount, command: res.command };
data.error = err;
data.duration = Math.ceil((end[0] * 1e3) + (end[1] / 1e6));
diagnostic_channel_1.channel.publish(publisherName, data);
if (err) {
if (cb) {
return cb.apply(this, arguments);
}
else if (queryResult && queryResult instanceof events_1.EventEmitter) {
queryResult.emit("error", err);
}
}
else if (cb) {
cb.apply(this, arguments);
}
});
try {
Object.defineProperty(trackingCallback, diagnosticOriginalFunc, { value: cb });
return trackingCallback;
}
catch (e) {
// this should never happen, but bailout in case it does
return cb;
}
}
// Only try to wrap the callback if it is a function. We want to keep the same
// behavior of returning a promise only if no callback is provided. Wrapping
// a nonfunction makes it a function and pg will interpret it as a callback
try {
if (typeof config === "string") {
if (values instanceof Array) {
data.query.preparable = {
text: config,
args: values
};
callbackProvided = typeof callback === "function";
callback = callbackProvided ? patchCallback(callback) : callback;
}
else {
data.query.text = config;
if (callback) {
callbackProvided = typeof callback === "function";
callback = callbackProvided ? patchCallback(callback) : callback;
}
else {
callbackProvided = typeof values === "function";
values = callbackProvided ? patchCallback(values) : values;
}
}
}
else {
if (typeof config.name === "string") {
data.query.plan = config.name;
}
else if (config.values instanceof Array) {
data.query.preparable = {
text: config.text,
args: config.values
};
}
else if (config.cursor) {
data.query.text = (_a = config.cursor) === null || _a === void 0 ? void 0 : _a.text;
}
else {
data.query.text = config.text;
}
if (callback) {
callbackProvided = typeof callback === "function";
callback = patchCallback(callback);
}
else if (values) {
callbackProvided = typeof values === "function";
values = callbackProvided ? patchCallback(values) : values;
}
else {
callbackProvided = typeof config.callback === "function";
config.callback = callbackProvided ? patchCallback(config.callback) : config.callback;
}
}
}
catch (e) {
// if our logic here throws, bail out and just let pg do its thing
return originalClientQuery.apply(this, arguments);
}
arguments[0] = config;
arguments[1] = values;
arguments[2] = callback;
arguments.length = (arguments.length > 3) ? arguments.length : 3;
try {
queryResult = originalClientQuery.apply(this, arguments);
}
catch (err) {
patchCallback()(err, undefined);
throw err;
}
if (!callbackProvided) {
if ((queryResult instanceof Promise)) {
return queryResult
// pass resolved promise after publishing the event
.then(function (result) {
patchCallback()(undefined, result);
return new _this._Promise(function (resolve, reject) {
resolve(result);
});
})
// pass along rejected promise after publishing the error
.catch(function (error) {
patchCallback()(error, undefined);
return new _this._Promise(function (resolve, reject) {
reject(error);
});
});
}
// Result could be a Cursor, QueryStream or Readable Stream
else {
var command = queryResult.text ? queryResult.text : "";
if (queryResult.cursor) {
command = (_b = queryResult.cursor) === null || _b === void 0 ? void 0 : _b.text;
}
if (command) {
var res = {
command: command,
rowCount: 0,
};
patchCallback()(undefined, res);
}
}
}
return queryResult;
};
return originalPg;
}
exports.postgres6 = {
versionSpecifier: "6.*",
patch: postgres6PatchFunction
};
exports.postgres = {
versionSpecifier: ">=7.* <=8.*",
patch: postgresLatestPatchFunction,
publisherName: publisherName
};
function enable() {
diagnostic_channel_1.channel.registerMonkeyPatch("pg", exports.postgres6);
diagnostic_channel_1.channel.registerMonkeyPatch("pg", exports.postgres);
}
exports.enable = enable;
//# sourceMappingURL=pg.pub.js.map
;