Skip to content

Commit 1b18784

Browse files
committed
feat: validator add query and test
1 parent c687862 commit 1b18784

3 files changed

Lines changed: 131 additions & 56 deletions

File tree

src/model/parse.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ impl OpenAPI {
5656
if valid.path(self).is_err() {
5757
return Err("Path validation failed".to_string());
5858
}
59+
if valid.query(self).is_err() {
60+
return Err("Query validation failed".to_string());
61+
}
5962
if valid.body(self).is_err() {
6063
return Err("Body validation failed".to_string());
6164
}

src/request/axum.rs

Lines changed: 54 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -59,79 +59,77 @@ impl ValidateRequest for RequestData {
5959
.get(self.path.as_str())
6060
.context("Path not found")?;
6161

62-
let path_base = path
63-
.get(&Method::Get)
64-
.context("GET method not defined for this path")?;
65-
66-
let query_str = self.inner.uri().query().unwrap_or_default();
67-
let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query_str.as_bytes())
68-
.into_owned()
69-
.collect();
62+
if let Some(path_base) = path.get(&Method::Get) {
63+
let query_str = self.inner.uri().query().unwrap_or_default();
64+
let query_pairs: HashMap<_, _> = url::form_urlencoded::parse(query_str.as_bytes())
65+
.into_owned()
66+
.collect();
7067

71-
let mut requireds: HashSet<String> = HashSet::new();
68+
let mut requireds: HashSet<String> = HashSet::new();
7269

73-
if let Some(parameters) = &path_base.parameters {
74-
for parameter in parameters {
75-
if parameter._in != In::Query {
76-
continue;
77-
}
70+
if let Some(parameters) = &path_base.parameters {
71+
for parameter in parameters {
72+
if parameter._in != In::Query {
73+
continue;
74+
}
7875

79-
if let Some(value) = query_pairs.get(&parameter.name) {
80-
validate_field_format(
81-
&parameter.name,
82-
&Value::from(value.as_str()),
83-
parameter.schema.format.clone(),
84-
)?;
85-
}
76+
if let Some(value) = query_pairs.get(&parameter.name) {
77+
validate_field_format(
78+
&parameter.name,
79+
&Value::from(value.as_str()),
80+
parameter.schema.format.clone(),
81+
)?;
82+
}
8683

87-
let mut refs = Vec::new();
88-
if let Some(r) = &parameter.schema._ref {
89-
refs.push(r.as_str());
90-
}
91-
if let Some(one_of) = &parameter.schema.one_of {
92-
for s in one_of {
93-
if let Some(r) = &s._ref {
94-
refs.push(r.as_str());
84+
let mut refs = Vec::new();
85+
if let Some(r) = &parameter.schema._ref {
86+
refs.push(r.as_str());
87+
}
88+
if let Some(one_of) = &parameter.schema.one_of {
89+
for s in one_of {
90+
if let Some(r) = &s._ref {
91+
refs.push(r.as_str());
92+
}
9593
}
9694
}
97-
}
98-
if let Some(all_of) = &parameter.schema.all_of {
99-
for s in all_of {
100-
if let Some(r) = &s._ref {
101-
refs.push(r.as_str());
95+
if let Some(all_of) = &parameter.schema.all_of {
96+
for s in all_of {
97+
if let Some(r) = &s._ref {
98+
refs.push(r.as_str());
99+
}
102100
}
103101
}
104-
}
105102

106-
for schema_ref in refs {
107-
if let Some(components) = &open_api.components {
108-
if let Some(schema) = components.schemas.get(schema_ref) {
109-
if !schema.required.is_empty() {
110-
requireds.extend(schema.required.clone());
111-
}
112-
if let Some(properties) = &schema.properties {
113-
for (key, prop) in properties {
114-
if let Some(value) = query_pairs.get(key) {
115-
validate_field_format(
116-
key,
117-
&Value::from(value.as_str()),
118-
prop.format.clone(),
119-
)?;
103+
for schema_ref in refs {
104+
if let Some(components) = &open_api.components {
105+
if let Some(schema) = components.schemas.get(schema_ref) {
106+
if !schema.required.is_empty() {
107+
requireds.extend(schema.required.clone());
108+
}
109+
if let Some(properties) = &schema.properties {
110+
for (key, prop) in properties {
111+
if let Some(value) = query_pairs.get(key) {
112+
validate_field_format(
113+
key,
114+
&Value::from(value.as_str()),
115+
prop.format.clone(),
116+
)?;
117+
}
120118
}
121119
}
122120
}
123121
}
124122
}
125123
}
126124
}
127-
}
128125

129-
for key in &requireds {
130-
if !query_pairs.contains_key(key) {
131-
return Err(anyhow::anyhow!(
132-
"Missing required query parameter: '{}'",
133-
key
134-
));
126+
for key in &requireds {
127+
if !query_pairs.contains_key(key) {
128+
return Err(anyhow::anyhow!(
129+
"Missing required query parameter: '{}'",
130+
key
131+
));
132+
}
135133
}
136134
}
137135

src/request/validator_test.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,80 @@ paths:
9494
);
9595
}
9696

97+
#[test]
98+
fn test_uuid_query_validation() {
99+
let content = r#"
100+
openapi: 3.1.0
101+
info:
102+
title: Example API
103+
description: API definitions for example
104+
version: '0.0.1'
105+
x-file-identifier: example
106+
107+
components:
108+
schemas:
109+
ExampleResponse:
110+
properties:
111+
uuid:
112+
type: string
113+
description: The UUID for this example.
114+
format: uuid
115+
example: 00000000-0000-0000-0000-000000000000
116+
117+
security: [ ]
118+
119+
paths:
120+
/example:
121+
get:
122+
summary: Get a example
123+
description: Get a example
124+
operationId: get-a-example
125+
parameters:
126+
- name: uuid
127+
description: UUID of the example
128+
in: query
129+
schema:
130+
type: string
131+
format: uuid
132+
example: "00000000-0000-0000-0000-000000000000"
133+
responses:
134+
'200':
135+
description: Get a Example response
136+
content:
137+
application/json:
138+
schema:
139+
$ref: '#/components/schemas/ExampleResponse'
140+
"#;
141+
142+
let openapi: OpenAPI = OpenAPI::yaml(content).expect("Failed to parse OpenAPI content");
143+
144+
fn make_request(uuid: &str) -> request::axum::RequestData {
145+
request::axum::RequestData {
146+
path: "/example".to_string(),
147+
inner: axum::http::Request::builder()
148+
.method("GET")
149+
.uri(format!("/example?uuid={}", uuid))
150+
.body(axum::body::Body::empty())
151+
.unwrap(),
152+
body: None,
153+
}
154+
}
155+
156+
assert!(
157+
openapi
158+
.validator(make_request("00000000-0000-0000-0000-000000000000"))
159+
.is_ok(),
160+
"Valid body should pass validation"
161+
);
162+
163+
assert!(
164+
!openapi
165+
.validator(make_request("00000000-0000-0000-0000-xxxx"))
166+
.is_ok(),
167+
"Valid body should pass validation"
168+
);
169+
}
170+
97171
#[test]
98172
fn test_uuid_body_validation() {
99173
let content = r#"

0 commit comments

Comments
 (0)