feat: configurable filename separator for split command (#610)

* Make file path separator configurable in split command

* chore: add tests for the file name separator

* chore: update snapshots

* docs: split separator option description

* chore: code style fixes
This commit is contained in:
volodymyr-rutskyi
2022-03-28 18:44:43 +03:00
committed by GitHub
parent 3e8b08449d
commit 1f10c7cd77
9 changed files with 54 additions and 15 deletions

1
.gitignore vendored
View File

@@ -7,5 +7,6 @@ coverage/
yarn.lock
dist/
lib/
output/
*.tar.gz
*.tsbuildinfo

View File

@@ -10,9 +10,10 @@ Positionals:
entrypoint API definition file that you want to split [string] [required]
Options:
--version Show version number. [boolean]
--help Show help. [boolean]
--outDir Output directory where files will be saved. [string] [required]
--version Show version number. [boolean]
--help Show help. [boolean]
--outDir Output directory where files will be saved. [string] [required]
--separator File path separator used while splitting. [string] [default: "_"]
Missing required argument: outDir

View File

@@ -109,7 +109,7 @@ paths:
/pets:
$ref: paths/pets.yaml
/pets/{petId}:
$ref: paths/pets@{petId}.yaml
$ref: paths/pets_{petId}.yaml
🪓 Document: ../../../__tests__/split/oas3-no-errors/openapi.yaml is successfully split
and all related files are saved to the directory: output

View File

@@ -22,6 +22,7 @@ Option | Type | Required | Default | Description
--------------------------|:---------:|:------------:|:-----------:|------------
`entrypoint` | `string` | yes | - | Path to the API definition file that you want to split into a multi-file structure
`--outDir` | `string` | yes | - | Path to the directory where you want to save split files. If the specified directory doesn't exist, it will be created automatically.
`separator` | `string` | no | `_` | File path separator used while splitting. Will affect file names in the `paths` folder (e.g. `user_create.yaml`)
`--help` | `boolean` | no | - | Show help
`--version` | `boolean` | no | - | Show version number

View File

@@ -1,4 +1,4 @@
import { isSubdir } from '../utils';
import { isSubdir, pathToFilename } from '../utils';
jest.mock("os");
@@ -40,3 +40,11 @@ describe('isSubdir', () => {
jest.resetModules()
})
});
describe('pathToFilename', () => {
it('should use correct path separator', () => {
const processedPath = pathToFilename('/user/createWithList', '_');
expect(processedPath).toEqual('user_createWithList');
});
});

View File

@@ -28,6 +28,7 @@ describe('#split', () => {
{
entrypoint: filePath,
outDir: openapiDir,
separator: '_',
}
);
@@ -41,12 +42,31 @@ describe('#split', () => {
);
});
it('should use the correct separator', async () => {
const filePath = "./packages/cli/src/commands/split/__tests__/fixtures/spec.json";
const utils = require('../../../utils');
jest.spyOn(utils, 'pathToFilename').mockImplementation(() => 'newFilePath');
await handleSplit (
{
entrypoint: filePath,
outDir: openapiDir,
separator: '_',
}
);
expect(utils.pathToFilename).toBeCalledWith(expect.anything(), '_');
utils.pathToFilename.mockRestore();
});
it('should have correct path with paths', () => {
const openapi = require("./fixtures/spec.json");
jest.spyOn(openapiCore, 'slash').mockImplementation(() => 'paths/test.yaml');
jest.spyOn(path, 'relative').mockImplementation(() => 'paths/test.yaml');
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles);
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles, '_');
expect(openapiCore.slash).toHaveBeenCalledWith('paths/test.yaml');
expect(path.relative).toHaveBeenCalledWith('test', 'test/paths/test.yaml');

View File

@@ -29,12 +29,13 @@ import {
export async function handleSplit (argv: {
entrypoint: string;
outDir: string
separator: string
}) {
const startedAt = performance.now();
const { entrypoint, outDir } = argv;
const { entrypoint, outDir, separator } = argv;
validateDefinitionFileName(entrypoint!);
const openapi = readYaml(entrypoint!) as Oas3Definition | Oas3_1Definition;
splitDefinition(openapi, outDir);
splitDefinition(openapi, outDir, separator);
process.stderr.write(
`🪓 Document: ${blue(entrypoint!)} ${green('is successfully split')}
and all related files are saved to the directory: ${blue(outDir)} \n`,
@@ -42,15 +43,15 @@ export async function handleSplit (argv: {
printExecutionTime('split', startedAt, entrypoint!);
}
function splitDefinition(openapi: Oas3Definition | Oas3_1Definition, openapiDir: string) {
function splitDefinition(openapi: Oas3Definition | Oas3_1Definition, openapiDir: string, pathSeparator: string) {
fs.mkdirSync(openapiDir, { recursive: true });
const componentsFiles: ComponentsFiles = {};
iterateComponents(openapi, openapiDir, componentsFiles);
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles);
iteratePathItems(openapi.paths, openapiDir, path.join(openapiDir, 'paths'), componentsFiles, pathSeparator);
const webhooks = (openapi as Oas3_1Definition).webhooks || (openapi as Oas3Definition)['x-webhooks'];
// use webhook_ prefix for code samples to prevent potential name-clashes with paths samples
iteratePathItems(webhooks, openapiDir, path.join(openapiDir, 'webhooks'), componentsFiles, 'webhook_');
iteratePathItems(webhooks, openapiDir, path.join(openapiDir, 'webhooks'), componentsFiles, pathSeparator, 'webhook_');
replace$Refs(openapi, openapiDir, componentsFiles);
writeYaml(openapi, path.join(openapiDir, 'openapi.yaml'));
@@ -247,13 +248,14 @@ function iteratePathItems(
openapiDir: string,
outDir: string,
componentsFiles: object,
pathSeparator: string,
codeSamplesPathPrefix: string = '',
) {
if (!pathItems) return;
fs.mkdirSync(outDir, { recursive: true });
for (const pathName of Object.keys(pathItems)) {
const pathFile = `${path.join(outDir, pathToFilename(pathName))}.yaml`;
const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.yaml`;
const pathData = pathItems[pathName] as Oas3PathItem;
if (isRef(pathData)) continue;
@@ -270,7 +272,7 @@ function iteratePathItems(
openapiDir,
'code_samples',
sample.lang,
codeSamplesPathPrefix + pathToFilename(pathName),
codeSamplesPathPrefix + pathToFilename(pathName, pathSeparator),
method + langToExt(sample.lang),
);

View File

@@ -46,6 +46,12 @@ yargs
required: true,
type: 'string',
},
separator: {
description: 'File path separator used while splitting.',
required: false,
type: 'string',
default: '_',
},
})
.demandOption('entrypoint'),
handleSplit,

View File

@@ -66,12 +66,12 @@ export function printExecutionTime(commandName: string, startedAt: number, entry
process.stderr.write(gray(`\n${entrypoint}: ${commandName} processed in ${elapsed}\n\n`));
}
export function pathToFilename(path: string) {
export function pathToFilename(path: string, pathSeparator: string) {
return path
.replace(/~1/g, '/')
.replace(/~0/g, '~')
.replace(/^\//, '')
.replace(/\//g, '@');
.replace(/\//g, pathSeparator);
}
export class CircularJSONNotSupportedError extends Error {