From 82c52f891fe5ae435d7cd4e467777ea73ce6b5e2 Mon Sep 17 00:00:00 2001 From: Niels Steenbeek Date: Fri, 17 Mar 2023 16:10:56 +0100 Subject: [PATCH 1/2] Command Line Options for Entity, Model, Webresource, New commands --- src/commands/Create.ts | 94 ++++++++++++++------------ src/commands/generators/Entity.ts | 9 +-- src/commands/generators/Generator.ts | 4 +- src/commands/generators/Model.ts | 17 +++-- src/commands/generators/Webresource.ts | 50 ++++++++------ src/commands/main.ts | 13 +++- src/routers/EntityRouter.ts | 7 +- src/routers/ModelRouter.ts | 16 +++-- 8 files changed, 123 insertions(+), 87 deletions(-) diff --git a/src/commands/Create.ts b/src/commands/Create.ts index e196c15..c9f0276 100644 --- a/src/commands/Create.ts +++ b/src/commands/Create.ts @@ -4,24 +4,25 @@ import * as inquirer from 'inquirer'; import * as fs from 'fs'; import {PCF} from './PCF'; -interface CreateAnswers { +interface CreateOptions { + environment?: string; publisher_name?: string; publisher_prefix?: string; - solution_name_deploy?: string; - solution_name_generate?: string; - solution_name_pcf?: string; - environment?: string; + solution_deploy?: string; + solution_generate?: string; + solution_pcf?: string; + namespace?: string; } export class Create { - public static createProject(projectname: string): Promise { + public static createProject(name: string, options: CreateOptions): Promise { if (process.argv[4]) { console.log(colors.red(`No spaces allowed!`)); - } else if (shell.test('-e', `${projectname}/Webresources`)) { - console.log(colors.red(`Project ${projectname}/Webresources already exist!`)); + } else if (shell.test('-e', `${name}/Webresources`)) { + console.log(colors.red(`Project ${name}/Webresources already exist!`)); } else { - return Create.create(projectname); + return Create.create(name, options); } } @@ -31,38 +32,43 @@ export class Create { console.log(` The project name of the new workspace and initial Webresource setup.`); } - private static async create(projectname: string): Promise { - const answers = await Create.inquirer(); - console.log(`Initializing D365 Project ${projectname}...`); - if (!shell.test('-e', `${projectname}`)) { - shell.mkdir(projectname); + private static async create(name: string, options: CreateOptions): Promise { + let createOptions: CreateOptions = options; + if (!options.environment || !options.solution_deploy || !options.publisher_name || !options.publisher_prefix || !options.namespace) { + console.log(colors.red(`Missing some required options (environment, solution_deploy, publisher_name, + publisher_prefix, namespace. Command line will request them now.`)); + createOptions = await Create.inquirer(); + } + console.log(`Initializing D365 Project ${name}...`); + if (!shell.test('-e', `${name}`)) { + shell.mkdir(name); } - shell.cd(projectname); + shell.cd(name); shell.cp('-R', `${__dirname}/root/crm.json`, '.'); - Create.setupWebresources(projectname, answers); - Create.setupPCF(projectname, answers); + Create.setupWebresources(name, createOptions); + Create.setupPCF(name, createOptions); } - private static setupPCF(projectname: string, answers: CreateAnswers): void { + private static setupPCF(name: string, options: CreateOptions): void { shell.mkdir('PCF'); shell.cd('PCF'); - const {publisher_name, publisher_prefix, solution_name_pcf} = answers; - PCF.initPcfSolution(solution_name_pcf, publisher_name, publisher_prefix); + const {publisher_name, publisher_prefix, solution_pcf} = options; + PCF.initPcfSolution(solution_pcf, publisher_name, publisher_prefix); shell.cd('..'); - console.log(colors.green(`Please fill ${projectname}/PCF/Solutions/src/Other/Solution.xml`)); + console.log(colors.green(`Please fill ${name}/PCF/Solutions/src/Other/Solution.xml`)); } - private static setupWebresources(projectname: string, answers: CreateAnswers): void { + private static setupWebresources(name: string, options: CreateOptions): void { shell.mkdir('Webresources'); shell.cp('-R', `${__dirname}/root/Webresources/*`, 'Webresources'); shell.cp('-R', `${__dirname}/root/Webresources/.*`, 'Webresources'); fs.renameSync(`./Webresources/gitignore`, './Webresources/.gitignore'); - Create.initCrmJson(answers); - Create.initWebresourcesCrmJson(answers); - Create.initWebresourcesPackageJson(projectname, answers); - Create.initWebresourcesWebpackConfig(answers); + Create.initCrmJson(options); + Create.initWebresourcesCrmJson(options); + Create.initWebresourcesPackageJson(name, options); + Create.initWebresourcesWebpackConfig(options); shell.cd('Webresources'); console.log(`Installing npm packages. This may take a while...`); @@ -74,36 +80,36 @@ export class Create { shell.cd('..'); } - private static initCrmJson(answers: CreateAnswers): void { + private static initCrmJson(options: CreateOptions): void { const crmJsonFile = shell.ls('./crm.json')[0]; - shell.sed('-i', new RegExp('<%= publisher_prefix %>', 'ig'), answers.publisher_prefix, crmJsonFile); - shell.sed('-i', new RegExp('<%= environment %>', 'ig'), answers.environment, crmJsonFile); - shell.sed('-i', new RegExp('<%= namespace %>', 'ig'), answers.namespace, crmJsonFile); + shell.sed('-i', new RegExp('<%= publisher_prefix %>', 'ig'), options.publisher_prefix, crmJsonFile); + shell.sed('-i', new RegExp('<%= environment %>', 'ig'), options.environment, crmJsonFile); + shell.sed('-i', new RegExp('<%= namespace %>', 'ig'), options.namespace, crmJsonFile); const version = shell.exec('hso-d365 --version').stdout.replace(/\n/ig, ''); shell.sed('-i', new RegExp('<%= version %>', 'ig'), version, crmJsonFile); } - private static initWebresourcesCrmJson(answers: CreateAnswers): void { + private static initWebresourcesCrmJson(options: CreateOptions): void { const crmJsonFile = shell.ls('Webresources/crm.json')[0]; - shell.sed('-i', new RegExp('<%= solution_name_deploy %>', 'ig'), answers.solution_name_deploy, crmJsonFile); - shell.sed('-i', new RegExp('<%= solution_name_generate %>', 'ig'), answers.solution_name_generate || answers.solution_name_deploy, crmJsonFile); + shell.sed('-i', new RegExp('<%= solution_name_deploy %>', 'ig'), options.solution_deploy, crmJsonFile); + shell.sed('-i', new RegExp('<%= solution_name_generate %>', 'ig'), options.solution_generate || options.solution_deploy, crmJsonFile); } - private static initWebresourcesPackageJson(projectName: string, answers: CreateAnswers): void { + private static initWebresourcesPackageJson(name: string, options: CreateOptions): void { const packageJsonFile = shell.ls('Webresources/package.json')[0]; - shell.sed('-i', '<%= projectname %>', projectName.toLowerCase(), packageJsonFile); - shell.sed('-i', new RegExp('<%= description %>', 'ig'), answers.solution_name_deploy, packageJsonFile); + shell.sed('-i', '<%= projectname %>', name.toLowerCase(), packageJsonFile); + shell.sed('-i', new RegExp('<%= description %>', 'ig'), options.solution_deploy, packageJsonFile); } - private static initWebresourcesWebpackConfig(answers: CreateAnswers): void { + private static initWebresourcesWebpackConfig(options: CreateOptions): void { const webpackConfigFile = shell.ls('Webresources/webpack.config.ts')[0]; - shell.sed('-i', new RegExp('<%= publisher_prefix %>', 'ig'), answers.publisher_prefix, webpackConfigFile); - shell.sed('-i', new RegExp('<%= namespace %>', 'ig'), answers.namespace, webpackConfigFile); - shell.sed('-i', new RegExp('<%= description %>', 'ig'), answers.namespace, webpackConfigFile); + shell.sed('-i', new RegExp('<%= publisher_prefix %>', 'ig'), options.publisher_prefix, webpackConfigFile); + shell.sed('-i', new RegExp('<%= namespace %>', 'ig'), options.namespace, webpackConfigFile); + shell.sed('-i', new RegExp('<%= description %>', 'ig'), options.namespace, webpackConfigFile); } // eslint-disable-next-line max-lines-per-function - private static inquirer(): Promise { + private static inquirer(): Promise { return inquirer.prompt([{ type: 'input', name: 'environment', @@ -120,7 +126,7 @@ export class Create { } }, { type: 'input', - name: 'solution_name_deploy', + name: 'solution_deploy', message: `D365 deployment Solution ('Name' column):`, validate: async (input) => { if (!input) { @@ -134,7 +140,7 @@ export class Create { } }, { type: 'input', - name: 'solution_name_generate', + name: 'solution_generate', message: `D365 generate Solution ('Name' column)\nIf equal to deployment Solution keep blank:`, validate: async (input) => { if (input) { @@ -147,7 +153,7 @@ export class Create { } }, { type: 'input', - name: 'solution_name_pcf', + name: 'solution_pcf', message: `D365 PCF Solution ('Name' column)\nIf equal to deployment Solution keep blank:`, validate: async (input) => { if (input) { diff --git a/src/commands/generators/Entity.ts b/src/commands/generators/Entity.ts index 8d33cf5..1df91ec 100644 --- a/src/commands/generators/Entity.ts +++ b/src/commands/generators/Entity.ts @@ -9,16 +9,13 @@ import {Enum} from './Enum'; import {AttributeFormContext} from './AttributeFormContext'; import {Form} from './Form'; import {AttributeTypings} from './AttributeTypings'; - -interface EntityOptions { - skipForms?: boolean; -} +import {EntityOptions} from '../../routers/EntityRouter'; export class Entity { private readonly bearer: string; private readonly entityName: string; - private readonly options: EntityOptions; private entityLogicalName: string; + private readonly options: EntityOptions; constructor(bearer: string, entityName: string, entityLogicalName: string, options: EntityOptions) { this.bearer = bearer; @@ -32,7 +29,7 @@ export class Entity { await this.generateEntityFiles(); console.log(`Generating files for Entity '${this.entityName}'`); console.log(`Using entityLogicalName '${this.entityLogicalName}'`); - const model = new Model(this.bearer, this.entityName); + const model = new Model(this.bearer, this.entityName, this.entityLogicalName); await model.generate(); await Enum.generateEnum(this.bearer, this.entityName, this.entityLogicalName); if (!this.options.skipForms) { diff --git a/src/commands/generators/Generator.ts b/src/commands/generators/Generator.ts index d34db3f..4e68833 100644 --- a/src/commands/generators/Generator.ts +++ b/src/commands/generators/Generator.ts @@ -20,9 +20,9 @@ export class Generator { } else if (schematic.toLowerCase() === 'entity') { return EntityRouter.generateEntity(name, options); } else if (schematic.toLowerCase() === 'webresource') { - return Webresource.generateWebresource(name); + return Webresource.generateWebresource(name, options); } else if (schematic.toLocaleLowerCase() === 'model') { - return ModelRouter.generateModel(name); + return ModelRouter.generateModel(name, options); } else if (schematic.toLowerCase() === 'licensevalidator') { return LicenseValidator.generateLicenseValidator(name); } else if (schematic.toLowerCase() === 'environmentvariable') { diff --git a/src/commands/generators/Model.ts b/src/commands/generators/Model.ts index d09e585..e128f8b 100644 --- a/src/commands/generators/Model.ts +++ b/src/commands/generators/Model.ts @@ -15,9 +15,10 @@ export class Model { private entityLogicalName: string; private attributesMetadata: AttributeMetadata[]; - constructor(bearer: string, entityName: string) { + constructor(bearer: string, entityName: string, entityLogicalName: string) { this.bearer = bearer; this.entityName = entityName; + this.entityLogicalName = entityLogicalName; } public async generate(): Promise { @@ -27,12 +28,14 @@ export class Model { private async generateModelFile(): Promise { const folderPath = `src/${this.entityName}`; if (!shell.test('-d', folderPath)) { - const answers = await inquirer.prompt([{ - type: 'input', - name: 'entityLogicalName', - message: 'Entity LogicalName:' - }]); - this.entityLogicalName = answers.entityLogicalName; + if (!this.entityLogicalName) { + const answers = await inquirer.prompt([{ + type: 'input', + name: 'entityLogicalName', + message: 'Entity LogicalName:' + }]); + this.entityLogicalName = answers.entityLogicalName; + } try { await NodeApi.getEntityDefinition(this.entityLogicalName, this.bearer, ['PrimaryIdAttribute']); } catch (e) { diff --git a/src/commands/generators/Webresource.ts b/src/commands/generators/Webresource.ts index 06daa73..014a37a 100644 --- a/src/commands/generators/Webresource.ts +++ b/src/commands/generators/Webresource.ts @@ -5,12 +5,17 @@ import * as fs from 'fs'; import cp from 'child_process'; import {CrmJson} from '../../root/CrmJson'; -interface Answers { - template: 'React' | 'HTML'; +type Template = 'React' | 'HTML'; +interface Answer { + template: Template; +} + +export interface WebresourceOptions { + template?: Template; } export class Webresource { - public static generateWebresource(webresourcename: string): Promise { + public static generateWebresource(webresourcename: string, options: WebresourceOptions): Promise { const check = shell.grep(` ${webresourcename}:`, 'webpack.config.ts'); if (!webresourcename) { console.log(colors.red('Webresource name missing')); @@ -20,35 +25,40 @@ export class Webresource { console.log(colors.red(`echo Webresource ${webresourcename} already exist!`)); } else if (process.argv[5]) { console.log(colors.red(`echo No spaces allowed!`)); + } else if (options.template && !['React', 'HTML'].includes(options.template)) { + console.log(colors.red(`echo Webresource template must be 'React' or 'HTML'`)); } else { - return Webresource.generate(webresourcename); + return Webresource.generate(webresourcename, options); } } - private static async generate(webresourcename: string): Promise { + private static async generate(webresourcename: string, options: WebresourceOptions): Promise { const folderPath = `src/${webresourcename}`; if (!shell.test('-d', folderPath)) { console.log(`Adding Webresource ${webresourcename}...`); - const answers = await inquirer.prompt([{ - type: 'list', - name: 'template', - message: 'Which template do you want?', - choices: [ - 'HTML', - 'React' - ] - }]); + let template = options.template; + if (!template) { + const answer = await inquirer.prompt([{ + type: 'list', + name: 'template', + message: 'Which template do you want?', + choices: [ + 'HTML', + 'React' + ] + }]); + template = (answer as Answer).template; + } shell.mkdir(folderPath); - await Webresource.addWebresourceFiles(webresourcename, answers); - await Webresource.addBuildFile(webresourcename, answers); + await Webresource.addWebresourceFiles(webresourcename, template); + await Webresource.addBuildFile(webresourcename, template); console.log(`Added Webresource ${webresourcename}`); } else { console.log(colors.magenta(`Webresource ${webresourcename} already exist`)); } } - private static async addWebresourceFiles(webresourcename: string, answers: Answers): Promise { - const template = answers.template; + private static async addWebresourceFiles(webresourcename: string, template: Template): Promise { const settings: CrmJson = JSON.parse(fs.readFileSync('../crm.json', 'utf8')); const {namespace, publisher_prefix} = settings.crm; const srcDir = `${__dirname}/Webresource${template === 'React' ? 'Tsx' : ''}/*.*`; @@ -70,7 +80,7 @@ export class Webresource { }); } - private static async addBuildFile(webresourcename: string, answers: Answers) : Promise { + private static async addBuildFile(webresourcename: string, template: Template) : Promise { console.log(`Adding ${webresourcename}/build.json`); const filepath = `src/${webresourcename}/build.json`; shell.cp('-r', `${__dirname}/Entity/build.json`, filepath); @@ -80,7 +90,7 @@ export class Webresource { buildJson.webresources.push({ name: webresourcename, build: true, - template: answers.template + template: template }); shell.ShellString(JSON.stringify(buildJson, null, 2)).to(filepath); } diff --git a/src/commands/main.ts b/src/commands/main.ts index 751f354..17237cc 100644 --- a/src/commands/main.ts +++ b/src/commands/main.ts @@ -64,12 +64,19 @@ program program .command('new ') .alias('n') + .option('--environment', 'Environment') + .option('--solution_deploy', 'Deploy Solution') + .option('--solution_generate', 'Generate Solution') + .option('--solution_pcf', 'PCF Solution') + .option('--publisher_name', 'Publisher name') + .option('--publisher_prefix', 'Publisher prefix') + .option('--namespace', 'Namespace') .description('Creates a new workspace and an initial Webresource and PCF setup or creates a new PCF component') - .action((name: string) => { + .action((name: string, options) => { if (shell.test('-e', '../pcf')) { PCF.createComponent(name); } else { - Create.createProject(name); + Create.createProject(name, options); } }) .on('--help', () => { @@ -93,6 +100,8 @@ program .command('generate [name]') .alias('g') .option('-s, --skipForms', 'Skip generating form files') + .option('-logicalName, --entityLogicalName ', 'LogicalName of the Entity') + .option('-t, --template