fakecloud_sagemaker/
validate.rs1use http::StatusCode;
12use serde_json::{Map, Value};
13
14use fakecloud_core::service::AwsServiceError;
15
16use crate::generated::{OpMeta, K};
17
18pub const VALIDATION_ERROR: &str = "ValidationException";
23
24pub fn invalid(msg: impl Into<String>) -> AwsServiceError {
25 AwsServiceError::aws_error(StatusCode::BAD_REQUEST, VALIDATION_ERROR, msg)
26}
27
28pub fn validate(meta: &OpMeta, body: &Map<String, Value>) -> Result<(), AwsServiceError> {
30 for rule in meta.rules {
31 let present = body.get(rule.wire).map(|v| !v.is_null()).unwrap_or(false);
32 if rule.req && !present {
33 return Err(invalid(format!(
34 "Missing required parameter: {}",
35 rule.wire
36 )));
37 }
38 if !present {
39 continue;
40 }
41 let val = &body[rule.wire];
42
43 if matches!(rule.kind, K::Str) {
45 let s = match val {
46 Value::String(s) => s,
47 _ => {
48 return Err(invalid(format!("Member {} must be a string.", rule.wire)));
49 }
50 };
51 let len = s.chars().count() as u64;
52 if let Some(min) = rule.min_len {
53 if len < min {
54 return Err(invalid(format!(
55 "Member {} is shorter than the minimum length {min}.",
56 rule.wire
57 )));
58 }
59 }
60 if let Some(max) = rule.max_len {
61 if len > max {
62 return Err(invalid(format!(
63 "Member {} is longer than the maximum length {max}.",
64 rule.wire
65 )));
66 }
67 }
68 if !rule.enums.is_empty() && !rule.enums.contains(&s.as_str()) {
69 return Err(invalid(format!(
70 "Member {} has an invalid enum value.",
71 rule.wire
72 )));
73 }
74 }
75
76 if matches!(rule.kind, K::Int) && (rule.min_val.is_some() || rule.max_val.is_some()) {
78 if let Some(n) = val.as_i64() {
79 if let Some(min) = rule.min_val {
80 if n < min {
81 return Err(invalid(format!(
82 "Member {} is below the minimum {min}.",
83 rule.wire
84 )));
85 }
86 }
87 if let Some(max) = rule.max_val {
88 if n > max {
89 return Err(invalid(format!(
90 "Member {} is above the maximum {max}.",
91 rule.wire
92 )));
93 }
94 }
95 }
96 }
97 }
98 Ok(())
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104 use crate::generated::OPS;
105
106 fn meta(op: &str) -> &'static OpMeta {
107 OPS.iter().find(|m| m.op == op).unwrap()
108 }
109
110 #[test]
111 fn rejects_missing_required_member() {
112 let m = meta("CreateModel");
114 let body = Map::new();
115 assert!(validate(m, &body).is_err());
116 }
117
118 #[test]
119 fn accepts_valid_body() {
120 let m = meta("CreateModel");
121 let mut body = Map::new();
122 body.insert("ModelName".to_string(), Value::String("m".to_string()));
123 assert!(validate(m, &body).is_ok());
124 }
125
126 #[test]
127 fn rejects_invalid_enum() {
128 let m = meta("CreateApp");
130 let mut body = Map::new();
131 body.insert("DomainId".to_string(), Value::String("d".to_string()));
132 body.insert(
133 "AppType".to_string(),
134 Value::String("BogusType".to_string()),
135 );
136 body.insert("AppName".to_string(), Value::String("a".to_string()));
137 assert!(validate(m, &body).is_err());
138 }
139}