Skip to content

Commit

Permalink
chore: input coerce function returns paths matching glob pattern in…
Browse files Browse the repository at this point in the history
… openapi generator (SAP#3498)

* opena api input resolve

* unused dep

* fix tests

* remarks
  • Loading branch information
deekshas8 authored Feb 10, 2023
1 parent 9896098 commit 3fbac9a
Show file tree
Hide file tree
Showing 8 changed files with 82 additions and 76 deletions.
4 changes: 3 additions & 1 deletion packages/generator-common/src/options-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,9 @@ describe('options parser', () => {
{ include, config },
{ include: '**/*.json', config: '/dummy/root/config.json' }
).include
).toEqual(['file-1.json', 'sub-1/file-2.json'].map(s => resolve(s)));
).toEqual(
['file-1.json', 'sub-1/file-2.json'].map(s => resolve('/dummy/root', s))
);
mock.restore();
});

Expand Down
56 changes: 48 additions & 8 deletions packages/generator-common/src/options-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { existsSync, lstatSync } from 'fs';
import { createLogger } from '@sap-cloud-sdk/util';
import { InferredOptionType, Options as YargsOption } from 'yargs';
const logger = createLogger('generator-options');
import { sync as globSync } from 'glob';
import { glob, sync as globSync } from 'glob';

/**
* @internal
Expand Down Expand Up @@ -71,7 +71,8 @@ type OptionsWith<
}[keyof CliOptionsT];

/**
* Resolves a string using glob notation. If a config is given in generatorOptions, the glob working directory is considered relative to this config.
* Resolves a string using glob notation.
* If a config is given in generatorOptions, the glob working directory is considered relative to this config.
* @internal
* @param arg - Value for the string for which the glob is resolved.
* @param options - Generator options.
Expand All @@ -84,14 +85,12 @@ export function resolveGlob<GeneratorOptionsT>(
return [];
}

const globConfig = options.config
? { cwd: resolve(dirname(options.config)) }
: { cwd: resolve() };
const inputPath = options.config
? resolve(dirname(options.config), arg.toString())
: resolve(arg.toString());

// Glob expressions only support unix style path separator (/). The below adjustment is made so it works on Windows. https://github.com/isaacs/node-glob#windows
return globSync(arg.split(sep).join(posix.sep), globConfig).map(s =>
resolve(s)
);
return globSync(inputPath.split(sep).join(posix.sep)).map(s => resolve(s));
}

/**
Expand All @@ -110,6 +109,47 @@ export function resolveRequiredPath<GeneratorOptionsT>(
: resolve(arg.toString());
}

/**
* Resolves input string using glob notation.
* @internal
* @param arg - Path argument as passed by the user.
* @param options - Options as passed by the user.
* @returns Absolute path as a `string`.
*/
export function resolveInputGlob<GeneratorOptionsT>(
arg: string,
options: GeneratorOptionsT & { config?: string }
): string[] {
const inputPath = options.config
? resolve(dirname(options.config), arg.toString())
: resolve(arg.toString());

return getInputFilePaths(inputPath.split(sep).join(posix.sep));
}

/**
* Recursively searches through a given input path and returns all file paths as a string array.
* @param input - the path to the input directory.
* @returns all file paths as a string array.
* @internal
*/
export function getInputFilePaths(input: string): string[] {
// Check for any special characters in the input
if (glob.hasMagic(input)) {
return globSync(input)
.filter(path => /(.json|.JSON|.yaml|.YAML|.yml|.YML)$/.test(path))
.map(s => resolve(s));
}

if (lstatSync(input).isDirectory()) {
return globSync(
posix.join(input, '**/*.{json,JSON,yaml,YAML,yml,YML}')
).map(s => resolve(s));
}

return [resolve(input)];
}

