Skip to content

Commit

Permalink
(fix): x-fern-streaming wont duplicate referenced requests causing …
Browse files Browse the repository at this point in the history
…collision (#3136)

(fix): x-fern-streaming wont duplicate referenced requests causing collision
  • Loading branch information
dsinghvi authored Mar 9, 2024
1 parent 48a16a8 commit 4c498d5
Show file tree
Hide file tree
Showing 8 changed files with 203 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`x-fern-streaming-with-reference x-fern-streaming-with-reference simple 1`] = `
{
"definitionFiles": {
"chatCompletions.yml": {
"imports": {
"root": "__package__.yml",
},
"service": {
"auth": false,
"base-path": "",
"endpoints": {
"create": {
"auth": false,
"docs": undefined,
"examples": [
{
"request": {
"prompt": "prompt",
"stream": false,
},
"response": {
"body": {
"text": "text",
},
},
},
],
"method": "POST",
"path": "/completions/chat",
"request": {
"body": {
"properties": {
"prompt": "string",
"stream": "literal<false>",
},
},
"headers": undefined,
"name": "ChatCompletionsCreateRequest",
"query-parameters": undefined,
},
"response": {
"docs": "",
"type": "root.Completion",
},
},
"create_stream": {
"auth": false,
"docs": undefined,
"method": "POST",
"path": "/completions/chat",
"request": {
"body": {
"properties": {
"prompt": "string",
"stream": "literal<true>",
},
},
"headers": undefined,
"name": "ChatCompletionsCreateStreamRequest",
"query-parameters": undefined,
},
"response-stream": {
"docs": "",
"type": "root.CompletionChunk",
},
},
},
},
},
},
"packageMarkerFile": {
"types": {
"Completion": {
"docs": undefined,
"properties": {
"text": "optional<string>",
},
},
"CompletionChunk": {
"docs": undefined,
"properties": {
"text": "optional<string>",
},
},
},
},
"rootApiFile": {
"display-name": "Test extension \`x-fern-audiences\` alongside \`x-fern-streaming\`.",
"error-discrimination": {
"strategy": "status-code",
},
"name": "api",
},
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
openapi: 3.1.0
info:
title: Test extension `x-fern-audiences` alongside `x-fern-streaming`.
version: 1.0.0
paths:
"/completions/chat":
post:
x-fern-sdk-group-name: chatCompletions
x-fern-sdk-method-name: create
x-fern-streaming:
stream-condition: $request.stream
response:
$ref: "#/components/schemas/Completion"
response-stream:
$ref: "#/components/schemas/CompletionChunk"
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/CreateCompletionRequest"
responses:
"200":
description: "Success!"
content:
application/json:
schema:
type: string
components:
schemas:
CreateCompletionRequest:
type: object
properties:
stream:
type: boolean
prompt:
type: string
required:
- prompt
Completion:
type: object
properties:
text:
type: string
CompletionChunk:
type: object
properties:
text:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { testConvertOpenAPI } from "./testConvertOpenApi";

describe("x-fern-streaming-with-reference", () => {
testConvertOpenAPI("x-fern-streaming-with-reference", "openapi.yml");
});
Original file line number Diff line number Diff line change
Expand Up @@ -161,4 +161,8 @@ export abstract class AbstractOpenAPIV3ParserContext implements SchemaParserCont
): DiscriminatedUnionReference | undefined;

public abstract getErrors(): Record<StatusCode, HttpError>;

public abstract excludeSchema(schemaId: SchemaId): void;

public abstract isSchemaExcluded(schemaId: SchemaId): boolean;
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,12 @@ export class DummyOpenAPIV3ParserContext extends AbstractOpenAPIV3ParserContext
public getErrors(): Record<StatusCode, HttpError> {
return {};
}

public excludeSchema(_schemaId: SchemaId): void {
// noop
}

public isSchemaExcluded(_schemaId: SchemaId): boolean {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ export class OpenAPIV3ParserContext extends AbstractOpenAPIV3ParserContext {
private discrminatedUnionReferences: Record<string, DiscriminatedUnionReference> = {};
private errorBodies: Record<number, ErrorBodyCollector> = {};

private schemasToExclude: Set<SchemaId> = new Set();

constructor({
document,
taskContext,
Expand Down Expand Up @@ -99,4 +101,12 @@ export class OpenAPIV3ParserContext extends AbstractOpenAPIV3ParserContext {
});
return errors;
}

public excludeSchema(schemaId: SchemaId): void {
this.schemasToExclude.add(schemaId);
}

public isSchemaExcluded(schemaId: SchemaId): boolean {
return this.schemasToExclude.has(schemaId);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { assertNever } from "@fern-api/core-utils";
import { EndpointWithExample } from "@fern-api/openapi-ir-sdk";
import { OpenAPIV3 } from "openapi-types";
import { getSchemaIdFromReference } from "../../../../schema/convertSchemas";
import { isReferenceObject } from "../../../../schema/utils/isReferenceObject";
import { AbstractOpenAPIV3ParserContext } from "../../AbstractOpenAPIV3ParserContext";
import { FernStreamingExtension, StreamConditionEndpoint } from "../../extensions/getFernStreamingExtension";
Expand Down Expand Up @@ -43,6 +44,9 @@ export function convertStreamingOperation({
streamingExtension,
isStreaming: true
});
if (streamingRequestBody?.schemaReference != null) {
context.excludeSchema(getSchemaIdFromReference(streamingRequestBody.schemaReference));
}
const streamingResponses = getResponses({
operation: operationContext.operation,
response: streamingExtension.responseStream
Expand All @@ -59,7 +63,7 @@ export function convertStreamingOperation({
: undefined,
operation: {
...operationContext.operation,
requestBody: streamingRequestBody,
requestBody: streamingRequestBody?.requestBody,
responses: streamingResponses
},
baseBreadcrumbs: [...operationContext.baseBreadcrumbs, STREAM_SUFFIX]
Expand All @@ -84,7 +88,7 @@ export function convertStreamingOperation({
...operationContext,
operation: {
...operationContext.operation,
requestBody: nonStreamingRequestBody,
requestBody: nonStreamingRequestBody?.requestBody,
responses: nonStreamingResponses
}
},
Expand All @@ -101,6 +105,11 @@ export function convertStreamingOperation({
}
}

interface RequestBody {
requestBody: OpenAPIV3.RequestBodyObject;
schemaReference: OpenAPIV3.ReferenceObject | undefined;
}

function getRequestBody({
context,
operation,
Expand All @@ -111,7 +120,7 @@ function getRequestBody({
operation: OpenAPIV3.OperationObject;
streamingExtension: StreamConditionEndpoint;
isStreaming: boolean;
}): OpenAPIV3.RequestBodyObject | undefined {
}): RequestBody | undefined {
if (operation.requestBody == null) {
return undefined;
}
Expand Down Expand Up @@ -150,15 +159,21 @@ function getRequestBody({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any
},
// Set to undefined because we inline both the streaming and non-streaming request schemas
// and title would cause conflicting names
title: undefined,
required: [...(resolvedRequstBodySchema.required ?? []), streamingExtension.streamConditionProperty]
};

return {
content: {
"application/json": {
schema: requestBodySchemaWithLiteralProperty
requestBody: {
content: {
"application/json": {
schema: requestBodySchemaWithLiteralProperty
}
}
}
},
schemaReference: isReferenceObject(jsonMediaObject.schema) ? jsonMediaObject.schema : undefined
};
}

Expand Down
16 changes: 9 additions & 7 deletions packages/cli/openapi-parser/src/openapi/v3/generateIr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,15 @@ export function generateIr({
};
});

const schemas: Record<string, Schema> = Object.fromEntries(
Object.entries(schemasWithExample).map(([key, schemaWithExample]) => {
taskContext.logger.debug(`Converting schema ${key}`);
const schema = convertSchemaWithExampleToSchema(schemaWithExample);
return [key, schema];
})
);
const schemas: Record<string, Schema> = {};
for (const [key, schemaWithExample] of Object.entries(schemasWithExample)) {
const schema = convertSchemaWithExampleToSchema(schemaWithExample);
if (context.isSchemaExcluded(key)) {
continue;
}
schemas[key] = schema;
taskContext.logger.debug(`Converted schema ${key}`);
}

const ir: OpenApiIntermediateRepresentation = {
title: openApi.info.title,
Expand Down

0 comments on commit 4c498d5

Please sign in to comment.