Demo for query storing

Change-Id: I947bcac841992c3f6cfd01ab337c265b0d01cb70
diff --git a/node_modules/bin-wrapper/index.js b/node_modules/bin-wrapper/index.js
new file mode 100644
index 0000000..1aba001
--- /dev/null
+++ b/node_modules/bin-wrapper/index.js
@@ -0,0 +1,208 @@
+'use strict';
+const fs = require('fs');
+const path = require('path');
+const url = require('url');
+const pify = require('pify');
+const importLazy = require('import-lazy')(require);
+
+const binCheck = importLazy('bin-check');
+const binVersionCheck = importLazy('bin-version-check');
+const download = importLazy('download');
+const osFilterObj = importLazy('os-filter-obj');
+
+const statAsync = pify(fs.stat);
+const chmodAsync = pify(fs.chmod);
+
+/**
+ * Initialize a new `BinWrapper`
+ *
+ * @param {Object} options
+ * @api public
+ */
+module.exports = class BinWrapper {
+	constructor(options = {}) {
+		this.options = options;
+
+		if (this.options.strip <= 0) {
+			this.options.strip = 0;
+		} else if (!this.options.strip) {
+			this.options.strip = 1;
+		}
+	}
+
+	/**
+	 * Get or set files to download
+	 *
+	 * @param {String} src
+	 * @param {String} os
+	 * @param {String} arch
+	 * @api public
+	 */
+	src(src, os, arch) {
+		if (arguments.length === 0) {
+			return this._src;
+		}
+
+		this._src = this._src || [];
+		this._src.push({
+			url: src,
+			os,
+			arch
+		});
+
+		return this;
+	}
+
+	/**
+	 * Get or set the destination
+	 *
+	 * @param {String} dest
+	 * @api public
+	 */
+	dest(dest) {
+		if (arguments.length === 0) {
+			return this._dest;
+		}
+
+		this._dest = dest;
+		return this;
+	}
+
+	/**
+	 * Get or set the binary
+	 *
+	 * @param {String} bin
+	 * @api public
+	 */
+	use(bin) {
+		if (arguments.length === 0) {
+			return this._use;
+		}
+
+		this._use = bin;
+		return this;
+	}
+
+	/**
+	 * Get or set a semver range to test the binary against
+	 *
+	 * @param {String} range
+	 * @api public
+	 */
+	version(range) {
+		if (arguments.length === 0) {
+			return this._version;
+		}
+
+		this._version = range;
+		return this;
+	}
+
+	/**
+	 * Get path to the binary
+	 *
+	 * @api public
+	 */
+	path() {
+		return path.join(this.dest(), this.use());
+	}
+
+	/**
+	 * Run
+	 *
+	 * @param {Array} cmd
+	 * @api public
+	 */
+	run(cmd = ['--version']) {
+		return this.findExisting().then(() => {
+			if (this.options.skipCheck) {
+				return;
+			}
+
+			return this.runCheck(cmd);
+		});
+	}
+
+	/**
+	 * Run binary check
+	 *
+	 * @param {Array} cmd
+	 * @api private
+	 */
+	runCheck(cmd) {
+		return binCheck(this.path(), cmd).then(works => {
+			if (!works) {
+				throw new Error(`The \`${this.path()}\` binary doesn't seem to work correctly`);
+			}
+
+			if (this.version()) {
+				return binVersionCheck(this.path(), this.version());
+			}
+
+			return Promise.resolve();
+		});
+	}
+
+	/**
+	 * Find existing files
+	 *
+	 * @api private
+	 */
+	findExisting() {
+		return statAsync(this.path()).catch(error => {
+			if (error && error.code === 'ENOENT') {
+				return this.download();
+			}
+
+			return Promise.reject(error);
+		});
+	}
+
+	/**
+	 * Download files
+	 *
+	 * @api private
+	 */
+	download() {
+		const files = osFilterObj(this.src() || []);
+		const urls = [];
+
+		if (files.length === 0) {
+			return Promise.reject(new Error('No binary found matching your system. It\'s probably not supported.'));
+		}
+
+		files.forEach(file => urls.push(file.url));
+
+		return Promise.all(urls.map(url => download(url, this.dest(), {
+			extract: true,
+			strip: this.options.strip
+		}))).then(result => {
+			const resultingFiles = flatten(result.map((item, index) => {
+				if (Array.isArray(item)) {
+					return item.map(file => file.path);
+				}
+
+				const parsedUrl = url.parse(files[index].url);
+				const parsedPath = path.parse(parsedUrl.pathname);
+
+				return parsedPath.base;
+			}));
+
+			return Promise.all(resultingFiles.map(fileName => {
+				return chmodAsync(path.join(this.dest(), fileName), 0o755);
+			}));
+		});
+	}
+};
+
+function flatten(arr) {
+	return arr.reduce((acc, elem) => {
+		if (Array.isArray(elem)) {
+			acc.push(...elem);
+		} else {
+			acc.push(elem);
+		}
+
+		return acc;
+	}, []);
+}