Skip to content

Commit

Permalink
Adding a fix for empty header objects.
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinswiber committed Jan 31, 2024
1 parent c5365fe commit 921cbd2
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 24 deletions.
30 changes: 20 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -459,22 +459,26 @@ impl<'a> Transpiler<'a> {
let mut content_type: Option<String> = None;

if let Some(postman::HeaderUnion::HeaderArray(headers)) = &request.header {
for header in headers.iter() {
let key = header.key.to_lowercase();
for header in headers
.iter()
.filter(|hdr| hdr.key.is_some() && hdr.value.is_some())
{
let key = header.key.as_ref().unwrap().to_lowercase();
let value = header.value.as_ref().unwrap();
if key == "accept" || key == "authorization" {
continue;
}
if key == "content-type" {
let content_type_parts: Vec<&str> = header.value.split(';').collect();
let content_type_parts: Vec<&str> = value.split(';').collect();
content_type = Some(content_type_parts[0].to_owned());
} else {
let param = Parameter {
location: "header".to_owned(),
name: header.key.to_owned(),
name: key.to_owned(),
description: extract_description(&header.description),
schema: Some(openapi3::Schema {
schema_type: Some("string".to_owned()),
example: Some(serde_json::Value::String(header.value.to_owned())),
example: Some(serde_json::Value::String(value.to_owned())),
..openapi3::Schema::default()
}),
..Parameter::default()
Expand Down Expand Up @@ -558,19 +562,25 @@ impl<'a> Transpiler<'a> {
BTreeMap::<String, openapi3::ObjectOrReference<openapi3::Header>>::new();
for h in headers {
if let postman::HeaderElement::Header(hdr) = h {
if hdr.value.is_empty() || hdr.key.to_lowercase() == "content-type" {
if hdr.key.is_none()
|| hdr.value.is_none()
|| hdr.value.as_ref().unwrap().is_empty()
|| hdr.key.as_ref().unwrap().to_lowercase() == "content-type"
{
continue;
}
let mut oas_header = openapi3::Header::default();
let header_schema = openapi3::Schema {
schema_type: Some("string".to_string()),
example: Some(serde_json::Value::String(hdr.value.to_string())),
example: Some(serde_json::Value::String(
hdr.value.clone().unwrap().to_string(),
)),
..Default::default()
};
oas_header.schema = Some(header_schema);

oas_headers.insert(
hdr.key.clone(),
hdr.key.clone().unwrap(),
openapi3::ObjectOrReference::Object(oas_header),
);
}
Expand Down Expand Up @@ -1724,7 +1734,7 @@ mod tests {
.as_ref()
.unwrap();
assert_eq!(
sr1.get(0)
sr1.first()
.unwrap()
.requirement
.as_ref()
Expand All @@ -1743,7 +1753,7 @@ mod tests {
.as_ref()
.unwrap();
assert_eq!(
sr1.get(0)
sr1.first()
.unwrap()
.requirement
.as_ref()
Expand Down
35 changes: 21 additions & 14 deletions src/postman/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,60 +682,67 @@ pub struct Key {
/// Represents a single HTTP Header
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
pub struct Header {
#[serde(rename = "description")]
#[serde(rename = "description", default)]
pub description: Option<DescriptionUnion>,

/// If set to true, the current header will not be sent with requests.
#[serde(rename = "disabled")]
#[serde(rename = "disabled", default)]
pub disabled: Option<bool>,

/// This holds the LHS of the HTTP Header, e.g ``Content-Type`` or ``X-Custom-Header``
#[serde(rename = "key")]
pub key: String,
#[serde(rename = "key", default)]
pub key: Option<String>,

/// The value (or the RHS) of the Header is stored in this field.
#[serde(rename = "value", deserialize_with = "deserialize_as_string")]
pub value: String,
#[serde(rename = "value", deserialize_with = "deserialize_as_string", default)]
pub value: Option<String>,
}

struct DeserializeAnyAsString;
impl<'de> serde::de::Visitor<'de> for DeserializeAnyAsString {
type Value = String;
type Value = Option<String>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("an integer or a string")
formatter.write_str("missing, null, an integer, or a string")
}

fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.to_string())
Ok(Some(v.to_string()))
}

fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
Ok(v.to_string())
Ok(Some(v.to_string()))
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
Ok(v)
Ok(Some(v))
}

fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.to_string())
Ok(Some(v.to_string()))
}

fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.to_string())
Ok(Some(v.to_string()))
}

fn visit_none<E>(self) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(None)
}
}

fn deserialize_as_string<'de, D>(deserializer: D) -> Result<String, D::Error>
fn deserialize_as_string<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
Expand Down
119 changes: 119 additions & 0 deletions tests/fixtures/empty-header-object.postman.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
{
"info": {
"_postman_id": "2a763711-fdbf-4c82-a7eb-f27c3db887e7",
"name": "Empty Header Object example",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
"updatedAt": "2023-11-22T17:23:18.000Z",
"uid": "10354132-2a763711-fdbf-4c82-a7eb-f27c3db887e7"
},
"item": [
{
"name": "GET Request",
"event": [
{
"listen": "test",
"script": {
"id": "2298dced-107f-4b06-afe7-8f1835d4477f",
"exec": [
"pm.test(\"response is ok\", function () {",
" pm.response.to.have.status(200);",
"});",
"",
"pm.test(\"response body has json with request queries\", function () {",
" pm.response.to.have.jsonBody('args.foo1', 'bar1')",
" .and.have.jsonBody('args.foo2', 'bar2');",
"});"
],
"type": "text/javascript"
}
}
],
"request": {
"method": "GET",
"header": [],
"url": {
"raw": "https://postman-echo.com/get?foo1=bar1&foo2=bar2",
"protocol": "https",
"host": ["postman-echo", "com"],
"path": ["get"],
"query": [
{
"key": "foo1",
"value": "bar1"
},
{
"key": "foo2",
"value": "bar2"
}
]
},
"description": "The HTTP `GET` request method is meant to retrieve data from a server. The data\nis identified by a unique URI (Uniform Resource Identifier). \n\nA `GET` request can pass parameters to the server using \"Query String \nParameters\". For example, in the following request,\n\n> http://example.com/hi/there?hand=wave\n\nThe parameter \"hand\" has the value \"wave\".\n\nThis endpoint echoes the HTTP headers, request parameters and the complete\nURI requested."
},
"response": [
{
"name": "GET Request Woops",
"originalRequest": {
"method": "GET",
"header": [],
"url": {
"raw": "https://postman-echo.com/get?foo1=bar1&foo2=bar2",
"protocol": "https",
"host": ["postman-echo", "com"],
"path": ["get"],
"query": [
{
"key": "foo1",
"value": "bar1"
},
{
"key": "foo2",
"value": "bar2"
}
]
}
},
"status": "OK",
"code": 200,
"_postman_previewlanguage": "json",
"header": [
{},
{
"key": "Content-Type",
"value": "application/json; charset=utf-8"
},
{
"key": "Date",
"value": "Tue, 11 Jun 2019 10:43:13 GMT"
},
{
"key": "ETag",
"value": "W/\"161-aLhNcsGArlgLSKbxPqfBW3viHPI\""
},
{
"key": "Server",
"value": "nginx"
},
{
"key": "set-cookie",
"value": "sails.sid=s%3AGz-wblZgXE8FCDq7aJpx_tUgZUcG3Nsw.LdNEN8L0C7nGWkvGLwvdw6R2s6Syjr%2FzkvyevA8qR0c; Path=/; HttpOnly"
},
{
"key": "Vary",
"value": "Accept-Encoding"
},
{
"key": "Content-Length",
"value": "249"
},
{
"key": "Connection",
"value": "keep-alive"
}
],
"cookie": [],
"body": "{\n \"args\": {\n \"foo1\": \"bar1\",\n \"foo2\": \"bar2\"\n },\n \"headers\": {\n \"x-forwarded-proto\": \"https\",\n \"host\": \"postman-echo.com\",\n \"accept\": \"*/*\",\n \"accept-encoding\": \"gzip, deflate\",\n \"cache-control\": \"no-cache\",\n \"postman-token\": \"5c27cd7d-6b16-4e5a-a0ef-191c9a3a275f\",\n \"user-agent\": \"PostmanRuntime/7.6.1\",\n \"x-forwarded-port\": \"443\"\n },\n \"url\": \"https://postman-echo.com/get?foo1=bar1&foo2=bar2\"\n}"
}
]
}
]
}
4 changes: 4 additions & 0 deletions tests/integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ test_fixture!(
);
test_fixture!(it_parses_oauth2_code_collection, "oauth2-code.postman.json");
test_fixture!(it_parses_api_key_collection, "api-key.postman.json");
test_fixture!(
it_parses_empty_header_object_collection,
"empty-header-object.postman.json"
);

fn get_fixture(filename: &str) -> String {
let filename: std::path::PathBuf = [env!("CARGO_MANIFEST_DIR"), "./tests/fixtures/", filename]
Expand Down

0 comments on commit 921cbd2

Please sign in to comment.