generator-luna
Version:
generate specific EHR application,it's built beyond the jhipster
1,134 lines (1,101 loc) • 43.1 kB
JavaScript
/**
* Copyright 2013-2019 the original author or authors from the JHipster project.
*
* This file is part of the JHipster project, see https://www.jhipster.tech/
* for more information.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const chalk = require('chalk');
const path = require('path');
const _ = require('lodash');
const jhiCore = require('jhipster-core');
const shelljs = require('shelljs');
module.exports = {
askForMicroserviceJson,
askForUpdate,
askForFields,
askForFieldsToRemove,
askForRelationships,
askForRelationsToRemove,
askForTableName,
askForDTO,
askForService,
askForFiltering,
askForPagination
};
function askForMicroserviceJson() {
const context = this.context;
if (context.applicationType !== 'gateway' || context.useConfigurationFile) {
return;
}
const done = this.async();
const databaseType = context.databaseType;
const prompts = [
{
when: () => databaseType !== 'no',
type: 'confirm',
name: 'useMicroserviceJson',
message: 'Do you want to generate this entity from an existing microservice?',
default: true
},
{
when: response => response.useMicroserviceJson === true || databaseType === 'no',
type: 'input',
name: 'microservicePath',
message: 'Enter the path to the microservice root directory:',
store: true,
validate: input => {
let fromPath = '';
if (path.isAbsolute(input)) {
fromPath = `${input}/${context.filename}`;
} else {
fromPath = this.destinationPath(`${input}/${context.filename}`);
}
if (shelljs.test('-f', fromPath)) {
return true;
}
return `${context.filename} not found in ${input}/`;
}
}
];
this.prompt(prompts).then(props => {
if (props.microservicePath) {
this.log(chalk.green(`\nFound the ${context.filename} configuration file, entity can be automatically generated!\n`));
if (path.isAbsolute(props.microservicePath)) {
context.microservicePath = props.microservicePath;
} else {
context.microservicePath = path.resolve(props.microservicePath);
}
context.useConfigurationFile = true;
context.useMicroserviceJson = true;
const fromPath = `${context.microservicePath}/${context.jhipsterConfigDirectory}/${context.entityNameCapitalized}.json`;
this.loadEntityJson(fromPath);
}
done();
});
}
function askForUpdate() {
const context = this.context;
// ask only if running an existing entity without arg option --force or --regenerate
const isForce = context.options.force || context.regenerate;
context.updateEntity = 'regenerate'; // default if skipping questions by --force
if (isForce || !context.useConfigurationFile) {
return;
}
const done = this.async();
const prompts = [
{
type: 'list',
name: 'updateEntity',
message:
'Do you want to update the entity? This will replace the existing files for this entity, all your custom code will be overwritten',
choices: [
{
value: 'regenerate',
name: 'Yes, re generate the entity'
},
{
value: 'add',
name: 'Yes, add more fields and relationships'
},
{
value: 'remove',
name: 'Yes, remove fields and relationships'
},
{
value: 'none',
name: 'No, exit'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
context.updateEntity = props.updateEntity;
if (context.updateEntity === 'none') {
this.env.error(chalk.green('Aborting entity update, no changes were made.'));
}
done();
});
}
function askForFields() {
const context = this.context;
// don't prompt if data is imported from a file
if (context.useConfigurationFile && context.updateEntity !== 'add') {
return;
}
if (context.updateEntity === 'add') {
logFieldsAndRelationships.call(this);
}
const done = this.async();
askForField.call(this, done);
}
function askForFieldsToRemove() {
const context = this.context;
// prompt only if data is imported from a file
if (!context.useConfigurationFile || context.updateEntity !== 'remove' || context.fieldNameChoices.length === 0) {
return;
}
const done = this.async();
const prompts = [
{
type: 'checkbox',
name: 'fieldsToRemove',
message: 'Please choose the fields you want to remove',
choices: context.fieldNameChoices
},
{
when: response => response.fieldsToRemove.length !== 0,
type: 'confirm',
name: 'confirmRemove',
message: 'Are you sure to remove these fields?',
default: true
}
];
this.prompt(prompts).then(props => {
if (props.confirmRemove) {
this.log(chalk.red(`\nRemoving fields: ${props.fieldsToRemove}\n`));
for (let i = context.fields.length - 1; i >= 0; i -= 1) {
const field = context.fields[i];
if (props.fieldsToRemove.filter(val => val === field.fieldName).length > 0) {
context.fields.splice(i, 1);
}
}
}
done();
});
}
function askForRelationships() {
const context = this.context;
// don't prompt if data is imported from a file
if (context.useConfigurationFile && context.updateEntity !== 'add') {
return;
}
if (['cassandra', 'couchbase'].includes(context.databaseType)) {
return;
}
const done = this.async();
askForRelationship.call(this, done);
}
function askForRelationsToRemove() {
const context = this.context;
// prompt only if data is imported from a file
if (!context.useConfigurationFile || context.updateEntity !== 'remove' || context.relNameChoices.length === 0) {
return;
}
if (['cassandra', 'couchbase'].includes(context.databaseType)) {
return;
}
const done = this.async();
const prompts = [
{
type: 'checkbox',
name: 'relsToRemove',
message: 'Please choose the relationships you want to remove',
choices: context.relNameChoices
},
{
when: response => response.relsToRemove.length !== 0,
type: 'confirm',
name: 'confirmRemove',
message: 'Are you sure to remove these relationships?',
default: true
}
];
this.prompt(prompts).then(props => {
if (props.confirmRemove) {
this.log(chalk.red(`\nRemoving relationships: ${props.relsToRemove}\n`));
for (let i = context.relationships.length - 1; i >= 0; i -= 1) {
const rel = context.relationships[i];
if (props.relsToRemove.filter(val => val === `${rel.relationshipName}:${rel.relationshipType}`).length > 0) {
context.relationships.splice(i, 1);
}
}
}
done();
});
}
function askForTableName() {
const context = this.context;
// don't prompt if there are no relationships
const entityTableName = context.entityTableName;
const prodDatabaseType = context.prodDatabaseType;
const skipCheckLengthOfIdentifier = context.skipCheckLengthOfIdentifier;
if (
skipCheckLengthOfIdentifier ||
!context.relationships ||
context.relationships.length === 0 ||
!((prodDatabaseType === 'oracle' && entityTableName.length > 14) || entityTableName.length > 30)
) {
return;
}
const done = this.async();
const prompts = [
{
type: 'input',
name: 'entityTableName',
message: 'The table name for this entity is too long to form constraint names. Please use a shorter table name',
validate: input => {
if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'The table name cannot contain special characters';
}
if (input === '') {
return 'The table name cannot be empty';
}
if (prodDatabaseType === 'oracle' && input.length > 14 && !skipCheckLengthOfIdentifier) {
return 'The table name is too long for Oracle, try a shorter name';
}
if (input.length > 30 && !skipCheckLengthOfIdentifier) {
return 'The table name is too long, try a shorter name';
}
return true;
},
default: entityTableName
}
];
this.prompt(prompts).then(props => {
/* overwrite the table name for the entity using name obtained from the user */
if (props.entityTableName !== context.entityTableName) {
context.entityTableName = _.snakeCase(props.entityTableName).toLowerCase();
}
done();
});
}
function askForFiltering() {
const context = this.context;
// don't prompt if server is skipped, or the backend is not sql, or no service requested
if (context.useConfigurationFile || context.skipServer || context.databaseType !== 'sql' || context.service === 'no') {
return;
}
const done = this.async();
const prompts = [
{
type: 'list',
name: 'filtering',
message: 'Do you want to add filtering?',
choices: [
{
value: 'no',
name: 'Not needed'
},
{
name: 'Dynamic filtering for the entities with JPA Static metamodel',
value: 'jpaMetamodel'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
context.jpaMetamodelFiltering = props.filtering === 'jpaMetamodel';
done();
});
}
function askForDTO() {
const context = this.context;
// don't prompt if data is imported from a file or server is skipped or if no service layer
if (context.useConfigurationFile || context.skipServer || context.service === 'no') {
context.dto = context.dto || 'no';
return;
}
const done = this.async();
const prompts = [
{
type: 'list',
name: 'dto',
message: 'Do you want to use a Data Transfer Object (DTO)?',
choices: [
{
value: 'no',
name: 'No, use the entity directly'
},
{
value: 'mapstruct',
name: 'Yes, generate a DTO with MapStruct'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
context.dto = props.dto;
done();
});
}
function askForService() {
const context = this.context;
// don't prompt if data is imported from a file or server is skipped
if (context.useConfigurationFile || context.skipServer) {
return;
}
const done = this.async();
const prompts = [
{
type: 'list',
name: 'service',
message: 'Do you want to use separate service class for your business logic?',
choices: [
{
value: 'no',
name: 'No, the REST controller should use the repository directly'
},
{
value: 'serviceClass',
name: 'Yes, generate a separate service class'
},
{
value: 'serviceImpl',
name: 'Yes, generate a separate service interface and implementation'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
context.service = props.service;
done();
});
}
function askForPagination() {
const context = this.context;
// don't prompt if data are imported from a file
if (context.useConfigurationFile) {
return;
}
if (context.databaseType === 'cassandra') {
return;
}
const done = this.async();
const prompts = [
{
type: 'list',
name: 'pagination',
message: 'Do you want pagination on your entity?',
choices: [
{
value: 'no',
name: 'No'
},
{
value: 'pagination',
name: 'Yes, with pagination links'
},
{
value: 'infinite-scroll',
name: 'Yes, with infinite scroll'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
context.pagination = props.pagination;
this.log(chalk.green('\nEverything is configured, generating the entity...\n'));
done();
});
}
/**
* ask question for a field creation
*/
function askForField(done) {
const context = this.context;
this.log(chalk.green(`\nGenerating field #${context.fields.length + 1}\n`));
const skipServer = context.skipServer;
const prodDatabaseType = context.prodDatabaseType;
const databaseType = context.databaseType;
const clientFramework = context.clientFramework;
const fieldNamesUnderscored = context.fieldNamesUnderscored;
const skipCheckLengthOfIdentifier = context.skipCheckLengthOfIdentifier;
const prompts = [
{
type: 'confirm',
name: 'fieldAdd',
message: 'Do you want to add a field to your entity?',
default: true
},
{
when: response => response.fieldAdd === true,
type: 'input',
name: 'fieldName',
validate: input => {
if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'Your field name cannot contain special characters';
}
if (input === '') {
return 'Your field name cannot be empty';
}
if (input.charAt(0) === input.charAt(0).toUpperCase()) {
return 'Your field name cannot start with an upper case letter';
}
if (input === 'id' || fieldNamesUnderscored.includes(_.snakeCase(input))) {
return 'Your field name cannot use an already existing field name';
}
if ((clientFramework === undefined || clientFramework === 'angularX') && jhiCore.isReservedFieldName(input, 'angularX')) {
return 'Your field name cannot contain a Java or Angular reserved keyword';
}
if ((clientFramework !== undefined || clientFramework === 'react') && jhiCore.isReservedFieldName(input, 'react')) {
return 'Your field name cannot contain a Java or React reserved keyword';
}
if (prodDatabaseType === 'oracle' && input.length > 30 && !skipCheckLengthOfIdentifier) {
return 'The field name cannot be of more than 30 characters';
}
return true;
},
message: 'What is the name of your field?'
},
{
when: response => response.fieldAdd === true && (skipServer || ['sql', 'mongodb', 'couchbase'].includes(databaseType)),
type: 'list',
name: 'fieldType',
message: 'What is the type of your field?',
choices: [
{
value: 'String',
name: 'String'
},
{
value: 'Integer',
name: 'Integer'
},
{
value: 'Long',
name: 'Long'
},
{
value: 'Float',
name: 'Float'
},
{
value: 'Double',
name: 'Double'
},
{
value: 'BigDecimal',
name: 'BigDecimal'
},
{
value: 'LocalDate',
name: 'LocalDate'
},
{
value: 'Instant',
name: 'Instant'
},
{
value: 'ZonedDateTime',
name: 'ZonedDateTime'
},
{
value: 'Boolean',
name: 'Boolean'
},
{
value: 'enum',
name: 'Enumeration (Java enum type)'
},
{
value: 'byte[]',
name: '[BETA] Blob'
}
],
default: 0
},
{
when: response => {
if (response.fieldType === 'enum') {
response.fieldIsEnum = true;
return true;
}
response.fieldIsEnum = false;
return false;
},
type: 'input',
name: 'fieldType',
validate: input => {
if (input === '') {
return 'Your class name cannot be empty.';
}
if (jhiCore.isReservedKeyword(input, 'JAVA')) {
return 'Your enum name cannot contain a Java reserved keyword';
}
if (!/^[A-Za-z0-9_]*$/.test(input)) {
return 'Your enum name cannot contain special characters (allowed characters: A-Z, a-z, 0-9 and _)';
}
if (context.enums.includes(input)) {
context.existingEnum = true;
} else {
context.enums.push(input);
}
return true;
},
message: 'What is the class name of your enumeration?'
},
{
when: response => response.fieldIsEnum,
type: 'input',
name: 'fieldValues',
validate: input => {
if (input === '' && context.existingEnum) {
context.existingEnum = false;
return true;
}
if (input === '') {
return 'You must specify values for your enumeration';
}
// Commas allowed so that user can input a list of values split by commas.
if (!/^[A-Za-z0-9_,]+$/.test(input)) {
return 'Enum values cannot contain special characters (allowed characters: A-Z, a-z, 0-9 and _)';
}
const enums = input.replace(/\s/g, '').split(',');
if (_.uniq(enums).length !== enums.length) {
return `Enum values cannot contain duplicates (typed values: ${input})`;
}
for (let i = 0; i < enums.length; i++) {
if (/^[0-9].*/.test(enums[i])) {
return `Enum value "${enums[i]}" cannot start with a number`;
}
if (enums[i] === '') {
return 'Enum value cannot be empty (did you accidentally type "," twice in a row?)';
}
}
return true;
},
message: answers => {
if (!context.existingEnum) {
return 'What are the values of your enumeration (separated by comma, no spaces)?';
}
return 'What are the new values of your enumeration (separated by comma, no spaces)?\nThe new values will replace the old ones.\nNothing will be done if there are no new values.';
}
},
{
when: response => response.fieldAdd === true && databaseType === 'cassandra',
type: 'list',
name: 'fieldType',
message: 'What is the type of your field?',
choices: [
{
value: 'UUID',
name: 'UUID'
},
{
value: 'String',
name: 'String'
},
{
value: 'Integer',
name: 'Integer'
},
{
value: 'Long',
name: 'Long'
},
{
value: 'Float',
name: 'Float'
},
{
value: 'Double',
name: 'Double'
},
{
value: 'BigDecimal',
name: 'BigDecimal'
},
{
value: 'LocalDate',
name: 'LocalDate (Warning: only compatible with Cassandra v3)'
},
{
value: 'Instant',
name: 'Instant'
},
{
value: 'ZonedDateTime',
name: 'ZonedDateTime'
},
{
value: 'Boolean',
name: 'Boolean'
},
{
value: 'ByteBuffer',
name: '[BETA] blob'
}
],
default: 0
},
{
when: response => response.fieldAdd === true && response.fieldType === 'byte[]',
type: 'list',
name: 'fieldTypeBlobContent',
message: 'What is the content of the Blob field?',
choices: [
{
value: 'image',
name: 'An image'
},
{
value: 'any',
name: 'A binary file'
},
{
value: 'text',
name: 'A CLOB (Text field)'
}
],
default: 0
},
{
when: response => response.fieldAdd === true && response.fieldType === 'ByteBuffer',
type: 'list',
name: 'fieldTypeBlobContent',
message: 'What is the content of the Blob field?',
choices: [
{
value: 'image',
name: 'An image'
},
{
value: 'any',
name: 'A binary file'
}
],
default: 0
},
{
when: response => response.fieldAdd === true && response.fieldType !== 'ByteBuffer',
type: 'confirm',
name: 'fieldValidate',
message: 'Do you want to add validation rules to your field?',
default: false
},
{
when: response => response.fieldAdd === true && response.fieldValidate === true,
type: 'checkbox',
name: 'fieldValidateRules',
message: 'Which validation rules do you want to add?',
choices: response => {
// Default rules applicable for fieldType 'LocalDate', 'Instant',
// 'ZonedDateTime', 'UUID', 'Boolean', 'ByteBuffer' and 'Enum'
const opts = [
{
name: 'Required',
value: 'required'
},
{
name: 'Unique',
value: 'unique'
}
];
if (response.fieldType === 'String' || response.fieldTypeBlobContent === 'text') {
opts.push(
{
name: 'Minimum length',
value: 'minlength'
},
{
name: 'Maximum length',
value: 'maxlength'
},
{
name: 'Regular expression pattern',
value: 'pattern'
}
);
} else if (['Integer', 'Long', 'Float', 'Double', 'BigDecimal'].includes(response.fieldType)) {
opts.push(
{
name: 'Minimum',
value: 'min'
},
{
name: 'Maximum',
value: 'max'
}
);
}
return opts;
},
default: 0
},
{
when: response =>
response.fieldAdd === true && response.fieldValidate === true && response.fieldValidateRules.includes('minlength'),
type: 'input',
name: 'fieldValidateRulesMinlength',
validate: input => (this.isNumber(input) ? true : 'Minimum length must be a positive number'),
message: 'What is the minimum length of your field?',
default: 0
},
{
when: response =>
response.fieldAdd === true && response.fieldValidate === true && response.fieldValidateRules.includes('maxlength'),
type: 'input',
name: 'fieldValidateRulesMaxlength',
validate: input => (this.isNumber(input) ? true : 'Maximum length must be a positive number'),
message: 'What is the maximum length of your field?',
default: 20
},
{
when: response => response.fieldAdd === true && response.fieldValidate === true && response.fieldValidateRules.includes('min'),
type: 'input',
name: 'fieldValidateRulesMin',
message: 'What is the minimum of your field?',
validate: (input, response) => {
if (['Float', 'Double', 'BigDecimal'].includes(response.fieldType)) {
return this.isSignedDecimalNumber(input) ? true : 'Minimum must be a decimal number';
}
return this.isSignedNumber(input) ? true : 'Minimum must be a number';
},
default: 0
},
{
when: response => response.fieldAdd === true && response.fieldValidate === true && response.fieldValidateRules.includes('max'),
type: 'input',
name: 'fieldValidateRulesMax',
message: 'What is the maximum of your field?',
validate: (input, response) => {
if (['Float', 'Double', 'BigDecimal'].includes(response.fieldType)) {
return this.isSignedDecimalNumber(input) ? true : 'Maximum must be a decimal number';
}
return this.isSignedNumber(input) ? true : 'Maximum must be a number';
},
default: 100
},
{
when: response =>
response.fieldAdd === true &&
response.fieldValidate === true &&
response.fieldValidateRules.includes('minbytes') &&
response.fieldType === 'byte[]' &&
response.fieldTypeBlobContent !== 'text',
type: 'input',
name: 'fieldValidateRulesMinbytes',
message: 'What is the minimum byte size of your field?',
validate: input => (this.isNumber(input) ? true : 'Minimum byte size must be a positive number'),
default: 0
},
{
when: response =>
response.fieldAdd === true &&
response.fieldValidate === true &&
response.fieldValidateRules.includes('maxbytes') &&
response.fieldType === 'byte[]' &&
response.fieldTypeBlobContent !== 'text',
type: 'input',
name: 'fieldValidateRulesMaxbytes',
message: 'What is the maximum byte size of your field?',
validate: input => (this.isNumber(input) ? true : 'Maximum byte size must be a positive number'),
default: 5000000
},
{
when: response =>
response.fieldAdd === true && response.fieldValidate === true && response.fieldValidateRules.includes('pattern'),
type: 'input',
name: 'fieldValidateRulesPattern',
message: 'What is the regular expression pattern you want to apply on your field?',
default: '^[a-zA-Z0-9]*$'
}
];
this.prompt(prompts).then(props => {
if (props.fieldAdd) {
if (props.fieldIsEnum) {
props.fieldType = _.upperFirst(props.fieldType);
props.fieldValues = props.fieldValues.toUpperCase();
}
const field = {
fieldName: props.fieldName,
fieldType: props.fieldType,
fieldTypeBlobContent: props.fieldTypeBlobContent,
fieldValues: props.fieldValues,
fieldValidateRules: props.fieldValidateRules,
fieldValidateRulesMinlength: props.fieldValidateRulesMinlength,
fieldValidateRulesMaxlength: props.fieldValidateRulesMaxlength,
fieldValidateRulesPattern: props.fieldValidateRulesPattern,
fieldValidateRulesMin: props.fieldValidateRulesMin,
fieldValidateRulesMax: props.fieldValidateRulesMax,
fieldValidateRulesMinbytes: props.fieldValidateRulesMinbytes,
fieldValidateRulesMaxbytes: props.fieldValidateRulesMaxbytes
};
fieldNamesUnderscored.push(_.snakeCase(props.fieldName));
context.fields.push(field);
}
logFieldsAndRelationships.call(this);
if (props.fieldAdd) {
askForField.call(this, done);
} else {
done();
}
});
}
/**
* ask question for a relationship creation
*/
function askForRelationship(done) {
const context = this.context;
const name = context.name;
this.log(chalk.green('\nGenerating relationships to other entities\n'));
const fieldNamesUnderscored = context.fieldNamesUnderscored;
const prompts = [
{
type: 'confirm',
name: 'relationshipAdd',
message: 'Do you want to add a relationship to another entity?',
default: true
},
{
when: response => response.relationshipAdd === true,
type: 'input',
name: 'otherEntityName',
validate: input => {
if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'Your other entity name cannot contain special characters';
}
if (input === '') {
return 'Your other entity name cannot be empty';
}
if (jhiCore.isReservedKeyword(input, 'JAVA')) {
return 'Your other entity name cannot contain a Java reserved keyword';
}
if (input.toLowerCase() === 'user' && context.applicationType === 'microservice') {
return "Your entity cannot have a relationship with User because it's a gateway entity";
}
return true;
},
message: 'What is the name of the other entity?'
},
{
when: response => response.relationshipAdd === true,
type: 'input',
name: 'relationshipName',
validate: input => {
if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'Your relationship cannot contain special characters';
}
if (input === '') {
return 'Your relationship cannot be empty';
}
if (input.charAt(0) === input.charAt(0).toUpperCase()) {
return 'Your relationship cannot start with an upper case letter';
}
if (input === 'id' || fieldNamesUnderscored.includes(_.snakeCase(input))) {
return 'Your relationship cannot use an already existing field name';
}
if (jhiCore.isReservedKeyword(input, 'JAVA')) {
return 'Your relationship cannot contain a Java reserved keyword';
}
return true;
},
message: 'What is the name of the relationship?',
default: response => _.lowerFirst(response.otherEntityName)
},
{
when: response => response.relationshipAdd === true,
type: 'list',
name: 'relationshipType',
message: 'What is the type of the relationship?',
choices: response => {
const opts = [
{
value: 'many-to-one',
name: 'many-to-one'
},
{
value: 'many-to-many',
name: 'many-to-many'
},
{
value: 'one-to-one',
name: 'one-to-one'
}
];
if (response.otherEntityName.toLowerCase() !== 'user') {
opts.unshift({
value: 'one-to-many',
name: 'one-to-many'
});
}
return opts;
},
default: 0
},
{
when: response =>
response.relationshipAdd === true &&
response.otherEntityName.toLowerCase() !== 'user' &&
(response.relationshipType === 'many-to-many' || response.relationshipType === 'one-to-one'),
type: 'confirm',
name: 'ownerSide',
message: 'Is this entity the owner of the relationship?',
default: false
},
{
when: response =>
context.databaseType === 'sql' &&
response.relationshipAdd === true &&
response.relationshipType === 'one-to-one' &&
(response.ownerSide === true || response.otherEntityName.toLowerCase() === 'user'),
type: 'confirm',
name: 'useJPADerivedIdentifier',
message: 'Do you want to use JPA Derived Identifier - @MapsId?',
default: false
},
{
when: response =>
response.relationshipAdd === true &&
(response.relationshipType === 'one-to-many' ||
((response.relationshipType === 'many-to-many' || response.relationshipType === 'one-to-one') &&
response.otherEntityName.toLowerCase() !== 'user')),
type: 'input',
name: 'otherEntityRelationshipName',
message: 'What is the name of this relationship in the other entity?',
default: response => _.lowerFirst(name)
},
{
when: response =>
response.relationshipAdd === true &&
(response.relationshipType === 'many-to-one' ||
(response.relationshipType === 'many-to-many' && response.ownerSide === true) ||
(response.relationshipType === 'one-to-one' && response.ownerSide === true)),
type: 'input',
name: 'otherEntityField',
message: response =>
`When you display this relationship on client-side, which field from '${
response.otherEntityName
}' do you want to use? This field will be displayed as a String, so it cannot be a Blob`,
default: 'id'
},
{
when: response =>
response.relationshipAdd === true &&
response.otherEntityName.toLowerCase() !== context.name.toLowerCase() &&
(response.relationshipType === 'many-to-one' ||
(response.relationshipType === 'many-to-many' &&
(response.ownerSide === true || response.otherEntityName.toLowerCase() === 'user')) ||
(response.relationshipType === 'one-to-one' &&
(response.ownerSide === true || response.otherEntityName.toLowerCase() === 'user'))),
type: 'confirm',
name: 'relationshipValidate',
message: 'Do you want to add any validation rules to this relationship?',
default: false
},
{
when: response => response.relationshipValidate === true,
type: 'checkbox',
name: 'relationshipValidateRules',
message: 'Which validation rules do you want to add?',
choices: [
{
name: 'Required',
value: 'required'
}
],
default: 0
}
];
this.prompt(prompts).then(props => {
if (props.relationshipAdd) {
const relationship = {
relationshipName: props.relationshipName,
otherEntityName: _.lowerFirst(props.otherEntityName),
relationshipType: props.relationshipType,
relationshipValidateRules: props.relationshipValidateRules,
otherEntityField: props.otherEntityField,
ownerSide: props.ownerSide,
useJPADerivedIdentifier: props.useJPADerivedIdentifier,
otherEntityRelationshipName: props.otherEntityRelationshipName
};
if (props.otherEntityName.toLowerCase() === 'user') {
relationship.ownerSide = true;
relationship.otherEntityField = 'login';
relationship.otherEntityRelationshipName = _.lowerFirst(name);
}
fieldNamesUnderscored.push(_.snakeCase(props.relationshipName));
context.relationships.push(relationship);
}
logFieldsAndRelationships.call(this);
if (props.relationshipAdd) {
askForRelationship.call(this, done);
} else {
this.log('\n');
done();
}
});
}
/**
* Show the entity and it's fields and relationships in console
*/
function logFieldsAndRelationships() {
const context = this.context;
if (context.fields.length > 0 || context.relationships.length > 0) {
this.log(chalk.red(chalk.white('\n================= ') + context.entityNameCapitalized + chalk.white(' =================')));
}
if (context.fields.length > 0) {
this.log(chalk.white('Fields'));
context.fields.forEach(field => {
const validationDetails = [];
const fieldValidate = _.isArray(field.fieldValidateRules) && field.fieldValidateRules.length >= 1;
if (fieldValidate === true) {
if (field.fieldValidateRules.includes('required')) {
validationDetails.push('required');
}
if (field.fieldValidateRules.includes('unique')) {
validationDetails.push('unique');
}
if (field.fieldValidateRules.includes('minlength')) {
validationDetails.push(`minlength='${field.fieldValidateRulesMinlength}'`);
}
if (field.fieldValidateRules.includes('maxlength')) {
validationDetails.push(`maxlength='${field.fieldValidateRulesMaxlength}'`);
}
if (field.fieldValidateRules.includes('pattern')) {
validationDetails.push(`pattern='${field.fieldValidateRulesPattern}'`);
}
if (field.fieldValidateRules.includes('min')) {
validationDetails.push(`min='${field.fieldValidateRulesMin}'`);
}
if (field.fieldValidateRules.includes('max')) {
validationDetails.push(`max='${field.fieldValidateRulesMax}'`);
}
if (field.fieldValidateRules.includes('minbytes')) {
validationDetails.push(`minbytes='${field.fieldValidateRulesMinbytes}'`);
}
if (field.fieldValidateRules.includes('maxbytes')) {
validationDetails.push(`maxbytes='${field.fieldValidateRulesMaxbytes}'`);
}
}
this.log(
chalk.red(field.fieldName) +
chalk.white(` (${field.fieldType}${field.fieldTypeBlobContent ? ` ${field.fieldTypeBlobContent}` : ''}) `) +
chalk.cyan(validationDetails.join(' '))
);
});
this.log();
}
if (context.relationships.length > 0) {
this.log(chalk.white('Relationships'));
context.relationships.forEach(relationship => {
const validationDetails = [];
if (relationship.relationshipValidateRules && relationship.relationshipValidateRules.includes('required')) {
validationDetails.push('required');
}
this.log(
`${chalk.red(relationship.relationshipName)} ${chalk.white(`(${_.upperFirst(relationship.otherEntityName)})`)} ${chalk.cyan(
relationship.relationshipType
)} ${chalk.cyan(validationDetails.join(' '))}`
);
});
this.log();
}
}