UNPKG

cordite-cli

Version:

a command line tool for accessing a Corda node running cordite cordapps or braid

279 lines (250 loc) 6.93 kB
#!/usr/bin/env node /* * 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();