Skip to content

Commit

Permalink
feat(cli): specify openrpc in docs.yml (#5613)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsinghvi authored Jan 15, 2025
1 parent c683306 commit ee431ae
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 10 deletions.
10 changes: 10 additions & 0 deletions docs-yml.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,16 @@
}
]
},
"openrpc": {
"oneOf": [
{
"type": "string"
},
{
"type": "null"
}
]
},
"audiences": {
"oneOf": [
{
Expand Down
4 changes: 4 additions & 0 deletions fern/apis/docs-yml/definition/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,10 @@ types:
"api-name":
type: optional<string>
docs: Name of API that we are referencing
openrpc:
type: optional<string>
docs: Path to an openrpc spec.
availability: pre-release
audiences: optional<list<AudienceId>>
display-errors:
type: optional<boolean>
Expand Down
1 change: 1 addition & 0 deletions generators/python/sdk/versions.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# For unreleased changes, use unreleased.yml

- version: 4.3.11
irVersion: 53
changelogEntry:
Expand Down
14 changes: 14 additions & 0 deletions packages/cli/cli/versions.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
- changelogEntry:
- summary: |
The docs now includes alpha support for parsing openrpc specs. To leverage this feature,
simply define an API section in your docs.yml and point at an openrpc spec.
```yml docs.yml
navigation:
- api: API Reference
openrpc: <path to openrpc file>
```
type: fix
irVersion: 55
version: 0.50.2

- changelogEntry:
- summary: |
Fixes an issue where nullable schemas were not coerced into optional values.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,7 @@ async function convertNavigationItem({
if (isRawApiSectionConfig(rawConfig)) {
return {
type: "apiSection",
openrpc: rawConfig.openrpc,
title: rawConfig.api,
icon: rawConfig.icon,
apiName: rawConfig.apiName ?? undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export declare namespace DocsNavigationItem {
title: string;
icon: string | undefined;
apiName: string | undefined;
openrpc: string | undefined;
audiences: Audiences;
showErrors: boolean;
snippetsConfiguration: SnippetsConfiguration | undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export interface ApiReferenceConfiguration extends FernDocsConfig.WithPermission
api: string;
/** Name of API that we are referencing */
apiName?: string;
/** Path to an openrpc spec. */
openrpc?: string;
audiences?: FernDocsConfig.AudienceId[];
/** Defaults to false */
displayErrors?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const ApiReferenceConfiguration: core.serialization.ObjectSchema<
.object({
api: core.serialization.string(),
apiName: core.serialization.property("api-name", core.serialization.string().optional()),
openrpc: core.serialization.string().optional(),
audiences: core.serialization.list(AudienceId).optional(),
displayErrors: core.serialization.property("display-errors", core.serialization.boolean().optional()),
snippets: SnippetsConfiguration.optional(),
Expand All @@ -37,6 +38,7 @@ export declare namespace ApiReferenceConfiguration {
export interface Raw extends WithPermissions.Raw {
api: string;
"api-name"?: string | null;
openrpc?: string | null;
audiences?: AudienceId.Raw[] | null;
"display-errors"?: boolean | null;
snippets?: SnippetsConfiguration.Raw | null;
Expand Down
3 changes: 2 additions & 1 deletion packages/cli/docs-resolver/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@
"gray-matter": "^4.0.3",
"lodash-es": "^4.17.21",
"url-join": "^5.0.0",
"openapi-types": "^12.1.3"
"openapi-types": "^12.1.3",
"@open-rpc/meta-schema": "^1.14.9"
},
"devDependencies": {
"@fern-api/configs": "workspace:*",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export class ApiReferenceNodeConverterLatest {
private apiSection: docsYml.DocsNavigationItem.ApiSection,
api: FdrAPI.api.latest.ApiDefinition,
parentSlug: FernNavigation.V1.SlugGenerator,
private workspace: OSSWorkspace,
private workspace: OSSWorkspace | undefined,
private docsWorkspace: DocsWorkspace,
private taskContext: TaskContext,
private markdownFilesToFullSlugs: Map<AbsoluteFilePath, string>,
Expand Down Expand Up @@ -104,7 +104,7 @@ export class ApiReferenceNodeConverterLatest {
const pointsTo = FernNavigation.V1.followRedirects(this.#children);
const changelogNodeConverter = new ChangelogNodeConverter(
this.markdownFilesToFullSlugs,
this.workspace.changelog?.files.map((file) => file.absoluteFilepath),
this.workspace?.changelog?.files.map((file) => file.absoluteFilepath),
this.docsWorkspace,
this.#idgen
).orUndefined();
Expand Down
36 changes: 33 additions & 3 deletions packages/cli/docs-resolver/src/DocsDefinitionResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ import {
replaceReferencedCode,
replaceReferencedMarkdown
} from "@fern-api/docs-markdown-utils";
import { APIV1Read, APIV1Write, DocsV1Write, FdrAPI, FernNavigation } from "@fern-api/fdr-sdk";
import { AbsoluteFilePath, RelativeFilePath, listFiles, relative, resolve } from "@fern-api/fs-utils";
import { APIV1Write, DocsV1Write, FdrAPI, FernNavigation } from "@fern-api/fdr-sdk";
import { AbsoluteFilePath, RelativeFilePath, doesPathExist, listFiles, relative, resolve } from "@fern-api/fs-utils";
import { generateIntermediateRepresentation } from "@fern-api/ir-generator";
import { IntermediateRepresentation } from "@fern-api/ir-sdk";
import { OSSWorkspace } from "@fern-api/lazy-fern-workspace";
import { TaskContext } from "@fern-api/task-context";
import { AbstractAPIWorkspace, DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader";
import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader";

import { ApiReferenceNodeConverter } from "./ApiReferenceNodeConverter";
import { ApiReferenceNodeConverterLatest } from "./ApiReferenceNodeConverterLatest";
Expand All @@ -29,6 +29,7 @@ import { NodeIdGenerator } from "./NodeIdGenerator";
import { convertDocsSnippetsConfigToFdr } from "./utils/convertDocsSnippetsConfigToFdr";
import { convertIrToApiDefinition } from "./utils/convertIrToApiDefinition";
import { generateFdrFromOpenApiWorkspace } from "./utils/generateFdrFromOpenApiWorkspace";
import { generateFdrFromOpenrpc } from "./utils/generateFdrFromOpenrpc";
import { collectFilesFromDocsConfig } from "./utils/getImageFilepathsToUpload";
import { visitNavigationAst } from "./visitNavigationAst";
import { wrapWithHttps } from "./wrapWithHttps";
Expand Down Expand Up @@ -593,6 +594,35 @@ export class DocsDefinitionResolver {
item: docsYml.DocsNavigationItem.ApiSection,
parentSlug: FernNavigation.V1.SlugGenerator
): Promise<FernNavigation.V1.ApiReferenceNode> {
if (item.openrpc != null) {
const absoluteFilepathToOpenrpc = resolve(
this.docsWorkspace.absoluteFilePath,
RelativeFilePath.of(item.openrpc)
);
if (!(await doesPathExist(absoluteFilepathToOpenrpc))) {
throw new Error(`OpenRPC file does not exist at path: ${absoluteFilepathToOpenrpc}`);
}
const api = await generateFdrFromOpenrpc(absoluteFilepathToOpenrpc, this.taskContext);
if (api == null) {
throw new Error("Failed to generate API Definition from OpenRPC document");
}
await this.registerApiV2({
api,
apiName: item.apiName
});
const node = new ApiReferenceNodeConverterLatest(
item,
api,
parentSlug,
undefined,
this.docsWorkspace,
this.taskContext,
this.markdownFilesToFullSlugs,
this.#idgen
);
return node.get();
}

if (this.parsedDocsConfig.experimental?.openapiParserV2) {
const workspace = this.getOpenApiWorkspaceForApiSection(item);
const api = await generateFdrFromOpenApiWorkspace(workspace, this.taskContext);
Expand Down
29 changes: 29 additions & 0 deletions packages/cli/docs-resolver/src/utils/generateFdrFromOpenrpc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { OpenrpcDocument } from "@open-rpc/meta-schema";
import { readFile } from "fs/promises";

import { OpenrpcContext, OpenrpcDocumentConverterNode } from "@fern-api/docs-parsers";
import { AbsoluteFilePath } from "@fern-api/fs-utils";
import { TaskContext } from "@fern-api/task-context";

export async function generateFdrFromOpenrpc(
openrpcPath: AbsoluteFilePath,
context: TaskContext
): Promise<ReturnType<OpenrpcDocumentConverterNode["convert"]>> {
// Read and parse the OpenRPC document
const openrpcContent = await readFile(openrpcPath, "utf-8");
const openrpcDocument = JSON.parse(openrpcContent) as OpenrpcDocument;

const openrpcContext = new OpenrpcContext({
openrpc: openrpcDocument,
logger: context.logger
});

const openrpcFdrJson = new OpenrpcDocumentConverterNode({
input: openrpcDocument,
context: openrpcContext,
accessPath: [],
pathId: "openrpc"
});

return openrpcFdrJson.convert();
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import { docsYml } from "@fern-api/configuration";
import { createFdrService } from "@fern-api/core";
import { MediaType } from "@fern-api/core-utils";
import { DocsDefinitionResolver, UploadedFile, wrapWithHttps } from "@fern-api/docs-resolver";
import { AbsoluteFilePath, RelativeFilePath, resolve } from "@fern-api/fs-utils";
import { convertToFernHostRelativeFilePath } from "@fern-api/fs-utils";
import { AbsoluteFilePath, RelativeFilePath, convertToFernHostRelativeFilePath, resolve } from "@fern-api/fs-utils";
import { convertIrToFdrApi } from "@fern-api/register";
import { TaskContext } from "@fern-api/task-context";
import { DocsWorkspace, FernWorkspace } from "@fern-api/workspace-loader";
Expand Down Expand Up @@ -214,11 +213,11 @@ export async function publishDocs({
default:
if (apiName != null) {
return context.failAndThrow(
`Failed to publish docs because API definition (${apiName}) could not be uploaded. Please contact [email protected]\n ${response.error}`
`Failed to publish docs because API definition (${apiName}) could not be uploaded. Please contact [email protected]\n ${JSON.stringify(response.error)}`
);
} else {
return context.failAndThrow(
`Failed to publish docs because API definition could not be uploaded. Please contact [email protected]\n ${response.error}`
`Failed to publish docs because API definition could not be uploaded. Please contact [email protected]\n ${JSON.stringify(response.error)}`
);
}
}
Expand Down
13 changes: 13 additions & 0 deletions packages/cli/yaml/docs-validator/src/docsAst/visitNavigationAst.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,19 @@ async function visitNavigationItem({
api: noop,
apiName: noop,
audiences: noop,
openrpc: async (path: string | undefined): Promise<void> => {
if (path == null) {
return;
}

await visitFilepath({
absoluteFilepathToConfiguration,
rawUnresolvedFilepath: path,
visitor,
nodePath: [...nodePath, "openrpc"],
willBeUploaded: false
});
},
displayErrors: noop,
snippets: noop,
summary: noop,
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit ee431ae

Please sign in to comment.