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

Add CDS sources using the CLI #32

Merged
merged 21 commits into from
Dec 23, 2019
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,11 @@ module.exports = {
'<rootDir>/test/express',
'<rootDir>/test/[^/]*/[^/]*-spec'
],
roots: ['test']
setupFilesAfterEnv: ['jest-extended'],
roots: ['test'],
globals: {
'ts-jest': {
diagnostics: false,
}
}
};
565 changes: 395 additions & 170 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@oclif/plugin-help": "^2.2.2",
"@oclif/plugin-not-found": "^1.2.3",
"@oclif/plugin-warn-if-update-available": "^1.7.0",
"boxen": "^4.2.0",
"cli-ux": "^5.3.3",
"execa": "^3.4.0",
"fast-glob": "^3.1.1",
Expand All @@ -29,6 +30,7 @@
"devDependencies": {
"@oclif/dev-cli": "^1.22.2",
"@oclif/tslint": "^3.1.1",
"@types/boxen": "^3.0.1",
"@types/fs-extra": "^8.0.1",
"@types/jest": "^24.0.23",
"@types/js-yaml": "^3.12.1",
Expand All @@ -38,6 +40,7 @@
"fs-extra": "^8.1.0",
"globby": "^10.0.1",
"jest": "^24.9.0",
"jest-extended": "^0.11.2",
florian-richter marked this conversation as resolved.
Show resolved Hide resolved
"np": "^5.2.1",
"prettier": "^1.19.1",
"ts-jest": "^24.2.0",
Expand Down Expand Up @@ -90,6 +93,7 @@
"fix:formatting": "tslint -p . -t stylish --fix && tslint -p test -t stylish --fix && prettier --write \"**/*.ts\"",
"prepack": "rm -rf lib && tsc -b && cp -R src/templates lib/ && oclif-dev manifest && oclif-dev readme",
"test": "jest",
"test:debug": "node --inspect-brk node_modules/.bin/jest --runInBand",
"test:watch-debug": "node --inspect-brk node_modules/.bin/jest --watch --runInBand",
"version": "oclif-dev readme && git add README.md"
},
Expand Down
42 changes: 17 additions & 25 deletions src/commands/add-approuter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import * as fs from 'fs';
import * as yaml from 'js-yaml';
import * as Listr from 'listr';
import { getProjectNameFromManifest } from '../utils/manifest-yaml';
import { copyFiles, findConflicts, getCopyDescriptors, getTemplatePaths } from '../utils/templates';

