Skip to content

Commit

Permalink
java: add java:server generator (#27839)
Browse files Browse the repository at this point in the history
  • Loading branch information
mshima authored Nov 7, 2024
1 parent b6a70f7 commit c7b2c68
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 143 deletions.
3 changes: 3 additions & 0 deletions generators/bootstrap-application-base/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ export default class BootstrapApplicationBase extends BaseApplicationGenerator {
this.fetchFromInstalledJHipster(GENERATOR_COMMON, 'resources', 'package.json'),
);
},
loadPackageJson({ application }) {
application.jhipsterPackageJson = packageJson;
},
});
}

Expand Down
26 changes: 26 additions & 0 deletions generators/java/generators/bootstrap/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,32 @@ export default class BootstrapGenerator extends BaseApplicationGenerator {
return this.delegateTasksToBlueprint(() => this.configuring);
}

get loading() {
return this.asLoadingTaskGroup({
loadEnvironmentVariables({ application }) {
application.packageInfoJavadocs?.push(
{ packageName: `${application.packageName}.aop.logging`, documentation: 'Logging aspect.' },
{ packageName: `${application.packageName}.management`, documentation: 'Application management.' },
{ packageName: `${application.packageName}.repository.rowmapper`, documentation: 'Webflux database column mapper.' },
{ packageName: `${application.packageName}.security`, documentation: 'Application security utilities.' },
{ packageName: `${application.packageName}.service.dto`, documentation: 'Data transfer objects for rest mapping.' },
{ packageName: `${application.packageName}.service.mapper`, documentation: 'Data transfer objects mappers.' },
{ packageName: `${application.packageName}.web.filter`, documentation: 'Request chain filters.' },
{ packageName: `${application.packageName}.web.rest.errors`, documentation: 'Rest layer error handling.' },
{ packageName: `${application.packageName}.web.rest.vm`, documentation: 'Rest layer visual models.' },
);

if (application.defaultPackaging === 'war') {
this.log.info(`Using ${application.defaultPackaging} as default packaging`);
}
},
});
}

get [BaseApplicationGenerator.LOADING]() {
return this.delegateTasksToBlueprint(() => this.loading);
}

