generator-begcode
Version:
Spring Boot + Angular/React/Vue in one handy generator
251 lines (250 loc) • 13.6 kB
JavaScript
import { intersection } from 'lodash-es';
import BaseApplicationGenerator from '../base-application/index.js';
import { createDockerComposeFile, createDockerExtendedServices } from '../docker/support/index.js';
import { createFaker, stringHashCode } from '../base/support/index.js';
import { getJdbcUrl, getR2dbcUrl } from '../spring-data-relational/support/index.js';
import { dockerFiles } from './files.js';
import { SERVICE_COMPLETED_SUCCESSFULLY, SERVICE_HEALTHY } from './constants.js';
export default class DockerGenerator extends BaseApplicationGenerator {
hasServicesFile = false;
async beforeQueue() {
if (!this.fromBlueprint) {
await this.composeWithBlueprints();
}
if (!this.delegateToBlueprint) {
await this.dependsOnBootstrapApplicationServer();
}
}
get loading() {
return this.asLoadingTaskGroup({
loading({ applicationDefaults }) {
applicationDefaults({
dockerServices: [],
});
},
});
}
get [BaseApplicationGenerator.LOADING]() {
return this.delegateTasksToBlueprint(() => this.loading);
}
get preparing() {
return this.asPreparingTaskGroup({
dockerServices({ application }) {
const dockerServices = application.dockerServices;
if (application.authenticationTypeOauth2) {
dockerServices.push('keycloak');
}
if (application.searchEngineElasticsearch) {
dockerServices.push('elasticsearch');
}
if (application.messageBrokerKafka || application.messageBrokerPulsar) {
dockerServices.push(application.messageBroker);
}
if (application.serviceDiscoveryConsul || application.serviceDiscoveryEureka || application.serviceDiscoveryNacos) {
dockerServices.push(application.serviceDiscoveryType);
}
if (application.serviceDiscoveryAny || application.applicationTypeGateway || application.applicationTypeMicroservice) {
dockerServices.push('zipkin');
}
if (application.enableSwaggerCodegen) {
dockerServices.push('swagger-editor');
}
if (application.cacheProviderMemcached || application.cacheProviderRedis || application.cacheProviderHazelcast) {
dockerServices.push(application.cacheProvider);
}
if (application.databaseTypeCassandra ||
application.databaseTypeCouchbase ||
application.databaseTypeMongodb ||
application.databaseTypeNeo4j) {
dockerServices.push(application.databaseType);
}
if (application.prodDatabaseTypePostgresql ||
application.prodDatabaseTypeMariadb ||
application.prodDatabaseTypeMysql ||
application.prodDatabaseTypeMssql) {
dockerServices.push(application.prodDatabaseType);
}
},
addAppServices({ application, source }) {
const writeInitialServicesFile = () => {
if (!this.hasServicesFile) {
const dockerFile = createDockerComposeFile(application.lowercaseBaseName);
this.writeDestination(`${application.dockerServicesDir}services.yml`, dockerFile);
}
this.hasServicesFile = true;
};
source.addDockerExtendedServiceToApplicationAndServices = (...services) => {
const extendedServices = createDockerExtendedServices(...services);
writeInitialServicesFile();
this.mergeDestinationYaml(`${application.dockerServicesDir}services.yml`, extendedServices);
this.mergeDestinationYaml(`${application.dockerServicesDir}app.yml`, extendedServices);
};
source.addDockerExtendedServiceToServices = (...services) => {
const extendedServices = createDockerExtendedServices(...services);
writeInitialServicesFile();
this.mergeDestinationYaml(`${application.dockerServicesDir}services.yml`, extendedServices);
};
source.addDockerExtendedServiceToApplication = (...services) => {
const extendedServices = createDockerExtendedServices(...services);
this.mergeDestinationYaml(`${application.dockerServicesDir}app.yml`, extendedServices);
};
source.addDockerDependencyToApplication = (...services) => {
this.mergeDestinationYaml(`${application.dockerServicesDir}app.yml`, {
services: {
app: {
depends_on: Object.fromEntries(services.map(({ serviceName, condition }) => [
serviceName,
{
condition,
},
])),
},
},
});
};
},
});
}
get [BaseApplicationGenerator.PREPARING]() {
return this.preparing;
}
get writing() {
return this.asWritingTaskGroup({
async writeDockerFiles({ application }) {
if (application.authenticationTypeOauth2) {
const faker = await createFaker();
faker.seed(stringHashCode(application.baseName));
application.keycloakSecrets = Array.from(Array(6), () => faker.string.uuid());
}
await this.writeFiles({
sections: dockerFiles,
context: application,
});
},
});
}
get [BaseApplicationGenerator.WRITING]() {
return this.writing;
}
get postWriting() {
return this.asPostWritingTaskGroup({
async dockerServices({ application, source }) {
if (application.dockerServices.includes('cassandra')) {
const serviceName = application.databaseType;
source.addDockerExtendedServiceToApplicationAndServices({ serviceName }, { serviceFile: './cassandra.yml', serviceName: 'cassandra-migration' });
source.addDockerDependencyToApplication({ serviceName, condition: SERVICE_HEALTHY }, { serviceName: 'cassandra-migration', condition: SERVICE_COMPLETED_SUCCESSFULLY });
}
for (const serviceName of intersection(['postgresql', 'mysql', 'mariadb', 'mssql'], application.dockerServices)) {
const profiles = application.prodDatabaseType === application.devDatabaseType ? undefined : ['', 'prod'];
source.addDockerExtendedServiceToApplication({ serviceName });
source.addDockerExtendedServiceToServices({ serviceName, additionalConfig: { profiles } });
source.addDockerDependencyToApplication({ serviceName, condition: SERVICE_HEALTHY });
}
for (const serviceName of intersection(['couchbase', 'mongodb', 'neo4j', 'elasticsearch', 'keycloak'], application.dockerServices)) {
source.addDockerExtendedServiceToApplicationAndServices({ serviceName });
source.addDockerDependencyToApplication({ serviceName, condition: SERVICE_HEALTHY });
}
for (const serviceName of application.dockerServices.filter(service => ['redis', 'memcached', 'pulsar'].includes(service))) {
source.addDockerExtendedServiceToApplicationAndServices({ serviceName });
}
if (application.dockerServices.includes('eureka')) {
const depends_on = application.authenticationTypeOauth2
? {
keycloak: {
condition: SERVICE_HEALTHY,
},
}
: undefined;
source.addDockerExtendedServiceToApplicationAndServices({
serviceName: 'jhipster-registry',
additionalConfig: {
depends_on,
},
});
source.addDockerDependencyToApplication({ serviceName: 'jhipster-registry', condition: SERVICE_HEALTHY });
}
if (application.dockerServices.includes('consul')) {
source.addDockerExtendedServiceToApplicationAndServices({ serviceName: 'consul' }, { serviceFile: './consul.yml', serviceName: 'consul-config-loader' });
}
if (application.dockerServices.includes('kafka')) {
source.addDockerExtendedServiceToApplicationAndServices({ serviceName: 'kafka' });
}
},
packageJsonScripts({ application }) {
const scriptsStorage = this.packageJson.createStorage('scripts');
const { databaseType, databaseTypeSql, prodDatabaseType, prodDatabaseTypeNo, prodDatabaseTypeOracle } = application;
let postServicesSleep;
if (databaseTypeSql) {
if (prodDatabaseTypeNo || prodDatabaseTypeOracle) {
scriptsStorage.set('docker:db:up', `echo "Docker for db ${prodDatabaseType} not configured for application ${application.baseName}"`);
}
else {
const dockerFile = `${application.dockerServicesDir}${prodDatabaseType}.yml`;
scriptsStorage.set({
'docker:db:up': `docker compose -f ${dockerFile} up --wait`,
'docker:db:down': `docker compose -f ${dockerFile} down -v`,
});
}
}
else {
const dockerFile = `${application.dockerServicesDir}${databaseType}.yml`;
if (this.fs.exists(this.destinationPath(dockerFile))) {
scriptsStorage.set({
'docker:db:up': `docker compose -f ${dockerFile} up --wait`,
'docker:db:down': `docker compose -f ${dockerFile} down -v`,
});
if (application.databaseTypeCassandra) {
postServicesSleep = 10;
}
}
else {
scriptsStorage.set('docker:db:up', `echo "Docker for db ${databaseType} not configured for application ${application.baseName}"`);
}
}
['keycloak', 'elasticsearch', 'kafka', 'consul', 'redis', 'memcached', 'jhipster-registry'].forEach(dockerConfig => {
const dockerFile = `${application.dockerServicesDir}${dockerConfig}.yml`;
if (this.fs.exists(this.destinationPath(dockerFile))) {
scriptsStorage.set(`docker:${dockerConfig}:up`, `docker compose -f ${dockerFile} up --wait`);
scriptsStorage.set(`docker:${dockerConfig}:down`, `docker compose -f ${dockerFile} down -v`);
}
});
if (postServicesSleep) {
scriptsStorage.set({
'postservices:up': `sleep ${postServicesSleep}`,
});
}
if (this.hasServicesFile) {
scriptsStorage.set({
'services:up': `docker compose -f ${application.dockerServicesDir}services.yml up --wait`,
'ci:e2e:teardown:docker': `docker compose -f ${application.dockerServicesDir}services.yml down -v && docker ps -a`,
});
}
scriptsStorage.set({
'app:up': `docker compose -f ${application.dockerServicesDir}app.yml up --wait`,
'ci:e2e:prepare:docker': 'npm run services:up --if-present && docker ps -a',
'ci:e2e:prepare': 'npm run ci:e2e:prepare:docker',
'ci:e2e:teardown': 'npm run ci:e2e:teardown:docker --if-present',
});
},
});
}
get [BaseApplicationGenerator.POST_WRITING]() {
return this.asPostWritingTaskGroup(this.delegateToBlueprint ? {} : this.postWriting);
}
get end() {
return this.asEndTaskGroup({
async dockerComposeUp({ control }) {
if (!control.enviromentHasDockerCompose) {
this.log('');
this.log.warn('Docker Compose V2 is not installed on your computer. Some features may not work as expected. Read https://docs.docker.com/compose/install/');
}
},
});
}
getJDBCUrl(databaseType, options = {}) {
return getJdbcUrl(databaseType, options);
}
getR2DBCUrl(databaseType, options = {}) {
return getR2dbcUrl(databaseType, options);
}
}