Skip to content

Commit

Permalink
Merge pull request #4 from layer5io/utkarsh-pro/feature/resolve-ref
Browse files Browse the repository at this point in the history
Add support for resolving "$ref" in the CLI
  • Loading branch information
leecalcote authored Sep 28, 2021
2 parents 445c5fc + 947170e commit a9d63e5
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 17 deletions.
14 changes: 12 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This is a very basic node based CLI for converting OpenAPI schema to JSON Schema


```
Usage: openapi-jsonschema [options]
Usage: kubeopenapi-jsonschema [options]
Options:
-t, --type [type] set type of input, can be either yaml or json (default: "yaml")
Expand All @@ -14,6 +14,7 @@ Options:
-o [output-format] output format (default: "json")
--o-filter [output-filter] output filter query
--silent skip output (default: false)
--resolve [resolve-filter] root of the OpenAPI spec to resolve the $ref. It is important to note that this jsonpath MUST evaluate to one object (default: "")
-h, --help display help for command
```

Expand All @@ -22,9 +23,18 @@ Options:
Download the binaries from the github releases. Only linux-x64, darwin-x64 and windows-x64 binaries are released

```bash
openapi-jsonschema --location ./istio.yaml -t yaml --filter '$[?(@.kind=="CustomResourceDefinition" && @.spec.names.kind=="EnvoyFilter")]..validation.openAPIV3Schema.properties.spec' -o yaml --o-filter '$[0]'
kubeopenapi-jsonschema --location ./k8s.json -f '$.definitions' -t json --o-filter '$[0][?(@["x-kubernetes-group-version-kind"][0].kind=="Deployment")].properties.spec' --resolve "$"
```

The above will consume kubernetes open API schema and will produce schema for Kubernetes `Deployment`


```bash
kubeopenapi-jsonschema --location ./istio.yaml -t yaml --filter '$[?(@.kind=="CustomResourceDefinition")]..schema.openAPIV3Schema.properties.spec' --o-filter '$'
```

The above will consume istio CRD manifest and will produce schema for all of the CustomResourceDefinition objects

<div>&nbsp;</div>

## Join the service mesh community!
Expand Down
10 changes: 6 additions & 4 deletions helper/output.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ const jp = require("jsonpath");
/**
* Output takes in the data that needs to be printed and
* an output format
* @param {*} data
* @param {Promise<any>} data
* @param {"json" | "yaml"} format output format
*/
function Output(data, format = "json", filter = "$", silent = false) {
if (silent) return;

data = jp.query(data, filter);
if (format === "yaml") return console.log(dump(data));
if (format === "json") return console.log(JSON.stringify(data, null, 2));
data.then(data => {
data = jp.query(data, filter);
if (format === "yaml") return console.log(dump(data));
if (format === "json") return console.log(JSON.stringify(data, null, 2));
})
}

module.exports = Output;
44 changes: 35 additions & 9 deletions helper/toJSONSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { readFileSync, writeFileSync } = require("fs");
const { tmpdir } = require("os");
const path = require("path");
const jp = require("jsonpath");
const { Resolver } = require("@stoplight/json-ref-resolver");

/**
* convertAllSchemasToJSONSchema takes in the OpenAPIV3 Schemas in an array
Expand All @@ -23,17 +24,39 @@ function convertAllSchemasToJSONSchema(schemas) {
* readSchema will read schema file from the given location, it expects
* the schema to be in JSON format
*
* readSchema will also apply the given jsonpath filter to the read schema
* and will return only the filtered JSONs
* readSchema will also resolve the references if a resolveQuery is passed
* @param {string} location
* @param {string} query jsonpath based query
* @returns {any[]}
* @param {string} resolveQuery jsonpath based query - must resolve to EXACTLY one match or else is ignored
* @returns {Promise<any[]>}
*/
function readSchema(location, query) {
async function readSchema(location, resolveQuery) {
const data = readFileSync(location, "utf-8");
const parsed = JSON.parse(data);

return jp.query(parsed, query);
if (resolveQuery) {
const inner = jp.query(parsed, resolveQuery);

if (inner.length !== 1) return parsed;

const resolver = new Resolver();
const resolved = await resolver.resolve(inner[0], {});

if (resolved.errors.length) console.error(resolved.errors);

return resolved.result;
}

return parsed;
}

/**
* filterSchemas takes in an array of schemas and will return an array of filtered schemas
* @param {Array<any>} schemas - OpenAPI schema in JSON format
* @param {string} query jsonpath based query to filter out the data
* @returns {Array<any>}
*/
function filterSchemas(schemas, query) {
return jp.query(schemas, query);
}

/**
Expand Down Expand Up @@ -72,16 +95,19 @@ function setupFiles(location, type) {
* @param {string} location location of the schemas in open api v3 format
* @param {"yaml" | "json"} type encoding in which the openapi schema is present
* @param {string} query jsonpath query to filter the read schemas
* @param {string} resolve jsonpath query to reach to the root of the openAPI spec
*/
function ToJSONSchema(location, type = "yaml", query = "") {
async function ToJSONSchema(location, type = "yaml", query = "", resolve = "") {
if (type !== "yaml" && type !== "json")
throw Error('invalid type received: can be either "yaml" or "json"');

const source = setupFiles(location, type);

const schemas = readSchema(source, query);
const schemas = await readSchema(source, resolve);

const filtered = filterSchemas(schemas, query);

return convertAllSchemasToJSONSchema(schemas);
return convertAllSchemasToJSONSchema(filtered);
}

module.exports = ToJSONSchema;
6 changes: 4 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ program
.option("--kubernetes", "enable kubernetes specific filters", false)
.option("-o [output-format]", "output format", "json")
.option("--o-filter [output-filter]", "output filter query")
.option("--silent", "skip output", false);
.option("--silent", "skip output", false)
.option("--resolve [resolve-filter]", "root of the OpenAPI spec to resolve the $ref", "")

program.parse(process.argv);

Expand All @@ -25,7 +26,8 @@ Output(
ToJSONSchema(
options.location,
options.type,
CreateQuery(options.filter, options.kubernetes)
CreateQuery(options.filter, options.kubernetes),
options.resolve,
),
options.o,
options.oFilter,
Expand Down
114 changes: 114 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"license": "MIT",
"dependencies": {
"@openapi-contrib/openapi-schema-to-json-schema": "^3.1.1",
"@stoplight/json-ref-resolver": "^3.1.3",
"commander": "^7.2.0",
"js-yaml": "^4.1.0",
"jsonpath": "^1.1.1"
Expand Down

0 comments on commit a9d63e5

Please sign in to comment.