generator-pyhipster
Version:
Python (Flask) + Angular/React/Vue in one handy generator
267 lines (243 loc) • 8.46 kB
JavaScript
/**
* Copyright 2013-2022 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.
*/
const path = require('path');
const shelljs = require('shelljs');
const request = require('then-request');
const OptionNames = require('../../jdl/jhipster/application-options');
const { EUREKA } = require('../../jdl/jhipster/service-discovery-types');
const { REACTIVE, SERVICE_DISCOVERY_TYPE } = OptionNames;
module.exports = {
askActionType,
askExistingAvailableDocs,
askGenerationInfos,
};
async function fetchSwaggerResources(input) {
const availableDocs = [];
const baseUrl = input.replace(/\/$/, '');
const swaggerResources = await request('GET', `${baseUrl}/swagger-resources`, {
// This header is needed to use the custom /swagger-resources controller
// and not the default one that has only the gateway's swagger resource
headers: { Accept: 'application/json, text/javascript;' },
});
JSON.parse(swaggerResources.getBody()).forEach(swaggerResource => {
const specPath = swaggerResource.location.replace(/^\/+/g, '');
availableDocs.push({
value: { url: `${baseUrl}/${specPath}`, name: swaggerResource.name },
name: `${swaggerResource.name} (${swaggerResource.location})`,
});
});
return availableDocs;
}
function askActionType() {
if (this.options.regen) {
return undefined;
}
const hasExistingApis = Object.keys(this.openApiClients).length !== 0;
const actionList = [
{
value: 'new',
name: 'Generate a new API client',
},
];
if (hasExistingApis) {
actionList.push({ value: 'all', name: 'Generate all stored API clients' });
actionList.push({ value: 'select', name: 'Select stored API clients to generate' });
}
const newClient = actionList.length === 1;
const prompts = [
{
when: !newClient,
type: 'list',
name: 'action',
message: 'What do you want to do ?',
choices: actionList,
},
{
when: response => response.action === 'new' || newClient,
type: 'list',
name: 'specOrigin',
message: 'Where do you want to import your OpenAPI/Swagger specification from ?',
choices: [
{ value: 'jhipster-endpoint', name: 'From a Jhipster /swagger-resources live doc endpoint' },
{ value: 'jhipster-directory', name: 'From the api.yml spec of an existing Jhipster project' },
{ value: 'custom-endpoint', name: 'From a custom specification file or endpoint' },
],
},
{
when: response => response.specOrigin === 'jhipster-endpoint',
type: 'input',
name: 'jhipsterEndpoint',
message: 'Enter the URL of the running Jhipster instance',
default: 'http://localhost:8080',
validate: async input => {
try {
const availableDocs = await fetchSwaggerResources(input);
if (availableDocs.length === 0) {
return `No live doc found at ${input}`;
}
return true;
} catch (err) {
return `Error while fetching live doc from '${input}'. "${err.message}"`;
}
},
},
{
when: response => response.specOrigin === 'jhipster-directory',
type: 'input',
name: 'jhipsterDirectory',
message: 'Enter the path to the jhipster project root directory',
default: '../',
validate: input => {
let fromPath;
if (path.isAbsolute(input)) {
fromPath = `${input}/src/main/resources/swagger/api.yml`;
} else {
fromPath = this.destinationPath(`${input}/src/main/resources/swagger/api.yml`);
}
if (shelljs.test('-f', fromPath)) {
return true;
}
return `api.yml not found in ${input}/`;
},
},
{
when: response => response.specOrigin === 'custom-endpoint',
type: 'input',
name: 'customEndpoint',
message: 'Where is your Swagger/OpenAPI spec (URL or path) ?',
default: 'http://petstore.swagger.io/v2/swagger.json',
store: true,
validate: async input => {
try {
if (/^((http|https):\/\/)/.test(input)) {
await request('GET', `${input}`, {
// headers: { Accept: 'application/json, text/javascript;' }
});
} else if (!shelljs.test('-f', input)) {
return `file '${input}' not found`;
}
return true;
} catch (err) {
return `Cannot read from ${input}. ${err.message}`;
}
},
},
];
return this.prompt(prompts).then(props => {
if (newClient) {
props.action = 'new';
}
props.generatorName = this.config.get(REACTIVE) ? 'java' : 'spring';
this.props = props;
if (props.jhipsterEndpoint !== undefined) {
return fetchSwaggerResources(props.jhipsterEndpoint).then(availableDocs => {
props.availableDocs = availableDocs;
});
}
if (props.jhipsterDirectory !== undefined) {
props.inputSpec = `${props.jhipsterDirectory}/src/main/resources/swagger/api.yml`;
} else if (props.customEndpoint !== undefined) {
props.inputSpec = props.customEndpoint;
}
return undefined;
});
}
function askExistingAvailableDocs() {
if (this.options.regen) {
return undefined;
}
const prompts = [
{
when: !this.options.regen && this.props.availableDocs !== undefined,
type: 'list',
name: 'availableDoc',
message: 'Select the doc for which you want to create a client',
choices: this.props.availableDocs,
},
];
return this.prompt(prompts).then(props => {
if (props.availableDoc !== undefined) {
this.props.inputSpec = props.availableDoc.url;
this.props.cliName = props.availableDoc.name === 'default' ? 'apidocs' : props.availableDoc.name; // "default" cannot be used as it's a keyword in java
}
});
}
function askGenerationInfos() {
if (this.options.regen) {
return undefined;
}
const prompts = [
{
when:
this.props.specOrigin === 'jhipster-endpoint' &&
this.config.get(SERVICE_DISCOVERY_TYPE) === EUREKA &&
this.props.generatorName === 'spring',
type: 'confirm',
name: 'useServiceDiscovery',
message: 'Do you want to use Eureka service discovery ?',
default: true,
},
{
when: response => this.props.action === 'new' && !response.useServiceDiscovery,
type: 'input',
name: 'cliName',
message: 'What is the unique name for your API client (please avoid using Java keywords) ?',
default: this.props.cliName || 'petstore',
validate: input => {
if (!/^([a-zA-Z0-9_]*)$/.test(input)) {
return 'Your API client name cannot contain special characters or a blank space';
}
if (input === '') {
return 'Your API client name cannot be empty';
}
return true;
},
},
{
when: this.props.action === 'new',
type: 'confirm',
name: 'saveConfig',
message: 'Do you want to save this config for future reuse ?',
default: false,
},
{
when: this.props.action === 'select',
type: 'checkbox',
name: 'selected',
message: 'Select which APIs you want to generate',
choices: () => {
const choices = [];
Object.keys(this.openApiClients).forEach(cliName => {
choices.push({
name: `${cliName} (${this.openApiClients[cliName].spec})`,
value: { cliName, spec: this.openApiClients[cliName] },
});
});
return choices;
},
},
];
return this.prompt(prompts).then(props => {
if (props.cliName !== undefined) {
this.props.cliName = props.cliName;
}
this.props.saveConfig = props.saveConfig;
this.props.selected = props.selected;
});
}