Skip to content

Commit

Permalink
Allow manually setting field as nullable (#319)
Browse files Browse the repository at this point in the history
This adds support to the schema object for the nullable field, and allows manually setting it via the nullable attribute.
  • Loading branch information
jacob-pro committed Oct 15, 2022
1 parent 3180d18 commit 4c28dcc
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 4 deletions.
12 changes: 10 additions & 2 deletions utoipa-gen/src/component/schema/attr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ pub struct NamedField<'c> {
xml_attr: Option<XmlAttr>,
pub(super) xml: Option<Xml>,
inline: bool,
nullable: Option<bool>,
}

impl IsInline for NamedField<'_> {
Expand Down Expand Up @@ -348,7 +349,7 @@ fn is_valid_xml_attr(attrs: &SchemaAttr<NamedField>, component_part: &TypeTree)

impl<'c> Parse for SchemaAttr<NamedField<'c>> {
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: example, format, default, write_only, read_only, xml, value_type, inline";
const EXPECTED_ATTRIBUTE_MESSAGE: &str = "unexpected attribute, expected any of: example, format, default, write_only, read_only, xml, value_type, inline, nullable";
let mut field = NamedField::default();

while !input.is_empty() {
Expand Down Expand Up @@ -388,7 +389,8 @@ impl<'c> Parse for SchemaAttr<NamedField<'c>> {
field.value_type = Some(parse_utils::parse_next(input, || {
input.parse::<syn::Type>()
})?);
}
},
"nullable" => field.nullable = Some(parse_utils::parse_bool_or_true(input)?),
_ => return Err(Error::new(ident.span(), EXPECTED_ATTRIBUTE_MESSAGE)),
}

Expand Down Expand Up @@ -511,5 +513,11 @@ impl ToTokens for NamedField<'_> {
.read_only(Some(#read_only))
})
}

if let Some(nullable) = self.nullable {
tokens.extend(quote! {
.nullable(#nullable)
})
}
}
}
1 change: 1 addition & 0 deletions utoipa-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ use ext::ArgumentResolver;
/// Value can be any Rust type what normally could be used to serialize to JSON or custom type such as _`Object`_.
/// * `inline` If the type of this field implements [`ToSchema`][to_schema], then the schema definition
/// will be inlined. **warning:** Don't use this for recursive data types!
/// * `nullable` Defines property is nullable (note this is different to non-required).
///
/// [^json2]: Values are converted to string if **json** feature is not enabled.
///
Expand Down
20 changes: 20 additions & 0 deletions utoipa-gen/tests/schema_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1369,6 +1369,26 @@ fn derive_struct_with_read_only_and_write_only() {
}
}

#[test]
fn derive_struct_with_nullable() {
let user = api_doc! {
struct User {
#[schema(nullable)]
phone: Option<Option<String>>,
#[schema(nullable = false)]
email: String,
name: String
}
};

assert_value! {user=>
"properties.phone.type" = r###""string""###, "User phone type"
"properties.phone.nullable" = r###"true"###, "User phone nullable"
"properties.email.nullable" = r###"null"###, "User email not nullable"
"properties.name.nullable" = r###"null"###, "User name not nullable"
}
}

#[test]
fn derive_struct_xml() {
let user = api_doc! {
Expand Down
20 changes: 18 additions & 2 deletions utoipa/src/openapi/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,13 @@ pub struct Object {
/// Additional [`Xml`] formatting of the [`Object`].
#[serde(skip_serializing_if = "Option::is_none")]
pub xml: Option<Xml>,

#[serde(default, skip_serializing_if = "is_false")]
pub nullable: bool,
}

fn is_false(value: &bool) -> bool {
!*value
}

impl Object {
Expand Down Expand Up @@ -753,6 +760,8 @@ pub struct ObjectBuilder {
example: Option<String>,

xml: Option<Xml>,

nullable: bool,
}

impl ObjectBuilder {
Expand Down Expand Up @@ -870,14 +879,21 @@ impl ObjectBuilder {
set_value!(self xml xml)
}

/// Add or change nullable flag for [`Object`].
pub fn nullable(mut self, nullable: bool) -> Self {
set_value!(self nullable nullable)
}

to_array_builder!();

build_fn!(pub Object schema_type, format, title, required, properties, description,
deprecated, default, enum_values, example, write_only, read_only, xml, additional_properties);
deprecated, default, enum_values, example, write_only, read_only, xml,
additional_properties, nullable);
}

from!(Object ObjectBuilder schema_type, format, title, required, properties, description,
deprecated, default, enum_values, example, write_only, read_only, xml, additional_properties);
deprecated, default, enum_values, example, write_only, read_only, xml,
additional_properties, nullable);

component_from_builder!(ObjectBuilder);

Expand Down

0 comments on commit 4c28dcc

Please sign in to comment.