From d4eb17eaf52fd40437deca1c8aa40f90ecf550ae Mon Sep 17 00:00:00 2001 From: Baptiste LE MORLEC Date: Wed, 28 May 2025 16:00:25 +0200 Subject: [PATCH] Adding support for Content in Parameters --- utoipa/src/openapi/path.rs | 46 +++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/utoipa/src/openapi/path.rs b/utoipa/src/openapi/path.rs index 79bac8af..b11a8cf6 100644 --- a/utoipa/src/openapi/path.rs +++ b/utoipa/src/openapi/path.rs @@ -4,6 +4,7 @@ use crate::Path; use serde::{Deserialize, Serialize}; use serde_json::Value; +use std::collections::BTreeMap; use super::{ builder, @@ -11,7 +12,7 @@ use super::{ request_body::RequestBody, response::{Response, Responses}, security::SecurityRequirement, - set_value, Deprecated, ExternalDocs, RefOr, Required, Schema, Server, + set_value, Content, Deprecated, ExternalDocs, RefOr, Required, Schema, Server, }; #[cfg(not(feature = "preserve_path_order"))] @@ -705,6 +706,10 @@ builder! { #[serde(skip_serializing_if = "Option::is_none")] pub schema: Option>, + /// For more complex scenarios, the content property can define the media type and schema of the parameter. + #[serde(skip_serializing_if = "BTreeMap::is_empty")] + pub content: BTreeMap, + /// Describes how [`Parameter`] is being serialized depending on [`Parameter::schema`] (type of a content). /// Default value is based on [`ParameterIn`]. #[serde(skip_serializing_if = "Option::is_none")] @@ -791,6 +796,12 @@ impl ParameterBuilder { set_value!(self schema component.map(|component| component.into())) } + /// Add a content for a specific media type + pub fn content, V: Into>(mut self, media_type: K, content: V) -> Self { + self.content.insert(media_type.into(), content.into()); + self + } + /// Add or change serialization style of [`Parameter`]. pub fn style(mut self, style: Option) -> Self { set_value!(self style style) @@ -871,8 +882,11 @@ pub enum ParameterStyle { #[cfg(test)] mod tests { - use super::{HttpMethod, Operation, OperationBuilder}; - use crate::openapi::{security::SecurityRequirement, server::Server, PathItem, PathsBuilder}; + use super::{HttpMethod, Operation, OperationBuilder, ParameterBuilder}; + use crate::openapi::{ + schema::RefBuilder, security::SecurityRequirement, server::Server, ContentBuilder, + PathItem, PathsBuilder, + }; #[test] fn test_path_order() { @@ -1012,4 +1026,30 @@ mod tests { assert!(operation.servers.is_some()); } + + #[test] + fn parameter_with_content() { + let param = ParameterBuilder::new() + .name("filter") + .parameter_in(super::ParameterIn::Query) + .required(crate::openapi::Required::True) + .content( + "application/json", + ContentBuilder::new() + .schema(Some( + RefBuilder::new() + .ref_location_from_schema_name("Filter") + .build(), + )) + .build(), + ) + .build(); + + let param = serde_json::to_string(¶m).unwrap(); + + assert_eq!( + param, + r###"{"name":"filter","in":"query","required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Filter"}}}}"### + ) + } }