get preparing() {
return this.asPreparingTaskGroup({
applicationDefaults({ application }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`generator - java:server with defaults options should call source snapshot 1`] = `{}`;

exports[`generator - java:server with defaults options should match files snapshot 1`] = `
{
".yo-rc.json": {
"stateCleared": "modified",
},
"package.json": {
"stateCleared": "modified",
},
}
`;
26 changes: 26 additions & 0 deletions generators/java/generators/server/command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* 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 type { JHipsterCommandDefinition } from '../../../../lib/command/types.js';

const command = {
configs: {},
import: [],
} as const satisfies JHipsterCommandDefinition;

export default command;
53 changes: 53 additions & 0 deletions generators/java/generators/server/generator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* 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 { basename, dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { before, describe, expect, it } from 'esmocha';

import { shouldSupportFeatures, testBlueprintSupport } from '../../../../test/support/tests.js';
import { defaultHelpers as helpers, result } from '../../../../lib/testing/index.js';
import Generator from './index.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);

const generator = `${basename(resolve(__dirname, '../../'))}:${basename(__dirname)}`;

describe(`generator - ${generator}`, () => {
shouldSupportFeatures(Generator);
describe('blueprint support', () => testBlueprintSupport(generator));

describe('with defaults options', () => {
before(async () => {
await helpers.runJHipster(generator).withMockedJHipsterGenerators().withMockedSource().withSharedApplication({}).withJHipsterConfig();
});

it('should match files snapshot', () => {
expect(result.getStateSnapshot()).toMatchSnapshot();
});

it('should call source snapshot', () => {
expect(result.sourceCallsArg).toMatchSnapshot();
});

it('should compose with generators', () => {
expect(result.composedMockedGenerators).toMatchInlineSnapshot(`[]`);
});
});
});
130 changes: 130 additions & 0 deletions generators/java/generators/server/generator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
/**
* 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 BaseApplicationGenerator from '../../../base-application/index.js';

const WAIT_TIMEOUT = 3 * 60000;

export default class ServerGenerator extends BaseApplicationGenerator {
async beforeQueue() {
if (!this.fromBlueprint) {
await this.composeWithBlueprints();
}

if (!this.delegateToBlueprint) {
await this.dependsOnBootstrapApplication();
}
}

get postWriting() {
return this.asPostWritingTaskGroup({
packageJsonScripts({ application }) {
const packageJsonConfigStorage = this.packageJson.createStorage('config').createProxy();
(packageJsonConfigStorage as any).backend_port = application.gatewayServerPort || application.serverPort;
(packageJsonConfigStorage as any).packaging = application.defaultPackaging;
},
packageJsonBackendScripts({ application }) {
const scriptsStorage = this.packageJson.createStorage('scripts');
const javaCommonLog = `-Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.${application.packageName}=OFF`;
const javaTestLog =
'-Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF';

const buildTool = application.buildTool;
let e2ePackage = 'target/e2e';
if (buildTool === 'maven') {
const excludeWebapp = application.skipClient ? '' : ' -Dskip.installnodenpm -Dskip.npm';
scriptsStorage.set({
'app:start': './mvnw',
'backend:info': './mvnw --version',
'backend:doc:test': './mvnw -ntp javadoc:javadoc --batch-mode',
'backend:nohttp:test': './mvnw -ntp checkstyle:check --batch-mode',
'backend:start': `./mvnw${excludeWebapp}`,
'java:jar': './mvnw -ntp verify -DskipTests --batch-mode',
'java:war': './mvnw -ntp verify -DskipTests --batch-mode -Pwar',
'java:docker': './mvnw -ntp verify -DskipTests -Pprod jib:dockerBuild',
'java:docker:arm64': 'npm run java:docker -- -Djib-maven-plugin.architecture=arm64',
'backend:unit:test': `./mvnw -ntp${excludeWebapp} verify --batch-mode ${javaCommonLog} ${javaTestLog}`,
'backend:build-cache': './mvnw dependency:go-offline -ntp',
'backend:debug': './mvnw -Dspring-boot.run.jvmArguments="-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000"',
});
} else if (buildTool === 'gradle') {
const excludeWebapp = application.skipClient ? '' : '-x webapp -x webapp_test';
e2ePackage = 'e2e';
scriptsStorage.set({
'app:start': './gradlew',
'backend:info': './gradlew -v',
'backend:doc:test': `./gradlew javadoc ${excludeWebapp}`,
'backend:nohttp:test': `./gradlew checkstyleNohttp ${excludeWebapp}`,
'backend:start': `./gradlew ${excludeWebapp}`,
'java:jar': './gradlew bootJar -x test -x integrationTest',
'java:war': './gradlew bootWar -Pwar -x test -x integrationTest',
'java:docker': './gradlew bootJar -Pprod jibDockerBuild',
'java:docker:arm64': 'npm run java:docker -- -PjibArchitecture=arm64',
'backend:unit:test': `./gradlew test integrationTest ${excludeWebapp} ${javaCommonLog} ${javaTestLog}`,
'postci:e2e:package': 'cp build/libs/*.$npm_package_config_packaging e2e.$npm_package_config_packaging',
'backend:build-cache':
'npm run backend:info && npm run backend:nohttp:test && npm run ci:e2e:package -- -x webapp -x webapp_test',
});
}

scriptsStorage.set({
'java:jar:dev': 'npm run java:jar -- -Pdev,webapp',
'java:jar:prod': 'npm run java:jar -- -Pprod',
'java:war:dev': 'npm run java:war -- -Pdev,webapp',
'java:war:prod': 'npm run java:war -- -Pprod',
'java:docker:dev': 'npm run java:docker -- -Pdev,webapp',
'java:docker:prod': 'npm run java:docker -- -Pprod',
'ci:backend:test':
'npm run backend:info && npm run backend:doc:test && npm run backend:nohttp:test && npm run backend:unit:test -- -P$npm_package_config_default_environment',
'ci:e2e:package':
'npm run java:$npm_package_config_packaging:$npm_package_config_default_environment -- -Pe2e -Denforcer.skip=true',
'preci:e2e:server:start': 'npm run services:db:await --if-present && npm run services:others:await --if-present',
'ci:e2e:server:start': `java -jar ${e2ePackage}.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment ${javaCommonLog} ${javaTestLog} --logging.level.org.springframework.web=ERROR`,
});
},
packageJsonE2eScripts({ application }) {
const scriptsStorage = this.packageJson.createStorage('scripts');
const buildCmd = application.buildToolGradle ? 'gradlew' : 'mvnw -ntp';

let applicationWaitTimeout = WAIT_TIMEOUT * (application.applicationTypeGateway ? 2 : 1);
applicationWaitTimeout = application.authenticationTypeOauth2 ? applicationWaitTimeout * 2 : applicationWaitTimeout;
const applicationEndpoint = application.applicationTypeMicroservice
? `http-get://127.0.0.1:${application.gatewayServerPort}/${application.endpointPrefix}/management/health/readiness`
: 'http-get://127.0.0.1:$npm_package_config_backend_port/management/health';
scriptsStorage.set({
'ci:server:await': `echo "Waiting for server at port $npm_package_config_backend_port to start" && wait-on -t ${applicationWaitTimeout} ${applicationEndpoint} && echo "Server at port $npm_package_config_backend_port started"`,
});

// TODO add e2eTests property to application.
if (this.jhipsterConfig.testFrameworks?.includes('cypress')) {
scriptsStorage.set({
'pree2e:headless': 'npm run ci:server:await',
'ci:e2e:run': 'concurrently -k -s first -n application,e2e -c red,blue npm:ci:e2e:server:start npm:e2e:headless',
'ci:e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue "./${buildCmd}" npm:e2e:headless`,
'e2e:dev': `concurrently -k -s first -n application,e2e -c red,blue "./${buildCmd}" npm:e2e`,
'e2e:devserver': `concurrently -k -s first -n backend,frontend,e2e -c red,yellow,blue npm:backend:start npm:start "wait-on -t ${WAIT_TIMEOUT} http-get://127.0.0.1:9000 && npm run e2e:headless -- -c baseUrl=http://localhost:9000"`,
});
}
},
});
}

get [BaseApplicationGenerator.POST_WRITING]() {
return this.delegateTasksToBlueprint(() => this.postWriting);
}
}
20 changes: 20 additions & 0 deletions generators/java/generators/server/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* 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.
*/
export { default } from './generator.js';
export { default as command } from './command.js';
Loading

0 comments on commit c7b2c68

Please sign in to comment.