diff --git a/README.md b/README.md index 71d633b..22a9776 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,10 @@ Create config file in project root module.exports = { input: 'api', // "input" of aspida is "output" for openapi2aspida outputEachDir: true, // Generate $api.ts in each endpoint directory - openapi: { inputFile: 'https://petstore.swagger.io/v2/swagger.json' }, + openapi: { + inputFile: 'https://petstore.swagger.io/v2/swagger.json', + includeDeprecated: false, // Optional: include deprecated fields/endpoints (default: false) + }, }; ``` diff --git a/samples/deprecated-test.yml b/samples/deprecated-test.yml new file mode 100644 index 0000000..a1b0193 --- /dev/null +++ b/samples/deprecated-test.yml @@ -0,0 +1,25 @@ +openapi: 3.0.0 +info: + title: Test API + version: 1.0.0 +paths: + /test: + get: + deprecated: true + responses: + '200': + description: Success + post: + responses: + '200': + description: Success +components: + schemas: + TestSchema: + type: object + properties: + active_field: + type: string + deprecated_field: + type: string + deprecated: true diff --git a/src/buildTemplate.ts b/src/buildTemplate.ts index 5c13f4c..be0888f 100644 --- a/src/buildTemplate.ts +++ b/src/buildTemplate.ts @@ -6,11 +6,14 @@ import resolveExternalRefs from './resolveExternalRefs'; const isV3 = (openapi: OpenAPI.Document): openapi is OpenAPIV3.Document => 'openapi' in openapi; -export default async ({ input, isYaml }: Config) => { +export default async (config: Config) => { + const { input, isYaml } = config; const openapi = await SwaggerParser.parse(input, { parse: { json: !isYaml } }); const docs = isV3(openapi) ? openapi : await require('swagger2openapi').convertObj(openapi, { direct: true, resolveInternal: true }); - return resolveExternalRefs(docs, typeof input === 'string' ? input : '').then(buildV3); + return resolveExternalRefs(docs, typeof input === 'string' ? input : '').then((resolvedDocs) => + buildV3(resolvedDocs, config), + ); }; diff --git a/src/buildV3.ts b/src/buildV3.ts index e137248..ecf0aaf 100644 --- a/src/buildV3.ts +++ b/src/buildV3.ts @@ -1,4 +1,5 @@ import type { OpenAPIV3 } from 'openapi-types'; +import { setIncludeDeprecated } from './builderUtils/conversionContext'; import { $ref2Type, BINARY_TYPE, @@ -15,6 +16,7 @@ import requestBodies2Props from './builderUtils/requestBodies2Props'; import { resolveParamsRef, resolveReqRef, resolveResRef } from './builderUtils/resolvers'; import responses2Props from './builderUtils/responses2Props'; import schemas2Props from './builderUtils/schemas2Props'; +import type { Config } from './getConfig'; const methodNames = ['get', 'post', 'put', 'delete', 'head', 'options', 'patch'] as const; @@ -23,10 +25,14 @@ const getParamsList = ( params?: (OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject)[], ) => params?.map((p) => (isRefObject(p) ? resolveParamsRef(openapi, p.$ref) : p)) || []; -export default (openapi: OpenAPIV3.Document) => { +export default (openapi: OpenAPIV3.Document, config: Config) => { + setIncludeDeprecated(config.includeDeprecated || false); + const files: { file: string[]; methods: string }[] = []; - const schemas = schemas2Props(openapi.components?.schemas, openapi) || []; - const parameters = parameters2Props(openapi.components?.parameters, openapi) || []; + const schemas = + schemas2Props(openapi.components?.schemas, openapi, config.includeDeprecated) || []; + const parameters = + parameters2Props(openapi.components?.parameters, openapi, config.includeDeprecated) || []; const requestBodies = requestBodies2Props(openapi.components?.requestBodies) || []; const responses = responses2Props(openapi.components?.responses) || []; const headers = headers2Props(openapi.components?.headers) || []; @@ -67,7 +73,7 @@ export default (openapi: OpenAPIV3.Document) => { .map((method) => { const target = openapi.paths[path]![method]!; - if (target.deprecated) return null; + if (target.deprecated && !config.includeDeprecated) return null; const params: Prop[] = []; @@ -82,6 +88,9 @@ export default (openapi: OpenAPIV3.Document) => { (p) => { if (isRefObject(p)) { const ref = resolveParamsRef(openapi, p.$ref); + + if (ref.deprecated && !config.includeDeprecated) return; + const val = { isArray: false, isEnum: false, @@ -102,6 +111,8 @@ export default (openapi: OpenAPIV3.Document) => { break; } } else { + if (p.deprecated && !config.includeDeprecated) return; + const value = schema2value(p.schema); if (!value) return; diff --git a/src/builderUtils/conversionContext.ts b/src/builderUtils/conversionContext.ts new file mode 100644 index 0000000..5de3959 --- /dev/null +++ b/src/builderUtils/conversionContext.ts @@ -0,0 +1,7 @@ +let includeDeprecated = false; + +export const setIncludeDeprecated = (value: boolean) => { + includeDeprecated = value; +}; + +export const getIncludeDeprecated = (): boolean => includeDeprecated; diff --git a/src/builderUtils/converters.ts b/src/builderUtils/converters.ts index c1a40a3..3a4b532 100644 --- a/src/builderUtils/converters.ts +++ b/src/builderUtils/converters.ts @@ -1,4 +1,5 @@ import type { OpenAPIV3 } from 'openapi-types'; +import { getIncludeDeprecated } from './conversionContext'; import type { Prop, PropValue } from './props2String'; export const defKey2defName = (key: string) => @@ -53,7 +54,8 @@ const object2value = (obj: OpenAPIV3.NonArraySchemaObject): Prop[] => { const value = Object.keys(properties) .filter((name) => { const target = properties[name]; - return isRefObject(target) || !target.deprecated; + if (isRefObject(target)) return true; + return getIncludeDeprecated() || !target.deprecated; }) .map((name) => { const val = schema2value(properties[name]); diff --git a/src/builderUtils/parameters2Props.ts b/src/builderUtils/parameters2Props.ts index 5d50b76..c2d4366 100644 --- a/src/builderUtils/parameters2Props.ts +++ b/src/builderUtils/parameters2Props.ts @@ -11,12 +11,17 @@ import { resolveParamsRef } from './resolvers'; export type Parameter = { name: string; prop: string | Prop }; -export default (params: OpenAPIV3.ComponentsObject['parameters'], openapi: OpenAPIV3.Document) => +export default ( + params: OpenAPIV3.ComponentsObject['parameters'], + openapi: OpenAPIV3.Document, + includeDeprecated = false, +) => params && Object.keys(params) .filter((defKey) => { const target = params[defKey]; - return !(isRefObject(target) ? resolveParamsRef(openapi, target.$ref) : target).deprecated; + const resolved = isRefObject(target) ? resolveParamsRef(openapi, target.$ref) : target; + return includeDeprecated || !resolved.deprecated; }) .map((defKey) => { const target = params[defKey]; diff --git a/src/builderUtils/schemas2Props.ts b/src/builderUtils/schemas2Props.ts index 2a0fee6..73bee0a 100644 --- a/src/builderUtils/schemas2Props.ts +++ b/src/builderUtils/schemas2Props.ts @@ -5,12 +5,17 @@ import { resolveSchemasRef } from './resolvers'; export type Schema = { name: string; value: PropValue }; -export default (schemas: OpenAPIV3.ComponentsObject['schemas'], openapi: OpenAPIV3.Document) => +export default ( + schemas: OpenAPIV3.ComponentsObject['schemas'], + openapi: OpenAPIV3.Document, + includeDeprecated = false, +) => schemas && Object.keys(schemas) .filter((defKey) => { const target = schemas[defKey]; - return !(isRefObject(target) ? resolveSchemasRef(openapi, target.$ref) : target).deprecated; + const resolved = isRefObject(target) ? resolveSchemasRef(openapi, target.$ref) : target; + return includeDeprecated || !resolved.deprecated; }) .map((defKey) => { const value = schema2value(schemas[defKey]); diff --git a/src/getConfig.ts b/src/getConfig.ts index 5a1413e..dcf00e9 100644 --- a/src/getConfig.ts +++ b/src/getConfig.ts @@ -6,6 +6,7 @@ export type Config = Pick { outputEachDir: config.outputEachDir, outputMode: config.outputMode, isYaml: openapi.yaml ?? !openapi.inputFile.endsWith('.json'), + includeDeprecated: openapi.includeDeprecated ?? false, }; }; diff --git a/tests/index.spec.ts b/tests/index.spec.ts index 2ea6ac0..23a9f14 100644 --- a/tests/index.spec.ts +++ b/tests/index.spec.ts @@ -37,4 +37,51 @@ describe('cli test', () => { }), ); }); + + describe('includeDeprecated configuration', () => { + beforeAll(async () => { + const configExclude = { + input: '_test-deprecated-exclude', + outputEachDir: true, + openapi: { inputFile: 'samples/deprecated-test.yml', includeDeprecated: false }, + }; + + const configInclude = { + input: '_test-deprecated-include', + outputEachDir: true, + openapi: { inputFile: 'samples/deprecated-test.yml', includeDeprecated: true }, + }; + + await Promise.all([build(configExclude)[0], build(configInclude)[0]]); + }); + + afterAll(() => { + fs.rmSync('_test-deprecated-exclude', { recursive: true }); + fs.rmSync('_test-deprecated-include', { recursive: true }); + }); + + test('main', async () => { + const excludeIndexContent = fs.readFileSync('_test-deprecated-exclude/test/index.ts', 'utf8'); + expect(excludeIndexContent).not.toContain('get:'); + expect(excludeIndexContent).toContain('post:'); + + const includeIndexContent = fs.readFileSync('_test-deprecated-include/test/index.ts', 'utf8'); + expect(includeIndexContent).toContain('get:'); + expect(includeIndexContent).toContain('post:'); + + const excludeTypesContent = fs.readFileSync( + '_test-deprecated-exclude/@types/index.ts', + 'utf8', + ); + expect(excludeTypesContent).not.toContain('deprecated_field'); + expect(excludeTypesContent).toContain('active_field'); + + const includeTypesContent = fs.readFileSync( + '_test-deprecated-include/@types/index.ts', + 'utf8', + ); + expect(includeTypesContent).toContain('deprecated_field'); + expect(includeTypesContent).toContain('active_field'); + }); + }); });