Nullable enums don't parse appropriately
coriolinus opened this issue · comments
OpenAPI has a rule: for an enum to be nullable, it must explicitly include null
within its enumeration. IMO this was a bad call, but it's well-documented as the rule. 1 2.
However, adding null
to an enum's variant list forces the schema kind to parse as Any
:
/*
[dependencies]
openapiv3 = { version = "2.2.1", package = "openapiv3-extended" }
serde_yaml = "0.9.22"
*/
use openapiv3::{OpenAPI, SchemaKind, Type};
const SCHEMA: &str = r#"
openapi: "3.0.3"
info:
title: demo unexpected schema parse behavior
version: "0.1.0"
paths: {}
components:
schemas:
Color:
type: string
nullable: true
enum: [red, green, blue, null]
"#;
fn main() {
let spec: OpenAPI = serde_yaml::from_str(SCHEMA).unwrap();
let color_schema = spec
.components
.as_ref()
.unwrap()
.schemas
.get("Color")
.unwrap()
.resolve(&spec);
dbg!(&color_schema);
assert!(matches!(
&color_schema.schema_kind,
SchemaKind::Type(Type::String(_))
));
}
This fails, with this output:
$ cargo run
Tue 27 Jun 2023 12:35:16 PM CEST
Finished dev [unoptimized + debuginfo] target(s) in 0.09s
Running `target/debug/demo-openapi-parse-fail`
[src/main.rs:36] &color_schema = Schema {
schema_data: SchemaData {
nullable: true,
read_only: false,
write_only: false,
deprecated: false,
external_docs: None,
example: None,
title: None,
description: None,
discriminator: None,
default: None,
extensions: {},
},
schema_kind: Any(
AnySchema {
typ: Some(
"string",
),
pattern: None,
multiple_of: None,
exclusive_minimum: None,
exclusive_maximum: None,
minimum: None,
maximum: None,
properties: {},
required: [],
additional_properties: None,
min_properties: None,
max_properties: None,
items: None,
min_items: None,
max_items: None,
unique_items: None,
enumeration: [
String("red"),
String("green"),
String("blue"),
Null,
],
format: None,
min_length: None,
max_length: None,
one_of: [],
all_of: [],
any_of: [],
not: None,
},
),
}
thread 'main' panicked at 'assertion failed: matches!(& color_schema.schema_kind, SchemaKind :: Type(Type :: String(_)))', src/main.rs:37:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Given that schema authors are forced to include null
among the enum variants, it's not helpful to lose the rest of the parse information by parsing as AnySchema
. Instead, it would be more helpful to parse as Type::String
(or whatever the type definition actually refers to) and simply strip the null
variant from the variants list. We can still losslessly reconstruct the original schema from the OpenApi
instance, because we have nullable: true
and enumeration.is_some()
.