Skip to content

Commit

Permalink
feat(openrpc): introduce json schema examle generator (#2017)
Browse files Browse the repository at this point in the history
  • Loading branch information
dsinghvi authored Jan 15, 2025
1 parent 8449221 commit 6692418
Show file tree
Hide file tree
Showing 6 changed files with 2,847 additions and 48 deletions.
2 changes: 1 addition & 1 deletion packages/parsers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fern-api/docs-parsers",
"version": "0.0.36",
"version": "0.0.37",
"repository": {
"type": "git",
"url": "https://github.com/fern-api/fern-platform.git",
Expand Down
75 changes: 75 additions & 0 deletions packages/parsers/src/examples/generateExampleForJsonSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { JSONSchema } from "@open-rpc/meta-schema";

export function generateExampleForJsonSchema(schema: JSONSchema): unknown {
if (typeof schema === "boolean") {
return schema;
}

if (schema.enum != null && schema.enum.length > 0) {
return schema.enum[0];
}

if (schema.const != null) {
return schema.const;
}

if (schema.examples != null && schema.examples.length > 0) {
return schema.examples[0];
}

if (schema.type === "object") {
const example: Record<string, unknown> = {};
if (schema.properties != null) {
for (const [key, value] of Object.entries(schema.properties)) {
example[key] = generateExampleForJsonSchema(value);
}
}
return example;
}

if (schema.type === "array") {
if (schema.items == null) {
return [];
}
if (Array.isArray(schema.items)) {
return schema.items.map((item) => generateExampleForJsonSchema(item));
}
return [generateExampleForJsonSchema(schema.items)];
}

if (schema.type === "string") {
return schema.default ?? "string";
}

if (schema.type === "number" || schema.type === "integer") {
return schema.default ?? 0;
}

if (schema.type === "boolean") {
return schema.default ?? false;
}

if (schema.type === "null") {
return null;
}

if (schema.oneOf?.[0] != null) {
return generateExampleForJsonSchema(schema.oneOf[0]);
}

if (schema.anyOf?.[0] != null) {
return generateExampleForJsonSchema(schema.anyOf[0]);
}

if (schema.allOf != null && schema.allOf.length > 0) {
const example: Record<string, unknown> = {};
for (const subSchema of schema.allOf) {
if (subSchema != null) {
Object.assign(example, generateExampleForJsonSchema(subSchema));
}
}
return example;
}

return undefined;
}
101 changes: 81 additions & 20 deletions packages/parsers/src/openrpc/1.x/MethodConverter.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { MethodObject } from "@open-rpc/meta-schema";
import { camelCase } from "es-toolkit";
import { UnreachableCaseError } from "ts-essentials";
import { FernRegistry } from "../../client/generated";
import { generateExampleForJsonSchema } from "../../examples/generateExampleForJsonSchema";
import { SchemaConverterNode, ServerObjectConverterNode } from "../../openapi";
import { maybeSingleValueToArray } from "../../openapi/utils/maybeSingleValueToArray";
import {
Expand Down Expand Up @@ -100,25 +101,8 @@ export class MethodConverterNode extends BaseOpenrpcConverterNode<
}
: undefined;

// Convert method to HTTP endpoint
// This is a basic implementation that needs to be expanded
return {
id: FernRegistry.EndpointId(this.input.name),
displayName: camelCase(this.input.name),
method: "POST",
path: [{ type: "literal", value: "" }],
auth: undefined,
pathParameters: [],
queryParameters: [],
requests: request != null ? [request] : undefined,
responses:
response != null
? [
this.convertToHttpResponse(response, this.input.description),
].filter(isNonNullish)
: [],
errors: [],
examples: this.method.examples
const examples =
this.method.examples
?.map(
(
example
Expand Down Expand Up @@ -157,7 +141,84 @@ export class MethodConverterNode extends BaseOpenrpcConverterNode<
};
}
)
.filter(isNonNullish),
.filter(isNonNullish) ?? [];

if (examples.length <= 0) {
const example = {
name: "Example",
path: "",
pathParameters: {},
queryParameters: {},
headers: {},
requestBody: {
type: "json" as const,
value: generateExampleForJsonSchema({
type: "object",
properties: Object.fromEntries(
this.method.params?.map((param) => {
const resolvedParam = resolveContentDescriptorObject(
param,
this.context.openrpc
);
return [
resolvedParam?.name ?? "",
resolvedParam?.schema ?? {},
];
}) ?? []
),
}),
},
responseStatusCode: 200,
responseBody: {
type: "json" as const,
value: generateExampleForJsonSchema(
resolveContentDescriptorObject(
this.method.result,
this.context.openrpc
)?.schema ?? {}
),
},
snippets: undefined,
description: undefined,
};
examples.push(example);
}

const examplesWithJsonRPCMetadata = examples.map((example) => {
const originalRequestBody = example.requestBody?.value;
return {
...example,
requestBody: {
type: "json" as const,
value: {
id: 1,
jsonrpc: "2.0",
method: this.method.name,
params: originalRequestBody,
},
},
};
});

// Convert method to HTTP endpoint
// This is a basic implementation that needs to be expanded
return {
id: FernRegistry.EndpointId(this.input.name),
displayName: camelCase(this.input.name),
method: "POST",
path: [{ type: "literal", value: "" }],
auth: undefined,
pathParameters: [],
queryParameters: [],
requests: request != null ? [request] : undefined,
responses:
response != null
? [
this.convertToHttpResponse(response, this.input.description),
].filter(isNonNullish)
: [],
errors: [],
examples: examplesWithJsonRPCMetadata,
description: this.input.description ?? this.input.summary,
operationId: this.input.name,
defaultEnvironment: undefined,
Expand Down
Loading

0 comments on commit 6692418

Please sign in to comment.