/**
* Resolve the optionsPerService. If a directory is given the default name 'options-per-service.json' is used.
* @internal
Expand Down
1 change: 0 additions & 1 deletion packages/openapi-generator/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
"@sap-cloud-sdk/generator-common": "^2.11.0",
"@sap-cloud-sdk/openapi": "^2.11.0",
"@sap-cloud-sdk/util": "^2.11.0",
"glob": "^8.1.0",
"js-yaml": "^4.0.0",
"openapi-types": "^12.1.0",
"swagger2openapi": "^7.0.4"
Expand Down
27 changes: 14 additions & 13 deletions packages/openapi-generator/src/generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { existsSync, promises } from 'fs';
import mock from 'mock-fs';
import { readJSON } from '@sap-cloud-sdk/util';
import prettier from 'prettier';
import { getInputFilePaths } from '@sap-cloud-sdk/generator-common/internal';
import { emptyDocument } from '../test/test-util';
import { generate, getInputFilePaths } from './generator';
import { generate } from './generator';

jest.mock('../../generator-common/internal', () => {
const actual = jest.requireActual('../../generator-common/internal');
Expand Down Expand Up @@ -47,14 +48,14 @@ describe('generator', () => {

const inputDir = 'root/inputDir';

it('should return an array with one file path for an input file', async () => {
expect(
await getInputFilePaths('root/inputDir/test-service.json')
).toEqual([resolve(inputDir, 'test-service.json')]);
it('should return an array with one file path for an input file', () => {
expect(getInputFilePaths('root/inputDir/test-service.json')).toEqual([
resolve(inputDir, 'test-service.json')
]);
});

it('should return an array with all JSON and YAML file paths within the input directory and all subdirectories', async () => {
expect(await getInputFilePaths(inputDir)).toEqual([
it('should return an array with all JSON and YAML file paths within the input directory and all subdirectories', () => {
expect(getInputFilePaths(inputDir)).toEqual([
resolve(inputDir, 'sub-dir/test-service.YAML'),
resolve(inputDir, 'sub-dir/test-service.yml'),
resolve(inputDir, 'sub-dir/test-service.YML'),
Expand All @@ -65,23 +66,23 @@ describe('generator', () => {
]);
});

it('should return an array with all `.json` files within the input directory and all subdirectories', async () => {
expect(await getInputFilePaths('root/inputDir/**/*.json')).toEqual([
it('should return an array with all `.json` files within the input directory and all subdirectories', () => {
expect(getInputFilePaths('root/inputDir/**/*.json')).toEqual([
resolve(inputDir, 'sub-dir/test-service2.json'),
resolve(inputDir, 'test-service.json')
]);
});

