Skip to content

Commit

Permalink
chore: add curl examples to snippets if not existing at all OpenApi P…
Browse files Browse the repository at this point in the history
…arser v2 (#2092)

Co-authored-by: fern-bot <[email protected]>
  • Loading branch information
RohinBhargava and fern-support authored Jan 31, 2025
1 parent 5c58596 commit 85c1df5
Show file tree
Hide file tree
Showing 4 changed files with 440 additions and 227 deletions.
101 changes: 58 additions & 43 deletions packages/fern-docs/cache/src/ApiDefinitionLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,15 +195,17 @@ export class ApiDefinitionLoader {
definition = await this.resolveDescriptions(definition);
}

if (this.edgeFlags.isHttpSnippetsEnabled) {
definition = await this.resolveHttpCodeSnippets(definition);
}
definition = await this.resolveHttpCodeSnippets(
definition,
this.edgeFlags.isHttpSnippetsEnabled
);

return definition;
};

private resolveHttpCodeSnippets = async (
apiDefinition: ApiDefinition
apiDefinition: ApiDefinition,
httpSnippetsEnabled: boolean
): Promise<ApiDefinition> => {
// Collect all endpoints first, so that we can resolve descriptions in a single batch
const collected: EndpointDefinition[] = [];
Expand All @@ -224,7 +226,12 @@ export class ApiDefinitionLoader {

const examples = await Promise.all(
endpoint.examples.map((example) =>
this.resolveExample(apiDefinition, endpoint, example)
this.resolveExample(
apiDefinition,
endpoint,
example,
httpSnippetsEnabled
)
)
);

Expand All @@ -243,7 +250,8 @@ export class ApiDefinitionLoader {
private resolveExample = async (
apiDefinition: ApiDefinition,
endpoint: EndpointDefinition,
example: ExampleEndpointCall
example: ExampleEndpointCall,
httpSnippetsEnabled: boolean
): Promise<ExampleEndpointCall> => {
const snippets = { ...example.snippets };

Expand Down Expand Up @@ -274,45 +282,52 @@ export class ApiDefinitionLoader {
});
}

const snippet = new HTTPSnippet(
getHarRequest(endpoint, example, apiDefinition.auths, example.requestBody)
);
for (const { clientId, targetId } of CLIENTS) {
/**
* If the snippet already exists, skip it
*/
if (snippets[targetId]?.length) {
continue;
}
if (httpSnippetsEnabled) {
const snippet = new HTTPSnippet(
getHarRequest(
endpoint,
example,
apiDefinition.auths,
example.requestBody
)
);
for (const { clientId, targetId } of CLIENTS) {
/**
* If the snippet already exists, skip it
*/
if (snippets[targetId]?.length) {
continue;
}

/**
* If alwaysEnableJavaScriptFetch is disabled, skip generating JavaScript snippets if TypeScript snippets are available
*/
if (
targetId === "javascript" &&
snippets[APIV1Read.SupportedLanguage.Typescript]?.length &&
!this.edgeFlags.alwaysEnableJavaScriptFetch
) {
continue;
}
/**
* If alwaysEnableJavaScriptFetch is disabled, skip generating JavaScript snippets if TypeScript snippets are available
*/
if (
targetId === "javascript" &&
snippets[APIV1Read.SupportedLanguage.Typescript]?.length &&
!this.edgeFlags.alwaysEnableJavaScriptFetch
) {
continue;
}

const convertedCode = await snippet.convert(targetId, clientId);
const code =
typeof convertedCode === "string"
? convertedCode
: convertedCode != null
? convertedCode[0]
: undefined;

if (code != null) {
pushSnippet({
name: undefined,
language: targetId,
install: undefined,
code,
generated: true,
description: undefined,
});
const convertedCode = await snippet.convert(targetId, clientId);
const code =
typeof convertedCode === "string"
? convertedCode
: convertedCode != null
? convertedCode[0]
: undefined;

if (code != null) {
pushSnippet({
name: undefined,
language: targetId,
install: undefined,
code,
generated: true,
description: undefined,
});
}
}
}

Expand Down
123 changes: 123 additions & 0 deletions packages/fern-docs/cache/src/__test__/ApiDefinitionLoader.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { APIV1Read } from "@fern-api/fdr-sdk";
import {
ApiDefinitionId,
convertToCurl,
EndpointDefinition,
EndpointId,
ExampleEndpointCall,
PropertyKey,
toSnippetHttpRequest,
} from "@fern-api/fdr-sdk/api-definition";
import { ApiDefinitionLoader } from "../ApiDefinitionLoader";

describe("curl snippet generation", () => {
it("generates correct curl snippet", async () => {
const example: ExampleEndpointCall = {
path: "/test",
description: "Test example",
responseStatusCode: 200,
name: "Test Example",
pathParameters: {},
queryParameters: {},
headers: {},
requestBody: {
type: "json",
value: { foo: "bar" },
},
responseBody: undefined,
snippets: {},
};

const endpoint: EndpointDefinition = {
id: EndpointId("testEndpoint"),
auth: undefined,
environments: [],
defaultEnvironment: undefined,
pathParameters: undefined,
queryParameters: undefined,
requestHeaders: undefined,
responseHeaders: undefined,
responses: undefined,
errors: undefined,
examples: [example],
snippetTemplates: undefined,
description: undefined,
availability: undefined,
namespace: [],
displayName: "Test Endpoint",
operationId: "testEndpoint",
method: "POST",
path: [{ type: "literal", value: "test" }],
requests: [
{
contentType: undefined,
description: undefined,
body: {
type: "object",
properties: [
{
key: PropertyKey("foo"),
description: "Test object",
availability: undefined,
valueShape: {
type: "alias",
value: {
type: "primitive",
value: {
type: "string",
format: undefined,
regex: undefined,
minLength: undefined,
maxLength: undefined,
default: undefined,
},
},
},
},
],
extends: [],
extraProperties: undefined,
},
},
],
};

const curlCode = convertToCurl(
toSnippetHttpRequest(endpoint, example, undefined),
{ usesApplicationJsonInFormDataValue: false }
);

const loader = ApiDefinitionLoader.create(
"testdomain",
ApiDefinitionId("testdefinitionid")
);

const apiDefinition = await loader
.withApiDefinition({
id: ApiDefinitionId("id"),
webhooks: {},
websockets: {},
types: {},
subpackages: {},
auths: {},
globalHeaders: [],
endpoints: {
[EndpointId("endpoint")]: endpoint,
},
})
.load();

expect(curlCode).toMatchInlineSnapshot(`
"curl -X POST /test \\
-H "Content-Type: application/json" \\
-d '{
"foo": "bar"
}'"
`);

expect(
apiDefinition?.endpoints?.[EndpointId("testEndpoint")]?.examples?.[0]
?.snippets?.[APIV1Read.SupportedLanguage.Curl]?.[0]?.code
).toEqual(curlCode);
});
});
4 changes: 2 additions & 2 deletions packages/parsers/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@
"openapi-types": "^12.1.3",
"ts-essentials": "^10.0.1",
"uuid": "^9.0.0",
"vitest": "^2.1.4",
"whatwg-mimetype": "^4.0.0"
},
"devDependencies": {
"@fern-fern/docs-parsers-fern-definition": "^0.0.3",
"@fern-platform/configs": "workspace:*",
"@types/uuid": "^9.0.1",
"@types/whatwg-mimetype": "^3.0.2",
"js-yaml": "^4.1.0"
"js-yaml": "^4.1.0",
"vitest": "^2.1.4"
}
}
Loading

0 comments on commit 85c1df5

Please sign in to comment.