Skip to content
This repository has been archived by the owner on Jan 5, 2022. It is now read-only.

Commit

Permalink
Remove generator as dependency (#52)
Browse files Browse the repository at this point in the history
* Install generator only if needed

* Add E2E generator test

* Install generator correctly if necessary

* Fix test

* Increase timeout

* Add more output to clarify whats happening
  • Loading branch information
Florian Richter authored Jan 17, 2020
1 parent b8a8765 commit c2e7d7c
Show file tree
Hide file tree
Showing 9 changed files with 265 additions and 652 deletions.
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
"@oclif/plugin-help": "^2.2.3",
"@oclif/plugin-not-found": "^1.2.3",
"@oclif/plugin-warn-if-update-available": "^1.7.0",
"@sap/cloud-sdk-generator": ">=1.14.0",
"cli-ux": "^5.4.1",
"execa": "^3.4.0",
"fast-glob": "^3.1.1",
Expand Down
31 changes: 28 additions & 3 deletions src/commands/generate-odata-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
*/

import { Command } from '@oclif/command';
import { generate, generatorOptionsCli as generatorOptionsSDK } from '@sap/cloud-sdk-generator';
import { BoolArgType, generatorOptionCli, StringArgType, toBooleanFlag, toGeneratorSDK, toStringFlag } from '../utils/generate-odata-client-util';
import cli from 'cli-ux';
import * as execa from 'execa';
import { generatorOptionsSDK } from '../utils';
import { BoolArgType, generatorOptionCli, StringArgType, toBooleanFlag, toStringFlag } from '../utils/generate-odata-client-util';

export default class GenerateODataClient extends Command {
static description =
Expand Down Expand Up @@ -41,6 +43,29 @@ export default class GenerateODataClient extends Command {
async run() {
const { flags } = this.parse(GenerateODataClient);

await generate(toGeneratorSDK(flags));
const yargsFlags = Object.entries(flags)
.filter(([key, value]) => typeof value !== 'undefined' && generatorOptionsSDK.hasOwnProperty(key))
.map(([key, value]) => `--${key}=${value}`);

try {
await execa('npm', ['ls', '-g', '@sap/cloud-sdk-generator']);
} catch ({ exitCode }) {
if (exitCode === 1) {
this.log('');
this.log('To generate an OData client, it is necessary to install the @sap/cloud-sdk-generator.');
this.log('For now, the CLI expects the generator to be installed globally.');
this.log('');

if (await cli.confirm('Do you want to install the @sap/cloud-sdk-generator globally? (y|n)')) {
await execa('npm', ['install', '--global', '--@sap:registry=https://npm.sap.com', '@sap/cloud-sdk-generator']);
} else {
this.error('It is required to have the @sap/cloud-sdk-generator installed globally. Please install and rerun.', { exit: 1 });
}
}
}

await execa('generate-odata-client', yargsFlags, {
cwd: flags.projectDir || '.'
});
}
}
16 changes: 7 additions & 9 deletions src/utils/generate-odata-client-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@
import { flags } from '@oclif/command';
import { AlphabetLowercase, AlphabetUppercase } from '@oclif/parser/lib/alphabet';
import { IBooleanFlag, IOptionFlag } from '@oclif/parser/lib/flags';
import { GeneratorOptions as GeneratorOptionsSDK, generatorOptionsCli as generatorOptionsSDK } from '@sap/cloud-sdk-generator';
import * as path from 'path';
import { Options } from 'yargs';
import { GeneratorOptionsSDK } from './generator-options';

interface GeneratorOptionCli {
projectDir: Options;
projectDir: string;
}

export const generatorOptionCli: GeneratorOptionCli = {
type KeysToOptions = {
[optionName in keyof GeneratorOptionCli]: Options;
};

export const generatorOptionCli: KeysToOptions = {
projectDir: {
default: '.',
describe: 'Path to the folder in which the VDM should be created. The input and output dir are relative to this directory.',
Expand Down Expand Up @@ -42,12 +46,6 @@ export type FlagsParsed = {
[Key in keyof AllOptions]: AllOptions[Key] extends boolean ? boolean : string | undefined;
};

export function toGeneratorSDK(cliFlags: FlagsParsed): GeneratorOptionsSDK {
return Object.entries(cliFlags)
.filter(([key, value]) => typeof value !== 'undefined' && generatorOptionsSDK.hasOwnProperty(key))
.reduce((prev, [key, value]) => ({ ...prev, [key]: value }), {}) as GeneratorOptionsSDK;
}

export function toBooleanFlag(yargsBool: Options): IBooleanFlag<boolean> {
const extendedDescription = `${yargsBool.describe} [default: ${yargsBool.default}].`;
return flags.boolean({
Expand Down
144 changes: 144 additions & 0 deletions src/utils/generator-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*!
* Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
*/

import { PathLike } from 'fs';
import { resolve } from 'path';
import { Options } from 'yargs';

export interface GeneratorOptionsSDK {
inputDir: PathLike;
outputDir: PathLike;
serviceMapping?: PathLike;
useSwagger: boolean;
writeReadme: boolean;
changelogFile?: PathLike;
forceOverwrite: boolean;
clearOutputDir: boolean;
aggregatorNpmPackageName?: string;
aggregatorDirectoryName?: string;
generateNpmrc: boolean;
generateTypedocJson: boolean;
generatePackageJson: boolean;
generateJs: boolean;
sdkAfterVersionScript: boolean;
s4hanaCloud: boolean;
generateCSN: boolean;
}

type KeysToOptions = {
[optionName in keyof GeneratorOptionsSDK]: Options;
};

export const generatorOptionsSDK: KeysToOptions = {
inputDir: {
alias: 'i',
describe: 'This directory will be recursively searched for .edmx/.xml files.',
normalize: true,
coerce: resolve,
type: 'string',
demandOption: true,
requiresArg: true
},
outputDir: {
alias: 'o',
describe: 'Directory to save the generated code in.',
normalize: true,
coerce: resolve,
type: 'string',
demandOption: true,
requiresArg: true
},
serviceMapping: {
alias: 's',
describe:
'Configuration file to ensure consistent names between multiple generation runs with updated / changed metadata files. Will be generated if not existent. By default it will be saved to/read from the input directory as "service-mapping.json".',
type: 'string',
coerce: resolve,
normalize: true
},
useSwagger: {
describe:
'Augment parsed information with information from swagger definition files. Files are expected to have the same name as the edmx file, but with .json as suffix.',
type: 'boolean',
default: false,
hidden: true
},
writeReadme: {
describe:
'When set to true, the generator will write a README.md file into the root folder of every package. This option does not make that much sense without also set useSwagger to "true".',
type: 'boolean',
default: false,
hidden: true
},
changelogFile: {
describe: 'Path to file that will be copied into the generated packages under the filename CHANGELOG.md.',
type: 'string',
coerce: resolve,
normalize: true,
hidden: true
},
forceOverwrite: {
describe:
'By default, the generator will exit when encountering a file that already exists. When set to true, it will be overwritten instead. Please note that compared to the --clearOutputDir option, this will not delete outdated files.',
type: 'boolean',
default: false
},
clearOutputDir: {
describe: 'When set to true, the generator will delete EVERYTHING in the specified output directory before generating code.',
type: 'boolean',
default: false
},
aggregatorNpmPackageName: {
describe:
'When provided, the generator will generate an additional package with the provided name that has dependencies to all other generated packages.',
type: 'string',
hidden: true
},
aggregatorDirectoryName: {
describe: 'Hack for cloud-sdk-vdm package',
type: 'string',
hidden: true
},
generateNpmrc: {
describe:
'By default, the generator will generate a .npmrc file specifying a registry for @sap scoped dependencies. When set to false, the generator will skip the generation of .npmrc.',
type: 'boolean',
default: true
},
generateTypedocJson: {
describe:
'By default, the generator will generate a typedoc.json file for each package, used for the corresponding "doc" npm script. When set to false, the generator will skip the generation of the typedoc.json.',
type: 'boolean',
default: true
},
generatePackageJson: {
describe:
'By default, the generator will generate a package.json file, specifying dependencies and scripts for compiling and generating documentation. When set to false, the generator will skip the generation of the package.json.',
type: 'boolean',
default: true
},
generateJs: {
describe:
'By default, the generator will also generate transpiled .js, .js.map, .d.ts and .d.ts.map files. When set to false, the generator will only generate .ts files.',
type: 'boolean',
default: true
},
sdkAfterVersionScript: {
describe: 'When set to true, the package.json of generated services will have the after-version script to internally keep the versions in sync.',
type: 'boolean',
default: false,
hidden: true
},
s4hanaCloud: {
describe: 'When set to true, the description of the generated packages will be specific to S/4HANA Cloud.',
type: 'boolean',
default: false,
hidden: true
},
generateCSN: {
describe: 'When set to true a CSN file will be generated for each service definition in the output directory.',
type: 'boolean',
default: false
}
};
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

export * from './copy-list';
export * from './generate-odata-client-util';
export * from './generator-options';
export * from './git-ignore';
export * from './jest-config';
export * from './manifest-yaml';
Expand Down
2 changes: 1 addition & 1 deletion test/add-cds.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('Add CDS', () => {
expect(dbFiles).toContain('data-model.cds');
const srvFiles = fs.readdirSync(path.resolve(projectDir, 'srv'));
expect(srvFiles).toContain('cat-service.cds');
});
}, 15000);

it('should detect and fail if there are conflicts', async () => {
const projectDir = getCleanProjectDir(testOutputDir, 'add-cds-conflicts');
Expand Down
40 changes: 40 additions & 0 deletions test/generate-odata-client.e2e.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*!
* Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved.
*/
jest.mock('cli-ux', () => ({
default: {
confirm: jest.fn().mockResolvedValue(true)
}
}));

import * as fs from 'fs-extra';
import * as path from 'path';
import GenerateODataClient from '../src/commands/generate-odata-client';

describe('generate-odata-client', () => {
const pathForTests = path.resolve(__dirname, __filename.replace(/\./g, '-')).replace('-ts', '');

beforeAll(() => {
const pathForResources = path.resolve(__dirname, 'resources', 'template-generator-odata-client');
fs.copySync(pathForResources, pathForTests);
});

afterAll(() => {
fs.removeSync(pathForTests);
});

it('[E2E] should generate a OData client', async () => {
expect(
await GenerateODataClient.run([
'-i',
path.resolve(pathForTests, 'edmxSource'),
'-o',
path.resolve(pathForTests, 'output'),
'--projectDir',
pathForTests
])
).toResolve();

expect(fs.readdirSync(path.resolve(pathForTests, 'output'))).toHaveLength(1);
}, 120000);
});
Loading

0 comments on commit c2e7d7c

Please sign in to comment.