diff --git a/generators/client/files-angular.js b/generators/client/files-angular.js new file mode 100644 index 00000000..3ca79d5c --- /dev/null +++ b/generators/client/files-angular.js @@ -0,0 +1,39 @@ +/** + * Copyright 2013-2020 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 + * + * http://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. + */ +const constants = require('generator-jhipster/generators/generator-constants'); + +const { ANGULAR_DIR } = constants; + +const filesAngular = { + angularAdminModule: [ + { + path: ANGULAR_DIR, + templates: [{ file: 'admin/health/health.component.html', method: 'processHtml' }, 'admin/health/health.service.ts'] + } + ] +}; + +function writeFiles() { + this.writeFilesToDisk(filesAngular, this, false, 'angular'); +} + +module.exports = { + writeFiles, + files: filesAngular +}; diff --git a/generators/client/files-react.js b/generators/client/files-react.js new file mode 100644 index 00000000..9663b48a --- /dev/null +++ b/generators/client/files-react.js @@ -0,0 +1,39 @@ +/** + * Copyright 2013-2020 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 + * + * http://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. + */ +const constants = require('generator-jhipster/generators/generator-constants'); + +const { REACT_DIR } = constants; + +const filesReact = { + adminModule: [ + { + path: REACT_DIR, + templates: [{ file: 'modules/administration/health/health.tsx', method: 'processJsx' }] + } + ] +}; + +function writeFiles() { + this.writeFilesToDisk(filesReact, this, false, 'react'); +} + +module.exports = { + writeFiles, + files: filesReact +}; diff --git a/generators/client/index.js b/generators/client/index.js new file mode 100644 index 00000000..fb322ac8 --- /dev/null +++ b/generators/client/index.js @@ -0,0 +1,68 @@ +/* eslint-disable consistent-return */ +const chalk = require('chalk'); +const ClientGenerator = require('generator-jhipster/generators/client'); +const constants = require('generator-jhipster/generators/generator-constants'); +const writeAngularFiles = require('./files-angular').writeFiles; +const writeReactFiles = require('./files-react').writeFiles; + +const { ANGULAR, REACT } = constants.SUPPORTED_CLIENT_FRAMEWORKS; + +module.exports = class extends ClientGenerator { + constructor(args, opts) { + super(args, { fromBlueprint: true, ...opts }); // fromBlueprint variable is important + + const jhContext = (this.jhipsterContext = this.options.jhipsterContext); + + if (!jhContext) { + this.error(`This is a JHipster blueprint and should be used only like ${chalk.yellow('jhipster --blueprint quarkus')}`); + } + + this.configOptions = jhContext.configOptions || {}; + + // This sets up options for this sub generator and is being reused from JHipster + jhContext.setupClientOptions(this, jhContext); + } + + get initializing() { + return super._initializing(); + } + + get prompting() { + return super._prompting(); + } + + get configuring() { + // Here we are not overriding this phase and hence its being handled by JHipster + return super._configuring(); + } + + get default() { + // Here we are not overriding this phase and hence its being handled by JHipster + return super._default(); + } + + get writing() { + const phaseFromJHipster = super._writing(); + const phaseFromQuarkus = { + writeQuarkusFiles() { + if (this.skipClient) return; + if (this.clientFramework === ANGULAR) { + return writeAngularFiles.call(this); + } + if (this.clientFramework === REACT) { + return writeReactFiles.call(this); + } + } + }; + return { ...phaseFromJHipster, ...phaseFromQuarkus }; + } + + get install() { + // Here we are not overriding this phase and hence its being handled by JHipster + return super._install(); + } + + get end() { + return super._end(); + } +}; diff --git a/generators/client/templates/angular/src/main/webapp/app/admin/health/health.component.html.ejs b/generators/client/templates/angular/src/main/webapp/app/admin/health/health.component.html.ejs new file mode 100644 index 00000000..fbe548f9 --- /dev/null +++ b/generators/client/templates/angular/src/main/webapp/app/admin/health/health.component.html.ejs @@ -0,0 +1,56 @@ +<%# + Copyright 2013-2020 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 + + http://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. +-%> +
+

+ Health Checks + + +

+ +
+ + + + + + + + + + + + + + + +
Service NameStatusDetails
+ {{ componentHealth.value.name }} + + + {{ componentHealth.value.status }} + + + + + +
+
+
diff --git a/generators/client/templates/angular/src/main/webapp/app/admin/health/health.service.ts.ejs b/generators/client/templates/angular/src/main/webapp/app/admin/health/health.service.ts.ejs new file mode 100644 index 00000000..c61eda05 --- /dev/null +++ b/generators/client/templates/angular/src/main/webapp/app/admin/health/health.service.ts.ejs @@ -0,0 +1,76 @@ +<%# + Copyright 2013-2020 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 + + http://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 { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { SERVER_API_URL } from 'app/app.constants'; + +export type HealthStatus = 'UP' | 'DOWN' | 'UNKNOWN' | 'OUT_OF_SERVICE'; + +export type HealthKey = + <%_ if (messageBroker === 'kafka') { _%> + | 'binders' + <%_ } _%> + <%_ if (applicationType === 'gateway' || serviceDiscoveryType) { _%> + | 'discoveryComposite' + | 'refreshScope' + | 'clientConfigServer' + | 'hystrix' + <%_ } _%> + <%_ if (serviceDiscoveryType === 'consul') { _%> + | 'consul' + <%_ } _%> + | 'diskSpace' + | 'mail' + | 'ping' + <%_ if (searchEngine === 'elasticsearch') { _%> + | 'elasticsearch' + <%_ } _%> + <%_ if (databaseType === 'sql') { _%> + | 'db' + <%_ } else if (databaseType === 'mongodb') { _%> + | 'mongo' + <%_ } else if (databaseType === 'cassandra') { _%> + | 'cassandra' + <%_ } else if (databaseType === 'couchbase') { _%> + | 'couchbase' + <%_ } _%> + ; + +export interface Health { + status: HealthStatus; + checks: { + [key in HealthKey]?: HealthDetails; + }; +} + +export interface HealthDetails { + status: HealthStatus; + details: any; +} + +@Injectable({ providedIn: 'root' }) +export class HealthService { + constructor(private http: HttpClient) {} + + checkHealth(): Observable { + return this.http.get(SERVER_API_URL + 'management/health'); + } +} diff --git a/generators/client/templates/react/src/main/webapp/app/modules/administration/health/health.tsx.ejs b/generators/client/templates/react/src/main/webapp/app/modules/administration/health/health.tsx.ejs new file mode 100644 index 00000000..2c0b7a67 --- /dev/null +++ b/generators/client/templates/react/src/main/webapp/app/modules/administration/health/health.tsx.ejs @@ -0,0 +1,116 @@ +<%# + Copyright 2013-2020 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 + + http://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 React, { useState, useEffect } from 'react'; +import { connect } from 'react-redux'; +import { Translate } from 'react-jhipster'; +import { Table, Badge, Col, Row, Button } from 'reactstrap'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import { IRootState } from 'app/shared/reducers'; +import { systemHealth } from '../administration.reducer'; +import HealthModal from './health-modal'; + +export interface IHealthPageProps extends StateProps, DispatchProps {} + +export const HealthPage = (props: IHealthPageProps) => { + const [healthObject, setHealthObject] = useState({}); + const [showModal, setShowModal] = useState(false); + + useEffect(() => { + props.systemHealth(); + }, []); + + const getSystemHealth = () => { + if (!props.isFetching) { + props.systemHealth(); + } + }; + + const getSystemHealthInfo = (name, healthObj) => () => { + setShowModal(true); + setHealthObject({ ...healthObj, name }); + }; + + const handleClose = () => setShowModal(false); + + const renderModal = () => ; + + const { health, isFetching } = props; + const data = (health || {}).checks || {}; + + return ( +
+

Health Checks

+

+ +

+ + + + + + + + + + + + {Object.keys(data).map((configPropKey, configPropIndex) => + configPropKey !== 'status' ? ( + + + + + + ) : null + )} + +
Service NameStatusDetails
{data[configPropKey].name + {data[configPropKey].status} + + {data[configPropKey].details ? ( + + + + ) : null} +
+ +
+ {renderModal()} +
+ ); +}; + +const mapStateToProps = (storeState: IRootState) => ({ + health: storeState.administration.health, + isFetching: storeState.administration.loading +}); + +const mapDispatchToProps = { systemHealth }; + +type StateProps = ReturnType; +type DispatchProps = typeof mapDispatchToProps; + +export default connect(mapStateToProps, mapDispatchToProps)(HealthPage); diff --git a/generators/server/templates/quarkus/build.gradle.ejs b/generators/server/templates/quarkus/build.gradle.ejs index 24be65e9..e0ed2cc4 100644 --- a/generators/server/templates/quarkus/build.gradle.ejs +++ b/generators/server/templates/quarkus/build.gradle.ejs @@ -237,6 +237,7 @@ dependencies { implementation "io.quarkus:quarkus-elytron-security" implementation "io.quarkus:quarkus-smallrye-jwt" implementation "io.quarkus:quarkus-smallrye-openapi" + implementation "io.quarkus:quarkus-smallrye-health" implementation "org.mapstruct:mapstruct:${mapstruct_version}" implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}") diff --git a/generators/server/templates/quarkus/partials/data_pom.xml.ejs b/generators/server/templates/quarkus/partials/data_pom.xml.ejs index 43bf74dc..99e4ae61 100644 --- a/generators/server/templates/quarkus/partials/data_pom.xml.ejs +++ b/generators/server/templates/quarkus/partials/data_pom.xml.ejs @@ -16,47 +16,47 @@ See the License for the specific language governing permissions and limitations under the License. -%> - - io.quarkus - quarkus-hibernate-orm-panache - - - io.quarkus - quarkus-hibernate-validator - -<%_ if (devDatabaseType === 'mariadb' || prodDatabaseType === 'mariadb') { _%> - - io.quarkus - quarkus-jdbc-mariadb - -<%_ } _%> -<%_ if (devDatabaseType === 'mssql' || prodDatabaseType === 'mssql') { _%> - - io.quarkus - quarkus-jdbc-mssql - -<%_ } _%> -<%_ if (devDatabaseType === 'mysql' || prodDatabaseType === 'mysql') { _%> - - io.quarkus - quarkus-jdbc-mysql - -<%_ } _%> -<%_ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> - - io.quarkus - quarkus-jdbc-postgresql - -<%_ } _%> - - io.quarkus - quarkus-jdbc-h2 - - - io.quarkus - quarkus-test-h2 - - - io.quarkus - quarkus-liquibase - + + io.quarkus + quarkus-hibernate-orm-panache + + + io.quarkus + quarkus-hibernate-validator + + <%_ if (devDatabaseType === 'mariadb' || prodDatabaseType === 'mariadb') { _%> + + io.quarkus + quarkus-jdbc-mariadb + + <%_ } _%> + <%_ if (devDatabaseType === 'mssql' || prodDatabaseType === 'mssql') { _%> + + io.quarkus + quarkus-jdbc-mssql + + <%_ } _%> + <%_ if (devDatabaseType === 'mysql' || prodDatabaseType === 'mysql') { _%> + + io.quarkus + quarkus-jdbc-mysql + + <%_ } _%> + <%_ if (devDatabaseType === 'postgresql' || prodDatabaseType === 'postgresql') { _%> + + io.quarkus + quarkus-jdbc-postgresql + + <%_ } _%> + + io.quarkus + quarkus-jdbc-h2 + + + io.quarkus + quarkus-test-h2 + + + io.quarkus + quarkus-liquibase + diff --git a/generators/server/templates/quarkus/partials/security_pom.xml.ejs b/generators/server/templates/quarkus/partials/security_pom.xml.ejs index d1210d5d..a80841d5 100644 --- a/generators/server/templates/quarkus/partials/security_pom.xml.ejs +++ b/generators/server/templates/quarkus/partials/security_pom.xml.ejs @@ -16,11 +16,11 @@ See the License for the specific language governing permissions and limitations under the License. -%> - - io.quarkus - quarkus-elytron-security - - - io.quarkus - quarkus-smallrye-jwt - + + io.quarkus + quarkus-elytron-security + + + io.quarkus + quarkus-smallrye-jwt + diff --git a/generators/server/templates/quarkus/pom.xml.ejs b/generators/server/templates/quarkus/pom.xml.ejs index f50564c8..8633a2e0 100644 --- a/generators/server/templates/quarkus/pom.xml.ejs +++ b/generators/server/templates/quarkus/pom.xml.ejs @@ -116,6 +116,10 @@ prodDatabaseType}) _%> <%- include('./partials/security_pom.xml.ejs', { }) _%> + + io.quarkus + quarkus-smallrye-health + io.quarkus quarkus-smallrye-openapi diff --git a/generators/server/templates/quarkus/src/main/resources/application.properties.ejs b/generators/server/templates/quarkus/src/main/resources/application.properties.ejs index 55a3d65f..c9b7ff1f 100644 --- a/generators/server/templates/quarkus/src/main/resources/application.properties.ejs +++ b/generators/server/templates/quarkus/src/main/resources/application.properties.ejs @@ -26,6 +26,8 @@ quarkus.mailer.ssl=false quarkus.mailer.username= quarkus.mailer.password= +quarkus.smallrye-health.root-path=/management/health + mp.jwt.verify.publickey.location=META-INF/resources/publicKey.pem mp.jwt.verify.issuer=https://www.jhipster.tech quarkus.smallrye-jwt.enabled=true diff --git a/test/client.spec.js b/test/client.spec.js new file mode 100644 index 00000000..217e063f --- /dev/null +++ b/test/client.spec.js @@ -0,0 +1,64 @@ +const path = require('path'); +const assert = require('yeoman-assert'); +const helpers = require('yeoman-test'); +const constants = require('generator-jhipster/generators/generator-constants'); + +const ANGULAR_DIR = constants.ANGULAR_DIR; + +describe('Subgenerator client of quarkus JHipster blueprint', () => { + describe('Angular tests', () => { + before(initTests('angularX')); + + it('Angular health check files contain expected content', () => { + assert.fileContent( + `${ANGULAR_DIR}admin/health/health.component.html`, + '' + ); + assert.fileContent( + `${ANGULAR_DIR}admin/health/health.service.ts`, + 'export interface Health {\n' + + ' status: HealthStatus;\n' + + ' checks: {\n' + + ' [key in HealthKey]?: HealthDetails;\n' + + ' };\n' + + '}' + ); + }); + }); + + describe('React tests', () => { + before(initTests('react')); + + it('React health check files contain expected content', () => { + assert.fileContent(`${ANGULAR_DIR}modules/administration/health/health.tsx`, '{data[configPropKey].name'); + }); + }); + + function initTests(framework) { + return done => { + helpers + .run('generator-jhipster/generators/client') + .withOptions({ + 'from-cli': true, + skipInstall: true, + blueprint: 'quarkus', + skipChecks: true + }) + .withGenerators([ + [ + require('../generators/client/index.js'), // eslint-disable-line global-require + 'jhipster-quarkus:client', + path.join(__dirname, '../generators/client/index.js') + ] + ]) + .withPrompts({ + baseName: 'jhipster', + clientFramework: framework, + enableTranslation: true, + nativeLanguage: 'en', + languages: ['fr'] + }) + .on('end', done); + }; + } +}); diff --git a/test/server.spec.js b/test/server.spec.js index 8e593058..e9c65bc2 100644 --- a/test/server.spec.js +++ b/test/server.spec.js @@ -43,5 +43,15 @@ describe('Subgenerator server of quarkus JHipster blueprint', () => { assert.file(expectedFiles.server); assert.file(expectedFiles.maven); }); + + it('pom.xml contains health check dependency', () => { + assert.fileContent( + 'pom.xml', + ' \n' + + ' io.quarkus\n' + + ' quarkus-smallrye-health\n' + + ' ' + ); + }); }); });