From 15cef7ae78f41a5d9fd677aa7de5302ab95e9932 Mon Sep 17 00:00:00 2001 From: fern-bot Date: Wed, 27 Nov 2024 12:30:33 -0700 Subject: [PATCH] added new openapi-fdr command --- packages/cli/cli/package.json | 4 +- packages/cli/cli/src/cli.ts | 30 ++++++ ...eOpenApiToFdrApiDefinitionForWorkspaces.ts | 70 ++++++++++++ pnpm-lock.yaml | 100 +++++++++++++++++- 4 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts diff --git a/packages/cli/cli/package.json b/packages/cli/cli/package.json index dfd7dfd92ce..f405b1534dc 100644 --- a/packages/cli/cli/package.json +++ b/packages/cli/cli/package.json @@ -38,7 +38,6 @@ "publish:cli:prod": "cd dist/prod && npm publish" }, "devDependencies": { - "@fern-api/docs-resolver": "workspace:*", "@fern-api/api-workspace-commons": "workspace:*", "@fern-api/auth": "workspace:*", "@fern-api/cli-logger": "workspace:*", @@ -46,7 +45,9 @@ "@fern-api/configuration": "workspace:*", "@fern-api/core": "workspace:*", "@fern-api/core-utils": "workspace:*", + "@fern-api/docs-parsers": "^0.0.2", "@fern-api/docs-preview": "workspace:*", + "@fern-api/docs-resolver": "workspace:*", "@fern-api/docs-validator": "workspace:*", "@fern-api/fern-definition-formatter": "workspace:*", "@fern-api/fern-definition-schema": "workspace:*", @@ -102,6 +103,7 @@ "js-yaml": "^4.1.0", "latest-version": "^9.0.0", "lodash-es": "^4.17.21", + "openapi-types": "^12.1.3", "ora": "^7.0.1", "organize-imports-cli": "^0.10.0", "prettier": "^2.7.1", diff --git a/packages/cli/cli/src/cli.ts b/packages/cli/cli/src/cli.ts index cf73175b2fb..1265cb532da 100644 --- a/packages/cli/cli/src/cli.ts +++ b/packages/cli/cli/src/cli.ts @@ -46,6 +46,7 @@ import { generateJsonschemaForWorkspaces } from "./commands/jsonschema/generateJ import { generateDynamicIrForWorkspaces } from "./commands/generate-dynamic-ir/generateDynamicIrForWorkspaces"; import { writeDocsDefinitionForProject } from "./commands/write-docs-definition/writeDocsDefinitionForProject"; import { RUNTIME } from "@fern-typescript/fetcher"; +import { generateOpenApiToFdrApiDefinitionForWorkspaces } from "./commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces"; void runCli(); @@ -152,6 +153,7 @@ async function tryRunCli(cliContext: CliContext) { addGenerateCommand(cli, cliContext); addIrCommand(cli, cliContext); addFdrCommand(cli, cliContext); + addOpenAPIFdrCommand(cli, cliContext); addOpenAPIIrCommand(cli, cliContext); addDynamicIrCommand(cli, cliContext); addValidateCommand(cli, cliContext); @@ -618,6 +620,34 @@ function addFdrCommand(cli: Argv, cliContext: CliContext) { ); } +function addOpenAPIFdrCommand(cli: Argv, cliContext: CliContext) { + cli.command( + "openapi-fdr ", + false, + (yargs) => + yargs + .positional("path-to-output", { + type: "string", + description: "Path to write fern definition registry shape (FDR)", + demandOption: true + }) + .option("api", { + string: true, + description: "Only run the command on the provided API" + }), + async (argv) => { + await generateOpenApiToFdrApiDefinitionForWorkspaces({ + project: await loadProjectAndRegisterWorkspacesWithContext(cliContext, { + commandLineApiWorkspace: argv.api, + defaultToAllApiWorkspaces: false + }), + outputFilepath: resolve(cwd(), argv.pathToOutput), + cliContext + }); + } + ); +} + function addRegisterCommand(cli: Argv, cliContext: CliContext) { cli.command( ["register"], diff --git a/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts new file mode 100644 index 00000000000..45d4706e08c --- /dev/null +++ b/packages/cli/cli/src/commands/generate-openapi-fdr/generateOpenApiToFdrApiDefinitionForWorkspaces.ts @@ -0,0 +1,70 @@ +import { AbsoluteFilePath, stringifyLargeObject } from "@fern-api/fs-utils"; +import { Project } from "@fern-api/project-loader"; +import { writeFile } from "fs/promises"; +import path from "path"; +import { CliContext } from "../../cli-context/CliContext"; +import { getAllOpenAPISpecs, LazyFernWorkspace, OSSWorkspace } from "@fern-api/lazy-fern-workspace"; +import { OpenApiDocumentConverterNode } from "@fern-api/docs-parsers/dist/openapi/3.1/OpenApiDocumentConverter.node"; +import { ErrorCollector } from "@fern-api/docs-parsers/dist/ErrorCollector"; +import fs from "fs"; +import yaml from "js-yaml"; +import { BaseOpenApiV3_1ConverterNodeContext } from "@fern-api/docs-parsers/dist/openapi/BaseOpenApiV3_1Converter.node"; +import { OpenAPIV3_1 } from "openapi-types"; + +export async function generateOpenApiToFdrApiDefinitionForWorkspaces({ + project, + outputFilepath, + cliContext +}: { + project: Project; + outputFilepath: AbsoluteFilePath; + cliContext: CliContext; +}): Promise { + await Promise.all( + project.apiWorkspaces.map(async (workspace) => { + await cliContext.runTaskForWorkspace(workspace, async (context) => { + await cliContext.runTaskForWorkspace(workspace, async (context) => { + if (workspace instanceof LazyFernWorkspace) { + context.logger.info("Skipping, API is specified as a Fern Definition."); + return; + } else if (!(workspace instanceof OSSWorkspace)) { + return; + } + const openAPISpecs = await getAllOpenAPISpecs({ context, specs: workspace.specs }); + + const openApi = openAPISpecs[0]; + + if (openApi != null) { + const input = yaml.load( + fs.readFileSync(openApi.absoluteFilepath, "utf8") + ) as OpenAPIV3_1.Document; + + const oasContext: BaseOpenApiV3_1ConverterNodeContext = { + document: input, + logger: context.logger, + errors: new ErrorCollector() + }; + + const openApiFdrJson = new OpenApiDocumentConverterNode({ + input, + context: oasContext, + accessPath: [], + pathId: workspace.workspaceName ?? "openapi parser" + }); + + const fdrApiDefinition = await openApiFdrJson.convert(); + + const resolvedOutputFilePath = path.resolve(outputFilepath); + await writeFile( + resolvedOutputFilePath, + await stringifyLargeObject(fdrApiDefinition, { pretty: true }) + ); + context.logger.info(`Wrote FDR API definition to ${resolvedOutputFilePath}`); + } else { + context.logger.error("No OpenAPI spec found in the workspace"); + } + }); + }); + }) + ); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc5f26a8011..386fd3c0446 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3794,6 +3794,9 @@ importers: '@fern-api/core-utils': specifier: workspace:* version: link:../../commons/core-utils + '@fern-api/docs-parsers': + specifier: ^0.0.2 + version: 0.0.2(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4) '@fern-api/docs-preview': specifier: workspace:* version: link:../docs-preview @@ -3965,6 +3968,9 @@ importers: lodash-es: specifier: ^4.17.21 version: 4.17.21 + openapi-types: + specifier: ^12.1.3 + version: 12.1.3 ora: specifier: ^7.0.1 version: 7.0.1 @@ -4112,6 +4118,8 @@ importers: specifier: ^2.0.5 version: 2.0.5(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + packages/cli/cli/dist/local: {} + packages/cli/configuration: dependencies: '@fern-api/core-utils': @@ -7608,15 +7616,30 @@ packages: '@exodus/schemasafe@1.0.0': resolution: {integrity: sha512-2cyupPIZI69HQxEAPllLXBjQp4njDKkOjYRCYxvMZe3/LY9pp9fBM3Tb1wiFAdP6Emo4v3OEbCLGj6u73Q5KLw==} + '@fern-api/core-utils@0.4.24-rc1': + resolution: {integrity: sha512-aYu4lQK2qZIKzTF9TeFrICTPJ/zGEZUEWQmZt6pJeHu+R6afrcCBNkUleWU1OpHlDbe+xXUUBOktRg0PM9Hywg==} + + '@fern-api/docs-parsers@0.0.2': + resolution: {integrity: sha512-NKgnZVjM+V/HZQieQ/n2Ixo54/f3J2tItW9xvqGrrnHJn4QaU2JwO5bed6j3DmmmTQPe3DeAA+vePI0hU3fxUg==} + + '@fern-api/fdr-sdk@0.0.0': + resolution: {integrity: sha512-aifmZ3QzO+WOaKWza/c/FrUGsX05/gjXY8U2iz3/dbra/i1RKyn5EcROw41NKaIafVoAAiWK5cppC4vBWayn2w==} + '@fern-api/fdr-sdk@0.126.1-444264056': resolution: {integrity: sha512-Xl1Ctteav1GOulX40756FLEoJAI7VCn6zgkdDb6RRSZhm9Z8fjaBRv/cdMo1saupqHNd62Hm5b8FdmGjwWFAPw==} + '@fern-api/logger@0.4.24-rc1': + resolution: {integrity: sha512-yh0E2F3K3IPnJZcE4dv+u8I51iKgTgv/reinKo4K5YmYEG1iLtw5vBEYMOPkQmsYFPAKIh++OMB/6TrsahMWew==} + '@fern-api/sdk@0.12.3': resolution: {integrity: sha512-sexxpQEHBmsAXW3UHo5x7NW8Z9TrHWYLGo/J7eFqHYSH8Sq9LA3vRlR0ni9h4FNlKnPDu9Y55L0BhsQVXYqPKA==} '@fern-api/template-resolver@0.7.5': resolution: {integrity: sha512-hmvSxuhyWMjn1DxJJS/R+CIaZcF5c+UBEyFqFOw+laoBtdDvNgEyCu7XP5cILVMABPHmt7kkIO5KbrsCS5S2kw==} + '@fern-api/ui-core-utils@0.0.0': + resolution: {integrity: sha512-8T3YLd+n8z5Vs+WNRIwH6PUW31ZC4/lkRD5G2+qyBcdePfOVYV3CHp3eiUrSSArOr0SJmzN/mQwPm3iAaey7nw==} + '@fern-api/ui-core-utils@0.126.1-444264056': resolution: {integrity: sha512-9L3Tgl83nGaw9Jug17ZXSkPL7ZTeOP3SukG/Fz2Y2Aa/GqEToCrexuGkTO090nwuv2zO9gi7CYDHvQOEl5IIMQ==} @@ -8538,6 +8561,9 @@ packages: '@ungap/structured-clone@1.2.0': resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + '@ungap/url-search-params@0.2.2': + resolution: {integrity: sha512-qQsguKXZVKdCixOHX9jqnX/K/1HekPDpGKyEcXHT+zR6EjGA7S4boSuelL4uuPv6YfhN0n8c4UxW+v/Z3gM2iw==} + '@vitejs/plugin-react@4.2.1': resolution: {integrity: sha512-oojO9IDc4nCUUi8qIR11KoQm0XFFLIwsRBwHRR4d/88IWghn1y6ckz/bJ8GHDCsYEJee8mDzqtJxh15/cisJNQ==} engines: {node: ^14.18.0 || >=16.0.0} @@ -8872,6 +8898,9 @@ packages: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} + axios@0.27.2: + resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} + axios@1.7.7: resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==} @@ -14598,6 +14627,49 @@ snapshots: '@exodus/schemasafe@1.0.0': {} + '@fern-api/core-utils@0.4.24-rc1': + dependencies: + lodash-es: 4.17.21 + strip-ansi: 7.1.0 + + '@fern-api/docs-parsers@0.0.2(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5)(typescript@4.6.4)': + dependencies: + '@fern-api/fdr-sdk': 0.0.0 + '@fern-api/logger': 0.4.24-rc1 + '@fern-api/ui-core-utils': 0.0.0 + openapi-types: 12.1.3 + ts-essentials: 10.0.1(typescript@4.6.4) + uuid: 9.0.1 + vitest: 2.1.4(@types/node@18.7.18)(jsdom@20.0.3)(sass@1.72.0)(terser@5.31.5) + whatwg-mimetype: 4.0.0 + transitivePeerDependencies: + - '@edge-runtime/vm' + - '@types/node' + - '@vitest/browser' + - '@vitest/ui' + - debug + - happy-dom + - jsdom + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - typescript + + '@fern-api/fdr-sdk@0.0.0': + dependencies: + '@ungap/url-search-params': 0.2.2 + axios: 0.27.2 + js-base64: 3.7.2 + url-join: 4.0.1 + transitivePeerDependencies: + - debug + '@fern-api/fdr-sdk@0.126.1-444264056(typescript@4.6.4)': dependencies: '@fern-api/ui-core-utils': 0.126.1-444264056 @@ -14618,6 +14690,11 @@ snapshots: - encoding - typescript + '@fern-api/logger@0.4.24-rc1': + dependencies: + '@fern-api/core-utils': 0.4.24-rc1 + chalk: 5.3.0 + '@fern-api/sdk@0.12.3': dependencies: '@fern-api/template-resolver': 0.7.5 @@ -14634,6 +14711,12 @@ snapshots: dependencies: prettier: 3.3.3 + '@fern-api/ui-core-utils@0.0.0': + dependencies: + strip-ansi: 7.1.0 + title: 3.5.3 + ua-parser-js: 1.0.37 + '@fern-api/ui-core-utils@0.126.1-444264056': dependencies: strip-ansi: 7.1.0 @@ -15753,6 +15836,8 @@ snapshots: '@ungap/structured-clone@1.2.0': {} + '@ungap/url-search-params@0.2.2': {} + '@vitejs/plugin-react@4.2.1(vite@5.4.10(@types/node@18.7.18)(sass@1.72.0)(terser@5.31.5))': dependencies: '@babel/core': 7.25.2 @@ -15996,7 +16081,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -16172,6 +16257,13 @@ snapshots: dependencies: possible-typed-array-names: 1.0.0 + axios@0.27.2: + dependencies: + follow-redirects: 1.15.6 + form-data: 4.0.0 + transitivePeerDependencies: + - debug + axios@1.7.7: dependencies: follow-redirects: 1.15.6 @@ -17915,7 +18007,7 @@ snapshots: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -17924,7 +18016,7 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.6 + debug: 4.3.7 transitivePeerDependencies: - supports-color @@ -20338,7 +20430,7 @@ snapshots: dependencies: chokidar: 3.6.0 immutable: 4.3.5 - source-map-js: 1.2.0 + source-map-js: 1.2.1 sax@1.3.0: {}