Skip to content

Commit 249a302

Browse files
authored
Drop invalid OpenAPI schema examples (#7325)
1 parent 425457c commit 249a302

3 files changed

Lines changed: 78 additions & 2 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@effect/openapi-generator": patch
3+
---
4+
5+
Drop invalid OpenAPI schema examples from generated Effect Schema annotations.

packages/tools/openapi-generator/src/JsonSchemaGenerator.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import * as Arr from "effect/Array"
2222
import * as JsonSchema from "effect/JsonSchema"
2323
import * as Rec from "effect/Record"
24+
import * as Schema from "effect/Schema"
2425
import * as SchemaRepresentation from "effect/SchemaRepresentation"
2526

2627
type Source = "openapi-3.0" | "openapi-3.1"
@@ -211,17 +212,48 @@ function makeWithRepresentation() {
211212
schemas,
212213
definitions
213214
}
215+
const schemasWithExamples: Array<JsonSchema.JsonSchema> = []
216+
const preparedSchemas = new WeakMap<JsonSchema.JsonSchema, JsonSchema.JsonSchema>()
217+
const preparedOutputs = new WeakSet<JsonSchema.JsonSchema>()
214218
const importerOptions: SchemaRepresentation.FromJsonSchemaOptions = {
215219
patterns: "apply",
216220
onEnter(js: JsonSchema.JsonSchema) {
221+
if (preparedOutputs.has(js)) return js
222+
const cached = preparedSchemas.get(js)
223+
if (cached !== undefined) return cached
224+
217225
const out = { ...js }
218226
if (out.type === "object" && out.additionalProperties === undefined) {
219227
out.additionalProperties = false
220228
}
221-
return options?.onEnter === undefined ? out : options.onEnter(out)
229+
const transformed = { ...(options?.onEnter === undefined ? out : options.onEnter(out)) }
230+
preparedSchemas.set(js, transformed)
231+
preparedOutputs.add(transformed)
232+
if (Array.isArray(transformed.examples)) {
233+
schemasWithExamples.push(transformed)
234+
}
235+
return transformed
236+
}
237+
}
238+
let rootSchemas = SchemaRepresentation.fromJsonSchemaMultiDocument(document, importerOptions)
239+
if (Arr.isArrayNonEmpty(schemasWithExamples)) {
240+
const exampleSchemas = SchemaRepresentation.fromJsonSchemaMultiDocument(
241+
{ ...document, schemas: schemasWithExamples },
242+
importerOptions
243+
)
244+
for (let i = 0; i < schemasWithExamples.length; i++) {
245+
const node = schemasWithExamples[i]
246+
const examples = node.examples as ReadonlyArray<unknown>
247+
const validExamples = examples.filter(Schema.is(exampleSchemas[i]))
248+
if (validExamples.length === examples.length) continue
249+
if (validExamples.length === 0) {
250+
delete node.examples
251+
} else {
252+
node.examples = validExamples
253+
}
222254
}
255+
rootSchemas = SchemaRepresentation.fromJsonSchemaMultiDocument(document, importerOptions)
223256
}
224-
const rootSchemas = SchemaRepresentation.fromJsonSchemaMultiDocument(document, importerOptions)
225257
const codeDocument = SchemaRepresentation.toCodeDocument(
226258
SchemaRepresentation.toRepresentations(Arr.map(rootSchemas, (schema) => schema.ast))
227259
)

packages/tools/openapi-generator/test/OpenApiGenerator.test.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2718,6 +2718,45 @@ export const __HttpApiMultipartFiles = Multipart.FilesSchema`,
27182718
})
27192719

27202720
describe("regression", () => {
2721+
it.effect("emits compilable clients when schema examples are invalid", () =>
2722+
assertGeneratedClientsCompile({
2723+
openapi: "3.0.3",
2724+
info: {
2725+
title: "Invalid examples API",
2726+
version: "1.0.0"
2727+
},
2728+
paths: {
2729+
"/triggers": {
2730+
get: {
2731+
operationId: "getTriggers",
2732+
parameters: [],
2733+
responses: {
2734+
200: {
2735+
description: "OK",
2736+
content: {
2737+
"application/json": {
2738+
schema: {
2739+
type: "array",
2740+
items: { type: "string" },
2741+
example: "create"
2742+
}
2743+
}
2744+
}
2745+
}
2746+
},
2747+
tags: ["Triggers"],
2748+
security: []
2749+
}
2750+
}
2751+
},
2752+
components: {
2753+
schemas: {},
2754+
securitySchemes: {}
2755+
},
2756+
security: [],
2757+
tags: [{ name: "Triggers" }]
2758+
} as unknown as OpenAPISpec))
2759+
27212760
it.effect("runtime warnings do not report additional-tags-dropped outside httpapi", () =>
27222761
assertRuntimeStableWithWarnings(
27232762
{

0 commit comments

Comments
 (0)