generator-pyhipster
Version:
Python (Flask) + Angular/React/Vue in one handy generator
392 lines (358 loc) • 14 kB
JavaScript
/**
* Copyright 2013-2022 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
*
* https://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 JDLObject = require('../models/jdl-object');
const JDLEntity = require('../models/jdl-entity');
const JDLField = require('../models/jdl-field');
const JDLValidation = require('../models/jdl-validation');
const JDLEnum = require('../models/jdl-enum');
const JDLRelationship = require('../models/jdl-relationship');
const JDLUnaryOption = require('../models/jdl-unary-option');
const JDLBinaryOption = require('../models/jdl-binary-option');
const { BlobTypes, CommonDBTypes, RelationalOnlyDBTypes } = require('../jhipster/field-types');
const { OptionNames } = require('../jhipster/application-options');
const { ONE_TO_ONE, ONE_TO_MANY, MANY_TO_ONE, MANY_TO_MANY } = require('../jhipster/relationship-types');
const { JPA_DERIVED_IDENTIFIER } = require('../jhipster/relationship-options');
const { FILTER, NO_FLUENT_METHOD, READ_ONLY, EMBEDDED } = require('../jhipster/unary-options');
const { ANGULAR_SUFFIX, CLIENT_ROOT_FOLDER, DTO, MICROSERVICE, PAGINATION, SEARCH, SERVICE } =
require('../jhipster/binary-options').Options;
const { lowerFirst, upperFirst } = require('../utils/string-utils');
const { ANY, IMAGE, TEXT } = BlobTypes;
const { BYTES } = RelationalOnlyDBTypes;
module.exports = {
convertEntitiesToJDL,
};
const USER_ENTITY_NAME = 'User';
const USER_ENTITY = new JDLEntity({ name: USER_ENTITY_NAME });
let entities;
let jdlObject;
let skippedUserManagement;
/**
* Convert the passed entities (parsed from JSON files) to a JDL object.
* @param {Object} params - an object containing the entities and relevant options.
* @param {Map<string, Object>} params.entities - a Map having for keys the entity names and values the JSON entity files.
* @param {boolean} params.skippedUserManagement - whether management of the User entity by JHipster is skipped.
* @return {JDLObject} the parsed entities in the JDL form.
*/
function convertEntitiesToJDL(params) {
if (!params.entities) {
throw new Error('Entities have to be passed to be converted.');
}
init(params);
addEntities();
addRelationshipsToJDL();
return jdlObject;
}
function init(params) {
entities = params.entities;
jdlObject = new JDLObject();
skippedUserManagement = params.skippedUserManagement;
}
function addEntities() {
entities.forEach((entity, entityName) => {
addEntity(entity, entityName);
});
}
function addEntity(entity, entityName) {
if (entityName === USER_ENTITY_NAME && !skippedUserManagement) {
throw new Error(`User entity name is reserved if ${OptionNames.SKIP_USER_MANAGEMENT} is not set.`);
}
jdlObject.addEntity(convertJSONToJDLEntity(entity, entityName));
addEnumsToJDL(entity);
addEntityOptionsToJDL(entity, entityName);
}
function convertJSONToJDLEntity(entity, entityName) {
const jdlEntity = new JDLEntity({
name: entityName,
tableName: entity.entityTableName,
comment: entity.javadoc,
});
addFields(jdlEntity, entity);
return jdlEntity;
}
function addFields(jdlEntity, entity) {
entity.fields.forEach(field => {
jdlEntity.addField(convertJSONToJDLField(field));
});
}
function convertJSONToJDLField(field) {
const jdlField = new JDLField({
name: lowerFirst(field.fieldName),
type: field.fieldType,
comment: field.javadoc,
});
if (jdlField.type === BYTES) {
jdlField.type = getTypeForBlob(field.fieldTypeBlobContent);
}
if (field.fieldValidateRules) {
addValidations(jdlField, field);
}
return jdlField;
}
function getTypeForBlob(blobContentType) {
if ([ANY, IMAGE, TEXT].includes(blobContentType)) {
return CommonDBTypes[`${blobContentType.toUpperCase()}_BLOB`];
}
throw new Error(`Unrecognised blob type: '${blobContentType}'`);
}
function addValidations(jdlField, field) {
field.fieldValidateRules.forEach(rule => {
jdlField.addValidation(convertJSONToJDLValidation(rule, field));
});
}
function convertJSONToJDLValidation(rule, field) {
return new JDLValidation({
name: rule,
value: field[`fieldValidateRules${upperFirst(rule)}`],
});
}
function addEnumsToJDL(entity) {
entity.fields.forEach(field => {
if (field.fieldValues !== undefined) {
jdlObject.addEnum(
new JDLEnum({
name: field.fieldType,
values: getEnumValuesFromString(field.fieldValues),
comment: field.fieldTypeJavadoc,
})
);
}
});
}
function getEnumValuesFromString(valuesAsString) {
return valuesAsString.split(',').map(fieldValue => {
// if fieldValue looks like ENUM_VALUE (something)
if (fieldValue.includes('(')) {
const [key, value] = fieldValue
.replace(/^(\w+)\s\((\w+)\)$/, (match, matchedKey, matchedValue) => `${matchedKey},${matchedValue}`)
.split(',');
return {
key,
value,
};
}
return { key: fieldValue };
});
}
/*
* Adds relationships for entities to JDL.
* The jdl passed must contain the jdl entities concerned by the relationships
*/
function addRelationshipsToJDL() {
entities.forEach((entity, entityName) => {
dealWithRelationships(entity.relationships, entityName);
});
}
function dealWithRelationships(relationships, entityName) {
if (!relationships) {
return;
}
relationships.forEach(relationship => {
const jdlRelationship = getRelationship(relationship, entityName);
if (jdlRelationship) {
jdlObject.addRelationship(jdlRelationship);
}
});
}
function getRelationship(relationship, entityName) {
let relationshipConfiguration = {
sourceEntity: null,
destinationEntity: null,
type: null,
injectedFieldInSourceEntity: null,
injectedFieldInDestinationEntity: null,
injectedFieldInDestinationIsRequired: false,
injectedFieldInSourceIsRequired: false,
commentForSourceEntity: null,
commentForDestinationEntity: null,
destinationEntityAsJDLEntity: null,
options: {},
};
const sourceEntitySideAttributes = getSourceEntitySideAttributes(entityName, relationship);
relationshipConfiguration = {
...relationshipConfiguration,
...sourceEntitySideAttributes,
};
relationshipConfiguration.options = getRelationshipOptions(relationship);
if (shouldHandleUserEntity(relationship)) {
relationshipConfiguration.destinationEntityAsJDLEntity = USER_ENTITY;
} else {
const destinationEntity = upperFirst(relationship.otherEntityName);
relationshipConfiguration.destinationEntity = entities.get(destinationEntity);
relationshipConfiguration.destinationEntityAsJDLEntity = jdlObject.getEntity(destinationEntity);
if (!relationshipConfiguration.destinationEntity) {
return undefined;
}
const isEntityTheDestinationSideEntity = (otherEntityName, otherEntityRelationshipName) =>
otherEntityName === entityName && otherEntityRelationshipName === relationship.relationshipName;
const destinationSideAttributes = getDestinationEntitySideAttributes(
isEntityTheDestinationSideEntity,
relationshipConfiguration.destinationEntity.relationships
);
relationshipConfiguration = {
...relationshipConfiguration,
...destinationSideAttributes,
};
}
if (relationship.relationshipType === 'many-to-one') {
if (relationshipConfiguration.injectedFieldInDestinationEntity) {
// This is a bidirectional relationship so consider it as a OneToMany
return getBidirectionalOneToManyRelationship(relationshipConfiguration);
}
// Unidirectional ManyToOne
relationshipConfiguration.type = MANY_TO_ONE;
} else if (relationship.relationshipType === 'one-to-one' && relationship.ownerSide === true) {
relationshipConfiguration.type = ONE_TO_ONE;
} else if (relationship.relationshipType === 'many-to-many' && relationship.ownerSide === true) {
relationshipConfiguration.type = MANY_TO_MANY;
}
if (relationshipConfiguration.type) {
return getDefaultRelationship(relationshipConfiguration);
}
return undefined;
}
function shouldHandleUserEntity(relationship) {
return relationship.otherEntityName.toLowerCase() === USER_ENTITY_NAME.toLowerCase() && !skippedUserManagement;
}
function getSourceEntitySideAttributes(entityName, relationship) {
return {
sourceEntity: entityName,
injectedFieldInSourceEntity: getInjectedFieldInSourceEntity(relationship),
injectedFieldInSourceIsRequired: relationship.relationshipValidateRules,
commentForSourceEntity: relationship.javadoc,
};
}
function getDestinationEntitySideAttributes(isEntityTheDestinationSideEntity, destinationEntityRelationships) {
const foundDestinationSideEntity = destinationEntityRelationships.find(destinationEntityFromRelationship => {
return isEntityTheDestinationSideEntity(
upperFirst(destinationEntityFromRelationship.otherEntityName),
destinationEntityFromRelationship.otherEntityRelationshipName
);
});
if (!foundDestinationSideEntity) {
return {};
}
// Bidirectional relationship
let injectedFieldInDestinationEntity = foundDestinationSideEntity.relationshipName;
if (!!foundDestinationSideEntity.otherEntityField && foundDestinationSideEntity.otherEntityField !== 'id') {
injectedFieldInDestinationEntity += `(${foundDestinationSideEntity.otherEntityField})`;
}
const injectedFieldInDestinationIsRequired = !!foundDestinationSideEntity.relationshipValidateRules;
const commentForDestinationEntity = foundDestinationSideEntity.javadoc;
return {
injectedFieldInDestinationEntity,
injectedFieldInDestinationIsRequired,
commentForDestinationEntity,
};
}
function getRelationshipOptions(relationship) {
const options = relationship.options || {
global: {},
source: {},
destination: {},
};
if (relationship.useJPADerivedIdentifier) {
options[JPA_DERIVED_IDENTIFIER] = true;
}
return options;
}
function getDefaultRelationship(relationshipConfiguration) {
return new JDLRelationship({
from: relationshipConfiguration.sourceEntity,
to: relationshipConfiguration.destinationEntityAsJDLEntity.name,
type: relationshipConfiguration.type,
injectedFieldInFrom: relationshipConfiguration.injectedFieldInSourceEntity,
injectedFieldInTo: relationshipConfiguration.injectedFieldInDestinationEntity,
isInjectedFieldInFromRequired: relationshipConfiguration.injectedFieldInSourceIsRequired,
isInjectedFieldInToRequired: relationshipConfiguration.injectedFieldInDestinationIsRequired,
commentInFrom: relationshipConfiguration.commentForSourceEntity,
commentInTo: relationshipConfiguration.commentForDestinationEntity,
options: relationshipConfiguration.options,
});
}
// Based on a ManyToOne relationship, we determine the OneToMany relationship.
function getBidirectionalOneToManyRelationship(relationshipConfiguration) {
return new JDLRelationship({
from: relationshipConfiguration.destinationEntityAsJDLEntity.name,
to: relationshipConfiguration.sourceEntity,
type: ONE_TO_MANY,
injectedFieldInFrom: relationshipConfiguration.injectedFieldInDestinationEntity,
injectedFieldInTo: relationshipConfiguration.injectedFieldInSourceEntity,
isInjectedFieldInFromRequired: relationshipConfiguration.injectedFieldInDestinationIsRequired,
isInjectedFieldInToRequired: relationshipConfiguration.injectedFieldInSourceIsRequired,
commentInFrom: relationshipConfiguration.commentForDestinationEntity,
commentInTo: relationshipConfiguration.commentForSourceEntity,
options: relationshipConfiguration.options,
});
}
function getInjectedFieldInSourceEntity(relationship) {
return (
relationship.relationshipName +
(relationship.otherEntityField && relationship.otherEntityField !== 'id' ? `(${relationship.otherEntityField})` : '')
);
}
function addEntityOptionsToJDL(entity, entityName) {
if (entity.fluentMethods === false) {
addUnaryOptionToJDL(NO_FLUENT_METHOD, entityName);
}
[DTO, PAGINATION, SERVICE].forEach(option => {
if (entity[option] && entity[option] !== 'no') {
addBinaryOptionToJDL(option, entity[option], entityName);
}
});
if (entity.searchEngine) {
addBinaryOptionToJDL(SEARCH, entity.searchEngine, entityName);
}
// angularSuffix in BinaryOptions, angularJSSuffix in Json
if (entity.angularJSSuffix) {
addBinaryOptionToJDL(ANGULAR_SUFFIX, entity.angularJSSuffix, entityName);
}
// microservice in BinaryOptions, microserviceName in Json
if (entity.microserviceName !== undefined) {
addBinaryOptionToJDL(MICROSERVICE, entity.microserviceName, entityName);
}
if (entity.jpaMetamodelFiltering === true) {
addUnaryOptionToJDL(FILTER, entityName);
}
if (entity.readOnly === true) {
addUnaryOptionToJDL(READ_ONLY, entityName);
}
if (entity.embedded === true) {
addUnaryOptionToJDL(EMBEDDED, entityName);
}
if (entity.clientRootFolder) {
addBinaryOptionToJDL(CLIENT_ROOT_FOLDER, entity.clientRootFolder, entityName);
}
}
function addUnaryOptionToJDL(unaryOption, entityName) {
jdlObject.addOption(
new JDLUnaryOption({
name: unaryOption,
entityNames: [entityName],
})
);
}
function addBinaryOptionToJDL(binaryOption, value, entityName) {
jdlObject.addOption(
new JDLBinaryOption({
name: binaryOption,
value,
entityNames: [entityName],
})
);
}