diff --git a/docs/src/pages/reference/configuration/input.md b/docs/src/pages/reference/configuration/input.md index c06e07e20..5527a76a6 100644 --- a/docs/src/pages/reference/configuration/input.md +++ b/docs/src/pages/reference/configuration/input.md @@ -92,6 +92,25 @@ module.exports = { }; ``` +#### schemas + +Type: Array of `string` or `RegExp`. + +Only schemas names match the specified `string` or `RegExp` will be automatically generated. +For instance the example below only generates the `schema` object that matches string `Error` or regular expression `/Cat/`. + +```js +module.exports = { + petstore: { + input: { + filters: { + schemas: ['Error', /Cat/], + }, + }, + }, +}; +``` + ### converterOptions Type: `Object`. diff --git a/packages/core/src/generators/schema-definition.ts b/packages/core/src/generators/schema-definition.ts index 1a4384ebf..cf97d28ad 100644 --- a/packages/core/src/generators/schema-definition.ts +++ b/packages/core/src/generators/schema-definition.ts @@ -2,8 +2,15 @@ import isEmpty from 'lodash.isempty'; import { SchemaObject, SchemasObject } from 'openapi3-ts/oas30'; import { getEnum, resolveDiscriminators } from '../getters'; import { resolveRef, resolveValue } from '../resolvers'; -import { ContextSpecs, GeneratorSchema } from '../types'; -import { upath, isReference, jsDoc, pascal, sanitize } from '../utils'; +import { ContextSpecs, GeneratorSchema, InputFiltersOption } from '../types'; +import { + upath, + isReference, + jsDoc, + pascal, + sanitize, + isString, +} from '../utils'; import { generateInterface } from './interface'; /** @@ -15,91 +22,98 @@ export const generateSchemasDefinition = ( schemas: SchemasObject = {}, context: ContextSpecs, suffix: string, + schemasFilters?: InputFiltersOption['schemas'], ): GeneratorSchema[] => { if (isEmpty(schemas)) { return []; } - const transformedSchemas = resolveDiscriminators(schemas, context); - const models = Object.entries(transformedSchemas).reduce( - (acc, [name, schema]) => { - const schemaName = sanitize(`${pascal(name)}${suffix}`, { - underscore: '_', - whitespace: '_', - dash: true, - es5keyword: true, - es5IdentifierName: true, - }); - if (shouldCreateInterface(schema)) { - acc.push( - ...generateInterface({ - name: schemaName, - schema, - context, - suffix, - }), - ); - return acc; - } else { - const resolvedValue = resolveValue({ - schema, + let generateSchemas = Object.entries(transformedSchemas); + if (schemasFilters) { + generateSchemas = generateSchemas.filter(([schemaName]) => { + return schemasFilters.some((filter) => + isString(filter) ? filter === schemaName : filter.test(schemaName), + ); + }); + } + + const models = generateSchemas.reduce((acc, [name, schema]) => { + const schemaName = sanitize(`${pascal(name)}${suffix}`, { + underscore: '_', + whitespace: '_', + dash: true, + es5keyword: true, + es5IdentifierName: true, + }); + if (shouldCreateInterface(schema)) { + acc.push( + ...generateInterface({ name: schemaName, + schema, context, - }); + suffix, + }), + ); + + return acc; + } else { + const resolvedValue = resolveValue({ + schema, + name: schemaName, + context, + }); - let output = ''; + let output = ''; - let imports = resolvedValue.imports; + let imports = resolvedValue.imports; - output += jsDoc(schema); + output += jsDoc(schema); - if (resolvedValue.isEnum && !resolvedValue.isRef) { - output += getEnum( - resolvedValue.value, - schemaName, - resolvedValue.originalSchema?.['x-enumNames'], - context.output.override.useNativeEnums, + if (resolvedValue.isEnum && !resolvedValue.isRef) { + output += getEnum( + resolvedValue.value, + schemaName, + resolvedValue.originalSchema?.['x-enumNames'], + context.output.override.useNativeEnums, + ); + } else if (schemaName === resolvedValue.value && resolvedValue.isRef) { + // Don't add type if schema has same name and the referred schema will be an interface + const { schema: referredSchema } = resolveRef(schema, context); + if (!shouldCreateInterface(referredSchema as SchemaObject)) { + const imp = resolvedValue.imports.find( + (imp) => imp.name === schemaName, ); - } else if (schemaName === resolvedValue.value && resolvedValue.isRef) { - // Don't add type if schema has same name and the referred schema will be an interface - const { schema: referredSchema } = resolveRef(schema, context); - if (!shouldCreateInterface(referredSchema as SchemaObject)) { - const imp = resolvedValue.imports.find( - (imp) => imp.name === schemaName, - ); - if (!imp) { - output += `export type ${schemaName} = ${resolvedValue.value};\n`; - } else { - const alias = imp?.specKey - ? `${pascal(upath.getSpecName(imp.specKey, context.specKey))}${ - resolvedValue.value - }` - : `${resolvedValue.value}Bis`; + if (!imp) { + output += `export type ${schemaName} = ${resolvedValue.value};\n`; + } else { + const alias = imp?.specKey + ? `${pascal(upath.getSpecName(imp.specKey, context.specKey))}${ + resolvedValue.value + }` + : `${resolvedValue.value}Bis`; - output += `export type ${schemaName} = ${alias};\n`; + output += `export type ${schemaName} = ${alias};\n`; - imports = imports.map((imp) => - imp.name === schemaName ? { ...imp, alias } : imp, - ); - } + imports = imports.map((imp) => + imp.name === schemaName ? { ...imp, alias } : imp, + ); } - } else { - output += `export type ${schemaName} = ${resolvedValue.value};\n`; } + } else { + output += `export type ${schemaName} = ${resolvedValue.value};\n`; + } - acc.push(...resolvedValue.schemas, { - name: schemaName, - model: output, - imports, - }); + acc.push(...resolvedValue.schemas, { + name: schemaName, + model: output, + imports, + }); - return acc; - } - }, - [] as GeneratorSchema[], - ); + return acc; + } + }, [] as GeneratorSchema[]); return models; }; diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index 2c7b7e08a..6d972694b 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -152,9 +152,7 @@ export type NormalizedInputOptions = { override: OverrideInput; converterOptions: swagger2openapi.Options; parserOptions: SwaggerParserOptions; - filters?: { - tags?: (string | RegExp)[]; - }; + filters?: InputFiltersOption; }; export type OutputClientFunc = ( @@ -190,15 +188,18 @@ export type SwaggerParserOptions = Omit & { validate?: boolean; }; +export type InputFiltersOption = { + tags?: (string | RegExp)[]; + schemas?: (string | RegExp)[]; +}; + export type InputOptions = { target: string | Record | OpenAPIObject; validation?: boolean; override?: OverrideInput; converterOptions?: swagger2openapi.Options; parserOptions?: SwaggerParserOptions; - filters?: { - tags?: (string | RegExp)[]; - }; + filters?: InputFiltersOption; }; export const OutputClient = { diff --git a/packages/orval/src/import-open-api.ts b/packages/orval/src/import-open-api.ts index 16d3d2736..d119fbbeb 100644 --- a/packages/orval/src/import-open-api.ts +++ b/packages/orval/src/import-open-api.ts @@ -30,7 +30,7 @@ export const importOpenApi = async ({ }: ImportOpenApi): Promise => { const specs = await generateInputSpecs({ specs: data, input, workspace }); - const schemas = getApiSchemas({ output, target, workspace, specs }); + const schemas = getApiSchemas({ input, output, target, workspace, specs }); const api = await getApiBuilder({ // @ts-expect-error // FIXME @@ -93,11 +93,13 @@ const generateInputSpecs = async ({ }; const getApiSchemas = ({ + input, output, target, workspace, specs, }: { + input: InputOptions; output: NormalizedOutputOptions; workspace: string; target: string; @@ -113,12 +115,15 @@ const getApiSchemas = ({ output, }; + const parsedSchemas = spec.openapi + ? (spec.components?.schemas as SchemasObject) + : getAllSchemas(spec, specKey); + const schemaDefinition = generateSchemasDefinition( - !spec.openapi - ? getAllSchemas(spec, specKey) - : (spec.components?.schemas as SchemasObject), + parsedSchemas, context, output.override.components.schemas.suffix, + input.filters?.schemas, ); const responseDefinition = generateComponentDefinition( diff --git a/tests/configs/default.config.ts b/tests/configs/default.config.ts index 473453939..b2b5771a9 100644 --- a/tests/configs/default.config.ts +++ b/tests/configs/default.config.ts @@ -6,6 +6,16 @@ export default defineConfig({ input: '../specifications/petstore.yaml', output: '../generated/default/petstore/endpoints.ts', }, + 'petstore-filter': { + input: { + target: '../specifications/petstore.yaml', + filters: { + tags: ['health'], + schemas: ['Error', /Cat/], + }, + }, + output: '../generated/default/petstore-filter/endpoints.ts', + }, 'petstore-transfomer': { output: { target: '../generated/default/petstore-transformer/endpoints.ts',