Skip to content

Commit

Permalink
Extract and test CLI logic (#275)
Browse files Browse the repository at this point in the history
* Extract and test CLI logic

* Extract and test cli argument parsing

* also test -k ...

* Update cli.js
  • Loading branch information
ralfhandl authored Jan 17, 2024
1 parent 05136be commit 2f30352
Show file tree
Hide file tree
Showing 4 changed files with 286 additions and 91 deletions.
104 changes: 14 additions & 90 deletions lib/cli.js
Original file line number Diff line number Diff line change
@@ -1,111 +1,35 @@
#!/usr/bin/env node
"use strict";

//console.dir(argv);

//TODO: what to require?
const csdl = require("odata-csdl");
const lib = require("./csdl2openapi");
const minimist = require("minimist");
const fs = require("fs");
const { parseArgs } = require("./cliParts");
const { stringifyStream } = require("@discoveryjs/json-ext");

var unknown = false;

var argv = minimist(process.argv.slice(2), {
string: [
"basePath",
"description",
"host",
"keep",
"levels",
"openapi-version",
"scheme",
"target",
"title",
],
boolean: ["diagram", "help", "pretty", "skipBatchPath", "used-schemas-only"],
alias: {
d: "diagram",
h: "help",
k: "keep",
o: "openapi-version",
p: "pretty",
t: "target",
u: "used-schemas-only",
},
default: {
levels: 4,
pretty: false,
skipBatchPath: false,
},
unknown: (arg) => {
if (arg.substring(0, 1) == "-") {
console.error("Unknown option: " + arg);
unknown = true;
return false;
}
},
});

if (unknown || argv._.length == 0 || argv.h) {
console.log(`Usage: odata-openapi3 <options> <source file>
Options:
--basePath base path (default: /service-root)
--description default description if none is annotated
-d, --diagram include YUML diagram
-h, --help show this info
--host host (default: localhost)
-k, --keep root resource to keep (can be specified multiple times with one name each)
--levels maximum number of path segments
-o, --openapi-version 3.0.0 to 3.0.3 or 3.1.0 (default: 3.0.2)
-p, --pretty pretty-print JSON result
--scheme scheme (default: http)
--skipBatchPath skips the generation of the $batch path, (default: false)
-t, --target target file (default: source file basename + .openapi3.json)
--title default title if none is annotated`);
} else {
//TODO: further input parameters reserved for e.g. referenced CSDL documents
// for (var i = 0; i < argv._.length; i++) {
// convert(argv._[i]);
// }
convert(argv._[0]);
}
const args = parseArgs(process.argv.slice(2));
if (args.unknown)
args.unknown.map((arg) => console.error(`Unknown option: ${arg}`));
if (args.usage) console.log(args.usage);
else convert(args);

function convert(source) {
if (!fs.existsSync(source)) {
console.error("Source file not found: " + source);
function convert(args) {
if (!fs.existsSync(args.source)) {
console.error("Source file not found: " + args.source);
return;
}

const text = fs.readFileSync(source, "utf8");
const text = fs.readFileSync(args.source, "utf8");
const json = text.startsWith("<") ? csdl.xml2json(text) : JSON.parse(text);

const target =
argv.target ||
(source.lastIndexOf(".") <= 0
? source
: source.substring(0, source.lastIndexOf("."))) + ".openapi3.json";
console.log(target);
console.log(args.target);

const messages = [];

const openapi = lib.csdl2openapi(json, {
scheme: argv.scheme,
host: argv.host,
basePath: argv.basePath,
diagram: argv.diagram,
maxLevels: Number(argv.levels),
openapiVersion: argv.o,
messages,
skipBatchPath: argv.skipBatchPath,
defaultTitle: argv.title,
defaultDescription: argv.description,
rootResourcesToKeep: Array.isArray(argv.keep) ? argv.keep : argv.keep? [argv.keep]: undefined,
});
const openapi = lib.csdl2openapi(json, { messages, ...args.options });

stringifyStream(openapi, null, argv.pretty ? 4 : 0).pipe(
fs.createWriteStream(target),
stringifyStream(openapi, null, args.options.pretty ? 4 : 0).pipe(
fs.createWriteStream(args.target),
);

if (messages.length > 0) console.dir(messages);
Expand Down
111 changes: 111 additions & 0 deletions lib/cliParts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/**
* Parse command-line options
*
* Latest version: https://github.com/oasis-tcs/odata-openapi/blob/main/lib/cliOptions.js
*/

const minimist = require("minimist");

module.exports = { parseArgs };

function parseArgs(argv) {
const usage = `Usage: odata-openapi3 <options> <source file>
Options:
--basePath base path (default: /service-root)
--description default description if none is annotated
-d, --diagram include YUML diagram
-h, --help show this info
--host host (default: localhost)
-k, --keep root resource to keep (can be specified multiple times with one name each)
--levels maximum number of path segments
-o, --openapi-version 3.0.0 to 3.0.3 or 3.1.0 (default: 3.0.2)
-p, --pretty pretty-print JSON result
--scheme scheme (default: http)
--skipBatchPath skips the generation of the $batch path, (default: false)
-t, --target target file (default: source file basename + .openapi3.json)
--title default title if none is annotated`;
const unknown = [];

const args = minimist(argv, {
string: [
"basePath",
"description",
"host",
"keep",
"levels",
"openapi-version",
"scheme",
"target",
"title",
],
boolean: ["diagram", "help", "pretty", "skipBatchPath"],
alias: {
d: "diagram",
h: "help",
k: "keep",
o: "openapi-version",
p: "pretty",
t: "target",
},
default: {
pretty: false,
},
unknown: (arg) => {
if (arg.substring(0, 1) == "-") {
unknown.push(arg);
return false;
}
},
});

if (unknown.length > 0 || args.help || args._.length !== 1)
return {
unknown,
usage,
};

const source = args._[0];
const target =
args.target ||
(source.lastIndexOf(".") > 0
? source.substring(0, source.lastIndexOf("."))
: source) + ".openapi3.json";

const options = {};

for (const [name, value] of Object.entries(args)) {
if (name.length === 1) continue;
if (value === false) continue;
switch (name) {
case "description":
options.defaultDescription = value;
break;
case "keep":
if (Array.isArray(value)) options.rootResourcesToKeep = value;
else options.rootResourcesToKeep = [value];
break;
case "levels": {
const l = Number(value);
if (!isNaN(l)) options.maxLevels = l;
break;
}
case "openapi-version":
options.openapiVersion = value;
break;
case "target":
break;
case "title":
options.defaultTitle = value;
break;
default:
options[name] = value;
break;
}
}

return {
source,
target,
options,
};
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
},
"scripts": {
"build": "node lib/transform.js",
"pretest": "node lib/cli.js -p --skipBatchPath -t examples/PingTest_V1.no-batch.openapi3.json examples/PingTest_V1.xml",
"pretest": "rm examples/PingTest_V1.no-batch.openapi3.json && node lib/cli.js -p --skipBatchPath -t examples/PingTest_V1.no-batch.openapi3.json examples/PingTest_V1.xml",
"test": "c8 mocha",
"watch": "mocha --watch"
},
Expand Down
Loading

0 comments on commit 2f30352

Please sign in to comment.