UNPKG

gen-jhipster

Version:

VHipster - Spring Boot + Angular/React/Vue in one handy generator

172 lines (171 loc) 9.81 kB
/** * Copyright 2013-2026 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. */ import { existsSync, readFileSync } from 'node:fs'; import BaseApplicationGenerator from "../base-application/index.js"; import { PRIORITY_NAMES } from "../base-application/priorities.js"; import { loadEntitiesAnnotations, loadEntitiesOtherSide } from "../base-application/support/index.js"; import { relationshipEquals, relationshipNeedsForeignKeyRecreationOnly } from "../liquibase/support/index.js"; import { addEntitiesOtherRelationships } from "../server/support/index.js"; const { DEFAULT, WRITING_ENTITIES, POST_WRITING_ENTITIES } = PRIORITY_NAMES; const baseChangelog = () => ({ newEntity: false, changedEntity: false, incremental: false, previousEntity: undefined, addedFields: [], removedFields: [], addedRelationships: [], removedRelationships: [], relationshipsToRecreateForeignKeysOnly: [], removedDefaultValueFields: [], addedDefaultValueFields: [], }); /** * This is the base class for a generator for every generator. */ export default class BaseEntityChangesGenerator extends BaseApplicationGenerator { recreateInitialChangelog; entityChanges; getTaskFirstArgForPriority(priorityName) { const firstArg = super.getTaskFirstArgForPriority(priorityName); if ([DEFAULT, WRITING_ENTITIES, POST_WRITING_ENTITIES].includes(priorityName)) { const { application, entities } = firstArg; this.entityChanges = this.generateIncrementalChanges({ application, entities }); } if ([DEFAULT].includes(priorityName)) { return { ...firstArg, entityChanges: this.entityChanges }; } if ([WRITING_ENTITIES, POST_WRITING_ENTITIES].includes(priorityName)) { // const { entities = [] } = this.options; // const filteredEntities = data.entities.filter(entity => entities.includes(entity.name)); return { ...firstArg, entityChanges: this.entityChanges }; } return firstArg; } /** * Generate changelog from differences between the liquibase entity and current entity. */ generateIncrementalChanges({ application, entities: paramEntities, }) { const recreateInitialChangelog = this.recreateInitialChangelog; const { incrementalChangelog } = application; const entityNames = paramEntities.filter(e => !e.builtIn).map(e => e.name); const entitiesByName = Object.fromEntries(paramEntities.map(entity => [entity.name, entity])); const entitiesWithExistingChangelog = new Set(entityNames.filter(entityName => !this.isChangelogNew({ entityName, changelogDate: entitiesByName[entityName].annotations?.changelogDate }))); const previousEntitiesByName = Object.fromEntries(entityNames .map(entityName => ({ entityName, entityConfigPath: this.getEntityConfigPath(entityName) })) .filter(({ entityConfigPath }) => existsSync(entityConfigPath)) .map(({ entityName, entityConfigPath }) => [ entityName, { name: entityName, ...JSON.parse(readFileSync(entityConfigPath).toString()) }, ])); for (const [entityName, entity] of Object.entries(entitiesByName)) { if (entity.builtIn) { previousEntitiesByName[entityName] = entity; } } if (application.generateBuiltInUserEntity && !previousEntitiesByName.User) { previousEntitiesByName.User = { name: 'User', builtIn: true }; } const entities = Object.values(previousEntitiesByName); loadEntitiesAnnotations(entities); loadEntitiesOtherSide(entities); addEntitiesOtherRelationships(entities); // Compare entity changes and create changelogs return entityNames.map(entityName => { const newConfig = entitiesByName[entityName]; const newFields = (newConfig.fields || []).filter((field) => !field.transient); const newRelationships = newConfig.relationships || []; const oldConfig = previousEntitiesByName[entityName]; if (!oldConfig || recreateInitialChangelog || !incrementalChangelog || !entitiesWithExistingChangelog.has(entityName)) { return { ...baseChangelog(), incremental: newConfig.incrementalChangelog, changelogDate: newConfig.changelogDate, newEntity: true, entity: newConfig, entityName, }; } this._debug(`Calculating diffs for ${entityName}`); const oldFields = (oldConfig.fields || []).filter((field) => !field.transient); const oldFieldNames = oldFields.filter(field => !field.id).map(field => field.fieldName); const newFieldNames = newFields.filter(field => !field.id).map(field => field.fieldName); // Calculate new fields const addedFieldNames = newFieldNames.filter(fieldName => !oldFieldNames.includes(fieldName)); const addedFields = addedFieldNames.map(fieldName => newFields.find(field => fieldName === field.fieldName)); // Calculate removed fields const removedFieldNames = oldFieldNames.filter(fieldName => !newFieldNames.includes(fieldName)); const removedFields = removedFieldNames.map(fieldName => oldFields.find(field => fieldName === field.fieldName)); const oldRelationships = oldConfig.relationships || []; // Calculate changed/newly added relationships const addedRelationships = newRelationships.filter(newRelationship => // id changes are not supported !newRelationship.id && // check if the same relationship wasn't already part of the old config !oldRelationships.some(oldRelationship => relationshipEquals(oldRelationship, newRelationship))); // Calculate to be removed relationships const removedRelationships = oldRelationships.filter(oldRelationship => // id changes are not supported !oldRelationship.id && // check if there are relationships not anymore in the new config !newRelationships.some(newRelationship => relationshipEquals(newRelationship, oldRelationship))); // calculate relationships that only need a foreign key recreation from the ones that are added // we need both the added and the removed ones here const relationshipsToRecreateForeignKeysOnly = [ ...addedRelationships.filter(addedRelationship => removedRelationships.some(removedRelationship => relationshipNeedsForeignKeyRecreationOnly(removedRelationship, addedRelationship))), ...removedRelationships.filter(removedRelationship => addedRelationships.some(addedRelationship => relationshipNeedsForeignKeyRecreationOnly(addedRelationship, removedRelationship))), ]; const oldFieldsWithDefaultValues = oldFields.filter(field => this.hasAnyDefaultValue(field)); const newFieldsWithDefaultValues = newFields.filter(field => this.hasAnyDefaultValue(field)); // find the old fields that have not been deleted anyway or otherwise where the default value is different on the same new field const removedDefaultValueFields = oldFieldsWithDefaultValues .filter(oldField => !removedFieldNames.includes(oldField.fieldName)) .filter( // field was not removed, so check its default value oldField => this.doDefaultValuesDiffer(oldField, newFields.find(newField => newField.fieldName === oldField.fieldName))); // find the new fields that have not been added newly anyway or otherwise where the old field had a different default value const addedDefaultValueFields = newFieldsWithDefaultValues .filter(newField => !addedFieldNames.includes(newField.fieldName)) .filter( // field was not added newly, so check its default value newField => this.doDefaultValuesDiffer(oldFields.find(oldField => oldField.fieldName === newField.fieldName), newField)); return { ...baseChangelog(), previousEntity: oldConfig, entity: newConfig, incremental: true, changedEntity: true, entityName, addedFields, removedFields, addedRelationships, removedRelationships, relationshipsToRecreateForeignKeysOnly, removedDefaultValueFields, addedDefaultValueFields, }; }); } hasAnyDefaultValue(field) { return field.defaultValue !== undefined || field.defaultValueComputed !== undefined; } doDefaultValuesDiffer(field1, field2) { return field1.defaultValue !== field2.defaultValue || field1.defaultValueComputed !== field2.defaultValueComputed; } }