From d53fcd86c4588ecdae7d273496a91ea9884bf072 Mon Sep 17 00:00:00 2001 From: Rohin Bhargava Date: Thu, 9 Jan 2025 10:26:26 -0500 Subject: [PATCH] feat(cli): OpenApi v2 Parser Publishing (#5564) * publishing working * chore: update changelog * slightly change language * chore: update changelog * kebab case subpackage titles for slug * kebab case subpackage name slugs * update comments --------- Co-authored-by: RohinBhargava Co-authored-by: fern-support --- fern/pages/changelogs/cli/2025-01-09.mdx | 5 +++ packages/cli/cli/versions.yml | 8 ++++ .../src/ApiReferenceNodeConverterLatest.ts | 12 +++--- .../src/publishDocs.ts | 41 +++++++++++++++++-- packages/cli/register/src/registerApi.ts | 1 - 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 fern/pages/changelogs/cli/2025-01-09.mdx diff --git a/fern/pages/changelogs/cli/2025-01-09.mdx b/fern/pages/changelogs/cli/2025-01-09.mdx new file mode 100644 index 00000000000..18ed6d086f5 --- /dev/null +++ b/fern/pages/changelogs/cli/2025-01-09.mdx @@ -0,0 +1,5 @@ +## 0.47.0 +**`(feat):`** The CLI now supports publishing docs using the improved OpenAPI v2 parser. You can set `openapi-parser-v2: true` +in your `docs.yml` to use the new parser. + + diff --git a/packages/cli/cli/versions.yml b/packages/cli/cli/versions.yml index dca6e43377d..8efd9e3f2ca 100644 --- a/packages/cli/cli/versions.yml +++ b/packages/cli/cli/versions.yml @@ -1,3 +1,11 @@ +- changelogEntry: + - summary: | + The CLI now supports publishing docs using the improved OpenAPI v2 parser. You can set `openapi-parser-v2: true` + in your `docs.yml` to use the new parser. + type: feat + irVersion: 53 + version: 0.47.0 + - changelogEntry: - summary: | The CLI now validates that method and group name overrides in OpenAPI settings are not duplicated. diff --git a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts index 5a913ddf9d4..a8964f0aea0 100644 --- a/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts +++ b/packages/cli/docs-resolver/src/ApiReferenceNodeConverterLatest.ts @@ -22,19 +22,19 @@ import { toRelativeFilepath } from "./utils/toRelativeFilepath"; // TODO: these functions need an extra piece of information from the fdr latest shape, to see if the operation id takes precedence over slug generation function getLatestEndpointUrlSlug(endpoint: FdrAPI.api.latest.endpoint.EndpointDefinition) { - const slugParts = endpoint.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + const slugParts = endpoint.namespace?.map((subpackageId) => kebabCase(subpackageId.toString())) ?? []; slugParts.push(kebabCase(endpoint.id.split(".").pop() ?? "")); return endpoint.operationId != null ? kebabCase(endpoint.operationId) : urlJoin(slugParts); } function getLatestWebSocketUrlSlug(webSocket: FdrAPI.api.latest.websocket.WebSocketChannel) { - const slugParts = webSocket.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + const slugParts = webSocket.namespace?.map((subpackageId) => kebabCase(subpackageId.toString())) ?? []; slugParts.push(kebabCase(webSocket.id.split(".").pop() ?? "")); return webSocket.operationId != null ? kebabCase(webSocket.operationId) : urlJoin(slugParts); } function getLatestWebhookUrlSlug(webhook: FdrAPI.api.latest.webhook.WebhookDefinition) { - const slugParts = webhook.namespace?.map((subpackageId) => subpackageId.toString()) ?? []; + const slugParts = webhook.namespace?.map((subpackageId) => kebabCase(subpackageId.toString())) ?? []; slugParts.push(kebabCase(webhook.id.split(".").pop() ?? "")); return webhook.operationId != null ? kebabCase(webhook.operationId) : urlJoin(slugParts); } @@ -397,7 +397,7 @@ export class ApiReferenceNodeConverterLatest { this.#visitedSubpackages.add(subpackageId); this.#nodeIdToSubpackageId.set(subpackageNodeId, [subpackageId]); - const urlSlug = subpackage.name; + const urlSlug = kebabCase(subpackage.name); const slug = parentSlug.apply({ urlSlug }); const subpackageNode: FernNavigation.V1.ApiPackageNode = { id: subpackageNodeId, @@ -583,7 +583,7 @@ export class ApiReferenceNodeConverterLatest { } const slug = parentSlug.apply({ - urlSlug: subpackageMetadata.name + urlSlug: kebabCase(subpackageMetadata.name) }); const subpackageNode: FernNavigation.V1.ApiPackageNode = { id: FernNavigation.V1.NodeId(`${this.apiDefinitionId}:${subpackageId}`), @@ -647,7 +647,7 @@ export class ApiReferenceNodeConverterLatest { ); return; } - let slugGenerator = parentSlug.apply({ urlSlug: subpackageCursor.slug }); + let slugGenerator = parentSlug.apply({ urlSlug: kebabCase(subpackageCursor.slug) }); for (const namespacePart of endpoint.namespace.slice(1)) { let newSubpackageCursor: FdrAPI.navigation.v1.ApiPackageChild | undefined = diff --git a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts index d0941a96b7b..1cc35ffc47d 100644 --- a/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts +++ b/packages/cli/generation/remote-generation/remote-workspace-runner/src/publishDocs.ts @@ -161,7 +161,6 @@ export async function publishDocs({ }, async ({ ir, snippetsConfig, playgroundConfig, apiName }) => { const apiDefinition = convertIrToFdrApi({ ir, snippetsConfig, playgroundConfig }); - context.logger.debug("Calling registerAPI... ", JSON.stringify(apiDefinition, undefined, 4)); const response = await fdr.api.v1.register.registerApiDefinition({ orgId: CjsFdrSdk.OrgId(organization), apiId: CjsFdrSdk.ApiId(ir.apiName.originalName), @@ -182,9 +181,45 @@ export async function publishDocs({ } default: if (apiName != null) { - return context.failAndThrow(`Failed to register API ${apiName}`, response.error); + return context.failAndThrow( + `Failed to publish docs because API definition (${apiName}) could not be uploaded. Please contact support@buildwithfern.com\n ${response.error}` + ); } else { - return context.failAndThrow("Failed to register API", response.error); + return context.failAndThrow( + `Failed to publish docs because API definition could not be uploaded. Please contact support@buildwithfern.com\n ${response.error}` + ); + } + } + } + }, + async ({ api, apiName }) => { + const response = await fdr.api.v1.register.registerApiDefinition({ + orgId: CjsFdrSdk.OrgId(organization), + apiId: CjsFdrSdk.ApiId(apiName ?? api.id), + definition: undefined, + definitionV2: api + }); + + if (response.ok) { + context.logger.debug(`Registered API Definition ${response.body.apiDefinitionId}`); + return response.body.apiDefinitionId; + } else { + switch (response.error.error) { + case "UnauthorizedError": + case "UserNotInOrgError": { + return context.failAndThrow( + "You do not have permissions to register the docs. Reach out to support@buildwithfern.com" + ); + } + default: + if (apiName != null) { + return context.failAndThrow( + `Failed to publish docs because API definition (${apiName}) could not be uploaded. Please contact support@buildwithfern.com\n ${response.error}` + ); + } else { + return context.failAndThrow( + `Failed to publish docs because API definition could not be uploaded. Please contact support@buildwithfern.com\n ${response.error}` + ); } } } diff --git a/packages/cli/register/src/registerApi.ts b/packages/cli/register/src/registerApi.ts index 56ac879667b..481e41f0d9a 100644 --- a/packages/cli/register/src/registerApi.ts +++ b/packages/cli/register/src/registerApi.ts @@ -48,7 +48,6 @@ export async function registerApi({ }); const apiDefinition = convertIrToFdrApi({ ir, snippetsConfig, playgroundConfig }); - context.logger.debug("Calling registerAPI... ", JSON.stringify(apiDefinition, undefined, 4)); const response = await fdrService.api.v1.register.registerApiDefinition({ orgId: FdrCjsSdk.OrgId(organization), apiId: FdrCjsSdk.ApiId(ir.apiName.originalName),