Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): add prefix to openapi generator #10350

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/site/OpenAPI-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ lb4 openapi [<url>] [options]
- `--client`: Generate client-side service proxies and controllers with
implementation for the OpenAPI spec Default: `false`.
- `--datasource`: A valid datasource name
- `--prefix`: A prefix to artifacts Optional
- `--positional`: A flag to control if service methods use positional parameters
or an object with named properties. Default: `true`.

Expand Down
8 changes: 8 additions & 0 deletions packages/cli/.yo-rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -975,6 +975,14 @@
"name": "promote-anonymous-schemas",
"hide": false
},
"prefix": {
"description": "Provide a prefix to OpenAPIs",
"required": false,
"default": "",
"type": "String",
"name": "prefix",
"hide": false
},
"config": {
"type": "String",
"alias": "c",
Expand Down
42 changes: 42 additions & 0 deletions packages/cli/generators/openapi/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,13 @@ module.exports = class OpenApiGenerator extends BaseGenerator {
type: Boolean,
});

this.option('prefix', {
description: g.f('Provide a prefix to OpenAPIs'),
required: false,
default: '',
type: String,
});

return super._setupGenerator();
}

Expand Down Expand Up @@ -211,6 +218,26 @@ module.exports = class OpenApiGenerator extends BaseGenerator {
}
}

async askForPrefix() {
if (this.shouldExit()) return;
const prompts = [
{
name: 'prefix',
message: g.f('Provide a prefix to OpenAPIs'),
when: !this.options.prefix,
default: '',
},
];
const answers = await this.prompt(prompts);
if (answers.prefix) {
//assign prefix to options in PascalCase
this.options.prefix = answers.prefix.replace(
/\w+/g,
w => w[0].toUpperCase() + w.slice(1).toLowerCase(),
);
}
}

async askForSpecUrlOrPath() {
if (this.shouldExit()) return;
if (this.dataSourceInfo && this.dataSourceInfo.specPath) {
Expand Down Expand Up @@ -241,6 +268,7 @@ module.exports = class OpenApiGenerator extends BaseGenerator {
log: this.log,
validate: this.options.validate,
promoteAnonymousSchemas: this.options['promote-anonymous-schemas'],
prefix: this.options.prefix,
});
debugJson('OpenAPI spec', result.apiSpec);
Object.assign(this, result);
Expand All @@ -251,6 +279,16 @@ module.exports = class OpenApiGenerator extends BaseGenerator {

async selectControllers() {
if (this.shouldExit()) return;
if (this.options.prefix) {
//remove prefix from tag
this.controllerSpecs = this.controllerSpecs.map(c => {
if (c.tag.includes(this.options.prefix)) {
const splited = c.tag.split(this.options.prefix);
c.tag = splited.join('');
}
return c;
});
}
const choices = this.controllerSpecs.map(c => {
const names = [];
if (this.options.server !== false) {
Expand Down Expand Up @@ -288,6 +326,10 @@ module.exports = class OpenApiGenerator extends BaseGenerator {
this.selectedServices = this.selectedControllers;
this.selectedControllers.forEach(c => {
c.fileName = getControllerFileName(c.tag || c.className);
if (this.options.prefix) {
// adding the prefix to openapis
c.fileName = `${this.options.prefix.toLowerCase()}.` + c.fileName;
}
c.serviceFileName = getServiceFileName(c.tag || c.serviceClassName);
});
}
Expand Down
73 changes: 71 additions & 2 deletions packages/cli/generators/openapi/spec-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,80 @@ async function loadSpec(specUrlStr, {log, validate} = {}) {
return spec;
}

const getCircularReplacer = () => {
const seen = new WeakSet();
return (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) {
return;
}
seen.add(value);
}
return value;
};
};

async function loadAndBuildSpec(
url,
{log, validate, promoteAnonymousSchemas} = {},
{log, validate, promoteAnonymousSchemas, prefix} = {},
) {
const apiSpec = await loadSpec(url, {log, validate});
let apiSpec = await loadSpec(url, {log, validate});
const {components, paths} = apiSpec;
let stringifiedApiSpecs = JSON.stringify(apiSpec, getCircularReplacer());

if (prefix) {
// rewrite WithRelations and append prefix
stringifiedApiSpecs = stringifiedApiSpecs.replaceAll(
'WithRelations',
`${prefix}WithRelations`,
);
// adding prefix to paths
if (paths) {
Object.keys(paths).forEach(eachPath => {
if (!eachPath.includes('{id}') && !eachPath.includes('count')) {
const updatedPath =
eachPath.slice(0, 0) +
`/${prefix.toLowerCase()}/` +
eachPath.slice(1);
stringifiedApiSpecs = stringifiedApiSpecs.replaceAll(
eachPath,
updatedPath,
);
}
});
}
// rewrite every item and append prefix in the start
if (components) {
const {schemas} = components;
if (schemas) {
Object.keys(schemas).forEach(item => {
if (
!item.startsWith('loopback') &&
!item.startsWith('New') &&
!item.endsWith('Relations') &&
!item.endsWith('Partial') &&
!item.includes('Through') &&
!item.includes('.') &&
!item.includes('Ping')
) {
stringifiedApiSpecs = stringifiedApiSpecs.replaceAll(
item,
prefix + item,
);
}
if (item.includes('Ping')) {
stringifiedApiSpecs = stringifiedApiSpecs.replaceAll(
'Ping',
prefix + 'Ping',
);
}
});
}
}
}

apiSpec = JSON.parse(stringifiedApiSpecs);

// First populate the type registry for named schemas
const typeRegistry = {
objectTypeMapping: new Map(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1033,6 +1033,14 @@ exports[`cli saves command metadata to .yo-rc.json 1`] = `
"name": "promote-anonymous-schemas",
"hide": false
},
"prefix": {
"description": "Provide a prefix to OpenAPIs",
"required": false,
"default": "",
"type": "String",
"name": "prefix",
"hide": false
},
"config": {
"type": "String",
"alias": "c",
Expand Down
Loading