kinto-node-test-server
Version:
A node API for operating a Kinto test server.
133 lines (132 loc) • 5.1 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const node_fetch_1 = __importDefault(require("node-fetch"));
const child_process_1 = require("child_process");
const path_1 = __importDefault(require("path"));
const server_1 = __importDefault(require("./server"));
exports.KintoProxyServer = server_1.default;
const DEFAULT_OPTIONS = {
maxAttempts: 50,
kintoConfigPath: path_1.default.resolve(path_1.default.join(__dirname, "../kinto.ini")),
pservePath: process.env.KINTO_PSERVE_EXECUTABLE || "pserve",
};
function copyExisting(obj, keys) {
const ret = {};
for (const key of keys) {
if (obj.hasOwnProperty(key)) {
ret[key] = obj[key];
}
}
return ret;
}
function checkForPserve(pservePath) {
try {
child_process_1.execSync(`${pservePath} --help`, { stdio: "ignore" });
}
catch (err) {
throw new Error(pservePath === "pserve"
? "Unable to find pserve in PATH. Have you installed kinto or activated your virtualenv?"
: `Unable to find or execute ${pservePath}.`);
}
}
class KintoServer {
constructor(url, options = {}) {
this.url = url;
this.process = null;
this._logs = [];
this.http_api_version = null;
this.options = Object.assign({}, DEFAULT_OPTIONS, options);
}
async _retryRequest(url, options, attempt = 1) {
const { maxAttempts } = this.options;
try {
const res = await node_fetch_1.default(url, options);
if ([200, 202, 410].indexOf(res.status) === -1) {
throw new Error("Unable to start server, HTTP " + res.status);
}
return res;
}
catch (err) {
if (maxAttempts && attempt < maxAttempts) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(this._retryRequest(url, options, attempt + 1));
}, 100);
});
}
throw new Error(`Max attempts number reached (${maxAttempts}); ${err}`);
}
}
async loadConfig(pathToConfig) {
this.options.kintoConfigPath = pathToConfig;
}
start(env) {
if (this.process) {
throw new Error("Server is already started.");
}
// Keeping parent's environment is needed so that pserve's executable
// can be found (with PATH) if KINTO_PSERVE_EXECUTABLE env variable was
// not provided.
// However, Kinto's config parsing logic tries to interpolate any
// %-code in any environment variable, so rather than polluting
// the environment with everything, just copy the variables we
// think will be necessary.
const sanitizedEnv = copyExisting(process.env, [
"PATH",
"VIRTUAL_ENV",
"PYTHONPATH",
]);
// Add the provided environment variables to the child process environment.
env = Object.assign({}, sanitizedEnv, env);
checkForPserve(this.options.pservePath);
this.process = child_process_1.spawn(this.options.pservePath, [this.options.kintoConfigPath], { env, detached: true });
this.process.stderr.on("data", (data) => {
this._logs.push(data);
});
this.process.on("close", (code) => {
if (code && code > 0) {
console.error(new Error("Server errors encountered:\n" +
this._logs.map((line) => line.toString()).join("")));
console.error(new Error().stack);
}
});
return this.ping();
}
async ping() {
const endpoint = `${this.url}/`;
const res = await this._retryRequest(endpoint, {}, 1);
const json = await res.json();
this.http_api_version = json.http_api_version;
return this.http_api_version;
}
async flush() {
const endpoint = `${this.url}/__flush__`;
const res = await this._retryRequest(endpoint, { method: "POST" }, 1);
return { status: res.status };
}
stop() {
if (this.process)
this.process.kill();
this.process = null;
return new Promise((resolve) => {
setTimeout(() => resolve(), 500);
});
}
killAll() {
return new Promise((resolve) => {
if (process.platform === "win32") {
child_process_1.spawn("taskkill", ["/im", "pserve.exe"]).on("close", () => resolve());
}
else {
child_process_1.spawn("killall", ["pserve"]).on("close", () => resolve());
}
});
}
logs() {
return Promise.resolve(this._logs.map((line) => line.toString()).join(""));
}
}
exports.default = KintoServer;