Skip to content

Commit

Permalink
Add support for deprecation using schema attribute (juhaku#688)
Browse files Browse the repository at this point in the history
Add support for `deprecated` attribute using `#[schema(deprecated)]` to 
only mark `ToSchema` derived types or fields deprecated to OpenAPI schema 
without deprecating them in code base according to need in issue juhaku#685. 

---------

Co-authored-by: Juha Kukkonen <[email protected]>
  • Loading branch information
nullsauce and juhaku authored Jul 18, 2023
1 parent a334fda commit 0073541
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 12 deletions.
12 changes: 10 additions & 2 deletions utoipa-gen/src/component/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use syn::{
use crate::{
component::features::{Example, Rename},
doc_comment::CommentAttributes,
Array, ResultExt,
Array, Deprecated, ResultExt,
};

use self::{
Expand Down Expand Up @@ -333,13 +333,21 @@ impl NamedStructSchema<'_> {
}
}

// check for Rust's `#[deprecated]` attribute first, then check for `deprecated` feature
let deprecated = super::get_deprecated(&field.attrs).or_else(|| {
pop_feature!(field_features => Feature::Deprecated(_)).and_then(|feature| match feature
{
Feature::Deprecated(_) => Some(Deprecated::True),
_ => None,
})
});

let rename_field =
pop_feature!(field_features => Feature::Rename(_)).and_then(|feature| match feature {
Feature::Rename(rename) => Some(Cow::Owned(rename.into_value())),
_ => None,
});

let deprecated = super::get_deprecated(&field.attrs);
let value_type = field_features
.as_mut()
.and_then(|features| features.pop_value_type_feature());
Expand Down
25 changes: 16 additions & 9 deletions utoipa-gen/src/component/schema/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use syn::{

use crate::{
component::features::{
impl_into_inner, impl_merge, parse_features, AdditionalProperties, As, Default, Example,
ExclusiveMaximum, ExclusiveMinimum, Feature, Format, Inline, IntoInner, MaxItems,
impl_into_inner, impl_merge, parse_features, AdditionalProperties, As, Default, Deprecated,
Example, ExclusiveMaximum, ExclusiveMinimum, Feature, Format, Inline, IntoInner, MaxItems,
MaxLength, MaxProperties, Maximum, Merge, MinItems, MinLength, MinProperties, Minimum,
MultipleOf, Nullable, Pattern, ReadOnly, Rename, RenameAll, Required, SchemaWith, Title,
ValueType, WriteOnly, XmlAttr,
Expand All @@ -27,7 +27,8 @@ impl Parse for NamedFieldStructFeatures {
MaxProperties,
MinProperties,
As,
Default
Default,
Deprecated
)))
}
}
Expand All @@ -45,7 +46,8 @@ impl Parse for UnnamedFieldStructFeatures {
Title,
Format,
ValueType,
As
As,
Deprecated
)))
}
}
Expand All @@ -61,7 +63,8 @@ impl Parse for EnumFeatures {
Default,
Title,
RenameAll,
As
As,
Deprecated
)))
}
}
Expand All @@ -76,7 +79,8 @@ impl Parse for ComplexEnumFeatures {
input as Example,
Default,
RenameAll,
As
As,
Deprecated
)))
}
}
Expand Down Expand Up @@ -110,7 +114,8 @@ impl Parse for NamedFieldFeatures {
MinItems,
SchemaWith,
AdditionalProperties,
Required
Required,
Deprecated
)))
}
}
Expand All @@ -126,7 +131,8 @@ impl Parse for EnumNamedFieldVariantFeatures {
XmlAttr,
Title,
Rename,
RenameAll
RenameAll,
Deprecated
)))
}
}
Expand All @@ -143,7 +149,8 @@ impl Parse for EnumUnnamedFieldVariantFeatures {
Title,
Format,
ValueType,
Rename
Rename,
Deprecated
)))
}
}
Expand Down
14 changes: 13 additions & 1 deletion utoipa-gen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ use self::{
/// OpenAPI spec as _`path.to.Pet`_.
/// * `default` Can be used to populate default values on all fields using the struct's
/// [`Default`](std::default::Default) implementation.
///
/// * `deprecated` Can be used to mark all fields as deprecated in the generated OpenAPI spec but
/// not in the code. If you'd like to mark the fields as deprecated in the code as well use
/// Rust's own `#[deprecated]` attribute instead.
/// # Enum Optional Configuration Options for `#[schema(...)]`
/// * `example = ...` Can be method reference or _`json!(...)`_.
/// * `default = ...` Can be method reference or _`json!(...)`_.
Expand All @@ -104,6 +107,9 @@ use self::{
/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
/// OpenAPI spec as _`path.to.Pet`_.
/// * `deprecated` Can be used to mark the enum as deprecated in the generated OpenAPI spec but
/// not in the code. If you'd like to mark the enum as deprecated in the code as well use
/// Rust's own `#[deprecated]` attribute instead.
///
/// # Enum Variant Optional Configuration Options for `#[schema(...)]`
/// Supports all variant specific configuration options e.g. if variant is _`UnnamedStruct`_ then
Expand Down Expand Up @@ -134,6 +140,9 @@ use self::{
/// * `as = ...` Can be used to define alternative path and name for the schema what will be used in
/// the OpenAPI. E.g _`as = path::to::Pet`_. This would make the schema appear in the generated
/// OpenAPI spec as _`path.to.Pet`_.
/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
/// not in the code. If you'd like to mark the field as deprecated in the code as well use
/// Rust's own `#[deprecated]` attribute instead.
///
/// # Named Fields Optional Configuration Options for `#[schema(...)]`
/// * `example = ...` Can be method reference or _`json!(...)`_.
Expand Down Expand Up @@ -180,6 +189,9 @@ use self::{
/// [`HashMap`](std::collections::HashMap) and [`BTreeMap`](std::collections::BTreeMap).
/// Free form type enables use of arbitrary types within map values.
/// Supports formats _`additional_properties`_ and _`additional_properties = true`_.
/// * `deprecated` Can be used to mark the field as deprecated in the generated OpenAPI spec but
/// not in the code. If you'd like to mark the field as deprecated in the code as well use
/// Rust's own `#[deprecated]` attribute instead.
///
/// #### Field nullability and required rules
///
Expand Down
110 changes: 110 additions & 0 deletions utoipa-gen/tests/schema_derive_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,27 @@ fn derive_struct_with_deprecated() {
}
}

#[test]
fn derive_struct_with_schema_deprecated() {
let pet = api_doc! {
#[schema(deprecated)]
struct Pet {
name: String,
#[schema(deprecated)]
age: i32
}
};

assert_value! {pet=>
"deprecated" = r#"true"#, "Pet deprecated"
"properties.name.type" = r#""string""#, "Pet properties name type"
"properties.name.deprecated" = r#"null"#, "Pet properties name deprecated"
"properties.age.type" = r#""integer""#, "Pet properties age type"
"properties.age.deprecated" = r#"true"#, "Pet properties age deprecated"
"example" = r#"null"#, "Pet example"
}
}

#[test]
fn derive_unnamed_struct_deprecated_success() {
#[allow(deprecated)]
Expand All @@ -643,6 +664,19 @@ fn derive_unnamed_struct_deprecated_success() {
}
}

#[test]
fn derive_unnamed_struct_schema_deprecated_success() {
let pet_age = api_doc! {
#[schema(deprecated, example = 8)]
struct PetAge(u64);
};

assert_value! {pet_age=>
"deprecated" = r#"true"#, "PetAge deprecated"
"example" = r#"8"#, "PetAge example"
}
}

#[test]
fn derive_unnamed_struct_example_json_array_success() {
let pet_age = api_doc! {
Expand Down Expand Up @@ -678,6 +712,22 @@ fn derive_enum_with_deprecated() {
};
}

#[test]
fn derive_enum_with_schema_deprecated() {
let mode = api_doc! {
#[schema(deprecated)]
enum Mode {
Mode1, Mode2
}
};

assert_value! {mode=>
"enum" = r#"["Mode1","Mode2"]"#, "Mode enum variants"
"type" = r#""string""#, "Mode type"
"deprecated" = r#"true"#, "Mode deprecated"
};
}

#[test]
fn derive_struct_with_generics() {
#[allow(unused)]
Expand Down Expand Up @@ -4279,6 +4329,66 @@ fn derive_struct_with_deprecated_fields() {
)
}

#[test]
fn derive_struct_with_schema_deprecated_fields() {
struct Foobar;
let account = api_doc! {
struct AccountA {
#[schema(deprecated)]
id: i64,
#[schema(deprecated)]
username: String,
#[schema(deprecated)]
role_ids: Vec<i32>,
#[schema(deprecated)]
foobars: Vec<Foobar>,
#[schema(deprecated)]
map: HashMap<String, String>
}
};

assert_json_eq!(
account,
json!({
"properties": {
"id": {
"type": "integer",
"format": "int64",
"deprecated": true
},
"username": {
"type": "string",
"deprecated": true
},
"role_ids": {
"type": "array",
"deprecated": true,
"items": {
"type": "integer",
"format": "int32"
}
},
"foobars": {
"type": "array",
"deprecated": true,
"items": {
"$ref": "#/components/schemas/Foobar"
}
},
"map": {
"additionalProperties": {
"type": "string"
},
"deprecated": true,
"type": "object"
}
},
"required": ["id", "username", "role_ids", "foobars", "map"],
"type": "object"
})
)
}

#[test]
fn derive_schema_with_object_type_description() {
let value = api_doc! {
Expand Down

0 comments on commit 0073541

Please sign in to comment.