diff --git a/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/openapi-accept-header.swift b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/openapi-accept-header.swift new file mode 100644 index 000000000000..2fae599f9ebc --- /dev/null +++ b/generators/swift/sdk/src/generators/client/util/__test__/snapshots/formatted-endpoint-paths/openapi-accept-header.swift @@ -0,0 +1,5 @@ +// service_ +"/events" +"/data" +"/other" +"/more" \ No newline at end of file diff --git a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/buildGlobalHeaders.ts b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/buildGlobalHeaders.ts index a64d6ca582a1..9dc68f084e0c 100644 --- a/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/buildGlobalHeaders.ts +++ b/packages/cli/api-importers/openapi/openapi-ir-to-fern/src/buildGlobalHeaders.ts @@ -12,8 +12,9 @@ import { getTypeFromTypeReference } from "./utils/getTypeFromTypeReference"; import { wrapTypeReferenceAsOptional } from "./utils/wrapTypeReferenceAsOptional"; class HeaderWithCount { - public readonly schema: RawSchemas.HttpHeaderSchema; + public schema: RawSchemas.HttpHeaderSchema; public count = 0; + private hasIncompatibleSchemas = false; constructor(schema: RawSchemas.HttpHeaderSchema) { this.schema = schema; @@ -22,6 +23,14 @@ class HeaderWithCount { public increment(): void { this.count += 1; } + + public markIncompatible(): void { + this.hasIncompatibleSchemas = true; + } + + public isIncompatible(): boolean { + return this.hasIncompatibleSchemas; + } } /* 75% of endpoints must have header present, for it to be considered a global header*/ @@ -101,6 +110,20 @@ export function buildGlobalHeaders(context: OpenApiIrConverterContext): void { }); headerWithCount = new HeaderWithCount(convertedHeader); globalHeaders[header.name] = headerWithCount; + } else { + // Check if the current header schema is compatible with the existing one + const currentConvertedHeader = buildHeader({ + header, + fileContainingReference: RelativeFilePath.of(ROOT_API_FILENAME), + context, + namespace: undefined + }); + const existingType = getTypeFromHttpHeaderSchema(headerWithCount.schema); + const currentType = getTypeFromHttpHeaderSchema(currentConvertedHeader); + if (existingType !== currentType) { + // Schemas are incompatible, widen the type to string + headerWithCount.markIncompatible(); + } } headerWithCount.increment(); } @@ -115,16 +138,37 @@ export function buildGlobalHeaders(context: OpenApiIrConverterContext): void { if (predefinedHeader != null) { continue; // already added } else if (isRequired) { + // If schemas are incompatible, widen the type to string + const schema = header.isIncompatible() ? widenToString(header.schema) : header.schema; context.builder.addGlobalHeader({ name: headerName, - schema: header.schema + schema }); } else if (isOptional) { + // If schemas are incompatible, widen the type to string + const schema = header.isIncompatible() ? widenToString(header.schema) : header.schema; context.builder.addGlobalHeader({ name: headerName, - schema: wrapTypeReferenceAsOptional(header.schema) + schema: wrapTypeReferenceAsOptional(schema) }); } } } } + +function getTypeFromHttpHeaderSchema(schema: RawSchemas.HttpHeaderSchema): string { + if (typeof schema === "string") { + return schema; + } + return schema.type; +} + +function widenToString(schema: RawSchemas.HttpHeaderSchema): RawSchemas.HttpHeaderSchema { + if (typeof schema === "string") { + return "string"; + } + return { + ...schema, + type: "string" + }; +} diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetDataResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetDataResponse.json new file mode 100644 index 000000000000..05f2b9661f64 --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetDataResponse.json @@ -0,0 +1,17 @@ +{ + "type": "object", + "properties": { + "id": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": {} +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetMoreResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetMoreResponse.json new file mode 100644 index 000000000000..8b118f7aa83f --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetMoreResponse.json @@ -0,0 +1,17 @@ +{ + "type": "object", + "properties": { + "value": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": {} +} \ No newline at end of file diff --git a/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetOtherResponse.json b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetOtherResponse.json new file mode 100644 index 000000000000..005c29438195 --- /dev/null +++ b/packages/cli/fern-definition/ir-to-jsonschema/src/__test__/__snapshots__/openapi-accept-header/type__GetOtherResponse.json @@ -0,0 +1,17 @@ +{ + "type": "object", + "properties": { + "name": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ] + } + }, + "additionalProperties": false, + "definitions": {} +} \ No newline at end of file diff --git a/test-definitions/fern/apis/openapi-accept-header/generators.yml b/test-definitions/fern/apis/openapi-accept-header/generators.yml new file mode 100644 index 000000000000..d8bd49b048ba --- /dev/null +++ b/test-definitions/fern/apis/openapi-accept-header/generators.yml @@ -0,0 +1,3 @@ +api: + specs: + - openapi: ./openapi.yml diff --git a/test-definitions/fern/apis/openapi-accept-header/openapi.yml b/test-definitions/fern/apis/openapi-accept-header/openapi.yml new file mode 100644 index 000000000000..e98903d6bb48 --- /dev/null +++ b/test-definitions/fern/apis/openapi-accept-header/openapi.yml @@ -0,0 +1,89 @@ +openapi: 3.0.3 +info: + title: Accept Header Test API + version: 1.0.0 + description: Test API for Accept header with text/event-stream default +paths: + /events: + get: + operationId: getEvents + summary: Get events stream + description: Returns a stream of events + parameters: + - $ref: '#/components/parameters/AcceptEventStream' + responses: + '200': + description: Successful response + content: + text/event-stream: + schema: + type: string + /data: + get: + operationId: getData + summary: Get data as JSON + description: Returns data as JSON + parameters: + - $ref: '#/components/parameters/AcceptJson' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + id: + type: string + /other: + get: + operationId: getOther + summary: Get other data as JSON + description: Returns other data as JSON + parameters: + - $ref: '#/components/parameters/AcceptJson' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + name: + type: string + /more: + get: + operationId: getMore + summary: Get more data as JSON + description: Returns more data as JSON + parameters: + - $ref: '#/components/parameters/AcceptJson' + responses: + '200': + description: Successful response + content: + application/json: + schema: + type: object + properties: + value: + type: string +components: + parameters: + AcceptEventStream: + name: Accept + in: header + required: true + description: The MIME type of the response body. + schema: + type: string + default: 'text/event-stream' + AcceptJson: + name: Accept + in: header + required: true + description: The MIME type of the response body. + schema: + type: string + const: 'application/json'