cordite-cli
Version:
a command line tool for accessing a Corda node running cordite cordapps or braid
279 lines (250 loc) • 6.93 kB
JavaScript
/*
* Copyright 2018, Cordite Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const os = require("os");
const repl = require("repl");
const fs = require("fs");
const Proxy = require("braid-client").Proxy;
const deepromisify = require("./deepromise-repl");
const historyPath = os.homedir() + "/.cordite_repl_history";
const commandLineArgs = require("command-line-args");
const commandLineUsage = require("command-line-usage");
const rewrite = require("./deepromise");
const corditeBanner = ` _____ ___ __
/ ___/__ _______/ (_) /____
/ /__/ _ \\/ __/ _ / / __/ -_)
\\___/\\___/_/ \\_,_/_/\\__/\\__/`;
var corda = null;
const notaries = {};
const globalSymbolNames = [];
const sections = [
{
header: "Cordite",
content: "Client to connect to braid-enabled Corda nodes",
},
{
header: "Options",
optionList: [
{ name: "help", description: "print this usage guide" },
{
name: "url",
description: "connection <protocol>://<host>:<port>",
alias: "u",
type: String,
defaultOption: true,
defaultValue: "https://localhost:8081",
},
{
name: "strictSSL",
description: "enable or disable strict SSL checks",
alias: "s",
type: Boolean,
defaultValue: false,
},
{
name: "credentials",
description: "credentials payload",
type: String,
alias: "c",
defaultValue: "",
},
{
name: "scriptArgs",
description: "JSON args given to the script",
type: String,
alias: "a",
defaultValue: "{}",
},
{
name: "script",
description: "script file to be executed",
type: String,
alias: "f",
defaultValue: "",
},
],
},
];
function printBanner() {
console.log("");
console.log(corditeBanner);
}
function printHelp() {
console.log(commandLineUsage(sections));
console.log("example:");
console.log(
' cordite https://myhost.com:8081 --credentials \'{ "username": "admin", "password": "admin"}\' --strictSSL'
);
}
function run() {
const options = commandLineArgs(sections[1].optionList);
if (options.hasOwnProperty("help")) {
printHelp();
process.exit();
}
const config = {
url: options.url + "/api/",
};
if (options.credentials) {
config.credentials = JSON.parse(options.credentials);
}
strictSSL = false;
if (options.hasOwnProperty("strictSSL")) {
strictSSL = options.strictSSL;
}
const transportConfig = {
strictSSL: strictSSL,
};
printBanner();
console.log(
"connecting to",
config.url,
"with",
config,
"and",
transportConfig
);
corda = new Proxy(
config,
() => {
onOpen(options);
},
onClose,
onError,
transportConfig
);
}
async function onOpen(options) {
await getAndProcessNotaries();
if (options.script) {
executeScript(options.script, options.scriptArgs);
} else {
interactiveMode();
}
}
function read(filename) {
return fs.readFileSync(filename, "utf-8");
}
async function executeScript(file, jsonScriptArgs) {
let script;
try {
script = fs.readFileSync(file, "utf8");
const scriptArgs = JSON.parse(jsonScriptArgs)
console.log("json script args", jsonScriptArgs)
const AsyncFunction = Object.getPrototypeOf(async function () {})
.constructor;
const names = getPropertyNames(corda);
const values = getPropertyValues(corda);
const fn = new AsyncFunction("read", "corda", "notaries", "scriptArgs", ...names, script);
const result = await fn(read, corda, notaries, scriptArgs, ...values);
console.log(result);
process.exit();
} catch (err) {
console.error("failed to execute script: " + file);
console.error(err);
process.exit(-1);
}
}
async function interactiveMode() {
try {
await printMyInfo();
globalSymbolNames.push("corda", "notaries");
Object.getOwnPropertyNames(corda).forEach((name) => {
globalSymbolNames.push(name);
});
console.log("available objects:", globalSymbolNames.join(", "));
replServer = repl.start({
prompt: "cordite > ",
});
replServer.context.corda = corda;
replServer.context.notaries = notaries;
Object.getOwnPropertyNames(corda).forEach((name) => {
Object.defineProperty(replServer.context, name, {
value: corda[name],
writable: false,
});
globalSymbolNames.push(name);
});
if (fs.existsSync(historyPath)) {
fs.statSync(historyPath);
fs.readFileSync(historyPath)
.toString()
.split("\n")
.reverse()
.filter((line) => line.trim())
.map((line) => replServer.history.push(line));
}
replServer.on("exit", () => {
fs.appendFileSync(historyPath, replServer.lines.join("\n"));
process.exit();
});
deepromisify(replServer);
} catch (err) {
console.error("failed during initialisation with error:", err);
process.exit();
}
}
async function printMyInfo() {
if (corda.network) {
const ni = await corda.network.myNodeInfo();
console.log("");
console.log("connected to node:", ni.legalIdentities[0].name);
console.log("");
}
}
function onError(e) {
console.error("could not connect", e);
}
function onClose() {
console.log("closed");
}
async function getAndProcessNotaries() {
if (corda.network) {
const notaries = await corda.network.notaryIdentities();
processNotaries(notaries);
}
}
function processNotaries(unparsedNotaries) {
const parsed = unparsedNotaries.map((n) => {
const name = n.name;
const parsedName = name
.split(",")
.map((i) => i.trim())
.map((i) => i.split("="))
.map((i) => {
const o = {};
o[i[0]] = i[1];
return o;
})
.reduce((acc, current) => {
return Object.assign(acc, current);
}, {});
notaries[decapitalize(parsedName.O).split(" ").join("")] = n;
});
}
function decapitalize(str) {
if (str.length === 0) return str;
return str.charAt(0).toLowerCase() + str.substr(1);
}
function getPropertyNames(obj) {
return Object.getOwnPropertyNames(obj);
}
function getPropertyValues(obj) {
return getPropertyNames(obj).map((name) => {
return obj[name];
});
}
run();