src/executor.js
"use strict";
let debug = require("debug")("restwalker");
let Promise = Promise || require("bluebird");
class Executor {
constructor(runner) {
if (!runner) {
throw new Error("Expected runner to be defined.");
}
this.runner = runner;
}
setRoot(root) {
this.runner.setRoot(root);
}
execute(command, context) {
debug("Executing command", command);
// Walk the command path. Link methods must be GET
let pathLength = command.path.length;
let lastCommand = command.path[pathLength - 1];
let initialPathName = command.path[0].name;
let chain = null;
if (initialPathName === "root") {
chain = this.runner.get("/").then((res) => {
debug(`retrieved service root`);
return res.body;
});
} else {
let initialEntity = context[initialPathName];
if (!initialEntity) {
throw new Error(`Initial entity "${initialPathName}" is not defined`);
}
chain = Promise.resolve(context[initialPathName]);
}
for (let pathItem of command.path.slice(1, pathLength - 1)) {
chain = chain.then((result) => this.takeGetStep(result, pathItem));
}
return chain.then((result) => this.takeStep(result, lastCommand, context, command));
}
takeGetStep(res, step) {
debug(`taking step ${step.name}`);
// TODO: Configure link retrieval.
// TODO: Link method case insensitivity.
let link = res.links[step.name];
if (!link) {
throw new Error(`link "${step.name}" does not exist`);
}
if (link.method !== "GET") {
throw new Error("Expected 'GET' link");
}
let path = link.href;
if (step.query) {
path = path + "?" + step.query;
}
return this.runner.get(path).then((result) => {
let body = result.body;
if (step.index) {
// TODO: Configure collection indexing
return body.items[step.index];
} else {
return body;
}
});
}
takeStep(res, step, context, command) {
debug(`taking final step '${step.name}'`);
return new Promise((resolve, reject) => {
let link = res.links[step.name];
if (!link) {
reject(new Error(`link "${step.name}" does not exist`));
}
let method = link.method.toLowerCase();
let path = link.href;
if (!method) {
reject(new Error("Method must exist"));
}
if (!path) {
reject(new Error("Path must exist"));
}
if (step.query) {
path = path + "?" + step.query;
}
let invocation = this.runner.invokeRaw(method, path);
if (command.invocation.with) {
let payload = context[command.invocation.with];
debug("sending payload ", payload);
invocation = invocation.send(payload);
}
if (command.invocation.emits) {
invocation = invocation.expect(command.invocation.emits);
}
invocation.end((err, res) => {
let result = step.index ? res.body.items[step.index] : res.body;
if (err) {
return reject(err);
} else {
if (command.invocation.as) {
context[command.invocation.as] = result;
}
resolve();
}
});
});
}
}
module.exports = Executor;