UNPKG

gen-jhipster

Version:

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

170 lines (169 loc) 9.66 kB
/** * Copyright 2013-2024 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 'fs'; import GeneratorBaseApplication 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: [], changelogData: {}, }); /** * This is the base class for a generator for every generator. */ export default class GeneratorBaseEntityChanges extends GeneratorBaseApplication { recreateInitialChangelog; entityChanges; getTaskFirstArgForPriority(priorityName) { const firstArg = super.getTaskFirstArgForPriority(priorityName); if ([DEFAULT, WRITING_ENTITIES, POST_WRITING_ENTITIES].includes(priorityName)) { this.entityChanges = this.generateIncrementalChanges(); } 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() { const recreateInitialChangelog = this.recreateInitialChangelog; const { generateBuiltInUserEntity, generateBuiltInAuthorityEntity, incrementalChangelog } = this.sharedData.getApplication(); const entityNames = this.getExistingEntityNames(); const entitiesByName = Object.fromEntries(entityNames.map(entityName => [entityName, this.sharedData.getEntity(entityName)])); const entitiesWithExistingChangelog = entityNames.filter(entityName => !this.isChangelogNew({ entityName, changelogDate: entitiesByName[entityName].annotations?.changelogDate })); const previousEntitiesByName = Object.fromEntries(entityNames .filter(entityName => existsSync(this.getEntityConfigPath(entityName))) .map(entityName => [ entityName, { name: entityName, ...JSON.parse(readFileSync(this.getEntityConfigPath(entityName)).toString()) }, ])); if (generateBuiltInUserEntity) { const user = this.sharedData.getEntity('User'); previousEntitiesByName.User = user; } if (generateBuiltInAuthorityEntity) { const authority = this.sharedData.getEntity('Authority'); previousEntitiesByName.Authority = authority; } 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.includes(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))) .concat(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; } }