it('should return an array with all JSON and YAML file paths within the input directory', async () => {
expect(await getInputFilePaths('root/inputDir/*')).toEqual([
it('should return an array with all JSON and YAML file paths within the input directory', () => {
expect(getInputFilePaths('root/inputDir/*')).toEqual([
resolve(inputDir, 'test-service.json'),
resolve(inputDir, 'test-service.JSON'),
resolve(inputDir, 'test-service.yaml')
]);
});

it('should return an array with all `.json` and `.yaml` files within the input directory', async () => {
expect(await getInputFilePaths('root/inputDir/*.{json,yaml}')).toEqual([
it('should return an array with all `.json` and `.yaml` files within the input directory', () => {
expect(getInputFilePaths('root/inputDir/*.{json,yaml}')).toEqual([
resolve(inputDir, 'test-service.json'),
resolve(inputDir, 'test-service.yaml')
]);
Expand Down
42 changes: 4 additions & 38 deletions packages/openapi-generator/src/generator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { promises as promisesFs } from 'fs';
import { resolve, parse, basename, dirname, posix } from 'path';
import { resolve, parse, basename, dirname } from 'path';
import {
createLogger,
kebabCase,
Expand All @@ -20,7 +20,6 @@ import {
readPrettierConfig,
parseOptions
} from '@sap-cloud-sdk/generator-common/internal';
import { glob } from 'glob';
import { apiFile } from './file-serializer/api-file';
import { packageJson } from './file-serializer/package-json';
import { readme } from './file-serializer/readme';
Expand All @@ -44,7 +43,7 @@ import {
tsconfigJson
} from './options';

const { mkdir, lstat } = promisesFs;
const { mkdir } = promisesFs;
const logger = createLogger('openapi-generator');

/**
Expand Down Expand Up @@ -74,7 +73,7 @@ export async function generate(
export async function generateWithParsedOptions(
options: ParsedGeneratorOptions
): Promise<void> {
if (options.input === '' || options.outputDir === '') {
if (!options.input.length || options.outputDir === '') {
throw new Error('Either input or outputDir were not set.');
}
if (options.verbose) {
Expand All @@ -88,7 +87,7 @@ export async function generateWithParsedOptions(
typeof promisesFs.rm === 'undefined' ? {} : { force: true };
await rm(options.outputDir, { recursive: true, ...forceOption });
}
const inputFilePaths = await getInputFilePaths(options.input);
const inputFilePaths = options.input; // await getInputFilePaths(options.input);

const optionsPerService = await getOptionsPerService(inputFilePaths, options);
const tsConfig = await tsconfigJson(options);
Expand Down Expand Up @@ -285,39 +284,6 @@ async function generateService(
logger.info(`Successfully generated client for '${inputFilePath}'`);
}

/**
* Recursively searches through a given input path and returns all file paths as a string array.
* @param input - the path to the input directory.
* @returns all file paths as a string array.
* @internal
*/
export async function getInputFilePaths(input: string): Promise<string[]> {
if (glob.hasMagic(input)) {
return new Promise(resolvePromise => {
glob(input, (_error, paths) => {
resolvePromise(
paths
.filter(path => /(.json|.JSON|.yaml|.YAML|.yml|.YML)$/.test(path))
.map(path => resolve(path))
);
});
});
}

if ((await lstat(input)).isDirectory()) {
return new Promise(resolvePromise => {
glob(
posix.join(input, '**/*.{json,JSON,yaml,YAML,yml,YML}'),
(_error, paths) => {
resolvePromise(paths.map(path => resolve(path)));
}
);
});
}

return [resolve(input)];
}

async function generateReadme(
serviceDir: string,
openApiDocument: OpenApiDocument,
Expand Down
22 changes: 10 additions & 12 deletions packages/openapi-generator/src/options/generator-options.spec.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import { join, posix, resolve, sep } from 'path';
import { join, resolve } from 'path';
import mock from 'mock-fs';
import { parseOptions } from '@sap-cloud-sdk/generator-common/dist/options-parser';
import { parseOptions } from '@sap-cloud-sdk/generator-common/internal';
import { parseCmdArgs } from '../cli-parser';
import { cliOptions } from './options';

describe('parseGeneratorOptions', () => {
beforeEach(() => {
mock({
inputDir: {
'spec.json': ''
},
'existent-directory': {
'existent-file': 'file content'
}
Expand Down Expand Up @@ -43,7 +46,7 @@ describe('parseGeneratorOptions', () => {
outputDir: 'outputDir'
})
).toEqual({
input: join(process.cwd(), 'inputDir').split(sep).join(posix.sep),
input: [join(process.cwd(), 'inputDir', 'spec.json')],
outputDir: join(process.cwd(), 'outputDir'),
...optionsDefaultValues
});
Expand Down Expand Up @@ -162,20 +165,15 @@ describe('parseGeneratorOptions', () => {
).not.toThrowError();
});

it('parses a given path to a config file and returns its content as parsed generator options', async () => {
it('parses a given path to a config file and returns its content as parsed generator options', () => {
mock.restore();
const path = resolve(__dirname, '../../test/test-config.json');

const parsed = await parseOptions(
cliOptions,
parseCmdArgs(['--config', path])
);
expect(parsed.input).toContain('some-repository');
const parsed = parseOptions(cliOptions, parseCmdArgs(['--config', path]));
expect(parsed.input).toMatchObject([]);
expect(parsed.outputDir).toContain('some-output');
// RegEx to match paths for both *nix and Windows
expect(parsed.include).toMatchObject([
expect.stringMatching(join(resolve(), 'test-config.json'))
]);
expect(parsed.include).toMatchObject([path]);
});

it('fails if wrong configuration keys were used', async () => {
Expand Down
4 changes: 2 additions & 2 deletions packages/openapi-generator/src/options/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import {
ParsedOptions,
Options,
resolveRequiredPath,
resolveInputGlob,
getCommonCliOptions,
CommonGeneratorOptions
} from '@sap-cloud-sdk/generator-common/internal';
Expand Down Expand Up @@ -35,7 +35,7 @@ export const cliOptions = {
alias: 'i',
describe:
'Specify the path to the directory or file containing the OpenAPI service definition(s) to generate clients for. Accepts Swagger and OpenAPI definitions as YAML and JSON files. Throws an error if the path does not exist.',
coerce: resolveRequiredPath,
coerce: resolveInputGlob,
type: 'string',
demandOption: true,
requiresArg: true
Expand Down
2 changes: 1 addition & 1 deletion packages/openapi-generator/test/test-config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"input": "some-repository",
"input": "some-repository/*.json",
"outputDir": "some-output",
"include": "**/test-config.json"
}

0 comments on commit 3fbac9a

Please sign in to comment.