Skip to content

Commit

Permalink
generate server DTOs
Browse files Browse the repository at this point in the history
  • Loading branch information
tomsontom committed Oct 26, 2024
1 parent 7f50c58 commit 30b1e3d
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 0 deletions.
11 changes: 11 additions & 0 deletions dsl/src/cli/java-gen-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ export type JavaRestClientJDKGeneratorConfig = ArtifactGenerationConfig & {
nativeTypeSubstitues?: Record<string, string>
}

export type JavaServerJakartaWSConfig = ArtifactGenerationConfig & {
targetFolder: string
rootPackageName: string
nativeTypeSubstitues?: Record<string, string>
}

export function isJavaClientAPIGeneratorConfig(config: ArtifactGeneratorConfig): config is JavaClientAPIGeneratorConfig {
return 'targetFolder' in config && typeof config.targetFolder === 'string'
Expand All @@ -77,6 +82,12 @@ export function isJavaRestClientJDKGeneratorConfig(config: ArtifactGeneratorConf
&& 'rootPackageName' in config && typeof config.rootPackageName === 'string';
}

export function isJavaServerJakartaWSConfig(config: ArtifactGeneratorConfig): config is JavaServerJakartaWSConfig {
return 'targetFolder' in config && typeof config.targetFolder === 'string'
&& 'rootPackageName' in config && typeof config.rootPackageName === 'string';
}


export function generateCompilationUnit(packageName: string, importCollector: JavaImportsCollector, content: CompositeGeneratorNode) {
const node = new CompositeGeneratorNode()
node.append('// Generated by RSD - Do not modify',NL)
Expand Down
36 changes: 36 additions & 0 deletions dsl/src/cli/java-server-jakarta-ws/enum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { CompositeGeneratorNode, NL, toString } from "langium/generate";

import { Artifact } from "../artifact-generator.js";
import { JavaServerJakartaWSConfig, toPath } from "../java-gen-utils.js";
import { MEnumType, MInlineEnumType } from "../model.js";

export function generateEnum(t: MEnumType, artifactConfig: JavaServerJakartaWSConfig): Artifact {
const packageName = `${artifactConfig.rootPackageName}.dto`;

const node = new CompositeGeneratorNode()
node.append('// Generated by RSD - Do not modify',NL)
node.append(`package ${packageName};`, NL, NL)
node.append(`public enum ${t.name} {`, NL)
node.indent( child => {
t.entries.forEach( e => child.append(e.name, ',', NL) )
} )
node.append('}',NL)

return {
name: `${t.name}.java`,
content: toString(node),
path: toPath(artifactConfig.targetFolder, packageName)
};
}

export function generateInlineEnum(t: MInlineEnumType, name: string, node: CompositeGeneratorNode) {
node.indent( child => {
child.append(`public enum ${name} {`, NL);
child.indent( enumBody => {
t.entries.forEach( e => {
enumBody.append(`${e.name},`, NL);
})
} )
child.append('}', NL, NL)
});
}
36 changes: 36 additions & 0 deletions dsl/src/cli/java-server-jakarta-ws/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import chalk from "chalk";
import { isMEnumType, isMRecordType, isMUnionType, MResolvedRSDModel, MResolvedUserType } from "../model.js";
import { Artifact, ArtifactGenerationConfig, ArtifactGeneratorConfig } from "../artifact-generator.js";
import { isJavaServerJakartaWSConfig, JavaServerJakartaWSConfig } from "../java-gen-utils.js";
import { isDefined } from "../util.js";
import { generateRecord } from "./record.js";
import { generateUnion } from "./union.js";

export function generate(model: MResolvedRSDModel, generatorConfig: ArtifactGenerationConfig, artifactConfig: ArtifactGeneratorConfig): readonly Artifact [] {
console.log(chalk.cyan('Generating Java-Server-Jakarta-WS'));

if( ! isJavaServerJakartaWSConfig(artifactConfig) ) {
console.log(chalk.red(' Invalid configuration passed aborted artifact generation'));
return [];
}

const result = model.elements.map( e => generateType(e, artifactConfig) ).filter(isDefined)

return result;
}

function generateType(t: MResolvedUserType, artifactConfig: JavaServerJakartaWSConfig): Artifact | undefined {
if( isMEnumType(t) ) {

} else if( isMRecordType(t) ) {
return generateRecord(t, artifactConfig);
} else if( isMUnionType(t) ) {
return generateUnion(t, artifactConfig)
}
return undefined;
}

export default {
name: 'java-server-jakarta-ws',
generate
}
70 changes: 70 additions & 0 deletions dsl/src/cli/java-server-jakarta-ws/record.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { CompositeGeneratorNode, NL, toString } from "langium/generate";
import { Artifact } from "../artifact-generator.js";
import { builtinToJavaType, generateCompilationUnit, JavaImportsCollector, JavaServerJakartaWSConfig, resolveObjectType, resolveType, toPath } from "../java-gen-utils.js";
import { allRecordProperties, isMInlineEnumType, isMKeyProperty, isMProperty, isMRevisionProperty, MResolvedRecordType } from "../model.js";
import { toFirstUpper } from "../util.js";
import { generateInlineEnum } from "./enum.js";

export function generateRecord(t: MResolvedRecordType, artifactConfig: JavaServerJakartaWSConfig): Artifact | undefined {
if( t.resolved.unions.length === 1 ) {
return undefined;
}
const packageName = `${artifactConfig.rootPackageName}.dto`;

const importCollector = new JavaImportsCollector(packageName);
const fqn = importCollector.importType.bind(importCollector);

return {
name: `${t.name}DTO.java`,
content: toString(generateCompilationUnit(packageName, importCollector, generateRecordContent(t, artifactConfig, fqn))),
path: toPath(artifactConfig.targetFolder, packageName)
};
}

export function generateRecordContent(t: MResolvedRecordType, artifactConfig: JavaServerJakartaWSConfig, fqn: (type: string) => string): CompositeGeneratorNode {
const node = new CompositeGeneratorNode();

const allProps = allRecordProperties(t);

node.append(`public record ${t.name}DTO(`,NL)
node.indent( param => {
allProps.forEach( (property, idx, arr) => {
const end = idx + 1 < arr.length ? ',' : ') {'
if( isMKeyProperty(property) ) {
param.append(`${builtinToJavaType(property.type, fqn)} ${property.name}`, end, NL)
} else if( isMRevisionProperty(property) ) {
param.append(`${builtinToJavaType(property.type, fqn)} ${property.name}`, end, NL)
} else {
if( property.variant === 'union' || property.variant === 'record' ) {
if( property.array ) {
param.append(`${fqn('java.util.List')}<${property.type}DTO> ${property.name}`, end, NL)
} else {
param.append(`${property.type}DTO ${property.name}`, end, NL)
}
} else if( typeof property.type === 'string' ) {
if( property.array ) {
param.append(`${fqn('java.util.List')}<${resolveObjectType(property.type, artifactConfig.nativeTypeSubstitues, fqn)}> ${property.name}`, end, NL)
} else {
param.append(`${resolveType(property.type, artifactConfig.nativeTypeSubstitues, fqn)} ${property.name}`, end, NL)
}
} else {
param.append(`${toFirstUpper(property.name)} ${property.name}`, end, NL)
}
}
});
})

allProps
.filter(isMProperty)
.filter(p => p.variant === 'inline-enum')
.forEach( p => {
const inlineEnum = p.type;
if( isMInlineEnumType(inlineEnum) ) {
generateInlineEnum(inlineEnum, toFirstUpper(p.name), node)
}
});

node.append('}')

return node;
}
108 changes: 108 additions & 0 deletions dsl/src/cli/java-server-jakarta-ws/union.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { CompositeGeneratorNode, IndentNode, NL, toString } from "langium/generate";
import { Artifact } from "../artifact-generator.js";
import { builtinToJavaType, generateCompilationUnit, JavaImportsCollector, JavaServerJakartaWSConfig, resolveObjectType, resolveType, toPath } from "../java-gen-utils.js";
import { allRecordProperties, isMInlineEnumType, isMKeyProperty, isMProperty, isMRevisionProperty, MKeyProperty, MProperty, MResolvedRecordType, MResolvedUnionType, MRevisionProperty } from "../model.js";
import { generateInlineEnum } from "./enum.js";
import { toFirstUpper } from "../util.js";

export function generateUnion(t: MResolvedUnionType, artifactConfig: JavaServerJakartaWSConfig): Artifact {
const packageName = `${artifactConfig.rootPackageName}.dto`;

const importCollector = new JavaImportsCollector(packageName);
const fqn = importCollector.importType.bind(importCollector);

const JsonbTypeInfo = fqn('jakarta.json.bind.annotation.JsonbTypeInfo');
const JsonbSubtype = fqn('jakarta.json.bind.annotation.JsonbSubtype');

const childRecords = t.resolved.records.filter(r => r.resolved.unions.length === 1);
const node = new CompositeGeneratorNode();
node.append(`@${JsonbTypeInfo}({`,NL)
node.indent( child => {
childRecords.forEach( r => {
const desc = (t.descriminatorAliases ?? {})[r.name] ?? r.name;
child.append(`@${JsonbSubtype}(alias = "${desc}", type = ${t.name}DTO.${r.name}DTO.class),`,NL);
} )
} );

node.append('})',NL)
node.append(`public abstract class ${t.name}DTO {`,NL)
t.resolved.sharedProps
.filter(isMProperty)
.filter(p => p.variant === 'inline-enum')
.forEach( p => {
const inlineEnum = p.type;
if( isMInlineEnumType(inlineEnum) ) {
generateInlineEnum(inlineEnum, toFirstUpper(p.name), node)
}
});


node.indent( child => {
t.resolved.sharedProps.forEach( p => generateProperty(child, p, artifactConfig, fqn) )
})


node.indent( child => {
childRecords.forEach( r => {
child.appendNewLine()
generateUnionRecordContent(child, r, t, artifactConfig, fqn);
} )
})


node.append('}');

return {
name: `${t.name}DTO.java`,
content: toString(generateCompilationUnit(packageName, importCollector, node)),
path: toPath(artifactConfig.targetFolder, packageName)
};
}

function generateUnionRecordContent(node: IndentNode, t: MResolvedRecordType, p: MResolvedUnionType, artifactConfig: JavaServerJakartaWSConfig, fqn: (type: string) => string) {
node.append(`public static class ${t.name}DTO extends ${p.name}DTO {`,NL)

const sharedProps = t.resolved.unions.flatMap(u => u.resolved.sharedProps);

const allProps = allRecordProperties(t);
allProps
.filter(isMProperty)
.filter(p => p.variant === 'inline-enum')
.filter(p => !sharedProps.includes(p))
.forEach( p => {
const inlineEnum = p.type;
if( isMInlineEnumType(inlineEnum) ) {
generateInlineEnum(inlineEnum, toFirstUpper(p.name), node)
}
});

node.indent( child => {
allProps.filter(p => !sharedProps.includes(p)).forEach( p => generateProperty(child, p, artifactConfig, fqn))
})

node.append('}',NL)
}

function generateProperty(node: IndentNode, property: MKeyProperty | MRevisionProperty | MProperty, artifactConfig: JavaServerJakartaWSConfig, fqn: (type: string) => string) {
if( isMKeyProperty(property) ) {
node.append(`public ${builtinToJavaType(property.type, fqn)} ${property.name};`,NL)
} else if( isMRevisionProperty(property) ) {
node.append(`public ${builtinToJavaType(property.type, fqn)} ${property.name};`,NL)
} else {
if( property.variant === 'union' || property.variant === 'record' ) {
if( property.array ) {
node.append(`public ${fqn('java.util.List')}<${property.type}DTO> ${property.name};`,NL)
} else {
node.append(`public ${property.type}DTO ${property.name};`,NL)
}
} else if( typeof property.type === 'string' ) {
if( property.array ) {
node.append(`public ${fqn('java.util.List')}<${resolveObjectType(property.type, artifactConfig.nativeTypeSubstitues, fqn)}> ${property.name};`,NL)
} else {
node.append(`public ${resolveType(property.type, artifactConfig.nativeTypeSubstitues, fqn)} ${property.name};`,NL)
}
} else {
node.append(`public ${toFirstUpper(property.name)} ${property.name};`,NL)
}
}
}
2 changes: 2 additions & 0 deletions dsl/src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { Artifact, ArtifactGenerator, isArtifactGenerationConfig } from './artif

import JavaClientAPI from './java-client-api/generator.js';
import JavaRestClientJDK from './java-rest-client-jdk/generator.js';
import JavaServerJakartaWS from './java-server-jakarta-ws/generator.js';

import { existsSync } from 'node:fs';

Expand All @@ -26,6 +27,7 @@ const packageContent = await fs.readFile(packagePath, 'utf-8');
const generatorRegistry = new Map<string, ArtifactGenerator>();
generatorRegistry.set(JavaClientAPI.name, JavaClientAPI);
generatorRegistry.set(JavaRestClientJDK.name, JavaRestClientJDK);
generatorRegistry.set(JavaServerJakartaWS.name, JavaServerJakartaWS);

export const generateAction = async (fileName: string, opts: ModelGenerateOptions): Promise<void> => {
const services = createRemoteServiceDescriptionServices(NodeFileSystem);
Expand Down

0 comments on commit 30b1e3d

Please sign in to comment.