Skip to content

Commit

Permalink
Apply OpenAPI overlay actions (#150)
Browse files Browse the repository at this point in the history
* Add OpenAPI overlay option

* Added JsonPath-plus library

* Updated types

* Added overlay metrics

* Updated readme
  • Loading branch information
thim81 authored Jan 1, 2025
1 parent ee6c4a3 commit c72d4ce
Show file tree
Hide file tree
Showing 20 changed files with 1,035 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
## unreleased

## [1.25.0] - 2025-01-01

- CLI - Added option to apply OpenAPI overlay actions

## [1.24.2] - 2024-10-07

- fix: scalar $ref: >-
Expand Down
32 changes: 32 additions & 0 deletions bin/__snapshots__/cli.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ Options:
-k, --casingFile <casingFile> the file to specify casing rules
-f, --filterFile <filterFile> the file to specify filter rules
-g, --generateFile <generateFile> the file to specify generate rules
-l, --overlayFile <overlayFile> the file to specify OpenAPI overlay changes
-c, --configFile <configFile> the file with the OpenAPI-format CLI options
--no-sort don't sort the OpenAPI file
--keepComments don't remove the comments from the OpenAPI YAML file (default: false)
Expand Down Expand Up @@ -284,6 +285,37 @@ OpenAPI-Format CLI settings:
"
`;
exports[`openapi-format CLI command should use the overlayFile 1`] = `
"================================================================================
OpenAPI-Format CLI settings:
- Overlay file: test/overlay-combi/overlay.yaml
- Input file: test/overlay-combi/input.yaml
================================================================================
✅ OpenAPI formatted successfully
================================================================================
"
`;
exports[`openapi-format CLI command should use the overlayFile with verbose 1`] = `
"================================================================================
OpenAPI-Format CLI settings:
- Overlay file: test/overlay-combi/overlay.yaml
- Input file: test/overlay-combi/input.yaml
================================================================================
OpenAPI Overlay actions summary:
- Total actions: 4
- Applied actions: 3
- Unused actions: 1
================================================================================
Unused overlay actions:
- Target: $.server[*]
Type: remove
================================================================================
✅ OpenAPI formatted successfully
================================================================================
"
`;
exports[`openapi-format CLI command should use the rename 1`] = `
"================================================================================
OpenAPI-Format CLI settings:
Expand Down
53 changes: 53 additions & 0 deletions bin/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ program
.option('-k, --casingFile <casingFile>', 'the file to specify casing rules')
.option('-f, --filterFile <filterFile>', 'the file to specify filter rules')
.option('-g, --generateFile <generateFile>', 'the file to specify generate rules')
.option('-l, --overlayFile <overlayFile>', 'the file to specify OpenAPI overlay changes')
.option('-c, --configFile <configFile>', 'the file with the OpenAPI-format CLI options')
.option('--no-sort', `don't sort the OpenAPI file`)
.option('--keepComments', `don't remove the comments from the OpenAPI YAML file`, false)
Expand Down Expand Up @@ -198,6 +199,22 @@ async function run(oaFile, options) {
}
}

// Set OpenAPI overlay actions
if (options && options.overlayFile) {
infoOut(`- Overlay file:\t\t${options.overlayFile}`); // LOG - Casing file
try {
let overlayOptions = {overlaySet: {}};
overlayOptions.overlaySet = await openapiFormat.parseFile(options.overlayFile);
options = Object.assign({}, options, overlayOptions);
} catch (err) {
console.error('\x1b[31m', `Overlay file error - no such file or directory "${options.overlayOptions}"`);
if (options.verbose >= 1) {
console.error(err);
}
process.exit(1);
}
}

let resObj = {};
let output = {};
let input = {};
Expand Down Expand Up @@ -234,6 +251,19 @@ async function run(oaFile, options) {
resObj = resFilter.data;
}

// Apply OpenAPI overlay actions
if (options.overlaySet) {
const resOverlay = await openapiFormat.openapiOverlay(resObj, options);
if (resOverlay?.resultData &&
(resOverlay.resultData.unusedActions || resOverlay.resultData.appliedActions || resOverlay.resultData.totalActions)) {
cliLog.unusedActions = resOverlay.resultData.unusedActions || [];
cliLog.totalUsedActions = resOverlay.resultData.totalUsedActions || 0;
cliLog.totalUnusedActions = resOverlay.resultData.totalUnusedActions || 0;
cliLog.totalActions = resOverlay.resultData.totalActions || 0;
}
resObj = resOverlay.data;
}

// Format & Order OpenAPI document
if (options.sort === true) {
const resFormat = await openapiFormat.openapiSort(resObj, options);
Expand Down Expand Up @@ -327,6 +357,29 @@ async function run(oaFile, options) {
}
}

