Skip to content

Commit 12db6a1

Browse files
authored
feat(openapi): Allow setting info extensions in OApi specifications (#1158)
* feat(openapi): Allow setting info extensions in OApi specifications * test: check info extensions work * test: check info extensions work also for YAML
1 parent 4ebf3c4 commit 12db6a1

File tree

3 files changed

+80
-5
lines changed

3 files changed

+80
-5
lines changed

poem-openapi/src/openapi.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,8 @@ impl<T> OpenApiService<T, ()> {
281281
_webhook: PhantomData,
282282
info: MetaInfo {
283283
title: title.into(),
284-
summary: None,
285-
description: None,
286284
version: version.into(),
287-
terms_of_service: None,
288-
contact: None,
289-
license: None,
285+
..Default::default()
290286
},
291287
external_document: None,
292288
servers: Vec::new(),
@@ -418,6 +414,21 @@ impl<T, W> OpenApiService<T, W> {
418414
self
419415
}
420416

417+
/// Add info extension
418+
#[must_use]
419+
pub fn info_extension<E>(mut self, extension: E, value: serde_json::Value) -> Self
420+
where
421+
E: Into<String>,
422+
{
423+
let extension: String = extension.into();
424+
assert!(
425+
extension.starts_with("x-"),
426+
"Extensions need to begin with 'x-'"
427+
);
428+
self.info.extensions.insert(extension, value);
429+
self
430+
}
431+
421432
/// Sets the cookie key.
422433
#[must_use]
423434
#[cfg(feature = "cookie")]

poem-openapi/src/registry/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,8 @@ pub struct MetaInfo {
540540
pub contact: Option<MetaContact>,
541541
#[serde(skip_serializing_if = "Option::is_none")]
542542
pub license: Option<MetaLicense>,
543+
#[serde(flatten)]
544+
pub extensions: BTreeMap<String, serde_json::Value>,
543545
}
544546

545547
#[derive(Debug, Eq, PartialEq, Serialize, Clone)]

poem-openapi/tests/api.rs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,3 +1084,65 @@ async fn parameter_style_none() {
10841084
let spec = OpenApiService::new(Api {}, "test", "1.0").spec();
10851085
assert!(!spec.contains("\"style\"") && !spec.contains("\"style\": null"));
10861086
}
1087+
1088+
#[tokio::test]
1089+
async fn info_extensions() {
1090+
use serde_json::Value as JsonVal;
1091+
use serde_yaml::Value as YamlVal;
1092+
1093+
#[allow(dead_code)]
1094+
struct Api;
1095+
1096+
#[OpenApi]
1097+
impl Api {
1098+
#[oai(path = "/hello", method = "get")]
1099+
#[allow(dead_code)]
1100+
async fn index(&self) -> PlainText<String> {
1101+
PlainText("hello, world!".to_string())
1102+
}
1103+
}
1104+
1105+
let example_string = JsonVal::String("Example".to_string());
1106+
let example_string_yaml = YamlVal::String("Example".to_string());
1107+
let example_array = JsonVal::Array(vec![
1108+
JsonVal::String("B2B".to_string()),
1109+
JsonVal::String("B2C".to_string()),
1110+
]);
1111+
let example_array_yaml = YamlVal::Sequence(vec![
1112+
YamlVal::String("B2B".to_string()),
1113+
YamlVal::String("B2C".to_string()),
1114+
]);
1115+
1116+
let api = OpenApiService::new(Api {}, "test", "1.0")
1117+
.info_extension("x-category", example_string.clone())
1118+
.info_extension("x-segment", example_array.clone());
1119+
1120+
// check JSON:
1121+
let spec_parsed: JsonVal = serde_json::from_str(&api.spec()).expect("Generated invalid JSON");
1122+
if let JsonVal::Object(spec_root) = spec_parsed {
1123+
let info = spec_root.get("info").expect("Spec has no info");
1124+
if let JsonVal::Object(spec_info) = info {
1125+
assert_eq!(spec_info.get("x-category"), Some(&example_string));
1126+
assert_eq!(spec_info.get("x-segment"), Some(&example_array));
1127+
} else {
1128+
panic!("Spec info isn't a JSON object");
1129+
}
1130+
} else {
1131+
panic!("Spec root isn't a JSON object");
1132+
}
1133+
1134+
// check YAML:
1135+
let spec_parsed: YamlVal =
1136+
serde_yaml::from_str(&api.spec_yaml()).expect("Generated invalid YAML");
1137+
if let YamlVal::Mapping(spec_root) = spec_parsed {
1138+
let info = spec_root.get("info").expect("YAML Spec has no info");
1139+
if let YamlVal::Mapping(spec_info) = info {
1140+
assert_eq!(spec_info.get("x-category"), Some(&example_string_yaml));
1141+
assert_eq!(spec_info.get("x-segment"), Some(&example_array_yaml));
1142+
} else {
1143+
panic!("Spec info isn't a YAML mapping");
1144+
}
1145+
} else {
1146+
panic!("Spec root isn't a YAML mapping");
1147+
}
1148+
}

0 commit comments

Comments
 (0)