gen-jhipster
Version:
VHipster - Spring Boot + Angular/React/Vue in one handy generator
282 lines (281 loc) • 12.6 kB
JavaScript
/**
* 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 * as _ from 'lodash-es';
import BaseApplicationGenerator from "../base-application/index.js";
import writeEntitiesTasks, { cleanupEntitiesTask } from "./entity-files.js";
import writeGrpcFilesTask from "./files.js";
export default class GrpcGenerator extends BaseApplicationGenerator {
grpcConfig;
enableGrpc;
async beforeQueue() {
if (!this.fromBlueprint) {
await this.composeWithBlueprints();
}
if (!this.delegateToBlueprint) {
await this.dependsOnBootstrapApplication();
}
}
get initializing() {
return this.asInitializingTaskGroup({
async loadConfig() {
const config = this.jhipsterConfigWithDefaults;
const grpcEnabled = Boolean(config.enableGrpc ?? config.grpcEnabled);
this.grpcConfig = {
grpcEnabled,
grpcPort: config.grpcPort,
grpcSecured: config.grpcSecured,
grpcUseSsl: config.grpcUseSsl,
grpcCertificatePath: config.grpcCertificatePath,
grpcPrivateKeyPath: config.grpcPrivateKeyPath,
};
// Must match jhipsterConfigWithDefaults (same as entity-files writing), not raw jhipsterConfig only.
// Otherwise when enableGrpc is omitted from .yo-rc.json, defaults turn it on for writes but
// preparingEachEntity skips and fieldProtobufType is never set → invalid proto ("optional id = 1").
this.enableGrpc = grpcEnabled;
},
});
}
get [BaseApplicationGenerator.INITIALIZING]() {
return this.delegateTasksToBlueprint(() => this.initializing);
}
get prompting() {
return this.asPromptingTaskGroup({
async showPrompts() {
// gRPC prompts handled in spring-boot askForVhipsterOpts for correct prompt order
},
});
}
get [BaseApplicationGenerator.PROMPTING]() {
return this.delegateTasksToBlueprint(() => this.prompting);
}
get preparing() {
return this.asPreparingTaskGroup({
async preparing() {
// gRPC-specific preparing if needed
},
});
}
get [BaseApplicationGenerator.PREPARING]() {
return this.delegateTasksToBlueprint(() => this.preparing);
}
get preparingEachEntity() {
return this.asPreparingEachEntityTaskGroup({
prepareEntity({ entity }) {
if (this.enableGrpc && entity.fields) {
for (const field of entity.fields) {
const f = field;
if (f.fieldTypeBlobContent === 'text') {
f.fieldDomainType = 'String';
}
else {
f.fieldDomainType = f.fieldType;
}
f.fieldTypeUpperUnderscored = _.snakeCase(String(f.fieldType)).toUpperCase();
f.fieldProtobufType = getProtobufType(String(f.fieldDomainType));
f.isProtobufCustomType = isProtobufCustomType(String(f.fieldProtobufType));
}
entity.isFilterableType = (fieldType) => !['byte[]', 'ByteBuffer'].includes(fieldType);
}
},
});
}
get [BaseApplicationGenerator.PREPARING_EACH_ENTITY]() {
return this.delegateTasksToBlueprint(() => this.preparingEachEntity);
}
get preparingEachEntityRelationship() {
return this.asPreparingEachEntityRelationshipTaskGroup({
prepareRelationship({ application, relationship }) {
if (this.enableGrpc) {
const rel = relationship;
const otherEntityNameCapitalized = _.upperFirst(relationship.otherEntityName);
const otherEntityNameUnderscored = _.snakeCase(relationship.otherEntityName).toLowerCase();
const otherEntityFieldUnderscored = relationship.otherEntityField ? _.snakeCase(relationship.otherEntityField).toLowerCase() : '';
rel.relationshipNameUnderscored = _.snakeCase(relationship.relationshipName).toLowerCase();
rel.otherEntityNameUnderscored = otherEntityNameUnderscored;
rel.otherEntityFieldUnderscored = otherEntityFieldUnderscored;
rel.otherEntityNameCapitalized = otherEntityNameCapitalized;
if (otherEntityNameCapitalized === 'User') {
rel.otherEntityProtobufType = 'entity.UserProto';
rel.otherEntityProtoMapper = `${application.packageName}.web.grpc.mapper.UserProtoMapper`;
rel.otherEntityTest = `${application.packageName}.web.grpc.service.UserGrpcServiceIntTest`;
rel.otherEntityProtobufFile = 'entity/user.proto';
}
else {
rel.otherEntityProtobufType = `entity.${otherEntityNameCapitalized}Proto`;
rel.otherEntityProtoMapper = `${application.packageName}.web.grpc.mapper.${otherEntityNameCapitalized}ProtoMapper`;
rel.otherEntityTest = `${application.packageName}.web.grpc.service.${otherEntityNameCapitalized}GrpcServiceIntTest`;
rel.otherEntityProtobufFile = `entity/${otherEntityNameUnderscored}.proto`;
}
}
},
});
}
get [BaseApplicationGenerator.PREPARING_EACH_ENTITY_RELATIONSHIP]() {
return this.delegateTasksToBlueprint(() => this.preparingEachEntityRelationship);
}
get writing() {
return {
writeGrpcFilesTask,
};
}
get [BaseApplicationGenerator.WRITING]() {
return this.delegateTasksToBlueprint(() => this.writing);
}
get writingEntities() {
return {
cleanupEntitiesTask,
writeEntitiesTasks,
};
}
get [BaseApplicationGenerator.WRITING_ENTITIES]() {
return this.delegateTasksToBlueprint(() => this.writingEntities);
}
get postWriting() {
return this.asPostWritingTaskGroup({
addDependencies({ source, application }) {
source.addJavaDependencies?.([
{ groupId: 'net.devh', artifactId: 'grpc-spring-boot-starter', version: '3.1.0.RELEASE' },
{ groupId: 'javax.annotation', artifactId: 'javax.annotation-api', version: '1.3.2' },
{ groupId: 'build.buf', artifactId: 'protovalidate', version: '0.5.0' },
]);
if (application.reactive) {
source.addJavaDependencies?.([
{ groupId: 'com.salesforce.servicelibs', artifactId: 'reactor-grpc-stub', version: '1.2.4' },
]);
}
},
addPlugins({ source, application }) {
if (this.jhipsterConfig.buildTool === 'maven' || application.buildToolMaven) {
if (application.reactive) {
source.addMavenPlugin?.({
groupId: 'org.xolstice.maven.plugins',
artifactId: 'protobuf-maven-plugin',
version: '0.6.1',
additionalContent: `
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.23.4:exe:\${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:\${os.detected.classifier}
</pluginArtifact>
<useArgumentFile>true</useArgumentFile>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
<configuration>
<protocPlugins>
<protocPlugin>
<id>reactor-grpc</id>
<groupId>com.salesforce.servicelibs</groupId>
<artifactId>reactor-grpc</artifactId>
<version>1.2.4</version>
<mainClass>com.salesforce.reactorgrpc.ReactorGrpcGenerator</mainClass>
</protocPlugin>
</protocPlugins>
</configuration>
</execution>
</executions>`,
});
}
else {
source.addMavenPlugin?.({
groupId: 'org.xolstice.maven.plugins',
artifactId: 'protobuf-maven-plugin',
version: '0.6.1',
additionalContent: `
<configuration>
<protocArtifact>com.google.protobuf:protoc:3.23.4:exe:\${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>io.grpc:protoc-gen-grpc-java:1.58.0:exe:\${os.detected.classifier}
</pluginArtifact>
<useArgumentFile>true</useArgumentFile>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</configuration>
</execution>
</executions>`,
});
}
source.addMavenPlugin?.({
groupId: 'kr.motd.maven',
artifactId: 'os-maven-plugin',
version: '1.7.1',
additionalContent: `
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>detect</goal>
</goals>
</execution>
</executions>`,
});
}
},
});
}
get [BaseApplicationGenerator.POST_WRITING]() {
return this.delegateTasksToBlueprint(() => this.postWriting);
}
}
function getProtobufType(type) {
switch (type) {
case 'String':
case 'Float':
case 'Double':
return type.toLowerCase();
case 'Integer':
return 'int32';
case 'Long':
return 'int64';
case 'Boolean':
return 'bool';
case 'Instant':
case 'OffsetDateTime':
case 'ZonedDateTime':
case 'LocalDateTime':
case 'Date':
return 'google.protobuf.Timestamp';
case 'LocalDate':
return 'util.Date';
case 'BigDecimal':
return 'util.Decimal';
case 'byte[]':
case 'ByteBuffer':
return 'bytes';
case 'UUID':
return 'string';
default:
// It's an enum
return `${type}Proto`;
}
}
function isProtobufCustomType(type) {
return type.startsWith('google') || type.startsWith('util');
}