// Show unused components
if (options.overlaySet && (cliLog?.totalActions || cliLog?.appliedActions || cliLog?.unusedActions)) {
// Log summary of actions
logOut(`${consoleLine}`, options.verbose); // LOG - horizontal rule
logOut(`OpenAPI Overlay actions summary:`, options.verbose);
logOut(`- Total actions: \t${cliLog.totalActions}`, options.verbose);
logOut(`- Applied actions: \t${cliLog.totalUsedActions}`, options.verbose);
logOut(`- Unused actions: \t${cliLog.totalUnusedActions}`, options.verbose);

const cliOut = [];
cliLog.unusedActions.forEach(action => {
const description = action.description || 'No description provided';
cliOut.push(`- Target: ${action.target}\n Type: ${action.update ? 'update' : action.remove ? 'remove' : 'unknown'}`);
});

if (cliLog.unusedActions.length > 0) {
// Log unused actions
logOut(`${consoleLine}`, options.verbose); // LOG - horizontal rule
logOut(`Unused overlay actions:`, options.verbose);
logOut(cliOut.join('\n'), options.verbose);
}
}

// Final result
infoOut(`\x1b[32m${consoleLine}\x1b[0m`); // LOG - horizontal rule
infoOut(`\x1b[32m✅ OpenAPI ${outputLogFiltered}formatted successfully\x1b[0m`, 99); // LOG - success message
Expand Down
30 changes: 30 additions & 0 deletions bin/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,36 @@ describe('openapi-format CLI command', () => {
expect(sanitize(result.stderr)).toStrictEqual(sanitize(output));
});

it('should use the overlayFile', async () => {
const path = `test/overlay-combi`;
const inputFile = `${path}/input.yaml`;
const outputFile = `${path}/output.yaml`;
const output = await getLocalFile(outputFile);
const setting = `${path}/overlay.yaml`;

let result = await testUtils.cli([inputFile, `--overlayFile ${setting}`, `--no-sort`], '.');
// console.log('result', result)
expect(result.code).toBe(0);
expect(result.stdout).toContain('formatted successfully');
expect(result.stdout).toMatchSnapshot();
expect(sanitize(result.stderr)).toStrictEqual(sanitize(output));
});

it('should use the overlayFile with verbose', async () => {
const path = `test/overlay-combi`;
const inputFile = `${path}/input.yaml`;
const outputFile = `${path}/output.yaml`;
const output = await getLocalFile(outputFile);
const setting = `${path}/overlay.yaml`;

let result = await testUtils.cli([inputFile, `--overlayFile ${setting}`, `--no-sort`, `-v`], '.');
// console.log('result', result)
expect(result.code).toBe(0);
expect(result.stdout).toContain('formatted successfully');
expect(result.stdout).toMatchSnapshot();
expect(sanitize(result.stderr)).toStrictEqual(sanitize(output));
});

it('should bundle reference', async () => {
const path = `test/yaml-ref-quotes`;
const inputFile = `${path}/input.yaml`;
Expand Down
6 changes: 5 additions & 1 deletion openapi-format.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const {parseFile, writeFile, stringify, detectFormat, parseString, analyzeOpenAp
const {parseTpl, getOperation} = require('./utils/parseTpl');
const {writePaths, writeComponents, writeSplitOpenAPISpec} = require('./utils/split');
const {dirname, extname} = require('path');
const { openapiOverlay, resolveJsonPath, resolveJsonPathValue} = require('./utils/overlay');

/**
* OpenAPI sort function
Expand Down Expand Up @@ -1145,6 +1146,7 @@ module.exports = {
openapiGenerate: openapiGenerate,
openapiSort: openapiSort,
openapiChangeCase: openapiChangeCase,
openapiOverlay: openapiOverlay,
openapiSplit: openapiSplit,
openapiConvertVersion: openapiConvertVersion,
openapiRename: openapiRename,
Expand All @@ -1155,5 +1157,7 @@ module.exports = {
writeFile: writeFile,
detectFormat: detectFormat,
analyzeOpenApi: analyzeOpenApi,
changeCase: changeCase
changeCase: changeCase,
resolveJsonPath: resolveJsonPath,
resolveJsonPathValue: resolveJsonPathValue,
};
92 changes: 86 additions & 6 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 @@ -37,6 +37,7 @@
"api-ref-bundler": "^0.4.3",
"case-anything": "2.1.10",
"commander": "^7.2.0",
"jsonpath-plus": "^10.2.0",
"neotraverse": "^0.6.18"
},
"devDependencies": {
Expand Down
Loading

0 comments on commit c72d4ce

Please sign in to comment.