Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jhipster typescript iteration 4 #26133

Merged
merged 10 commits into from
May 17, 2024
Merged
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"generator-jhipster": {
"entities": ["Country", "Department", "Employee", "Job", "JobHistory", "Location", "Region", "Task"],
"promptValues": {
"packageName": "com.mycompany.myapp"
},
Expand Down
3 changes: 3 additions & 0 deletions jdl/converters/json-to-jdl-converter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ describe('jdl - JSONToJDLConverter', () => {
`);
});
});

describe('with entities', () => {
beforeEach(() => {
dir = path.join(__dirname, '..', '__test-files__', 'json_to_jdl_converter', 'app_with_entities');
Expand Down Expand Up @@ -354,6 +355,7 @@ describe('jdl - JSONToJDLConverter', () => {
beforeEach(() => {
jdl = convertSingleContentToJDL({
'generator-jhipster': {
baseName: 'x',
microfrontends: [
{
baseName: 'foo',
Expand All @@ -374,6 +376,7 @@ describe('jdl - JSONToJDLConverter', () => {
it('should not fail', () => {
convertSingleContentToJDL({
'generator-jhipster': {
baseName: 'x',
blueprints: null,
microfrontends: undefined,
},
Expand Down
40 changes: 21 additions & 19 deletions jdl/converters/json-to-jdl-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ import { readJSONFile } from '../readers/json-file-reader.js';
import { convertApplicationToJDL } from './json-to-jdl-application-converter.js';
import { convertEntitiesToJDL } from './json-to-jdl-entity-converter.js';
import exportJDLObject from '../exporters/jdl-exporter.js';
import { JSONEntity } from './types.js';
import { JSONEntity, JSONRootObject, PostProcessedJSONGeneratorJhipsterContent, PostProcessedJSONRootObject } from './types.js';
import { removeFieldsWithNullishValues } from '../../generators/base/support/config.js';
import { GENERATOR_JHIPSTER } from '../../generators/generator-constants.js';
import JDLApplication from '../models/jdl-application.js';

export default {
convertToJDL,
Expand All @@ -40,11 +41,11 @@ export default {
* @param directory the directory to find JHipster files.
* @param output the file where the JDL will be written
*/
export function convertToJDL(directory = '.', output: string | false = 'app.jdl') {
export function convertToJDL(directory = '.', output: string | false = 'app.jdl'): JDLObject | undefined {
let jdlObject: JDLObject;
if (doesFileExist(path.join(directory, '.yo-rc.json'))) {
const yoRcFileContent = readJSONFile(path.join(directory, '.yo-rc.json'));
let entities;
const yoRcFileContent: JSONRootObject = readJSONFile(path.join(directory, '.yo-rc.json'));
let entities: Map<string, JSONEntity> | undefined;
if (doesDirectoryExist(path.join(directory, '.jhipster'))) {
entities = getJSONEntityFiles(directory);
}
Expand All @@ -63,19 +64,19 @@ export function convertToJDL(directory = '.', output: string | false = 'app.jdl'
return jdlObject;
}

export function convertSingleContentToJDL(yoRcFileContent: Record<string, any>, entities?: Map<string, JSONEntity>) {
export function convertSingleContentToJDL(yoRcFileContent: JSONRootObject, entities?: Map<string, JSONEntity>): string {
return getJDLObjectFromSingleApplication(yoRcFileContent, entities).toString();
}

function getJDLObjectFromMultipleApplications(directory: string) {
function getJDLObjectFromMultipleApplications(directory: string): JDLObject {
const subDirectories = getSubdirectories(directory);
if (subDirectories.length === 0) {
throw new Error('There are no subdirectories.');
}
let jdlObject = new JDLObject();
subDirectories.forEach(subDirectory => {
const applicationDirectory = path.join(directory, subDirectory);
const yoRcFileContent = readJSONFile(path.join(applicationDirectory, '.yo-rc.json'));
const yoRcFileContent: JSONRootObject = readJSONFile(path.join(applicationDirectory, '.yo-rc.json'));
let entities: Map<string, JSONEntity> = new Map();
if (doesDirectoryExist(path.join(applicationDirectory, '.jhipster'))) {
entities = getJSONEntityFiles(applicationDirectory);
Expand All @@ -86,48 +87,49 @@ function getJDLObjectFromMultipleApplications(directory: string) {
}

export function getJDLObjectFromSingleApplication(
yoRcFileContent: Record<string, any>,
yoRcFileContent: JSONRootObject,
entities?: Map<string, JSONEntity>,
existingJDLObject = new JDLObject(),
): JDLObject {
const cleanedYoRcFileContent = cleanYoRcFileContent(yoRcFileContent);
const jdlApplication = convertApplicationToJDL({ application: cleanedYoRcFileContent });
const cleanedYoRcFileContent: PostProcessedJSONGeneratorJhipsterContent = cleanYoRcFileContent(yoRcFileContent);
const jdlApplication: JDLApplication = convertApplicationToJDL({ application: cleanedYoRcFileContent });
if (!entities) {
existingJDLObject.addApplication(jdlApplication);
return existingJDLObject;
}
const jdlObject = convertEntitiesToJDL(entities);
entities.forEach((entity, entityName) => jdlApplication.addEntityName(entityName));
const jdlObject: JDLObject = convertEntitiesToJDL(entities);
entities.forEach((entity: JSONEntity, entityName: string) => jdlApplication.addEntityName(entityName));
jdlObject.addApplication(jdlApplication);
return mergeJDLObjects(existingJDLObject, jdlObject);
}

function cleanYoRcFileContent(yoRcFileContent: Record<string, any>) {
function cleanYoRcFileContent(yoRcFileContent: JSONRootObject): PostProcessedJSONRootObject {
for (const key of Object.keys(yoRcFileContent)) {
yoRcFileContent[key] = removeFieldsWithNullishValues(yoRcFileContent[key]);
}
delete yoRcFileContent[GENERATOR_JHIPSTER].promptValues;
const result: PostProcessedJSONRootObject = structuredClone(yoRcFileContent) as PostProcessedJSONRootObject;
if (yoRcFileContent[GENERATOR_JHIPSTER].blueprints) {
yoRcFileContent[GENERATOR_JHIPSTER].blueprints = yoRcFileContent[GENERATOR_JHIPSTER].blueprints.map(blueprint => blueprint.name);
result[GENERATOR_JHIPSTER].blueprints = yoRcFileContent[GENERATOR_JHIPSTER].blueprints.map(blueprint => blueprint.name);
}
if (yoRcFileContent[GENERATOR_JHIPSTER].microfrontends) {
yoRcFileContent[GENERATOR_JHIPSTER].microfrontends = yoRcFileContent[GENERATOR_JHIPSTER].microfrontends.map(({ baseName }) => baseName);
result[GENERATOR_JHIPSTER].microfrontends = yoRcFileContent[GENERATOR_JHIPSTER].microfrontends.map(({ baseName }) => baseName);
}
return yoRcFileContent;
return result;
}

function getJSONEntityFiles(applicationDirectory: string) {
function getJSONEntityFiles(applicationDirectory: string): Map<string, JSONEntity> {
const entities: Map<string, JSONEntity> = new Map();
fs.readdirSync(path.join(applicationDirectory, '.jhipster')).forEach(file => {
const entityName = file.slice(0, file.indexOf('.json'));
const jsonFilePath = path.join(applicationDirectory, '.jhipster', file);
if (fs.statSync(jsonFilePath).isFile() && file.endsWith('.json')) {
const entityName = file.slice(0, file.indexOf('.json'));
entities.set(entityName, readJSONFile(jsonFilePath));
}
});
return entities;
}

function getSubdirectories(rootDirectory: string) {
function getSubdirectories(rootDirectory: string): string[] {
return fs.readdirSync(path.join(rootDirectory)).filter(file => doesDirectoryExist(path.join(rootDirectory, file)));
}
55 changes: 29 additions & 26 deletions jdl/converters/json-to-jdl-entity-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import JDLObject from '../models/jdl-object.js';
import { JDLEntity, JDLEnum } from '../models/index.js';
import JDLField from '../models/jdl-field.js';
import JDLValidation from '../models/jdl-validation.js';
import JDLRelationship, { JDLRelationshipModel, JDLRelationshipOptions } from '../models/jdl-relationship.js';
import JDLRelationship, { JDLRelationshipModel, JDLRelationshipOptions, JDLSourceEntitySide } from '../models/jdl-relationship.js';
import JDLUnaryOption from '../models/jdl-unary-option.js';
import JDLBinaryOption from '../models/jdl-binary-option.js';

Expand Down Expand Up @@ -62,18 +62,18 @@ export function convertEntitiesToJDL(entities?: Map<string, JSONEntity>): JDLObj
return jdlObject;
}

function init(ents: Map<string, JSONEntity>) {
function init(ents: Map<string, JSONEntity>): void {
entities = ents;
jdlObject = new JDLObject();
}

function addEntities() {
function addEntities(): void {
entities.forEach((entity, entityName) => {
addEntity(entity, entityName);
});
}

function addEntity(entity: JSONEntity, entityName: string) {
function addEntity(entity: JSONEntity, entityName: string): void {
jdlObject.addEntity(convertJSONToJDLEntity(entity, entityName));
addEnumsToJDL(entity);
addEntityOptionsToJDL(entity, entityName);
Expand All @@ -90,13 +90,13 @@ function convertJSONToJDLEntity(entity: JSONEntity, entityName: string): JDLEnti
return jdlEntity;
}

function addFields(jdlEntity: JDLEntity, entity: JSONEntity) {
function addFields(jdlEntity: JDLEntity, entity: JSONEntity): void {
entity?.fields?.forEach(field => {
jdlEntity.addField(convertJSONToJDLField(field));
});
}

function convertJSONToJDLField(field: JSONField) {
function convertJSONToJDLField(field: JSONField): JDLField {
const jdlField = new JDLField({
name: lowerFirst(field.fieldName),
type: field.fieldType,
Expand All @@ -105,33 +105,33 @@ function convertJSONToJDLField(field: JSONField) {
if (jdlField.type === BYTES) {
jdlField.type = getTypeForBlob(field.fieldTypeBlobContent);
}
if (field.fieldValidateRules) {
addValidations(jdlField, field);
}
addValidations(jdlField, field);
return jdlField;
}

function getTypeForBlob(blobContentType: string) {
function getTypeForBlob(blobContentType: string): string {
if ([ANY, IMAGE, TEXT].includes(blobContentType)) {
return CommonDBTypes[`${blobContentType.toUpperCase()}_BLOB`];
}
throw new Error(`Unrecognised blob type: '${blobContentType}'`);
}

function addValidations(jdlField: JDLField, field: JSONField) {
field.fieldValidateRules.forEach(rule => {
jdlField.addValidation(convertJSONToJDLValidation(rule, field));
});
function addValidations(jdlField: JDLField, field: JSONField): void {
if (field.fieldValidateRules) {
field.fieldValidateRules!.forEach((rule: string) => {
jdlField.addValidation(convertJSONToJDLValidation(rule, field));
});
}
}

function convertJSONToJDLValidation(rule, field: JSONField) {
function convertJSONToJDLValidation(rule: string, field: JSONField): JDLValidation {
return new JDLValidation({
name: rule,
value: field[`fieldValidateRules${upperFirst(rule)}`],
});
}

function addEnumsToJDL(entity: JSONEntity) {
function addEnumsToJDL(entity: JSONEntity): void {
entity?.fields?.forEach(field => {
if (field.fieldValues !== undefined) {
jdlObject.addEnum(
Expand All @@ -145,7 +145,7 @@ function addEnumsToJDL(entity: JSONEntity) {
});
}

function getEnumValuesFromString(valuesAsString: string) {
function getEnumValuesFromString(valuesAsString: string): any {
return valuesAsString.split(',').map(fieldValue => {
// if fieldValue looks like ENUM_VALUE (something)
if (fieldValue.includes('(')) {
Expand All @@ -165,8 +165,8 @@ function getEnumValuesFromString(valuesAsString: string) {
* Adds relationships for entities to JDL.
* The jdl passed must contain the jdl entities concerned by the relationships
*/
function addRelationshipsToJDL() {
entities.forEach((entity, entityName) => {
function addRelationshipsToJDL(): void {
entities.forEach((entity: JSONEntity, entityName: string) => {
dealWithRelationships(entity.relationships, entityName);
});
}
Expand Down Expand Up @@ -221,7 +221,7 @@ function getRelationship(relationship: JSONRelationship, entityName: string) {
}
return undefined;
}
const isEntityTheDestinationSideEntity = (otherEntityName, otherEntityRelationshipName) =>
const isEntityTheDestinationSideEntity = (otherEntityName: string, otherEntityRelationshipName: string) =>
otherEntityName === entityName && otherEntityRelationshipName === relationship.relationshipName;
const destinationSideAttributes = getDestinationEntitySideAttributes(isEntityTheDestinationSideEntity, destinationEntity.relationships);
relationshipConfiguration = {
Expand All @@ -233,7 +233,7 @@ function getRelationship(relationship: JSONRelationship, entityName: string) {
return new JDLRelationship(relationshipConfiguration);
}

function getSourceEntitySideAttributes(entityName: string, relationship: JSONRelationship) {
function getSourceEntitySideAttributes(entityName: string, relationship: JSONRelationship): JDLSourceEntitySide {
return {
sourceEntity: entityName,
injectedFieldInSourceEntity: getInjectedFieldInSourceEntity(relationship),
Expand All @@ -242,7 +242,10 @@ function getSourceEntitySideAttributes(entityName: string, relationship: JSONRel
};
}

function getDestinationEntitySideAttributes(isEntityTheDestinationSideEntity, destinationEntityRelationships?) {
function getDestinationEntitySideAttributes(
isEntityTheDestinationSideEntity: (otherEntityName: string, otherEntityRelationshipName: string) => boolean,
destinationEntityRelationships?: JSONRelationship[],
) {
const foundDestinationSideEntity = destinationEntityRelationships?.find(destinationEntityFromRelationship => {
return isEntityTheDestinationSideEntity(
upperFirst(destinationEntityFromRelationship.otherEntityName),
Expand Down Expand Up @@ -278,14 +281,14 @@ function getRelationshipOptions(relationship: JSONRelationship): JDLRelationship
return options;
}

function getInjectedFieldInSourceEntity(relationship: JSONRelationship) {
function getInjectedFieldInSourceEntity(relationship: JSONRelationship): string {
return (
relationship.relationshipName +
(relationship.otherEntityField && relationship.otherEntityField !== 'id' ? `(${relationship.otherEntityField})` : '')
);
}

function addEntityOptionsToJDL(entity: JSONEntity, entityName: string) {
function addEntityOptionsToJDL(entity: JSONEntity, entityName: string): void {
if (entity.fluentMethods === false) {
addUnaryOptionToJDL(NO_FLUENT_METHOD, entityName);
}
Expand Down Expand Up @@ -319,7 +322,7 @@ function addEntityOptionsToJDL(entity: JSONEntity, entityName: string) {
}
}

function addUnaryOptionToJDL(unaryOption: string, entityName: string) {
function addUnaryOptionToJDL(unaryOption: string, entityName: string): void {
jdlObject.addOption(
new JDLUnaryOption({
name: unaryOption,
Expand All @@ -328,7 +331,7 @@ function addUnaryOptionToJDL(unaryOption: string, entityName: string) {
);
}

function addBinaryOptionToJDL(binaryOption: string, value: string, entityName: string) {
function addBinaryOptionToJDL(binaryOption: string, value: string, entityName: string): void {
jdlObject.addOption(
new JDLBinaryOption({
name: binaryOption,
Expand Down
61 changes: 61 additions & 0 deletions jdl/converters/types.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { RelationshipType, RelationshipSide } from '../basic-types/relationships.js';
import { GENERATOR_JHIPSTER } from '../../generators/index.js';

export type JSONField = {
fieldName: string;
fieldType: string;
documentation?: string;
options?: Record<string, boolean | string | number>;
fieldValidateRules?: string[];
} & Record<string, any>;

export type JSONFieldEnum = JSONField & {
Expand All @@ -30,3 +32,62 @@ export type JSONEntity = {
fields?: JSONField[];
relationships?: JSONRelationship[];
} & Record<string, any>;

export type JSONBlueprint = {
name: string;
version?: string;
} & Record<string, any>;

export type JSONMicrofrontend = {
baseName: string;
};

export type JSONGeneratorJhipsterContent = {
baseName: string;
applicationType?: string;
entities?: string[];
jhipsterVersion?: string;
packageName?: string;
packageFolder?: string;
serverPort?: string;
authenticationType?: string;
buildTool?: string;
cacheProvider?: string;
clientPackageManager?: string;
creationTimestamp?: number;
databaseType?: string;
devDatabaseType?: string;
enableHibernateCache?: boolean;
enableTranslation?: boolean;
jhiPrefix?: string;
jwtSecretKey?: string;
languages?: string[];
messageBroker?: string;
nativeLanguage?: string;
prodDatabaseType?: string;
searchEngine?: string;
serviceDiscoveryType?: string;
skipClient?: boolean;
skipUserManagement?: boolean;
testFrameworks?: string[];
websocket?: string;
promptValues?: Partial<JSONGeneratorJhipsterContent>;
blueprints?: JSONBlueprint[] | null;
microfrontends?: JSONMicrofrontend[] | null;
} & Record<string, any>;

export type PostProcessedJSONGeneratorJhipsterContent = Omit<
JSONGeneratorJhipsterContent,
'promptValues' | 'blueprints' | 'microfrontends'
> & {
blueprints?: string[];
microfrontends?: string[];
};

export type PostProcessedJSONRootObject = {
[GENERATOR_JHIPSTER]: PostProcessedJSONGeneratorJhipsterContent;
};

export type JSONRootObject = {
[GENERATOR_JHIPSTER]: JSONGeneratorJhipsterContent;
};
Loading
Loading