Skip to content

Commit

Permalink
Merge branch 'release/7.0.4'
Browse files Browse the repository at this point in the history
  • Loading branch information
nsteenbeek committed Mar 17, 2023
2 parents 7967d25 + 8227fd9 commit aabd528
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 89 deletions.
2 changes: 1 addition & 1 deletion bin/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hso/d365-cli",
"version": "7.0.3",
"version": "7.0.4",
"author": "HSO Innovation <[email protected]> (https://www.hso.com)",
"description": "Dynamics 365 Command Line Interface for TypeScript projects for Dataverse",
"repository": {
Expand Down
94 changes: 50 additions & 44 deletions src/commands/Create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
public static createProject(name: string, options: CreateOptions): Promise<void> {
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);
}
}

Expand All @@ -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<void> {
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<void> {
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...`);
Expand All @@ -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<CreateAnswers> {
private static inquirer(): Promise<CreateOptions> {
return inquirer.prompt([{
type: 'input',
name: 'environment',
Expand All @@ -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) {
Expand All @@ -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) {
Expand All @@ -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) {
Expand Down
9 changes: 3 additions & 6 deletions src/commands/generators/Entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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) {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/generators/Generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') {
Expand Down
17 changes: 10 additions & 7 deletions src/commands/generators/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
Expand All @@ -27,12 +28,14 @@ export class Model {
private async generateModelFile(): Promise<void> {
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) {
Expand Down
50 changes: 30 additions & 20 deletions src/commands/generators/Webresource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> {
public static generateWebresource(webresourcename: string, options: WebresourceOptions): Promise<void> {
const check = shell.grep(` ${webresourcename}:`, 'webpack.config.ts');
if (!webresourcename) {
console.log(colors.red('Webresource name missing'));
Expand All @@ -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<void> {
private static async generate(webresourcename: string, options: WebresourceOptions): Promise<void> {
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<void> {
const template = answers.template;
private static async addWebresourceFiles(webresourcename: string, template: Template): Promise<void> {
const settings: CrmJson = JSON.parse(fs.readFileSync('../crm.json', 'utf8'));
const {namespace, publisher_prefix} = settings.crm;
const srcDir = `${__dirname}/Webresource${template === 'React' ? 'Tsx' : ''}/*.*`;
Expand All @@ -70,7 +80,7 @@ export class Webresource {
});
}

private static async addBuildFile(webresourcename: string, answers: Answers) : Promise<void> {
private static async addBuildFile(webresourcename: string, template: Template) : Promise<void> {
console.log(`Adding ${webresourcename}/build.json`);
const filepath = `src/${webresourcename}/build.json`;
shell.cp('-r', `${__dirname}/Entity/build.json`, filepath);
Expand All @@ -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);
}
Expand Down
13 changes: 11 additions & 2 deletions src/commands/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,19 @@ program
program
.command('new <name>')
.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', () => {
Expand All @@ -93,6 +100,8 @@ program
.command('generate <schematic> [name]')
.alias('g')
.option('-s, --skipForms', 'Skip generating form files')
.option('-logicalName, --entityLogicalName <entityLogicalName>', 'LogicalName of the Entity')
.option('-t, --template <template>', 'Template of Webresource')
.description('Generates and/or modifies files bases on a schematic.')
.action((schematic: string, name: string, options) => {
if (checkVersion()) {
Expand Down
Loading

0 comments on commit aabd528

Please sign in to comment.