export default class AddApprouter extends Command {
Expand All @@ -15,26 +14,33 @@ export default class AddApprouter extends Command {
static examples = ['$ sap-cloud-sdk add-approuter'];

static flags = {
projectDir: flags.string({
hidden: true,
default: '',
description: 'Path to the folder in which the project should be created.'
}),
force: flags.boolean({
description: 'Do not fail if a file already exist and overwrite it.'
}),
help: flags.help({ char: 'h' })
help: flags.help({
char: 'h',
description: 'Show help for the add-approuter command.'
})
};

static args = [
{
name: 'projectDir',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is necessary. In init I added this as you may create a new directory. Here you need to have a dir already otherwise it doesn't make sense. That's why the --projectDir is currently hidden (mainly there for tests and to quickly enable it if we find out it's needed).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But is it an issue? I would argue either we have it or we don't. So either remove the flag or keep both for consistency (and make it non-hidden, I guess). What do you think?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My assumption was that a simpler (public) API makes it easier to understand the docs. So omitting a flag/arg that is not useful for 99% of users (and can be simply worked around by cd projectDir) would be a positive change.

But I have not validated that assumption, so feel free to change my mind on this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed all the --projectDir flags as discussed, but I decided to leave the argument, because I think it makes our commands consistent. I don't think projectDir makes the API hard to understand, it is still very simple, while the change is tiny.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To throw my 2cts in, I also have no idea whether we need it or not. So the question is how do find that out in the future? Otherwise we'll have the same discussion on the next PR.

description: 'Path to the project directory to which the approuter should be added.'
}
];

async run() {
const { flags } = this.parse(AddApprouter);
const { flags, args } = this.parse(AddApprouter);
const projectDir = args.projectDir || '.';

try {
const options = await this.getOptions();
const tasks = new Listr([
{
title: 'Creating files',
task: () => {
const copyDescriptors = getCopyDescriptors(flags.projectDir, getTemplatePaths(['add-approuter']));
const copyDescriptors = getCopyDescriptors(projectDir, getTemplatePaths(['add-approuter']));
findConflicts(copyDescriptors, flags.force);
copyFiles(copyDescriptors, options);
}
Expand All @@ -50,21 +56,7 @@ export default class AddApprouter extends Command {
}

private async getOptions() {
const warn = () => this.warn('Could not read name from `manifest.yml`. Please ensure you ran `sap-cloud-sdk init` before adding the approuter.');
let projectName: string | null = null;
try {
const manifestStr = fs.readFileSync('manifest.yml', { encoding: 'utf8' });
const manifest = yaml.safeLoad(manifestStr, {
filename: 'manifest.yml',
onWarning: warn
});
if (manifest['applications'].length > 1) {
this.warn('There were multiple apps in the `manifest.yml`, this command only considers the first app.');
}
projectName = manifest['applications'].map((app: any) => app.name)[0];
} catch (error) {
this.log(`Unable to read "manifest.yml" (${error.message}).`);
}
const projectName = getProjectNameFromManifest(this);

const options: { [key: string]: string } = {
projectName: projectName || (await cli.prompt('Enter project name as maintained in Cloud Foundry'))
Expand Down
112 changes: 112 additions & 0 deletions src/commands/add-cds.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*!
* Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.
*/

import { Command, flags } from '@oclif/command';
import cli from 'cli-ux';
import * as Listr from 'listr';
import { installDependencies, modifyGitIgnore, modifyPackageJson } from '../utils';
import { getProjectNameFromManifest } from '../utils/manifest-yaml';
import { copyFiles, findConflicts, getCopyDescriptors, getTemplatePaths } from '../utils/templates';

export default class AddCds extends Command {
static description = 'Setup your Cloud Foundry app to use a CDS service';
static aliases = ['add-cap'];
static examples = ['$ sap-cloud-sdk add-cds'];

static flags = {
// visible
force: flags.boolean({
description: 'Do not fail if a file or npm script already exist and overwrite it.'
}),
help: flags.help({
char: 'h',
description: 'Show help for the add-cds command.'
}),
verbose: flags.boolean({
char: 'v',
description: 'Show more detailed output.'
}),
// hidden
projectName: flags.string({
hidden: true,
description: 'Give project name which is used for the Cloud Foundry mainfest.yml.'
}),
skipInstall: flags.boolean({
hidden: true,
description: 'Skip installing npm dependencies. If you use this, make sure to install manually afterwards.'
})
};

static args = [
{
name: 'projectDir',
description: 'Path to the project directory in which the cds sources should be added.'
}
];

async run() {
const { flags, args } = this.parse(AddCds);
const projectDir = args.projectDir || '.';

try {
const options = await this.getOptions();
const tasks = new Listr([
{
title: 'Creating files',
task: () => {
const copyDescriptors = getCopyDescriptors(projectDir, getTemplatePaths(['add-cds']));
findConflicts(copyDescriptors, flags.force);
copyFiles(copyDescriptors, options);
}
},
{
title: 'Adding dependencies to package.json',
task: () => modifyPackageJson({ projectDir, force: flags.force, addCds: true })
},
{
title: 'Installing dependencies',
task: () => installDependencies(projectDir, flags.verbose).catch(e => this.error(`Error during npm install: ${e.message}`, { exit: 13 })),
enabled: () => !flags.skipInstall
},
{
title: 'Modifying `.gitignore`',
task: () => modifyGitIgnore(projectDir, true)
}
]);

await tasks.run();

this.printSuccessMessage();
} catch (error) {
this.error(error, { exit: 1 });
}
}

private async getOptions() {
const projectName = getProjectNameFromManifest(this);

const options: { [key: string]: string } = {
projectName: projectName || (await cli.prompt('Enter project name as maintained in Cloud Foundry'))
};

return options;
}

private printSuccessMessage() {
this.log(
[
'✅ Successfully added a cds service to your project.',
'',
'Generated service needs to be exposed.',
'For express apps you can do this by adding the following snippet to your code:',
'cds',
' .connect()',
" .serve('CatalogService')",
' .in(<your-express-app>)',
'',
'For other frameworks please refer to the documentation.'
].join('\n')
);
marikaner marked this conversation as resolved.
Show resolved Hide resolved
}
}
28 changes: 17 additions & 11 deletions src/commands/add-cx-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,6 @@ export default class AddCxServer extends Command {
static examples = ['$ sap-cloud-sdk add-cx-server'];

static flags = {
projectDir: flags.string({
hidden: true,
default: '',
description: 'Path to the folder in which the project should be created.'
}),
force: flags.boolean({
description: 'Do not fail if a file already exist and overwrite it.'
}),
Expand All @@ -28,17 +23,28 @@ export default class AddCxServer extends Command {
default: process.platform,
description: 'The currently running OS.'
}),
help: flags.help({ char: 'h' })
help: flags.help({
char: 'h',
description: 'Show help for the add-cx-server command.'
})
};

static args = [
{
name: 'projectDir',
description: 'Path to the project directory to which the cx-server should be added.'
}
];

async run() {
const { flags } = this.parse(AddCxServer);
const { flags, args } = this.parse(AddCxServer);
const projectDir = args.projectDir || '.';
const options = await this.getOptions();

try {
const files = [this.copyDescriptorForGithub('cx-server', flags), this.copyDescriptorForGithub('server.cfg', flags)];
const files = [this.copyDescriptorForGithub('cx-server', projectDir), this.copyDescriptorForGithub('server.cfg', projectDir)];
if (flags.platform === 'win32') {
files.push(this.copyDescriptorForGithub('cx-server.bat', flags));
files.push(this.copyDescriptorForGithub('cx-server.bat', projectDir));
}

const tasks = new Listr([
Expand All @@ -58,12 +64,12 @@ export default class AddCxServer extends Command {
}
}

private copyDescriptorForGithub(fileName: string, flags: Flags): CopyDescriptor {
private copyDescriptorForGithub(fileName: string, projectDir: string): CopyDescriptor {
const githubPrefix = 'https://raw.githubusercontent.com/SAP/devops-docker-cx-server/master/cx-server-companion/life-cycle-scripts/';

return {
sourcePath: new URL(fileName, githubPrefix),
fileName: path.resolve(flags.projectDir, 'cx-server', fileName)
fileName: path.resolve(projectDir, 'cx-server', fileName)
};
}

Expand Down
Loading