1use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9
10pub type Extra = serde_json::Map<String, serde_json::Value>;
18
19fn one_or_many<'de, D, T>(deserializer: D) -> std::result::Result<Option<Vec<T>>, D::Error>
23where
24 D: serde::Deserializer<'de>,
25 T: Deserialize<'de>,
26{
27 #[derive(Deserialize)]
28 #[serde(untagged)]
29 enum OneOrMany<T> {
30 One(T),
31 Many(Vec<T>),
32 }
33 let opt = Option::<OneOrMany<T>>::deserialize(deserializer)?;
34 Ok(opt.map(|v| match v {
35 OneOrMany::One(t) => vec![t],
36 OneOrMany::Many(v) => v,
37 }))
38}
39
40#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
59#[serde(untagged)]
60pub enum ParameterValues {
61 Values(Vec<String>),
63 Matcher(serde_json::Value),
65}
66
67impl ParameterValues {
68 pub fn as_values(&self) -> Option<&[String]> {
70 match self {
71 ParameterValues::Values(v) => Some(v),
72 ParameterValues::Matcher(_) => None,
73 }
74 }
75}
76
77impl From<Vec<String>> for ParameterValues {
78 fn from(values: Vec<String>) -> Self {
79 ParameterValues::Values(values)
80 }
81}
82
83const NOT_CHAR: char = '!';
90const OPTIONAL_CHAR: char = '?';
91
92#[derive(Debug, Clone, PartialEq, Eq, Default)]
120pub struct MatcherValue {
121 pub value: String,
123 pub not: bool,
125 pub optional: bool,
127}
128
129fn is_blank(s: &str) -> bool {
131 s.trim().is_empty()
132}
133
134impl MatcherValue {
135 pub fn literal(value: impl Into<String>) -> Self {
137 Self {
138 value: value.into(),
139 not: false,
140 optional: false,
141 }
142 }
143
144 pub fn not_literal(value: impl Into<String>) -> Self {
146 Self {
147 value: value.into(),
148 not: true,
149 optional: false,
150 }
151 }
152
153 pub fn optional_literal(value: impl Into<String>) -> Self {
155 Self {
156 value: value.into(),
157 not: false,
158 optional: true,
159 }
160 }
161
162 fn serialise(&self) -> String {
164 let mut s = String::new();
165 if self.optional {
166 s.push(OPTIONAL_CHAR);
167 }
168 if self.not {
169 s.push(NOT_CHAR);
170 }
171 if !is_blank(&self.value) {
172 s.push_str(&self.value);
173 }
174 s
175 }
176
177 fn parse_plain(s: &str) -> Self {
181 let mut optional = false;
182 let mut not = false;
183 let mut rest = s;
184 if !is_blank(s) {
185 if let Some(r) = rest.strip_prefix(OPTIONAL_CHAR) {
186 optional = true;
187 rest = r;
188 }
189 if let Some(r) = rest.strip_prefix(NOT_CHAR) {
190 not = true;
191 rest = r;
192 }
193 if let Some(r) = rest.strip_prefix(OPTIONAL_CHAR) {
194 optional = true;
195 rest = r;
196 }
197 }
198 Self {
199 value: rest.to_string(),
200 not,
201 optional,
202 }
203 }
204
205 fn ambiguous(&self) -> bool {
210 if is_blank(&self.value) {
211 return false;
215 }
216 let reparsed = Self::parse_plain(&self.serialise());
217 reparsed.not != self.not
218 || reparsed.optional != self.optional
219 || reparsed.value != self.value
220 }
221}
222
223impl From<String> for MatcherValue {
224 fn from(s: String) -> Self {
227 Self::parse_plain(&s)
228 }
229}
230
231impl From<&str> for MatcherValue {
232 fn from(s: &str) -> Self {
233 Self::parse_plain(s)
234 }
235}
236
237impl std::fmt::Display for MatcherValue {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 f.write_str(&self.serialise())
240 }
241}
242
243impl Serialize for MatcherValue {
244 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
245 where
246 S: serde::Serializer,
247 {
248 if !self.ambiguous() {
249 return serializer.serialize_str(&self.serialise());
250 }
251 use serde::ser::SerializeMap;
252 let count = if self.optional { 3 } else { 2 };
255 let mut map = serializer.serialize_map(Some(count))?;
256 map.serialize_entry("not", &self.not)?;
257 if self.optional {
258 map.serialize_entry("optional", &self.optional)?;
259 }
260 map.serialize_entry("value", &self.value)?;
261 map.end()
262 }
263}
264
265impl<'de> Deserialize<'de> for MatcherValue {
266 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
267 where
268 D: serde::Deserializer<'de>,
269 {
270 struct MatcherValueVisitor;
277
278 impl<'de> serde::de::Visitor<'de> for MatcherValueVisitor {
279 type Value = MatcherValue;
280
281 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 f.write_str("a matcher string or an object with a \"value\" field")
283 }
284
285 fn visit_str<E>(self, v: &str) -> std::result::Result<MatcherValue, E>
286 where
287 E: serde::de::Error,
288 {
289 Ok(MatcherValue::parse_plain(v))
290 }
291
292 fn visit_map<A>(self, mut map: A) -> std::result::Result<MatcherValue, A::Error>
293 where
294 A: serde::de::MapAccess<'de>,
295 {
296 let mut value: Option<String> = None;
297 let mut not = false;
298 let mut optional = false;
299 while let Some(key) = map.next_key::<String>()? {
300 match key.as_str() {
301 "value" => value = Some(map.next_value()?),
302 "not" => not = map.next_value()?,
303 "optional" => optional = map.next_value()?,
304 _ => {
307 let _ = map.next_value::<serde::de::IgnoredAny>()?;
308 }
309 }
310 }
311 let value = value.ok_or_else(|| serde::de::Error::missing_field("value"))?;
312 Ok(MatcherValue {
313 value,
314 not,
315 optional,
316 })
317 }
318 }
319
320 deserializer.deserialize_any(MatcherValueVisitor)
321 }
322}
323
324fn de_lenient_optional_string<'de, D>(
332 deserializer: D,
333) -> std::result::Result<Option<String>, D::Error>
334where
335 D: serde::Deserializer<'de>,
336{
337 struct LenientString;
338
339 impl<'de> serde::de::Visitor<'de> for LenientString {
340 type Value = Option<String>;
341
342 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
343 f.write_str("a string, or an object matcher form that is discarded")
344 }
345
346 fn visit_str<E>(self, v: &str) -> std::result::Result<Option<String>, E>
347 where
348 E: serde::de::Error,
349 {
350 Ok(Some(v.to_owned()))
351 }
352
353 fn visit_none<E>(self) -> std::result::Result<Option<String>, E>
354 where
355 E: serde::de::Error,
356 {
357 Ok(None)
358 }
359
360 fn visit_unit<E>(self) -> std::result::Result<Option<String>, E>
361 where
362 E: serde::de::Error,
363 {
364 Ok(None)
365 }
366
367 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Option<String>, D::Error>
368 where
369 D: serde::Deserializer<'de>,
370 {
371 deserializer.deserialize_any(self)
372 }
373
374 fn visit_map<A>(self, mut map: A) -> std::result::Result<Option<String>, A::Error>
375 where
376 A: serde::de::MapAccess<'de>,
377 {
378 while map
381 .next_entry::<serde::de::IgnoredAny, serde::de::IgnoredAny>()?
382 .is_some()
383 {}
384 Ok(None)
385 }
386 }
387
388 deserializer.deserialize_any(LenientString)
389}
390
391#[derive(Debug, Clone, Default, PartialEq)]
412pub struct HttpRequest {
413 pub method: Option<String>,
414
415 pub path: Option<String>,
416
417 pub query_string_parameters: Option<HashMap<String, Vec<String>>>,
419
420 pub headers: Option<HashMap<String, Vec<String>>>,
422
423 pub body: Option<Body>,
424
425 pub jwt: Option<Jwt>,
426
427 pub socket_address: Option<SocketAddress>,
428
429 pub not: Option<bool>,
431
432 pub secure: Option<bool>,
434
435 pub keep_alive: Option<bool>,
437
438 pub protocol: Option<String>,
440
441 pub path_parameters: Option<HashMap<String, ParameterValues>>,
446
447 pub cookies: Option<HashMap<String, String>>,
449
450 pub header_matchers: Option<HashMap<String, Vec<MatcherValue>>>,
456
457 pub query_string_parameter_matchers: Option<HashMap<String, Vec<MatcherValue>>>,
460
461 pub cookie_matchers: Option<HashMap<String, MatcherValue>>,
465
466 pub extra: Extra,
469}
470
471fn plain_multi_as_matchers(
475 map: &HashMap<String, Vec<String>>,
476) -> HashMap<String, Vec<MatcherValue>> {
477 map.iter()
478 .map(|(k, vs)| {
479 (
480 k.clone(),
481 vs.iter().map(|v| MatcherValue::from(v.clone())).collect(),
482 )
483 })
484 .collect()
485}
486
487fn effective_multi(
491 plain: &Option<HashMap<String, Vec<String>>>,
492 matchers: &Option<HashMap<String, Vec<MatcherValue>>>,
493) -> Option<HashMap<String, Vec<MatcherValue>>> {
494 match matchers {
495 Some(m) if !m.is_empty() => Some(m.clone()),
496 _ => plain.as_ref().map(plain_multi_as_matchers),
497 }
498}
499
500fn effective_single(
502 plain: &Option<HashMap<String, String>>,
503 matchers: &Option<HashMap<String, MatcherValue>>,
504) -> Option<HashMap<String, MatcherValue>> {
505 match matchers {
506 Some(m) if !m.is_empty() => Some(m.clone()),
507 _ => plain.as_ref().map(|p| {
508 p.iter()
509 .map(|(k, v)| (k.clone(), MatcherValue::from(v.clone())))
510 .collect()
511 }),
512 }
513}
514
515type SplitMulti = (
518 Option<HashMap<String, Vec<String>>>,
519 Option<HashMap<String, Vec<MatcherValue>>>,
520);
521
522type SplitSingle = (
524 Option<HashMap<String, String>>,
525 Option<HashMap<String, MatcherValue>>,
526);
527
528fn split_multi(decoded: Option<HashMap<String, Vec<MatcherValue>>>) -> SplitMulti {
533 let Some(map) = decoded else {
534 return (None, None);
535 };
536 if map.values().flatten().any(MatcherValue::ambiguous) {
537 return (None, Some(map));
538 }
539 let plain = map
540 .into_iter()
541 .map(|(k, vs)| (k, vs.iter().map(MatcherValue::serialise).collect()))
542 .collect();
543 (Some(plain), None)
544}
545
546fn split_single(decoded: Option<HashMap<String, MatcherValue>>) -> SplitSingle {
548 let Some(map) = decoded else {
549 return (None, None);
550 };
551 if map.values().any(MatcherValue::ambiguous) {
552 return (None, Some(map));
553 }
554 let plain = map.into_iter().map(|(k, v)| (k, v.serialise())).collect();
555 (Some(plain), None)
556}
557
558impl Serialize for HttpRequest {
559 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
560 where
561 S: serde::Serializer,
562 {
563 #[derive(Serialize)]
564 #[serde(rename_all = "camelCase")]
565 struct Wire<'a> {
566 #[serde(skip_serializing_if = "Option::is_none")]
567 method: &'a Option<String>,
568 #[serde(skip_serializing_if = "Option::is_none")]
569 path: &'a Option<String>,
570 #[serde(skip_serializing_if = "Option::is_none")]
571 query_string_parameters: Option<HashMap<String, Vec<MatcherValue>>>,
572 #[serde(skip_serializing_if = "Option::is_none")]
573 headers: Option<HashMap<String, Vec<MatcherValue>>>,
574 #[serde(skip_serializing_if = "Option::is_none")]
575 body: &'a Option<Body>,
576 #[serde(skip_serializing_if = "Option::is_none")]
577 jwt: &'a Option<Jwt>,
578 #[serde(skip_serializing_if = "Option::is_none")]
579 socket_address: &'a Option<SocketAddress>,
580 #[serde(skip_serializing_if = "Option::is_none")]
581 not: &'a Option<bool>,
582 #[serde(skip_serializing_if = "Option::is_none")]
583 secure: &'a Option<bool>,
584 #[serde(skip_serializing_if = "Option::is_none")]
585 keep_alive: &'a Option<bool>,
586 #[serde(skip_serializing_if = "Option::is_none")]
587 protocol: &'a Option<String>,
588 #[serde(skip_serializing_if = "Option::is_none")]
589 path_parameters: &'a Option<HashMap<String, ParameterValues>>,
590 #[serde(skip_serializing_if = "Option::is_none")]
591 cookies: Option<HashMap<String, MatcherValue>>,
592 #[serde(flatten)]
593 extra: &'a Extra,
594 }
595
596 Wire {
597 method: &self.method,
598 path: &self.path,
599 query_string_parameters: effective_multi(
600 &self.query_string_parameters,
601 &self.query_string_parameter_matchers,
602 ),
603 headers: effective_multi(&self.headers, &self.header_matchers),
604 body: &self.body,
605 jwt: &self.jwt,
606 socket_address: &self.socket_address,
607 not: &self.not,
608 secure: &self.secure,
609 keep_alive: &self.keep_alive,
610 protocol: &self.protocol,
611 path_parameters: &self.path_parameters,
612 cookies: effective_single(&self.cookies, &self.cookie_matchers),
613 extra: &self.extra,
614 }
615 .serialize(serializer)
616 }
617}
618
619impl<'de> Deserialize<'de> for HttpRequest {
620 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
621 where
622 D: serde::Deserializer<'de>,
623 {
624 #[derive(Deserialize)]
625 #[serde(rename_all = "camelCase")]
626 struct Wire {
627 #[serde(default, deserialize_with = "de_lenient_optional_string")]
628 method: Option<String>,
629 #[serde(default, deserialize_with = "de_lenient_optional_string")]
630 path: Option<String>,
631 #[serde(default)]
632 query_string_parameters: Option<HashMap<String, Vec<MatcherValue>>>,
633 #[serde(default)]
634 headers: Option<HashMap<String, Vec<MatcherValue>>>,
635 #[serde(default)]
636 body: Option<Body>,
637 #[serde(default)]
638 jwt: Option<Jwt>,
639 #[serde(default)]
640 socket_address: Option<SocketAddress>,
641 #[serde(default)]
642 not: Option<bool>,
643 #[serde(default)]
644 secure: Option<bool>,
645 #[serde(default)]
646 keep_alive: Option<bool>,
647 #[serde(default)]
648 protocol: Option<String>,
649 #[serde(default)]
650 path_parameters: Option<HashMap<String, ParameterValues>>,
651 #[serde(default)]
652 cookies: Option<HashMap<String, MatcherValue>>,
653 #[serde(flatten)]
654 extra: Extra,
655 }
656
657 let wire = Wire::deserialize(deserializer)?;
658 let (headers, header_matchers) = split_multi(wire.headers);
659 let (query_string_parameters, query_string_parameter_matchers) =
660 split_multi(wire.query_string_parameters);
661 let (cookies, cookie_matchers) = split_single(wire.cookies);
662 Ok(HttpRequest {
663 method: wire.method,
664 path: wire.path,
665 query_string_parameters,
666 headers,
667 body: wire.body,
668 jwt: wire.jwt,
669 socket_address: wire.socket_address,
670 not: wire.not,
671 secure: wire.secure,
672 keep_alive: wire.keep_alive,
673 protocol: wire.protocol,
674 path_parameters: wire.path_parameters,
675 cookies,
676 header_matchers,
677 query_string_parameter_matchers,
678 cookie_matchers,
679 extra: wire.extra,
680 })
681 }
682}
683
684impl HttpRequest {
685 pub fn new() -> Self {
687 Self::default()
688 }
689
690 pub fn socket_address(mut self, socket_address: SocketAddress) -> Self {
696 self.socket_address = Some(socket_address);
697 self
698 }
699
700 pub fn method(mut self, method: impl Into<String>) -> Self {
702 self.method = Some(method.into());
703 self
704 }
705
706 pub fn path(mut self, path: impl Into<String>) -> Self {
708 self.path = Some(path.into());
709 self
710 }
711
712 pub fn query_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
720 let params = self
721 .query_string_parameters
722 .get_or_insert_with(HashMap::new);
723 params.entry(key.into()).or_default().push(value.into());
724 self
725 }
726
727 pub fn query_param_matcher(mut self, key: impl Into<String>, value: MatcherValue) -> Self {
736 if self.query_string_parameter_matchers.is_none() {
737 let migrated = self
738 .query_string_parameters
739 .take()
740 .map(|m| plain_multi_as_matchers(&m))
741 .unwrap_or_default();
742 self.query_string_parameter_matchers = Some(migrated);
743 }
744 self.query_string_parameter_matchers
745 .as_mut()
746 .unwrap()
747 .entry(key.into())
748 .or_default()
749 .push(value);
750 self
751 }
752
753 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
760 let headers = self.headers.get_or_insert_with(HashMap::new);
761 headers.entry(key.into()).or_default().push(value.into());
762 self
763 }
764
765 pub fn header_matcher(mut self, key: impl Into<String>, value: MatcherValue) -> Self {
774 if self.header_matchers.is_none() {
775 let migrated = self
776 .headers
777 .take()
778 .map(|m| plain_multi_as_matchers(&m))
779 .unwrap_or_default();
780 self.header_matchers = Some(migrated);
781 }
782 self.header_matchers
783 .as_mut()
784 .unwrap()
785 .entry(key.into())
786 .or_default()
787 .push(value);
788 self
789 }
790
791 pub fn body(mut self, body: impl Into<String>) -> Self {
793 self.body = Some(Body::Plain(body.into()));
794 self
795 }
796
797 pub fn json_body(mut self, json: serde_json::Value) -> Self {
799 self.body = Some(Body::Typed {
800 body_type: "JSON".to_string(),
801 json: json.to_string(),
802 });
803 self
804 }
805
806 pub fn file_body(mut self, file_path: impl Into<String>) -> Self {
811 self.body = Some(Body::File {
812 file_path: file_path.into(),
813 content_type: None,
814 template_type: None,
815 });
816 self
817 }
818
819 pub fn body_value(mut self, body: Body) -> Self {
821 self.body = Some(body);
822 self
823 }
824
825 pub fn jwt(mut self, jwt: Jwt) -> Self {
845 self.jwt = Some(jwt);
846 self
847 }
848
849 pub fn not(mut self, not: bool) -> Self {
851 self.not = Some(not);
852 self
853 }
854
855 pub fn secure(mut self, secure: bool) -> Self {
857 self.secure = Some(secure);
858 self
859 }
860
861 pub fn keep_alive(mut self, keep_alive: bool) -> Self {
863 self.keep_alive = Some(keep_alive);
864 self
865 }
866
867 pub fn protocol(mut self, protocol: impl Into<String>) -> Self {
869 self.protocol = Some(protocol.into());
870 self
871 }
872
873 pub fn path_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
878 let params = self.path_parameters.get_or_insert_with(HashMap::new);
879 match params
880 .entry(key.into())
881 .or_insert_with(|| ParameterValues::Values(Vec::new()))
882 {
883 ParameterValues::Values(v) => v.push(value.into()),
884 ParameterValues::Matcher(_) => {
885 }
888 }
889 self
890 }
891
892 pub fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
899 let cookies = self.cookies.get_or_insert_with(HashMap::new);
900 cookies.insert(name.into(), value.into());
901 self
902 }
903
904 pub fn cookie_matcher(mut self, name: impl Into<String>, value: MatcherValue) -> Self {
913 if self.cookie_matchers.is_none() {
914 let migrated = self
915 .cookies
916 .take()
917 .map(|m| {
918 m.into_iter()
919 .map(|(k, v)| (k, MatcherValue::from(v)))
920 .collect()
921 })
922 .unwrap_or_default();
923 self.cookie_matchers = Some(migrated);
924 }
925 self.cookie_matchers
926 .as_mut()
927 .unwrap()
928 .insert(name.into(), value);
929 self
930 }
931
932 pub fn path_param_matcher(mut self, key: impl Into<String>, value: MatcherValue) -> Self {
942 let element = serde_json::to_value(&value)
943 .expect("BUG: MatcherValue::serialize returned Err for an in-memory value");
944 let params = self.path_parameters.get_or_insert_with(HashMap::new);
945 match params
946 .entry(key.into())
947 .or_insert_with(|| ParameterValues::Matcher(serde_json::Value::Array(Vec::new())))
948 {
949 ParameterValues::Matcher(serde_json::Value::Array(values)) => values.push(element),
950 slot => {
951 *slot = ParameterValues::Matcher(serde_json::Value::Array(vec![element]));
952 }
953 }
954 self
955 }
956}
957
958#[derive(Debug, Clone, PartialEq)]
964pub enum Body {
965 Plain(String),
967 Typed { body_type: String, json: String },
969 File {
971 file_path: String,
972 content_type: Option<String>,
973 template_type: Option<String>,
974 },
975 AllOf(Vec<Body>),
980 Matcher {
986 body_type: String,
987 value_key: String,
988 value: String,
989 },
990 Object(serde_json::Map<String, serde_json::Value>),
997}
998
999impl Body {
1000 pub fn file(file_path: impl Into<String>) -> Self {
1011 Body::File {
1012 file_path: file_path.into(),
1013 content_type: None,
1014 template_type: None,
1015 }
1016 }
1017
1018 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
1020 if let Body::File {
1021 content_type: ref mut ct,
1022 ..
1023 } = self
1024 {
1025 *ct = Some(content_type.into());
1026 }
1027 self
1028 }
1029
1030 pub fn with_template_type(mut self, template_type: impl Into<String>) -> Self {
1033 if let Body::File {
1034 template_type: ref mut tt,
1035 ..
1036 } = self
1037 {
1038 *tt = Some(template_type.into());
1039 }
1040 self
1041 }
1042
1043 pub fn all_of(bodies: Vec<Body>) -> Self {
1056 Body::AllOf(bodies)
1057 }
1058
1059 pub fn json_path(expression: impl Into<String>) -> Self {
1063 Body::Matcher {
1064 body_type: "JSON_PATH".to_string(),
1065 value_key: "jsonPath".to_string(),
1066 value: expression.into(),
1067 }
1068 }
1069
1070 pub fn regex(pattern: impl Into<String>) -> Self {
1074 Body::Matcher {
1075 body_type: "REGEX".to_string(),
1076 value_key: "regex".to_string(),
1077 value: pattern.into(),
1078 }
1079 }
1080
1081 pub fn xpath(expression: impl Into<String>) -> Self {
1083 Body::Matcher {
1084 body_type: "XPATH".to_string(),
1085 value_key: "xpath".to_string(),
1086 value: expression.into(),
1087 }
1088 }
1089
1090 pub fn string(value: impl Into<String>, sub_string: bool) -> Self {
1095 let mut map = serde_json::Map::new();
1096 map.insert("type".into(), serde_json::Value::from("STRING"));
1097 map.insert("string".into(), serde_json::Value::from(value.into()));
1098 map.insert("subString".into(), serde_json::Value::from(sub_string));
1099 Body::Object(map)
1100 }
1101
1102 pub fn xml(value: impl Into<String>) -> Self {
1104 Self::single_object("XML", "xml", value.into())
1105 }
1106
1107 pub fn xml_schema(schema: impl Into<String>) -> Self {
1110 Self::single_object("XML_SCHEMA", "xmlSchema", schema.into())
1111 }
1112
1113 pub fn json_schema(schema: impl Into<String>) -> Self {
1116 Self::single_object("JSON_SCHEMA", "jsonSchema", schema.into())
1117 }
1118
1119 pub fn parameters(parameters: HashMap<String, Vec<String>>) -> Self {
1122 let mut map = serde_json::Map::new();
1123 map.insert("type".into(), serde_json::Value::from("PARAMETERS"));
1124 map.insert(
1125 "parameters".into(),
1126 serde_json::to_value(parameters).unwrap_or(serde_json::Value::Null),
1127 );
1128 Body::Object(map)
1129 }
1130
1131 pub fn binary(data: impl AsRef<[u8]>, content_type: Option<String>) -> Self {
1134 let mut map = serde_json::Map::new();
1135 map.insert("type".into(), serde_json::Value::from("BINARY"));
1136 map.insert(
1137 "base64Bytes".into(),
1138 serde_json::Value::from(BASE64.encode(data.as_ref())),
1139 );
1140 if let Some(ct) = content_type {
1141 map.insert("contentType".into(), serde_json::Value::from(ct));
1142 }
1143 Body::Object(map)
1144 }
1145
1146 pub fn graphql(query: impl Into<String>) -> Self {
1148 let mut map = serde_json::Map::new();
1149 map.insert("type".into(), serde_json::Value::from("GRAPHQL"));
1150 map.insert("query".into(), serde_json::Value::from(query.into()));
1151 Body::Object(map)
1152 }
1153
1154 pub fn wasm(object: serde_json::Map<String, serde_json::Value>) -> Self {
1159 Body::Object(object)
1160 }
1161
1162 pub fn object(object: serde_json::Map<String, serde_json::Value>) -> Self {
1165 Body::Object(object)
1166 }
1167
1168 fn single_object(body_type: &str, key: &str, value: String) -> Self {
1169 let mut map = serde_json::Map::new();
1170 map.insert("type".into(), serde_json::Value::from(body_type));
1171 map.insert(key.into(), serde_json::Value::from(value));
1172 Body::Object(map)
1173 }
1174}
1175
1176impl Serialize for Body {
1177 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
1178 where
1179 S: serde::Serializer,
1180 {
1181 match self {
1182 Body::Plain(s) => serializer.serialize_str(s),
1183 Body::Typed { body_type, json } => {
1184 use serde::ser::SerializeMap;
1185 let mut map = serializer.serialize_map(Some(2))?;
1186 map.serialize_entry("type", body_type)?;
1187 map.serialize_entry("json", json)?;
1188 map.end()
1189 }
1190 Body::File {
1191 file_path,
1192 content_type,
1193 template_type,
1194 } => {
1195 use serde::ser::SerializeMap;
1196 let count = 2
1197 + content_type.as_ref().map_or(0, |_| 1)
1198 + template_type.as_ref().map_or(0, |_| 1);
1199 let mut map = serializer.serialize_map(Some(count))?;
1200 map.serialize_entry("type", "FILE")?;
1201 map.serialize_entry("filePath", file_path)?;
1202 if let Some(ct) = content_type {
1203 map.serialize_entry("contentType", ct)?;
1204 }
1205 if let Some(tt) = template_type {
1206 map.serialize_entry("templateType", tt)?;
1207 }
1208 map.end()
1209 }
1210 Body::AllOf(bodies) => {
1211 use serde::ser::SerializeMap;
1212 let mut map = serializer.serialize_map(Some(2))?;
1213 map.serialize_entry("type", "ALL_OF")?;
1214 map.serialize_entry("bodyAllOf", bodies)?;
1215 map.end()
1216 }
1217 Body::Matcher {
1218 body_type,
1219 value_key,
1220 value,
1221 } => {
1222 use serde::ser::SerializeMap;
1223 let mut map = serializer.serialize_map(Some(2))?;
1224 map.serialize_entry("type", body_type)?;
1225 map.serialize_entry(value_key.as_str(), value)?;
1226 map.end()
1227 }
1228 Body::Object(object) => object.serialize(serializer),
1229 }
1230 }
1231}
1232
1233fn matcher_key_value(
1236 body_type: &str,
1237 map: &serde_json::Map<String, serde_json::Value>,
1238) -> Option<(String, String)> {
1239 let key = match body_type {
1240 "JSON_PATH" => "jsonPath",
1241 "REGEX" => "regex",
1242 "XPATH" => "xpath",
1243 _ => return None,
1244 };
1245 map.get(key)
1246 .and_then(|v| v.as_str())
1247 .map(|s| (key.to_string(), s.to_string()))
1248}
1249
1250impl<'de> Deserialize<'de> for Body {
1251 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
1252 where
1253 D: serde::Deserializer<'de>,
1254 {
1255 use serde_json::Value;
1256 let v = Value::deserialize(deserializer)?;
1257 match v {
1258 Value::String(s) => Ok(Body::Plain(s)),
1259 Value::Object(map) => {
1260 let body_type = map
1261 .get("type")
1262 .and_then(|v| v.as_str())
1263 .unwrap_or("JSON")
1264 .to_string();
1265 if body_type == "FILE" {
1266 let file_path = map
1267 .get("filePath")
1268 .and_then(|v| v.as_str())
1269 .unwrap_or("")
1270 .to_string();
1271 let content_type = map
1272 .get("contentType")
1273 .and_then(|v| v.as_str())
1274 .map(|s| s.to_string());
1275 let template_type = map
1276 .get("templateType")
1277 .and_then(|v| v.as_str())
1278 .map(|s| s.to_string());
1279 Ok(Body::File {
1280 file_path,
1281 content_type,
1282 template_type,
1283 })
1284 } else if body_type == "ALL_OF" {
1285 let bodies = map
1286 .get("bodyAllOf")
1287 .and_then(|v| v.as_array())
1288 .map(|arr| {
1289 arr.iter()
1290 .cloned()
1291 .map(serde_json::from_value)
1292 .collect::<std::result::Result<Vec<Body>, _>>()
1293 })
1294 .transpose()
1295 .map_err(serde::de::Error::custom)?
1296 .unwrap_or_default();
1297 Ok(Body::AllOf(bodies))
1298 } else if map.len() == 2 && matcher_key_value(&body_type, &map).is_some() {
1299 let (value_key, value) = matcher_key_value(&body_type, &map)
1304 .expect("matcher_key_value checked above");
1305 Ok(Body::Matcher {
1306 body_type,
1307 value_key,
1308 value,
1309 })
1310 } else if body_type == "JSON"
1311 && map.len() == 2
1312 && map.get("json").is_some_and(|v| v.is_string())
1313 {
1314 let json = map
1320 .get("json")
1321 .and_then(|v| v.as_str())
1322 .unwrap_or("")
1323 .to_string();
1324 Ok(Body::Typed { body_type, json })
1325 } else {
1326 Ok(Body::Object(map))
1331 }
1332 }
1333 _ => Ok(Body::Plain(v.to_string())),
1334 }
1335 }
1336}
1337
1338#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1365#[serde(rename_all = "camelCase")]
1366pub struct Jwt {
1367 pub claims: HashMap<String, String>,
1369
1370 #[serde(skip_serializing_if = "Option::is_none")]
1371 pub issuer: Option<String>,
1372
1373 #[serde(skip_serializing_if = "Option::is_none")]
1374 pub audience: Option<String>,
1375
1376 #[serde(skip_serializing_if = "Option::is_none")]
1377 pub algorithm: Option<String>,
1378
1379 #[serde(skip_serializing_if = "Option::is_none")]
1380 pub header: Option<String>,
1381
1382 #[serde(skip_serializing_if = "Option::is_none")]
1383 pub scheme: Option<String>,
1384}
1385
1386impl Jwt {
1387 pub fn new() -> Self {
1389 Self::default()
1390 }
1391
1392 pub fn claim(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1395 self.claims.insert(name.into(), value.into());
1396 self
1397 }
1398
1399 pub fn claims(mut self, claims: HashMap<String, String>) -> Self {
1401 self.claims = claims;
1402 self
1403 }
1404
1405 pub fn issuer(mut self, issuer: impl Into<String>) -> Self {
1407 self.issuer = Some(issuer.into());
1408 self
1409 }
1410
1411 pub fn audience(mut self, audience: impl Into<String>) -> Self {
1413 self.audience = Some(audience.into());
1414 self
1415 }
1416
1417 pub fn algorithm(mut self, algorithm: impl Into<String>) -> Self {
1419 self.algorithm = Some(algorithm.into());
1420 self
1421 }
1422
1423 pub fn header(mut self, header: impl Into<String>) -> Self {
1425 self.header = Some(header.into());
1426 self
1427 }
1428
1429 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
1431 self.scheme = Some(scheme.into());
1432 self
1433 }
1434}
1435
1436#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1452#[serde(rename_all = "camelCase")]
1453pub struct HttpResponse {
1454 #[serde(skip_serializing_if = "Option::is_none")]
1455 pub status_code: Option<u16>,
1456
1457 #[serde(skip_serializing_if = "Option::is_none")]
1458 pub headers: Option<HashMap<String, Vec<String>>>,
1459
1460 #[serde(skip_serializing_if = "Option::is_none")]
1461 pub body: Option<String>,
1462
1463 #[serde(skip_serializing_if = "Option::is_none")]
1464 pub delay: Option<Delay>,
1465
1466 #[serde(skip_serializing_if = "Option::is_none")]
1468 pub cookies: Option<HashMap<String, String>>,
1469
1470 #[serde(skip_serializing_if = "Option::is_none")]
1473 pub reason_phrase: Option<String>,
1474
1475 #[serde(skip_serializing_if = "Option::is_none")]
1477 pub status_code_range: Option<String>,
1478
1479 #[serde(skip_serializing_if = "Option::is_none")]
1481 pub trailers: Option<HashMap<String, Vec<String>>>,
1482
1483 #[serde(skip_serializing_if = "Option::is_none")]
1485 pub generate_from_schema: Option<String>,
1486
1487 #[serde(skip_serializing_if = "Option::is_none")]
1489 pub connection_options: Option<ConnectionOptions>,
1490
1491 #[serde(skip_serializing_if = "Option::is_none")]
1493 pub recover_after: Option<RecoverAfter>,
1494
1495 #[serde(skip_serializing_if = "Option::is_none")]
1497 pub primary: Option<bool>,
1498
1499 #[serde(flatten, default)]
1502 pub extra: Extra,
1503}
1504
1505impl HttpResponse {
1506 pub fn new() -> Self {
1508 Self::default()
1509 }
1510
1511 pub fn status_code(mut self, code: u16) -> Self {
1513 self.status_code = Some(code);
1514 self
1515 }
1516
1517 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1519 let headers = self.headers.get_or_insert_with(HashMap::new);
1520 headers.entry(key.into()).or_default().push(value.into());
1521 self
1522 }
1523
1524 pub fn body(mut self, body: impl Into<String>) -> Self {
1526 self.body = Some(body.into());
1527 self
1528 }
1529
1530 pub fn delay(mut self, delay: Delay) -> Self {
1532 self.delay = Some(delay);
1533 self
1534 }
1535
1536 pub fn cookie(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
1538 let cookies = self.cookies.get_or_insert_with(HashMap::new);
1539 cookies.insert(name.into(), value.into());
1540 self
1541 }
1542
1543 pub fn reason_phrase(mut self, reason_phrase: impl Into<String>) -> Self {
1545 self.reason_phrase = Some(reason_phrase.into());
1546 self
1547 }
1548
1549 pub fn status_code_range(mut self, range: impl Into<String>) -> Self {
1551 self.status_code_range = Some(range.into());
1552 self
1553 }
1554
1555 pub fn trailer(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
1557 let trailers = self.trailers.get_or_insert_with(HashMap::new);
1558 trailers.entry(key.into()).or_default().push(value.into());
1559 self
1560 }
1561
1562 pub fn generate_from_schema(mut self, schema: impl Into<String>) -> Self {
1564 self.generate_from_schema = Some(schema.into());
1565 self
1566 }
1567
1568 pub fn connection_options(mut self, options: ConnectionOptions) -> Self {
1570 self.connection_options = Some(options);
1571 self
1572 }
1573
1574 pub fn recover_after(mut self, recover_after: RecoverAfter) -> Self {
1576 self.recover_after = Some(recover_after);
1577 self
1578 }
1579
1580 pub fn primary(mut self, primary: bool) -> Self {
1582 self.primary = Some(primary);
1583 self
1584 }
1585}
1586
1587#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1603#[serde(rename_all = "camelCase")]
1604pub struct HttpTemplate {
1605 #[serde(skip_serializing_if = "Option::is_none")]
1606 pub template_type: Option<String>,
1607
1608 #[serde(skip_serializing_if = "Option::is_none")]
1609 pub template: Option<String>,
1610
1611 #[serde(skip_serializing_if = "Option::is_none")]
1612 pub template_file: Option<String>,
1613}
1614
1615impl HttpTemplate {
1616 pub fn new(template_type: impl Into<String>, template: impl Into<String>) -> Self {
1618 Self {
1619 template_type: Some(template_type.into()),
1620 template: Some(template.into()),
1621 template_file: None,
1622 }
1623 }
1624
1625 pub fn from_file(template_type: impl Into<String>, file_path: impl Into<String>) -> Self {
1627 Self {
1628 template_type: Some(template_type.into()),
1629 template: None,
1630 template_file: Some(file_path.into()),
1631 }
1632 }
1633
1634 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
1636 self.template_type = Some(template_type.into());
1637 self
1638 }
1639
1640 pub fn template(mut self, template: impl Into<String>) -> Self {
1642 self.template = Some(template.into());
1643 self
1644 }
1645
1646 pub fn template_file(mut self, file_path: impl Into<String>) -> Self {
1648 self.template_file = Some(file_path.into());
1649 self
1650 }
1651}
1652
1653#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1666#[serde(rename_all = "camelCase")]
1667pub struct HttpForward {
1668 pub host: String,
1669
1670 #[serde(skip_serializing_if = "Option::is_none")]
1671 pub port: Option<u16>,
1672
1673 #[serde(skip_serializing_if = "Option::is_none")]
1674 pub scheme: Option<String>,
1675
1676 #[serde(skip_serializing_if = "Option::is_none")]
1678 pub delay: Option<Delay>,
1679
1680 #[serde(skip_serializing_if = "Option::is_none")]
1681 pub primary: Option<bool>,
1682
1683 #[serde(flatten, default)]
1686 pub extra: Extra,
1687}
1688
1689impl HttpForward {
1690 pub fn new(host: impl Into<String>, port: u16) -> Self {
1692 Self {
1693 host: host.into(),
1694 port: Some(port),
1695 scheme: None,
1696 delay: None,
1697 primary: None,
1698 extra: Extra::default(),
1699 }
1700 }
1701
1702 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
1704 self.scheme = Some(scheme.into());
1705 self
1706 }
1707
1708 pub fn delay(mut self, delay: Delay) -> Self {
1710 self.delay = Some(delay);
1711 self
1712 }
1713
1714 pub fn primary(mut self, primary: bool) -> Self {
1716 self.primary = Some(primary);
1717 self
1718 }
1719}
1720
1721#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1739#[serde(rename_all = "camelCase")]
1740pub struct HttpClassCallback {
1741 pub callback_class: String,
1742
1743 #[serde(skip_serializing_if = "Option::is_none")]
1744 pub delay: Option<Delay>,
1745
1746 #[serde(skip_serializing_if = "Option::is_none")]
1747 pub primary: Option<bool>,
1748}
1749
1750impl HttpClassCallback {
1751 pub fn new(callback_class: impl Into<String>) -> Self {
1754 Self {
1755 callback_class: callback_class.into(),
1756 delay: None,
1757 primary: None,
1758 }
1759 }
1760
1761 pub fn delay(mut self, delay: Delay) -> Self {
1763 self.delay = Some(delay);
1764 self
1765 }
1766
1767 pub fn primary(mut self, primary: bool) -> Self {
1769 self.primary = Some(primary);
1770 self
1771 }
1772}
1773
1774#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1792#[serde(rename_all = "camelCase")]
1793pub struct HttpObjectCallback {
1794 pub client_id: String,
1795
1796 #[serde(skip_serializing_if = "Option::is_none")]
1797 pub response_callback: Option<bool>,
1798
1799 #[serde(skip_serializing_if = "Option::is_none")]
1800 pub delay: Option<Delay>,
1801
1802 #[serde(skip_serializing_if = "Option::is_none")]
1803 pub primary: Option<bool>,
1804}
1805
1806impl HttpObjectCallback {
1807 pub fn new(client_id: impl Into<String>) -> Self {
1809 Self {
1810 client_id: client_id.into(),
1811 response_callback: None,
1812 delay: None,
1813 primary: None,
1814 }
1815 }
1816
1817 pub fn response_callback(mut self, response_callback: bool) -> Self {
1819 self.response_callback = Some(response_callback);
1820 self
1821 }
1822
1823 pub fn delay(mut self, delay: Delay) -> Self {
1825 self.delay = Some(delay);
1826 self
1827 }
1828
1829 pub fn primary(mut self, primary: bool) -> Self {
1831 self.primary = Some(primary);
1832 self
1833 }
1834}
1835
1836#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1842#[serde(rename_all = "camelCase")]
1843pub struct HttpError {
1844 #[serde(skip_serializing_if = "Option::is_none")]
1845 pub drop_connection: Option<bool>,
1846
1847 #[serde(skip_serializing_if = "Option::is_none")]
1848 pub response_bytes: Option<String>,
1849
1850 #[serde(skip_serializing_if = "Option::is_none")]
1852 pub delay: Option<Delay>,
1853
1854 #[serde(skip_serializing_if = "Option::is_none")]
1858 pub stream_error: Option<i64>,
1859
1860 #[serde(skip_serializing_if = "Option::is_none")]
1861 pub primary: Option<bool>,
1862
1863 #[serde(flatten, default)]
1866 pub extra: Extra,
1867}
1868
1869impl HttpError {
1870 pub fn new() -> Self {
1872 Self::default()
1873 }
1874
1875 pub fn drop_connection(mut self, drop: bool) -> Self {
1877 self.drop_connection = Some(drop);
1878 self
1879 }
1880
1881 pub fn response_bytes(mut self, bytes: impl Into<String>) -> Self {
1883 self.response_bytes = Some(bytes.into());
1884 self
1885 }
1886
1887 pub fn delay(mut self, delay: Delay) -> Self {
1889 self.delay = Some(delay);
1890 self
1891 }
1892
1893 pub fn stream_error(mut self, code: i64) -> Self {
1895 self.stream_error = Some(code);
1896 self
1897 }
1898
1899 pub fn primary(mut self, primary: bool) -> Self {
1901 self.primary = Some(primary);
1902 self
1903 }
1904}
1905
1906#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1914#[serde(rename_all = "camelCase")]
1915pub struct SseEvent {
1916 #[serde(skip_serializing_if = "Option::is_none")]
1917 pub event: Option<String>,
1918
1919 #[serde(skip_serializing_if = "Option::is_none")]
1920 pub data: Option<String>,
1921
1922 #[serde(skip_serializing_if = "Option::is_none")]
1923 pub id: Option<String>,
1924
1925 #[serde(skip_serializing_if = "Option::is_none")]
1926 pub retry: Option<u32>,
1927
1928 #[serde(skip_serializing_if = "Option::is_none")]
1929 pub delay: Option<Delay>,
1930}
1931
1932impl SseEvent {
1933 pub fn new() -> Self {
1935 Self::default()
1936 }
1937
1938 pub fn event(mut self, event: impl Into<String>) -> Self {
1940 self.event = Some(event.into());
1941 self
1942 }
1943
1944 pub fn data(mut self, data: impl Into<String>) -> Self {
1946 self.data = Some(data.into());
1947 self
1948 }
1949
1950 pub fn id(mut self, id: impl Into<String>) -> Self {
1952 self.id = Some(id.into());
1953 self
1954 }
1955
1956 pub fn retry(mut self, retry: u32) -> Self {
1958 self.retry = Some(retry);
1959 self
1960 }
1961
1962 pub fn delay(mut self, delay: Delay) -> Self {
1964 self.delay = Some(delay);
1965 self
1966 }
1967}
1968
1969#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
1984#[serde(rename_all = "camelCase")]
1985pub struct HttpSseResponse {
1986 #[serde(skip_serializing_if = "Option::is_none")]
1987 pub status_code: Option<u16>,
1988
1989 #[serde(skip_serializing_if = "Option::is_none")]
1990 pub headers: Option<HashMap<String, Vec<String>>>,
1991
1992 #[serde(skip_serializing_if = "Option::is_none")]
1993 pub events: Option<Vec<SseEvent>>,
1994
1995 #[serde(skip_serializing_if = "Option::is_none")]
1996 pub close_connection: Option<bool>,
1997
1998 #[serde(skip_serializing_if = "Option::is_none")]
1999 pub delay: Option<Delay>,
2000
2001 #[serde(skip_serializing_if = "Option::is_none")]
2002 pub template_type: Option<String>,
2003
2004 #[serde(skip_serializing_if = "Option::is_none")]
2005 pub primary: Option<bool>,
2006
2007 #[serde(flatten, default)]
2010 pub extra: Extra,
2011}
2012
2013impl HttpSseResponse {
2014 pub fn new() -> Self {
2016 Self::default()
2017 }
2018
2019 pub fn status_code(mut self, code: u16) -> Self {
2021 self.status_code = Some(code);
2022 self
2023 }
2024
2025 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
2027 let headers = self.headers.get_or_insert_with(HashMap::new);
2028 headers.entry(key.into()).or_default().push(value.into());
2029 self
2030 }
2031
2032 pub fn event(mut self, event: SseEvent) -> Self {
2034 self.events.get_or_insert_with(Vec::new).push(event);
2035 self
2036 }
2037
2038 pub fn events(mut self, events: Vec<SseEvent>) -> Self {
2040 self.events = Some(events);
2041 self
2042 }
2043
2044 pub fn close_connection(mut self, close: bool) -> Self {
2046 self.close_connection = Some(close);
2047 self
2048 }
2049
2050 pub fn delay(mut self, delay: Delay) -> Self {
2052 self.delay = Some(delay);
2053 self
2054 }
2055
2056 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
2058 self.template_type = Some(template_type.into());
2059 self
2060 }
2061
2062 pub fn primary(mut self, primary: bool) -> Self {
2064 self.primary = Some(primary);
2065 self
2066 }
2067}
2068
2069#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2078#[serde(rename_all = "camelCase")]
2079pub struct GraphqlSubscriptionFilter {
2080 pub query: String,
2081
2082 #[serde(skip_serializing_if = "Option::is_none")]
2083 pub operation_name: Option<String>,
2084
2085 #[serde(skip_serializing_if = "Option::is_none")]
2086 pub variables_schema: Option<String>,
2087
2088 #[serde(skip_serializing_if = "Option::is_none")]
2090 pub selection_set_match_type: Option<String>,
2091
2092 #[serde(skip_serializing_if = "Option::is_none")]
2093 pub fields: Option<Vec<String>>,
2094
2095 #[serde(flatten, default)]
2096 pub extra: Extra,
2097}
2098
2099impl GraphqlSubscriptionFilter {
2100 pub fn new(query: impl Into<String>) -> Self {
2102 Self { query: query.into(), ..Default::default() }
2103 }
2104
2105 pub fn operation_name(mut self, operation_name: impl Into<String>) -> Self {
2107 self.operation_name = Some(operation_name.into());
2108 self
2109 }
2110
2111 pub fn variables_schema(mut self, variables_schema: impl Into<String>) -> Self {
2113 self.variables_schema = Some(variables_schema.into());
2114 self
2115 }
2116
2117 pub fn selection_set_match_type(mut self, selection_set_match_type: impl Into<String>) -> Self {
2119 self.selection_set_match_type = Some(selection_set_match_type.into());
2120 self
2121 }
2122
2123 pub fn fields(mut self, fields: Vec<String>) -> Self {
2125 self.fields = Some(fields);
2126 self
2127 }
2128}
2129#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2136#[serde(rename_all = "camelCase")]
2137pub struct WebSocketMessage {
2138 #[serde(skip_serializing_if = "Option::is_none")]
2139 pub text: Option<String>,
2140
2141 #[serde(skip_serializing_if = "Option::is_none")]
2142 pub binary: Option<String>,
2143
2144 #[serde(skip_serializing_if = "Option::is_none")]
2145 pub delay: Option<Delay>,
2146}
2147
2148impl WebSocketMessage {
2149 pub fn text(text: impl Into<String>) -> Self {
2151 Self {
2152 text: Some(text.into()),
2153 binary: None,
2154 delay: None,
2155 }
2156 }
2157
2158 pub fn binary(data: impl AsRef<[u8]>) -> Self {
2160 Self {
2161 text: None,
2162 binary: Some(BASE64.encode(data.as_ref())),
2163 delay: None,
2164 }
2165 }
2166
2167 pub fn binary_base64(base64: impl Into<String>) -> Self {
2169 Self {
2170 text: None,
2171 binary: Some(base64.into()),
2172 delay: None,
2173 }
2174 }
2175
2176 pub fn delay(mut self, delay: Delay) -> Self {
2178 self.delay = Some(delay);
2179 self
2180 }
2181}
2182
2183#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2190#[serde(rename_all = "camelCase")]
2191pub struct WebSocketMatcher {
2192 #[serde(skip_serializing_if = "Option::is_none")]
2194 pub frame_type: Option<String>,
2195
2196 #[serde(skip_serializing_if = "Option::is_none")]
2198 pub text_matcher: Option<String>,
2199
2200 #[serde(skip_serializing_if = "Option::is_none")]
2202 pub responses: Option<Vec<WebSocketMessage>>,
2203
2204 #[serde(flatten, default)]
2206 pub extra: Extra,
2207}
2208
2209impl WebSocketMatcher {
2210 pub fn new() -> Self {
2212 Self::default()
2213 }
2214
2215 pub fn frame_type(mut self, frame_type: impl Into<String>) -> Self {
2217 self.frame_type = Some(frame_type.into());
2218 self
2219 }
2220
2221 pub fn text_matcher(mut self, text_matcher: impl Into<String>) -> Self {
2223 self.text_matcher = Some(text_matcher.into());
2224 self
2225 }
2226
2227 pub fn response(mut self, response: WebSocketMessage) -> Self {
2229 self.responses.get_or_insert_with(Vec::new).push(response);
2230 self
2231 }
2232
2233 pub fn responses(mut self, responses: Vec<WebSocketMessage>) -> Self {
2235 self.responses = Some(responses);
2236 self
2237 }
2238}
2239
2240#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2259#[serde(rename_all = "camelCase")]
2260pub struct HttpWebSocketResponse {
2261 #[serde(skip_serializing_if = "Option::is_none")]
2262 pub subprotocol: Option<String>,
2263
2264 #[serde(skip_serializing_if = "Option::is_none")]
2265 pub messages: Option<Vec<WebSocketMessage>>,
2266
2267 #[serde(skip_serializing_if = "Option::is_none")]
2270 pub matchers: Option<Vec<WebSocketMatcher>>,
2271
2272 #[serde(skip_serializing_if = "Option::is_none")]
2273 pub close_connection: Option<bool>,
2274
2275 #[serde(skip_serializing_if = "Option::is_none")]
2276 pub delay: Option<Delay>,
2277
2278 #[serde(skip_serializing_if = "Option::is_none")]
2279 pub template_type: Option<String>,
2280
2281 #[serde(skip_serializing_if = "Option::is_none")]
2283 pub graphql_subscription_filter: Option<GraphqlSubscriptionFilter>,
2284
2285 #[serde(skip_serializing_if = "Option::is_none")]
2286 pub primary: Option<bool>,
2287
2288 #[serde(flatten, default)]
2291 pub extra: Extra,
2292}
2293
2294impl HttpWebSocketResponse {
2295 pub fn new() -> Self {
2297 Self::default()
2298 }
2299
2300 pub fn matcher(mut self, matcher: WebSocketMatcher) -> Self {
2302 self.matchers.get_or_insert_with(Vec::new).push(matcher);
2303 self
2304 }
2305
2306 pub fn matchers(mut self, matchers: Vec<WebSocketMatcher>) -> Self {
2308 self.matchers = Some(matchers);
2309 self
2310 }
2311
2312 pub fn subprotocol(mut self, subprotocol: impl Into<String>) -> Self {
2314 self.subprotocol = Some(subprotocol.into());
2315 self
2316 }
2317
2318 pub fn message(mut self, message: WebSocketMessage) -> Self {
2320 self.messages.get_or_insert_with(Vec::new).push(message);
2321 self
2322 }
2323
2324 pub fn messages(mut self, messages: Vec<WebSocketMessage>) -> Self {
2326 self.messages = Some(messages);
2327 self
2328 }
2329
2330 pub fn close_connection(mut self, close: bool) -> Self {
2332 self.close_connection = Some(close);
2333 self
2334 }
2335
2336 pub fn delay(mut self, delay: Delay) -> Self {
2338 self.delay = Some(delay);
2339 self
2340 }
2341
2342 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
2344 self.template_type = Some(template_type.into());
2345 self
2346 }
2347
2348 pub fn graphql_subscription_filter(mut self, filter: GraphqlSubscriptionFilter) -> Self {
2350 self.graphql_subscription_filter = Some(filter);
2351 self
2352 }
2353
2354 pub fn primary(mut self, primary: bool) -> Self {
2356 self.primary = Some(primary);
2357 self
2358 }
2359}
2360
2361#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2367#[serde(rename_all = "camelCase")]
2368pub struct DnsRecord {
2369 #[serde(skip_serializing_if = "Option::is_none")]
2370 pub name: Option<String>,
2371
2372 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
2373 pub record_type: Option<String>,
2374
2375 #[serde(skip_serializing_if = "Option::is_none")]
2376 pub dns_class: Option<String>,
2377
2378 #[serde(skip_serializing_if = "Option::is_none")]
2379 pub ttl: Option<u32>,
2380
2381 #[serde(skip_serializing_if = "Option::is_none")]
2382 pub value: Option<String>,
2383
2384 #[serde(skip_serializing_if = "Option::is_none")]
2385 pub priority: Option<u32>,
2386
2387 #[serde(skip_serializing_if = "Option::is_none")]
2388 pub weight: Option<u32>,
2389
2390 #[serde(skip_serializing_if = "Option::is_none")]
2391 pub port: Option<u16>,
2392}
2393
2394impl DnsRecord {
2395 pub fn new() -> Self {
2397 Self::default()
2398 }
2399
2400 pub fn a(name: impl Into<String>, ip: impl Into<String>) -> Self {
2402 Self::new().name(name).record_type("A").value(ip)
2403 }
2404
2405 pub fn aaaa(name: impl Into<String>, ip: impl Into<String>) -> Self {
2407 Self::new().name(name).record_type("AAAA").value(ip)
2408 }
2409
2410 pub fn cname(name: impl Into<String>, target: impl Into<String>) -> Self {
2412 Self::new().name(name).record_type("CNAME").value(target)
2413 }
2414
2415 pub fn txt(name: impl Into<String>, text: impl Into<String>) -> Self {
2417 Self::new().name(name).record_type("TXT").value(text)
2418 }
2419
2420 pub fn name(mut self, name: impl Into<String>) -> Self {
2422 self.name = Some(name.into());
2423 self
2424 }
2425
2426 pub fn record_type(mut self, record_type: impl Into<String>) -> Self {
2428 self.record_type = Some(record_type.into());
2429 self
2430 }
2431
2432 pub fn dns_class(mut self, dns_class: impl Into<String>) -> Self {
2434 self.dns_class = Some(dns_class.into());
2435 self
2436 }
2437
2438 pub fn ttl(mut self, ttl: u32) -> Self {
2440 self.ttl = Some(ttl);
2441 self
2442 }
2443
2444 pub fn value(mut self, value: impl Into<String>) -> Self {
2446 self.value = Some(value.into());
2447 self
2448 }
2449
2450 pub fn priority(mut self, priority: u32) -> Self {
2452 self.priority = Some(priority);
2453 self
2454 }
2455
2456 pub fn weight(mut self, weight: u32) -> Self {
2458 self.weight = Some(weight);
2459 self
2460 }
2461
2462 pub fn port(mut self, port: u16) -> Self {
2464 self.port = Some(port);
2465 self
2466 }
2467}
2468
2469#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2482#[serde(rename_all = "camelCase")]
2483pub struct DnsResponse {
2484 #[serde(skip_serializing_if = "Option::is_none")]
2485 pub answer_records: Option<Vec<DnsRecord>>,
2486
2487 #[serde(skip_serializing_if = "Option::is_none")]
2488 pub authority_records: Option<Vec<DnsRecord>>,
2489
2490 #[serde(skip_serializing_if = "Option::is_none")]
2491 pub additional_records: Option<Vec<DnsRecord>>,
2492
2493 #[serde(skip_serializing_if = "Option::is_none")]
2494 pub response_code: Option<String>,
2495
2496 #[serde(skip_serializing_if = "Option::is_none")]
2497 pub delay: Option<Delay>,
2498
2499 #[serde(skip_serializing_if = "Option::is_none")]
2500 pub primary: Option<bool>,
2501
2502 #[serde(flatten, default)]
2505 pub extra: Extra,
2506}
2507
2508impl DnsResponse {
2509 pub fn new() -> Self {
2511 Self::default()
2512 }
2513
2514 pub fn answer_record(mut self, record: DnsRecord) -> Self {
2516 self.answer_records
2517 .get_or_insert_with(Vec::new)
2518 .push(record);
2519 self
2520 }
2521
2522 pub fn answer_records(mut self, records: Vec<DnsRecord>) -> Self {
2524 self.answer_records = Some(records);
2525 self
2526 }
2527
2528 pub fn authority_record(mut self, record: DnsRecord) -> Self {
2530 self.authority_records
2531 .get_or_insert_with(Vec::new)
2532 .push(record);
2533 self
2534 }
2535
2536 pub fn additional_record(mut self, record: DnsRecord) -> Self {
2538 self.additional_records
2539 .get_or_insert_with(Vec::new)
2540 .push(record);
2541 self
2542 }
2543
2544 pub fn response_code(mut self, code: impl Into<String>) -> Self {
2546 self.response_code = Some(code.into());
2547 self
2548 }
2549
2550 pub fn delay(mut self, delay: Delay) -> Self {
2552 self.delay = Some(delay);
2553 self
2554 }
2555
2556 pub fn primary(mut self, primary: bool) -> Self {
2558 self.primary = Some(primary);
2559 self
2560 }
2561}
2562
2563#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2580#[serde(rename_all = "camelCase")]
2581pub struct BinaryResponse {
2582 #[serde(skip_serializing_if = "Option::is_none")]
2583 pub binary_data: Option<String>,
2584
2585 #[serde(skip_serializing_if = "Option::is_none")]
2586 pub delay: Option<Delay>,
2587
2588 #[serde(skip_serializing_if = "Option::is_none")]
2589 pub primary: Option<bool>,
2590
2591 #[serde(flatten, default)]
2594 pub extra: Extra,
2595}
2596
2597impl BinaryResponse {
2598 pub fn new() -> Self {
2600 Self::default()
2601 }
2602
2603 pub fn from_bytes(data: impl AsRef<[u8]>) -> Self {
2605 Self {
2606 binary_data: Some(BASE64.encode(data.as_ref())),
2607 delay: None,
2608 ..Default::default()
2609 }
2610 }
2611
2612 pub fn from_base64(base64: impl Into<String>) -> Self {
2614 Self {
2615 binary_data: Some(base64.into()),
2616 delay: None,
2617 ..Default::default()
2618 }
2619 }
2620
2621 pub fn binary_data(mut self, data: impl AsRef<[u8]>) -> Self {
2623 self.binary_data = Some(BASE64.encode(data.as_ref()));
2624 self
2625 }
2626
2627 pub fn delay(mut self, delay: Delay) -> Self {
2629 self.delay = Some(delay);
2630 self
2631 }
2632
2633 pub fn primary(mut self, primary: bool) -> Self {
2635 self.primary = Some(primary);
2636 self
2637 }
2638}
2639
2640#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2646#[serde(rename_all = "camelCase")]
2647pub struct GrpcStreamMessage {
2648 #[serde(skip_serializing_if = "Option::is_none")]
2649 pub json: Option<String>,
2650
2651 #[serde(skip_serializing_if = "Option::is_none")]
2654 pub template_type: Option<String>,
2655
2656 #[serde(skip_serializing_if = "Option::is_none")]
2657 pub delay: Option<Delay>,
2658}
2659
2660impl GrpcStreamMessage {
2661 pub fn json(json: impl Into<String>) -> Self {
2663 Self {
2664 json: Some(json.into()),
2665 template_type: None,
2666 delay: None,
2667 }
2668 }
2669
2670 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
2672 self.template_type = Some(template_type.into());
2673 self
2674 }
2675
2676 pub fn delay(mut self, delay: Delay) -> Self {
2678 self.delay = Some(delay);
2679 self
2680 }
2681}
2682
2683#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2697#[serde(rename_all = "camelCase")]
2698pub struct GrpcStreamResponse {
2699 #[serde(skip_serializing_if = "Option::is_none")]
2700 pub status_name: Option<String>,
2701
2702 #[serde(skip_serializing_if = "Option::is_none")]
2703 pub status_message: Option<String>,
2704
2705 #[serde(skip_serializing_if = "Option::is_none")]
2706 pub headers: Option<HashMap<String, Vec<String>>>,
2707
2708 #[serde(skip_serializing_if = "Option::is_none")]
2709 pub messages: Option<Vec<GrpcStreamMessage>>,
2710
2711 #[serde(skip_serializing_if = "Option::is_none")]
2712 pub close_connection: Option<bool>,
2713
2714 #[serde(skip_serializing_if = "Option::is_none")]
2715 pub delay: Option<Delay>,
2716
2717 #[serde(skip_serializing_if = "Option::is_none")]
2718 pub primary: Option<bool>,
2719
2720 #[serde(flatten, default)]
2723 pub extra: Extra,
2724}
2725
2726impl GrpcStreamResponse {
2727 pub fn new() -> Self {
2729 Self::default()
2730 }
2731
2732 pub fn status_name(mut self, status_name: impl Into<String>) -> Self {
2734 self.status_name = Some(status_name.into());
2735 self
2736 }
2737
2738 pub fn status_message(mut self, status_message: impl Into<String>) -> Self {
2740 self.status_message = Some(status_message.into());
2741 self
2742 }
2743
2744 pub fn header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
2746 let headers = self.headers.get_or_insert_with(HashMap::new);
2747 headers.entry(key.into()).or_default().push(value.into());
2748 self
2749 }
2750
2751 pub fn message(mut self, message: GrpcStreamMessage) -> Self {
2753 self.messages.get_or_insert_with(Vec::new).push(message);
2754 self
2755 }
2756
2757 pub fn messages(mut self, messages: Vec<GrpcStreamMessage>) -> Self {
2759 self.messages = Some(messages);
2760 self
2761 }
2762
2763 pub fn close_connection(mut self, close: bool) -> Self {
2765 self.close_connection = Some(close);
2766 self
2767 }
2768
2769 pub fn delay(mut self, delay: Delay) -> Self {
2771 self.delay = Some(delay);
2772 self
2773 }
2774
2775 pub fn primary(mut self, primary: bool) -> Self {
2777 self.primary = Some(primary);
2778 self
2779 }
2780}
2781
2782#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
2803#[serde(rename_all = "camelCase")]
2804pub struct OpenApiExpectation {
2805 pub spec_url_or_payload: String,
2806
2807 #[serde(skip_serializing_if = "Option::is_none")]
2808 pub operations_and_responses: Option<HashMap<String, String>>,
2809
2810 #[serde(skip_serializing_if = "Option::is_none")]
2811 pub context_path_prefix: Option<String>,
2812}
2813
2814impl OpenApiExpectation {
2815 pub fn new(spec_url_or_payload: impl Into<String>) -> Self {
2818 Self {
2819 spec_url_or_payload: spec_url_or_payload.into(),
2820 operations_and_responses: None,
2821 context_path_prefix: None,
2822 }
2823 }
2824
2825 pub fn operation(
2830 mut self,
2831 operation_id: impl Into<String>,
2832 status_code: impl Into<String>,
2833 ) -> Self {
2834 self.operations_and_responses
2835 .get_or_insert_with(HashMap::new)
2836 .insert(operation_id.into(), status_code.into());
2837 self
2838 }
2839
2840 pub fn operations_and_responses(mut self, map: HashMap<String, String>) -> Self {
2842 self.operations_and_responses = Some(map);
2843 self
2844 }
2845
2846 pub fn context_path_prefix(mut self, prefix: impl Into<String>) -> Self {
2848 self.context_path_prefix = Some(prefix.into());
2849 self
2850 }
2851}
2852
2853#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2859#[serde(rename_all = "camelCase")]
2860pub struct Delay {
2861 pub time_unit: String,
2862 pub value: u64,
2863}
2864
2865impl Delay {
2866 pub fn milliseconds(value: u64) -> Self {
2868 Self {
2869 time_unit: "MILLISECONDS".to_string(),
2870 value,
2871 }
2872 }
2873
2874 pub fn seconds(value: u64) -> Self {
2876 Self {
2877 time_unit: "SECONDS".to_string(),
2878 value,
2879 }
2880 }
2881}
2882
2883#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2889#[serde(rename_all = "camelCase")]
2890pub struct Times {
2891 #[serde(skip_serializing_if = "Option::is_none")]
2892 pub remaining_times: Option<u32>,
2893
2894 #[serde(default)]
2895 pub unlimited: bool,
2896}
2897
2898impl Times {
2899 pub fn unlimited() -> Self {
2901 Self {
2902 remaining_times: None,
2903 unlimited: true,
2904 }
2905 }
2906
2907 pub fn exactly(n: u32) -> Self {
2909 Self {
2910 remaining_times: Some(n),
2911 unlimited: false,
2912 }
2913 }
2914
2915 pub fn once() -> Self {
2917 Self::exactly(1)
2918 }
2919}
2920
2921#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2927#[serde(rename_all = "camelCase")]
2928pub struct TimeToLive {
2929 #[serde(skip_serializing_if = "Option::is_none")]
2930 pub time_unit: Option<String>,
2931
2932 #[serde(skip_serializing_if = "Option::is_none")]
2933 pub time_to_live: Option<u64>,
2934
2935 #[serde(default)]
2936 pub unlimited: bool,
2937}
2938
2939impl TimeToLive {
2940 pub fn unlimited() -> Self {
2942 Self {
2943 time_unit: None,
2944 time_to_live: None,
2945 unlimited: true,
2946 }
2947 }
2948
2949 pub fn seconds(seconds: u64) -> Self {
2951 Self {
2952 time_unit: Some("SECONDS".to_string()),
2953 time_to_live: Some(seconds),
2954 unlimited: false,
2955 }
2956 }
2957
2958 pub fn milliseconds(millis: u64) -> Self {
2960 Self {
2961 time_unit: Some("MILLISECONDS".to_string()),
2962 time_to_live: Some(millis),
2963 unlimited: false,
2964 }
2965 }
2966}
2967
2968#[derive(Debug, Clone, Deserialize, PartialEq)]
2980#[serde(rename_all = "camelCase")]
2981pub struct VerificationTimes {
2982 pub at_least: Option<u32>,
2983 pub at_most: Option<u32>,
2984}
2985
2986impl Serialize for VerificationTimes {
2987 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
2988 where
2989 S: serde::Serializer,
2990 {
2991 use serde::ser::SerializeStruct;
2992 let mut state = serializer.serialize_struct("VerificationTimes", 2)?;
2993 state.serialize_field("atLeast", &self.at_least.map_or(-1_i64, i64::from))?;
2994 state.serialize_field("atMost", &self.at_most.map_or(-1_i64, i64::from))?;
2995 state.end()
2996 }
2997}
2998
2999impl VerificationTimes {
3000 pub fn at_least(n: u32) -> Self {
3002 Self {
3003 at_least: Some(n),
3004 at_most: None,
3005 }
3006 }
3007
3008 pub fn at_most(n: u32) -> Self {
3010 Self {
3011 at_least: None,
3012 at_most: Some(n),
3013 }
3014 }
3015
3016 pub fn exactly(n: u32) -> Self {
3018 Self {
3019 at_least: Some(n),
3020 at_most: Some(n),
3021 }
3022 }
3023
3024 pub fn between(min: u32, max: u32) -> Self {
3026 Self {
3027 at_least: Some(min),
3028 at_most: Some(max),
3029 }
3030 }
3031}
3032
3033#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
3047#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3048pub enum ResponseMode {
3049 Sequential,
3051 Random,
3053 Weighted,
3055 Switch,
3057}
3058
3059#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
3062#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
3063pub enum CrossProtocolTrigger {
3064 DnsQuery,
3066 WebsocketConnect,
3068 GrpcRequest,
3070 HttpRequest,
3072}
3073
3074#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3092#[serde(rename_all = "camelCase")]
3093pub struct CrossProtocolScenario {
3094 pub trigger: CrossProtocolTrigger,
3095
3096 #[serde(skip_serializing_if = "Option::is_none")]
3097 pub match_pattern: Option<String>,
3098
3099 pub scenario_name: String,
3100
3101 pub target_state: String,
3102}
3103
3104impl CrossProtocolScenario {
3105 pub fn new(
3108 trigger: CrossProtocolTrigger,
3109 scenario_name: impl Into<String>,
3110 target_state: impl Into<String>,
3111 ) -> Self {
3112 Self {
3113 trigger,
3114 match_pattern: None,
3115 scenario_name: scenario_name.into(),
3116 target_state: target_state.into(),
3117 }
3118 }
3119
3120 pub fn match_pattern(mut self, pattern: impl Into<String>) -> Self {
3122 self.match_pattern = Some(pattern.into());
3123 self
3124 }
3125}
3126
3127#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3134#[serde(rename_all = "camelCase")]
3135pub struct ConnectionOptions {
3136 #[serde(skip_serializing_if = "Option::is_none")]
3137 pub suppress_content_length_header: Option<bool>,
3138
3139 #[serde(skip_serializing_if = "Option::is_none")]
3140 pub content_length_header_override: Option<i64>,
3141
3142 #[serde(skip_serializing_if = "Option::is_none")]
3143 pub suppress_connection_header: Option<bool>,
3144
3145 #[serde(skip_serializing_if = "Option::is_none")]
3146 pub chunk_size: Option<i64>,
3147
3148 #[serde(skip_serializing_if = "Option::is_none")]
3149 pub chunk_delay: Option<Delay>,
3150
3151 #[serde(skip_serializing_if = "Option::is_none")]
3152 pub keep_alive_override: Option<bool>,
3153
3154 #[serde(skip_serializing_if = "Option::is_none")]
3155 pub close_socket: Option<bool>,
3156
3157 #[serde(skip_serializing_if = "Option::is_none")]
3158 pub close_socket_delay: Option<Delay>,
3159
3160 #[serde(flatten, default)]
3161 pub extra: Extra,
3162}
3163
3164impl ConnectionOptions {
3165 pub fn new() -> Self {
3167 Self::default()
3168 }
3169}
3170
3171#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3174#[serde(rename_all = "camelCase")]
3175pub struct RecoverAfter {
3176 #[serde(skip_serializing_if = "Option::is_none")]
3177 pub fail_times: Option<i64>,
3178
3179 #[serde(skip_serializing_if = "Option::is_none")]
3180 pub fail_response: Option<serde_json::Value>,
3181
3182 #[serde(skip_serializing_if = "Option::is_none")]
3183 pub idempotency_header: Option<String>,
3184
3185 #[serde(flatten, default)]
3186 pub extra: Extra,
3187}
3188
3189impl RecoverAfter {
3190 pub fn new() -> Self {
3192 Self::default()
3193 }
3194}
3195
3196#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3202#[serde(rename_all = "camelCase")]
3203pub struct RateLimit {
3204 #[serde(skip_serializing_if = "Option::is_none")]
3205 pub name: Option<String>,
3206
3207 #[serde(skip_serializing_if = "Option::is_none")]
3209 pub algorithm: Option<String>,
3210
3211 #[serde(skip_serializing_if = "Option::is_none")]
3212 pub limit: Option<i64>,
3213
3214 #[serde(skip_serializing_if = "Option::is_none")]
3215 pub window_millis: Option<i64>,
3216
3217 #[serde(skip_serializing_if = "Option::is_none")]
3218 pub burst: Option<i64>,
3219
3220 #[serde(skip_serializing_if = "Option::is_none")]
3221 pub refill_per_second: Option<f64>,
3222
3223 #[serde(skip_serializing_if = "Option::is_none")]
3224 pub error_status: Option<i32>,
3225
3226 #[serde(skip_serializing_if = "Option::is_none")]
3227 pub retry_after: Option<String>,
3228
3229 #[serde(flatten, default)]
3230 pub extra: Extra,
3231}
3232
3233impl RateLimit {
3234 pub fn new() -> Self {
3236 Self::default()
3237 }
3238
3239 pub fn fixed_window(limit: i64, window_millis: i64) -> Self {
3241 Self {
3242 algorithm: Some("fixed_window".to_string()),
3243 limit: Some(limit),
3244 window_millis: Some(window_millis),
3245 ..Default::default()
3246 }
3247 }
3248
3249 pub fn token_bucket(burst: i64, refill_per_second: f64) -> Self {
3252 Self {
3253 algorithm: Some("token_bucket".to_string()),
3254 burst: Some(burst),
3255 refill_per_second: Some(refill_per_second),
3256 ..Default::default()
3257 }
3258 }
3259}
3260
3261#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3268#[serde(rename_all = "camelCase")]
3269pub struct HttpForwardWithFallback {
3270 pub http_forward: HttpForward,
3271
3272 pub fallback_response: HttpResponse,
3273
3274 #[serde(skip_serializing_if = "Option::is_none")]
3275 pub fallback_on_status_codes: Option<Vec<i32>>,
3276
3277 #[serde(skip_serializing_if = "Option::is_none")]
3278 pub fallback_on_timeout: Option<bool>,
3279
3280 #[serde(skip_serializing_if = "Option::is_none")]
3281 pub delay: Option<Delay>,
3282
3283 #[serde(skip_serializing_if = "Option::is_none")]
3284 pub primary: Option<bool>,
3285
3286 #[serde(flatten, default)]
3287 pub extra: Extra,
3288}
3289
3290impl HttpForwardWithFallback {
3291 pub fn new(http_forward: HttpForward, fallback_response: HttpResponse) -> Self {
3293 Self {
3294 http_forward,
3295 fallback_response,
3296 fallback_on_status_codes: None,
3297 fallback_on_timeout: None,
3298 delay: None,
3299 primary: None,
3300 extra: Extra::new(),
3301 }
3302 }
3303
3304 pub fn fallback_on_status_codes(mut self, codes: Vec<i32>) -> Self {
3306 self.fallback_on_status_codes = Some(codes);
3307 self
3308 }
3309
3310 pub fn fallback_on_timeout(mut self, fallback: bool) -> Self {
3312 self.fallback_on_timeout = Some(fallback);
3313 self
3314 }
3315}
3316
3317#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3320#[serde(rename_all = "camelCase")]
3321pub struct HttpForwardValidateAction {
3322 pub spec_url_or_payload: String,
3323
3324 pub host: String,
3325
3326 #[serde(skip_serializing_if = "Option::is_none")]
3327 pub port: Option<u16>,
3328
3329 #[serde(skip_serializing_if = "Option::is_none")]
3330 pub scheme: Option<String>,
3331
3332 #[serde(skip_serializing_if = "Option::is_none")]
3333 pub validate_request: Option<bool>,
3334
3335 #[serde(skip_serializing_if = "Option::is_none")]
3336 pub validate_response: Option<bool>,
3337
3338 #[serde(skip_serializing_if = "Option::is_none")]
3340 pub validation_mode: Option<String>,
3341
3342 #[serde(skip_serializing_if = "Option::is_none")]
3343 pub delay: Option<Delay>,
3344
3345 #[serde(skip_serializing_if = "Option::is_none")]
3346 pub primary: Option<bool>,
3347
3348 #[serde(flatten, default)]
3349 pub extra: Extra,
3350}
3351
3352impl HttpForwardValidateAction {
3353 pub fn new(spec_url_or_payload: impl Into<String>, host: impl Into<String>) -> Self {
3355 Self {
3356 spec_url_or_payload: spec_url_or_payload.into(),
3357 host: host.into(),
3358 port: None,
3359 scheme: None,
3360 validate_request: None,
3361 validate_response: None,
3362 validation_mode: None,
3363 delay: None,
3364 primary: None,
3365 extra: Extra::new(),
3366 }
3367 }
3368}
3369
3370#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3380#[serde(rename_all = "camelCase")]
3381pub struct HttpOverrideForwardedRequest {
3382 #[serde(skip_serializing_if = "Option::is_none")]
3383 pub delay: Option<Delay>,
3384
3385 #[serde(skip_serializing_if = "Option::is_none")]
3386 pub request_override: Option<HttpRequest>,
3387
3388 #[serde(skip_serializing_if = "Option::is_none")]
3389 pub request_modifier: Option<serde_json::Value>,
3390
3391 #[serde(skip_serializing_if = "Option::is_none")]
3392 pub response_override: Option<HttpResponse>,
3393
3394 #[serde(skip_serializing_if = "Option::is_none")]
3395 pub response_modifier: Option<serde_json::Value>,
3396
3397 #[serde(skip_serializing_if = "Option::is_none")]
3398 pub response_template: Option<HttpTemplate>,
3399
3400 #[serde(skip_serializing_if = "Option::is_none")]
3402 pub http_request: Option<HttpRequest>,
3403
3404 #[serde(skip_serializing_if = "Option::is_none")]
3406 pub http_response: Option<HttpResponse>,
3407
3408 #[serde(skip_serializing_if = "Option::is_none")]
3409 pub primary: Option<bool>,
3410
3411 #[serde(flatten, default)]
3412 pub extra: Extra,
3413}
3414
3415impl HttpOverrideForwardedRequest {
3416 pub fn new() -> Self {
3418 Self::default()
3419 }
3420
3421 pub fn request_override(mut self, request: HttpRequest) -> Self {
3423 self.request_override = Some(request);
3424 self
3425 }
3426
3427 pub fn response_override(mut self, response: HttpResponse) -> Self {
3429 self.response_override = Some(response);
3430 self
3431 }
3432}
3433
3434#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3442#[serde(rename_all = "camelCase")]
3443pub struct ExpectationAction {
3444 #[serde(skip_serializing_if = "Option::is_none")]
3445 pub http_request: Option<HttpRequest>,
3446
3447 #[serde(skip_serializing_if = "Option::is_none")]
3448 pub http_class_callback: Option<HttpClassCallback>,
3449
3450 #[serde(skip_serializing_if = "Option::is_none")]
3451 pub http_object_callback: Option<HttpObjectCallback>,
3452
3453 #[serde(skip_serializing_if = "Option::is_none")]
3454 pub delay: Option<Delay>,
3455
3456 #[serde(skip_serializing_if = "Option::is_none")]
3457 pub blocking: Option<bool>,
3458
3459 #[serde(skip_serializing_if = "Option::is_none")]
3460 pub timeout: Option<Delay>,
3461
3462 #[serde(skip_serializing_if = "Option::is_none")]
3464 pub failure_policy: Option<String>,
3465
3466 #[serde(flatten, default)]
3467 pub extra: Extra,
3468}
3469
3470impl ExpectationAction {
3471 pub fn request(request: HttpRequest) -> Self {
3473 Self {
3474 http_request: Some(request),
3475 ..Default::default()
3476 }
3477 }
3478
3479 pub fn class_callback(callback: HttpClassCallback) -> Self {
3481 Self {
3482 http_class_callback: Some(callback),
3483 ..Default::default()
3484 }
3485 }
3486}
3487
3488#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
3491#[serde(rename_all = "camelCase")]
3492pub struct CaptureRule {
3493 pub source: String,
3496
3497 pub expression: String,
3498
3499 pub into: String,
3500
3501 #[serde(flatten, default)]
3502 pub extra: Extra,
3503}
3504
3505impl CaptureRule {
3506 pub fn new(
3509 source: impl Into<String>,
3510 expression: impl Into<String>,
3511 into: impl Into<String>,
3512 ) -> Self {
3513 Self {
3514 source: source.into(),
3515 expression: expression.into(),
3516 into: into.into(),
3517 extra: Extra::new(),
3518 }
3519 }
3520}
3521
3522#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3525#[serde(rename_all = "camelCase")]
3526pub struct ExpectationStep {
3527 #[serde(skip_serializing_if = "Option::is_none")]
3528 pub http_request: Option<HttpRequest>,
3529
3530 #[serde(skip_serializing_if = "Option::is_none")]
3531 pub http_class_callback: Option<HttpClassCallback>,
3532
3533 #[serde(skip_serializing_if = "Option::is_none")]
3534 pub http_object_callback: Option<HttpObjectCallback>,
3535
3536 #[serde(skip_serializing_if = "Option::is_none")]
3537 pub http_forward: Option<HttpForward>,
3538
3539 #[serde(skip_serializing_if = "Option::is_none")]
3540 pub http_override_forwarded_request: Option<HttpOverrideForwardedRequest>,
3541
3542 #[serde(skip_serializing_if = "Option::is_none")]
3543 pub http_response: Option<HttpResponse>,
3544
3545 #[serde(skip_serializing_if = "Option::is_none")]
3546 pub http_error: Option<HttpError>,
3547
3548 #[serde(skip_serializing_if = "Option::is_none")]
3549 pub responder: Option<bool>,
3550
3551 #[serde(skip_serializing_if = "Option::is_none")]
3552 pub delay: Option<Delay>,
3553
3554 #[serde(skip_serializing_if = "Option::is_none")]
3555 pub blocking: Option<bool>,
3556
3557 #[serde(skip_serializing_if = "Option::is_none")]
3558 pub timeout: Option<Delay>,
3559
3560 #[serde(skip_serializing_if = "Option::is_none")]
3562 pub failure_policy: Option<String>,
3563
3564 #[serde(flatten, default)]
3565 pub extra: Extra,
3566}
3567
3568impl ExpectationStep {
3569 pub fn new() -> Self {
3571 Self::default()
3572 }
3573
3574 pub fn response(response: HttpResponse) -> Self {
3576 Self {
3577 http_response: Some(response),
3578 responder: Some(true),
3579 ..Default::default()
3580 }
3581 }
3582}
3583
3584#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3590#[serde(rename_all = "camelCase")]
3591pub struct GrpcBidiMessage {
3592 #[serde(skip_serializing_if = "Option::is_none")]
3593 pub json: Option<String>,
3594
3595 #[serde(skip_serializing_if = "Option::is_none")]
3597 pub template_type: Option<String>,
3598
3599 #[serde(skip_serializing_if = "Option::is_none")]
3600 pub delay: Option<Delay>,
3601
3602 #[serde(flatten, default)]
3603 pub extra: Extra,
3604}
3605
3606impl GrpcBidiMessage {
3607 pub fn json(json: impl Into<String>) -> Self {
3609 Self {
3610 json: Some(json.into()),
3611 ..Default::default()
3612 }
3613 }
3614}
3615
3616#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3619#[serde(rename_all = "camelCase")]
3620pub struct GrpcBidiRule {
3621 #[serde(skip_serializing_if = "Option::is_none")]
3622 pub match_json: Option<String>,
3623
3624 #[serde(skip_serializing_if = "Option::is_none")]
3625 pub responses: Option<Vec<GrpcBidiMessage>>,
3626
3627 #[serde(flatten, default)]
3628 pub extra: Extra,
3629}
3630
3631#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3633#[serde(rename_all = "camelCase")]
3634pub struct GrpcBidiResponse {
3635 #[serde(skip_serializing_if = "Option::is_none")]
3636 pub status_name: Option<String>,
3637
3638 #[serde(skip_serializing_if = "Option::is_none")]
3639 pub status_message: Option<String>,
3640
3641 #[serde(skip_serializing_if = "Option::is_none")]
3642 pub headers: Option<HashMap<String, Vec<String>>>,
3643
3644 #[serde(skip_serializing_if = "Option::is_none")]
3645 pub messages: Option<Vec<GrpcBidiMessage>>,
3646
3647 #[serde(skip_serializing_if = "Option::is_none")]
3648 pub rules: Option<Vec<GrpcBidiRule>>,
3649
3650 #[serde(skip_serializing_if = "Option::is_none")]
3651 pub close_connection: Option<bool>,
3652
3653 #[serde(skip_serializing_if = "Option::is_none")]
3654 pub delay: Option<Delay>,
3655
3656 #[serde(skip_serializing_if = "Option::is_none")]
3657 pub primary: Option<bool>,
3658
3659 #[serde(flatten, default)]
3660 pub extra: Extra,
3661}
3662
3663impl GrpcBidiResponse {
3664 pub fn new() -> Self {
3666 Self::default()
3667 }
3668
3669 pub fn message(mut self, message: GrpcBidiMessage) -> Self {
3671 self.messages.get_or_insert_with(Vec::new).push(message);
3672 self
3673 }
3674
3675 pub fn rule(mut self, rule: GrpcBidiRule) -> Self {
3677 self.rules.get_or_insert_with(Vec::new).push(rule);
3678 self
3679 }
3680}
3681
3682#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3688#[serde(rename_all = "camelCase")]
3689pub struct LlmToolCall {
3690 #[serde(skip_serializing_if = "Option::is_none")]
3691 pub id: Option<String>,
3692
3693 #[serde(skip_serializing_if = "Option::is_none")]
3694 pub name: Option<String>,
3695
3696 #[serde(skip_serializing_if = "Option::is_none")]
3697 pub arguments: Option<String>,
3698
3699 #[serde(flatten, default)]
3700 pub extra: Extra,
3701}
3702
3703#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3705#[serde(rename_all = "camelCase")]
3706pub struct LlmUsage {
3707 #[serde(skip_serializing_if = "Option::is_none")]
3708 pub input_tokens: Option<i64>,
3709
3710 #[serde(skip_serializing_if = "Option::is_none")]
3711 pub output_tokens: Option<i64>,
3712
3713 #[serde(skip_serializing_if = "Option::is_none")]
3714 pub cached_input_tokens: Option<i64>,
3715
3716 #[serde(skip_serializing_if = "Option::is_none")]
3717 pub cache_creation_tokens: Option<i64>,
3718
3719 #[serde(skip_serializing_if = "Option::is_none")]
3720 pub reasoning_tokens: Option<i64>,
3721
3722 #[serde(flatten, default)]
3723 pub extra: Extra,
3724}
3725
3726#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3728#[serde(rename_all = "camelCase")]
3729pub struct LlmStreamingPhysics {
3730 #[serde(skip_serializing_if = "Option::is_none")]
3731 pub time_to_first_token: Option<Delay>,
3732
3733 #[serde(skip_serializing_if = "Option::is_none")]
3734 pub tokens_per_second: Option<i32>,
3735
3736 #[serde(skip_serializing_if = "Option::is_none")]
3737 pub jitter: Option<f64>,
3738
3739 #[serde(skip_serializing_if = "Option::is_none")]
3740 pub seed: Option<i64>,
3741
3742 #[serde(skip_serializing_if = "Option::is_none")]
3743 pub subword_streaming: Option<bool>,
3744
3745 #[serde(flatten, default)]
3746 pub extra: Extra,
3747}
3748
3749#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3751#[serde(rename_all = "camelCase")]
3752pub struct LlmCompletion {
3753 #[serde(skip_serializing_if = "Option::is_none")]
3754 pub text: Option<String>,
3755
3756 #[serde(skip_serializing_if = "Option::is_none")]
3757 pub tool_calls: Option<Vec<LlmToolCall>>,
3758
3759 #[serde(skip_serializing_if = "Option::is_none")]
3760 pub stop_reason: Option<String>,
3761
3762 #[serde(skip_serializing_if = "Option::is_none")]
3763 pub usage: Option<LlmUsage>,
3764
3765 #[serde(skip_serializing_if = "Option::is_none")]
3766 pub streaming: Option<bool>,
3767
3768 #[serde(skip_serializing_if = "Option::is_none")]
3769 pub output_schema: Option<String>,
3770
3771 #[serde(skip_serializing_if = "Option::is_none")]
3772 pub enforce_output_schema: Option<bool>,
3773
3774 #[serde(skip_serializing_if = "Option::is_none")]
3775 pub tool_choice: Option<String>,
3776
3777 #[serde(skip_serializing_if = "Option::is_none")]
3778 pub reasoning_text: Option<String>,
3779
3780 #[serde(skip_serializing_if = "Option::is_none")]
3781 pub reasoning_signature: Option<String>,
3782
3783 #[serde(skip_serializing_if = "Option::is_none")]
3784 pub model: Option<String>,
3785
3786 #[serde(skip_serializing_if = "Option::is_none")]
3787 pub streaming_physics: Option<LlmStreamingPhysics>,
3788
3789 #[serde(flatten, default)]
3790 pub extra: Extra,
3791}
3792
3793impl LlmCompletion {
3794 pub fn text(text: impl Into<String>) -> Self {
3796 Self {
3797 text: Some(text.into()),
3798 ..Default::default()
3799 }
3800 }
3801}
3802
3803#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3808#[serde(rename_all = "camelCase")]
3809pub struct HttpLlmResponse {
3810 #[serde(skip_serializing_if = "Option::is_none")]
3811 pub delay: Option<Delay>,
3812
3813 #[serde(skip_serializing_if = "Option::is_none")]
3817 pub provider: Option<String>,
3818
3819 #[serde(skip_serializing_if = "Option::is_none")]
3820 pub model: Option<String>,
3821
3822 #[serde(skip_serializing_if = "Option::is_none")]
3823 pub completion: Option<LlmCompletion>,
3824
3825 #[serde(skip_serializing_if = "Option::is_none")]
3827 pub embedding: Option<serde_json::Value>,
3828
3829 #[serde(skip_serializing_if = "Option::is_none")]
3831 pub rerank: Option<serde_json::Value>,
3832
3833 #[serde(skip_serializing_if = "Option::is_none")]
3835 pub moderation: Option<serde_json::Value>,
3836
3837 #[serde(skip_serializing_if = "Option::is_none")]
3839 pub content_filter: Option<serde_json::Value>,
3840
3841 #[serde(skip_serializing_if = "Option::is_none")]
3843 pub conversation_predicates: Option<serde_json::Value>,
3844
3845 #[serde(skip_serializing_if = "Option::is_none")]
3847 pub chaos: Option<serde_json::Value>,
3848
3849 #[serde(skip_serializing_if = "Option::is_none")]
3850 pub primary: Option<bool>,
3851
3852 #[serde(flatten, default)]
3853 pub extra: Extra,
3854}
3855
3856impl HttpLlmResponse {
3857 pub fn new() -> Self {
3859 Self::default()
3860 }
3861
3862 pub fn provider(mut self, provider: impl Into<String>) -> Self {
3864 self.provider = Some(provider.into());
3865 self
3866 }
3867
3868 pub fn model(mut self, model: impl Into<String>) -> Self {
3870 self.model = Some(model.into());
3871 self
3872 }
3873
3874 pub fn completion(mut self, completion: LlmCompletion) -> Self {
3876 self.completion = Some(completion);
3877 self
3878 }
3879}
3880
3881#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
3887#[serde(rename_all = "camelCase")]
3888pub struct Expectation {
3889 #[serde(skip_serializing_if = "Option::is_none")]
3890 pub id: Option<String>,
3891
3892 #[serde(skip_serializing_if = "Option::is_none")]
3893 pub priority: Option<i32>,
3894
3895 #[serde(skip_serializing_if = "Option::is_none")]
3897 pub percentage: Option<i32>,
3898
3899 #[serde(skip_serializing_if = "Option::is_none")]
3901 pub chaos: Option<HttpChaosProfile>,
3902
3903 #[serde(skip_serializing_if = "Option::is_none")]
3905 pub rate_limit: Option<RateLimit>,
3906
3907 #[serde(default, skip_serializing_if = "Option::is_none")]
3911 pub http_request: Option<HttpRequest>,
3912
3913 #[serde(skip_serializing_if = "Option::is_none")]
3914 pub http_response: Option<HttpResponse>,
3915
3916 #[serde(skip_serializing_if = "Option::is_none")]
3917 pub http_forward: Option<HttpForward>,
3918
3919 #[serde(skip_serializing_if = "Option::is_none")]
3920 pub http_response_template: Option<HttpTemplate>,
3921
3922 #[serde(skip_serializing_if = "Option::is_none")]
3923 pub http_forward_template: Option<HttpTemplate>,
3924
3925 #[serde(skip_serializing_if = "Option::is_none")]
3926 pub http_error: Option<HttpError>,
3927
3928 #[serde(skip_serializing_if = "Option::is_none")]
3930 pub http_response_class_callback: Option<HttpClassCallback>,
3931
3932 #[serde(skip_serializing_if = "Option::is_none")]
3934 pub http_forward_class_callback: Option<HttpClassCallback>,
3935
3936 #[serde(skip_serializing_if = "Option::is_none")]
3938 pub http_response_object_callback: Option<HttpObjectCallback>,
3939
3940 #[serde(skip_serializing_if = "Option::is_none")]
3942 pub http_forward_object_callback: Option<HttpObjectCallback>,
3943
3944 #[serde(skip_serializing_if = "Option::is_none")]
3946 pub http_override_forwarded_request: Option<HttpOverrideForwardedRequest>,
3947
3948 #[serde(skip_serializing_if = "Option::is_none")]
3950 pub http_forward_validate_action: Option<HttpForwardValidateAction>,
3951
3952 #[serde(skip_serializing_if = "Option::is_none")]
3954 pub http_forward_with_fallback: Option<HttpForwardWithFallback>,
3955
3956 #[serde(skip_serializing_if = "Option::is_none")]
3957 pub http_sse_response: Option<HttpSseResponse>,
3958
3959 #[serde(skip_serializing_if = "Option::is_none")]
3961 pub http_llm_response: Option<HttpLlmResponse>,
3962
3963 #[serde(skip_serializing_if = "Option::is_none")]
3964 pub http_web_socket_response: Option<HttpWebSocketResponse>,
3965
3966 #[serde(skip_serializing_if = "Option::is_none")]
3967 pub dns_response: Option<DnsResponse>,
3968
3969 #[serde(skip_serializing_if = "Option::is_none")]
3970 pub binary_response: Option<BinaryResponse>,
3971
3972 #[serde(skip_serializing_if = "Option::is_none")]
3973 pub grpc_stream_response: Option<GrpcStreamResponse>,
3974
3975 #[serde(skip_serializing_if = "Option::is_none")]
3977 pub grpc_bidi_response: Option<GrpcBidiResponse>,
3978
3979 #[serde(skip_serializing_if = "Option::is_none")]
3980 pub times: Option<Times>,
3981
3982 #[serde(skip_serializing_if = "Option::is_none")]
3983 pub time_to_live: Option<TimeToLive>,
3984
3985 #[serde(skip_serializing_if = "Option::is_none")]
3987 pub scenario_name: Option<String>,
3988
3989 #[serde(skip_serializing_if = "Option::is_none")]
3991 pub scenario_state: Option<String>,
3992
3993 #[serde(skip_serializing_if = "Option::is_none")]
3995 pub new_scenario_state: Option<String>,
3996
3997 #[serde(skip_serializing_if = "Option::is_none")]
3999 pub http_responses: Option<Vec<HttpResponse>>,
4000
4001 #[serde(skip_serializing_if = "Option::is_none")]
4003 pub response_mode: Option<ResponseMode>,
4004
4005 #[serde(skip_serializing_if = "Option::is_none")]
4007 pub response_weights: Option<Vec<i32>>,
4008
4009 #[serde(skip_serializing_if = "Option::is_none")]
4011 pub switch_after: Option<i32>,
4012
4013 #[serde(skip_serializing_if = "Option::is_none")]
4015 pub cross_protocol_scenarios: Option<Vec<CrossProtocolScenario>>,
4016
4017 #[serde(
4022 skip_serializing_if = "Option::is_none",
4023 deserialize_with = "one_or_many",
4024 default
4025 )]
4026 pub before_actions: Option<Vec<ExpectationAction>>,
4027
4028 #[serde(
4030 skip_serializing_if = "Option::is_none",
4031 deserialize_with = "one_or_many",
4032 default
4033 )]
4034 pub after_actions: Option<Vec<ExpectationAction>>,
4035
4036 #[serde(
4038 skip_serializing_if = "Option::is_none",
4039 deserialize_with = "one_or_many",
4040 default
4041 )]
4042 pub capture: Option<Vec<CaptureRule>>,
4043
4044 #[serde(skip_serializing_if = "Option::is_none")]
4046 pub namespace: Option<String>,
4047
4048 #[serde(skip_serializing_if = "Option::is_none")]
4050 pub steps: Option<Vec<ExpectationStep>>,
4051
4052 #[serde(skip_serializing_if = "Option::is_none")]
4054 pub timestamp: Option<String>,
4055
4056 #[serde(flatten, default)]
4060 pub extra: Extra,
4061}
4062
4063impl Expectation {
4064 pub fn new(request: HttpRequest) -> Self {
4066 Self {
4067 http_request: Some(request),
4068 ..Default::default()
4069 }
4070 }
4071
4072 pub fn id(mut self, id: impl Into<String>) -> Self {
4074 self.id = Some(id.into());
4075 self
4076 }
4077
4078 pub fn priority(mut self, priority: i32) -> Self {
4080 self.priority = Some(priority);
4081 self
4082 }
4083
4084 pub fn respond(mut self, response: HttpResponse) -> Self {
4086 self.http_response = Some(response);
4087 self
4088 }
4089
4090 pub fn forward(mut self, forward: HttpForward) -> Self {
4092 self.http_forward = Some(forward);
4093 self
4094 }
4095
4096 pub fn respond_template(mut self, template: HttpTemplate) -> Self {
4098 self.http_response_template = Some(template);
4099 self
4100 }
4101
4102 pub fn forward_template(mut self, template: HttpTemplate) -> Self {
4104 self.http_forward_template = Some(template);
4105 self
4106 }
4107
4108 pub fn error(mut self, error: HttpError) -> Self {
4110 self.http_error = Some(error);
4111 self
4112 }
4113
4114 pub fn respond_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
4121 self.http_response_class_callback = Some(HttpClassCallback::new(callback_class));
4122 self
4123 }
4124
4125 pub fn respond_class_callback(mut self, callback: HttpClassCallback) -> Self {
4127 self.http_response_class_callback = Some(callback);
4128 self
4129 }
4130
4131 pub fn forward_with_class_callback(mut self, callback_class: impl Into<String>) -> Self {
4133 self.http_forward_class_callback = Some(HttpClassCallback::new(callback_class));
4134 self
4135 }
4136
4137 pub fn forward_class_callback(mut self, callback: HttpClassCallback) -> Self {
4139 self.http_forward_class_callback = Some(callback);
4140 self
4141 }
4142
4143 pub fn respond_object_callback(mut self, callback: HttpObjectCallback) -> Self {
4150 self.http_response_object_callback = Some(callback);
4151 self
4152 }
4153
4154 pub fn forward_object_callback(mut self, callback: HttpObjectCallback) -> Self {
4156 self.http_forward_object_callback = Some(callback);
4157 self
4158 }
4159
4160 pub fn respond_sse(mut self, sse: HttpSseResponse) -> Self {
4162 self.http_sse_response = Some(sse);
4163 self
4164 }
4165
4166 pub fn respond_web_socket(mut self, ws: HttpWebSocketResponse) -> Self {
4168 self.http_web_socket_response = Some(ws);
4169 self
4170 }
4171
4172 pub fn respond_dns(mut self, dns: DnsResponse) -> Self {
4174 self.dns_response = Some(dns);
4175 self
4176 }
4177
4178 pub fn respond_binary(mut self, binary: BinaryResponse) -> Self {
4180 self.binary_response = Some(binary);
4181 self
4182 }
4183
4184 pub fn respond_grpc_stream(mut self, grpc: GrpcStreamResponse) -> Self {
4186 self.grpc_stream_response = Some(grpc);
4187 self
4188 }
4189
4190 pub fn times(mut self, times: Times) -> Self {
4192 self.times = Some(times);
4193 self
4194 }
4195
4196 pub fn time_to_live(mut self, ttl: TimeToLive) -> Self {
4198 self.time_to_live = Some(ttl);
4199 self
4200 }
4201
4202 pub fn scenario_name(mut self, name: impl Into<String>) -> Self {
4204 self.scenario_name = Some(name.into());
4205 self
4206 }
4207
4208 pub fn scenario_state(mut self, state: impl Into<String>) -> Self {
4210 self.scenario_state = Some(state.into());
4211 self
4212 }
4213
4214 pub fn new_scenario_state(mut self, state: impl Into<String>) -> Self {
4216 self.new_scenario_state = Some(state.into());
4217 self
4218 }
4219
4220 pub fn respond_with(mut self, response: HttpResponse) -> Self {
4225 self.http_responses
4226 .get_or_insert_with(Vec::new)
4227 .push(response);
4228 self
4229 }
4230
4231 pub fn http_responses(mut self, responses: Vec<HttpResponse>) -> Self {
4233 self.http_responses = Some(responses);
4234 self
4235 }
4236
4237 pub fn response_mode(mut self, mode: ResponseMode) -> Self {
4239 self.response_mode = Some(mode);
4240 self
4241 }
4242
4243 pub fn response_weights(mut self, weights: Vec<i32>) -> Self {
4245 self.response_weights = Some(weights);
4246 self
4247 }
4248
4249 pub fn switch_after(mut self, switch_after: i32) -> Self {
4252 self.switch_after = Some(switch_after);
4253 self
4254 }
4255
4256 pub fn cross_protocol_scenario(mut self, scenario: CrossProtocolScenario) -> Self {
4258 self.cross_protocol_scenarios
4259 .get_or_insert_with(Vec::new)
4260 .push(scenario);
4261 self
4262 }
4263
4264 pub fn cross_protocol_scenarios(mut self, scenarios: Vec<CrossProtocolScenario>) -> Self {
4266 self.cross_protocol_scenarios = Some(scenarios);
4267 self
4268 }
4269
4270 pub fn percentage(mut self, percentage: i32) -> Self {
4272 self.percentage = Some(percentage);
4273 self
4274 }
4275
4276 pub fn chaos(mut self, chaos: HttpChaosProfile) -> Self {
4278 self.chaos = Some(chaos);
4279 self
4280 }
4281
4282 pub fn rate_limit(mut self, rate_limit: RateLimit) -> Self {
4284 self.rate_limit = Some(rate_limit);
4285 self
4286 }
4287
4288 pub fn override_forwarded_request(
4290 mut self,
4291 override_request: HttpOverrideForwardedRequest,
4292 ) -> Self {
4293 self.http_override_forwarded_request = Some(override_request);
4294 self
4295 }
4296
4297 pub fn forward_validate(mut self, action: HttpForwardValidateAction) -> Self {
4299 self.http_forward_validate_action = Some(action);
4300 self
4301 }
4302
4303 pub fn forward_with_fallback(mut self, action: HttpForwardWithFallback) -> Self {
4305 self.http_forward_with_fallback = Some(action);
4306 self
4307 }
4308
4309 pub fn respond_llm(mut self, llm: HttpLlmResponse) -> Self {
4311 self.http_llm_response = Some(llm);
4312 self
4313 }
4314
4315 pub fn respond_grpc_bidi(mut self, grpc: GrpcBidiResponse) -> Self {
4317 self.grpc_bidi_response = Some(grpc);
4318 self
4319 }
4320
4321 pub fn before_action(mut self, action: ExpectationAction) -> Self {
4323 self.before_actions
4324 .get_or_insert_with(Vec::new)
4325 .push(action);
4326 self
4327 }
4328
4329 pub fn after_action(mut self, action: ExpectationAction) -> Self {
4331 self.after_actions.get_or_insert_with(Vec::new).push(action);
4332 self
4333 }
4334
4335 pub fn capture_rule(mut self, rule: CaptureRule) -> Self {
4337 self.capture.get_or_insert_with(Vec::new).push(rule);
4338 self
4339 }
4340
4341 pub fn namespace(mut self, namespace: impl Into<String>) -> Self {
4343 self.namespace = Some(namespace.into());
4344 self
4345 }
4346
4347 pub fn step(mut self, step: ExpectationStep) -> Self {
4349 self.steps.get_or_insert_with(Vec::new).push(step);
4350 self
4351 }
4352
4353 pub fn steps(mut self, steps: Vec<ExpectationStep>) -> Self {
4355 self.steps = Some(steps);
4356 self
4357 }
4358}
4359
4360#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4371#[serde(rename_all = "camelCase")]
4372pub struct Verification {
4373 #[serde(skip_serializing_if = "Option::is_none")]
4374 pub http_request: Option<HttpRequest>,
4375
4376 #[serde(skip_serializing_if = "Option::is_none")]
4377 pub http_response: Option<HttpResponse>,
4378
4379 #[serde(skip_serializing_if = "Option::is_none")]
4380 pub times: Option<VerificationTimes>,
4381
4382 #[serde(skip_serializing_if = "Option::is_none")]
4383 pub maximum_number_of_request_to_return_in_verification_failure: Option<u32>,
4384}
4385
4386#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4392#[serde(rename_all = "camelCase")]
4393pub struct VerificationSequence {
4394 #[serde(skip_serializing_if = "Option::is_none")]
4395 pub http_requests: Option<Vec<HttpRequest>>,
4396
4397 #[serde(skip_serializing_if = "Option::is_none")]
4398 pub http_responses: Option<Vec<HttpResponse>>,
4399}
4400
4401#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4407pub struct Ports {
4408 pub ports: Vec<u16>,
4409}
4410
4411#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
4418#[serde(rename_all = "camelCase")]
4419pub struct ScenarioState {
4420 pub scenario_name: String,
4422 pub current_state: String,
4424}
4425
4426#[derive(Debug, Clone, Deserialize)]
4429pub(crate) struct ScenarioList {
4430 #[serde(default)]
4431 pub scenarios: Vec<ScenarioState>,
4432}
4433
4434#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4440pub enum RetrieveType {
4441 Requests,
4443 ActiveExpectations,
4445 RecordedExpectations,
4447 Logs,
4449 RequestResponses,
4451}
4452
4453impl RetrieveType {
4454 pub fn as_str(&self) -> &'static str {
4456 match self {
4457 RetrieveType::Requests => "REQUESTS",
4458 RetrieveType::ActiveExpectations => "ACTIVE_EXPECTATIONS",
4459 RetrieveType::RecordedExpectations => "RECORDED_EXPECTATIONS",
4460 RetrieveType::Logs => "LOGS",
4461 RetrieveType::RequestResponses => "REQUEST_RESPONSES",
4462 }
4463 }
4464}
4465
4466#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4472pub enum RetrieveFormat {
4473 Json,
4474 LogEntries,
4475 Java,
4476 JavaScript,
4477 Python,
4478 Go,
4479 CSharp,
4480 Ruby,
4481 Rust,
4482 Php,
4483}
4484
4485impl RetrieveFormat {
4486 pub fn as_str(&self) -> &'static str {
4488 match self {
4489 RetrieveFormat::Json => "JSON",
4490 RetrieveFormat::LogEntries => "LOG_ENTRIES",
4491 RetrieveFormat::Java => "JAVA",
4492 RetrieveFormat::JavaScript => "JAVASCRIPT",
4493 RetrieveFormat::Python => "PYTHON",
4494 RetrieveFormat::Go => "GO",
4495 RetrieveFormat::CSharp => "CSHARP",
4496 RetrieveFormat::Ruby => "RUBY",
4497 RetrieveFormat::Rust => "RUST",
4498 RetrieveFormat::Php => "PHP",
4499 }
4500 }
4501}
4502
4503#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4505pub enum ClearType {
4506 All,
4507 Log,
4508 Expectations,
4509}
4510
4511impl ClearType {
4512 pub fn as_str(&self) -> &'static str {
4514 match self {
4515 ClearType::All => "ALL",
4516 ClearType::Log => "LOG",
4517 ClearType::Expectations => "EXPECTATIONS",
4518 }
4519 }
4520}
4521
4522#[derive(Debug, Clone, PartialEq, Eq)]
4532pub struct PactVerification {
4533 pub passed: bool,
4535 pub report: String,
4537}
4538
4539#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4557pub enum MockMode {
4558 Simulate,
4560 Spy,
4562 Capture,
4564}
4565
4566impl MockMode {
4567 pub fn as_str(&self) -> &'static str {
4569 match self {
4570 MockMode::Simulate => "SIMULATE",
4571 MockMode::Spy => "SPY",
4572 MockMode::Capture => "CAPTURE",
4573 }
4574 }
4575
4576 pub fn proxy_unmatched_requests(&self) -> bool {
4579 !matches!(self, MockMode::Simulate)
4580 }
4581}
4582
4583impl std::fmt::Display for MockMode {
4584 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4585 f.write_str(self.as_str())
4586 }
4587}
4588
4589impl std::str::FromStr for MockMode {
4590 type Err = String;
4591
4592 fn from_str(value: &str) -> std::result::Result<Self, Self::Err> {
4595 match value.trim().to_uppercase().as_str() {
4596 "" => Err("mode is required (one of SIMULATE, SPY, CAPTURE)".to_string()),
4597 "SIMULATE" => Ok(MockMode::Simulate),
4598 "SPY" => Ok(MockMode::Spy),
4599 "CAPTURE" => Ok(MockMode::Capture),
4600 other => Err(format!(
4601 "unknown mode '{other}' (expected one of SIMULATE, SPY, CAPTURE)"
4602 )),
4603 }
4604 }
4605}
4606
4607#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4619#[serde(rename_all = "camelCase")]
4620pub struct GrpcMethod {
4621 pub name: String,
4623
4624 pub input_type: String,
4626
4627 pub output_type: String,
4629
4630 pub client_streaming: bool,
4632
4633 pub server_streaming: bool,
4635}
4636
4637#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4644#[serde(rename_all = "camelCase")]
4645pub struct GrpcService {
4646 pub name: String,
4648
4649 pub methods: Vec<GrpcMethod>,
4651}
4652
4653#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
4662#[serde(rename_all = "camelCase")]
4663pub struct SocketAddress {
4664 pub host: String,
4666
4667 pub port: u16,
4669
4670 #[serde(skip_serializing_if = "Option::is_none")]
4673 pub scheme: Option<String>,
4674}
4675
4676impl SocketAddress {
4677 pub fn new(host: impl Into<String>, port: u16) -> Self {
4679 Self {
4680 host: host.into(),
4681 port,
4682 scheme: None,
4683 }
4684 }
4685
4686 pub fn scheme(mut self, scheme: impl Into<String>) -> Self {
4688 self.scheme = Some(scheme.into());
4689 self
4690 }
4691
4692 pub fn https(host: impl Into<String>, port: u16) -> Self {
4694 Self::new(host, port).scheme("HTTPS")
4695 }
4696}
4697
4698#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4707#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4708pub enum RampCurve {
4709 Linear,
4711 Quadratic,
4713 Exponential,
4715}
4716
4717#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4723#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4724pub enum LoadStageType {
4725 Vu,
4727 Rate,
4729 Pause,
4731}
4732
4733#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4742#[serde(rename_all = "camelCase")]
4743pub struct LoadStage {
4744 #[serde(rename = "type")]
4746 pub stage_type: LoadStageType,
4747
4748 pub duration_millis: u64,
4750
4751 #[serde(skip_serializing_if = "Option::is_none")]
4753 pub curve: Option<RampCurve>,
4754
4755 #[serde(skip_serializing_if = "Option::is_none")]
4757 pub vus: Option<u32>,
4758
4759 #[serde(skip_serializing_if = "Option::is_none")]
4761 pub start_vus: Option<u32>,
4762
4763 #[serde(skip_serializing_if = "Option::is_none")]
4765 pub end_vus: Option<u32>,
4766
4767 #[serde(skip_serializing_if = "Option::is_none")]
4769 pub rate: Option<f64>,
4770
4771 #[serde(skip_serializing_if = "Option::is_none")]
4773 pub start_rate: Option<f64>,
4774
4775 #[serde(skip_serializing_if = "Option::is_none")]
4777 pub end_rate: Option<f64>,
4778
4779 #[serde(skip_serializing_if = "Option::is_none")]
4781 pub max_vus: Option<u32>,
4782}
4783
4784impl LoadStage {
4785 fn base(stage_type: LoadStageType, duration_millis: u64) -> Self {
4786 Self {
4787 stage_type,
4788 duration_millis,
4789 curve: None,
4790 vus: None,
4791 start_vus: None,
4792 end_vus: None,
4793 rate: None,
4794 start_rate: None,
4795 end_rate: None,
4796 max_vus: None,
4797 }
4798 }
4799
4800 pub fn vu_hold(vus: u32, duration_millis: u64) -> Self {
4802 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
4803 stage.vus = Some(vus);
4804 stage
4805 }
4806
4807 pub fn vu_ramp(start_vus: u32, end_vus: u32, duration_millis: u64, curve: RampCurve) -> Self {
4810 let mut stage = Self::base(LoadStageType::Vu, duration_millis);
4811 stage.start_vus = Some(start_vus);
4812 stage.end_vus = Some(end_vus);
4813 stage.curve = Some(curve);
4814 stage
4815 }
4816
4817 pub fn rate_hold(rate: f64, duration_millis: u64) -> Self {
4819 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
4820 stage.rate = Some(rate);
4821 stage
4822 }
4823
4824 pub fn rate_ramp(
4827 start_rate: f64,
4828 end_rate: f64,
4829 duration_millis: u64,
4830 curve: RampCurve,
4831 ) -> Self {
4832 let mut stage = Self::base(LoadStageType::Rate, duration_millis);
4833 stage.start_rate = Some(start_rate);
4834 stage.end_rate = Some(end_rate);
4835 stage.curve = Some(curve);
4836 stage
4837 }
4838
4839 pub fn pause(duration_millis: u64) -> Self {
4841 Self::base(LoadStageType::Pause, duration_millis)
4842 }
4843
4844 pub fn max_vus(mut self, max_vus: u32) -> Self {
4846 self.max_vus = Some(max_vus);
4847 self
4848 }
4849}
4850
4851#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4854#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4855pub enum LoadShapeType {
4856 Spike,
4858 Stairs,
4860 RampHold,
4862}
4863
4864#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
4869#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
4870pub enum LoadShapeMetric {
4871 Vu,
4873 Rate,
4875}
4876
4877#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
4886#[serde(rename_all = "camelCase")]
4887pub struct LoadShape {
4888 #[serde(rename = "type")]
4890 pub shape_type: LoadShapeType,
4891
4892 #[serde(skip_serializing_if = "Option::is_none")]
4894 pub metric: Option<LoadShapeMetric>,
4895
4896 #[serde(skip_serializing_if = "Option::is_none")]
4898 pub curve: Option<RampCurve>,
4899
4900 #[serde(skip_serializing_if = "Option::is_none")]
4902 pub baseline: Option<f64>,
4903
4904 #[serde(skip_serializing_if = "Option::is_none")]
4906 pub peak: Option<f64>,
4907
4908 #[serde(skip_serializing_if = "Option::is_none")]
4910 pub ramp_up_millis: Option<u64>,
4911
4912 #[serde(skip_serializing_if = "Option::is_none")]
4915 pub hold_millis: Option<u64>,
4916
4917 #[serde(skip_serializing_if = "Option::is_none")]
4919 pub ramp_down_millis: Option<u64>,
4920
4921 #[serde(skip_serializing_if = "Option::is_none")]
4923 pub recovery_hold_millis: Option<u64>,
4924
4925 #[serde(skip_serializing_if = "Option::is_none")]
4927 pub start: Option<f64>,
4928
4929 #[serde(skip_serializing_if = "Option::is_none")]
4931 pub step: Option<f64>,
4932
4933 #[serde(skip_serializing_if = "Option::is_none")]
4935 pub steps: Option<u32>,
4936
4937 #[serde(skip_serializing_if = "Option::is_none")]
4939 pub step_duration_millis: Option<u64>,
4940
4941 #[serde(skip_serializing_if = "Option::is_none")]
4943 pub target: Option<f64>,
4944
4945 #[serde(skip_serializing_if = "Option::is_none")]
4947 pub ramp_millis: Option<u64>,
4948}
4949
4950impl LoadShape {
4951 fn base(shape_type: LoadShapeType) -> Self {
4952 Self {
4953 shape_type,
4954 metric: None,
4955 curve: None,
4956 baseline: None,
4957 peak: None,
4958 ramp_up_millis: None,
4959 hold_millis: None,
4960 ramp_down_millis: None,
4961 recovery_hold_millis: None,
4962 start: None,
4963 step: None,
4964 steps: None,
4965 step_duration_millis: None,
4966 target: None,
4967 ramp_millis: None,
4968 }
4969 }
4970
4971 pub fn spike(
4974 baseline: f64,
4975 peak: f64,
4976 ramp_up_millis: u64,
4977 hold_millis: u64,
4978 ramp_down_millis: u64,
4979 ) -> Self {
4980 let mut shape = Self::base(LoadShapeType::Spike);
4981 shape.baseline = Some(baseline);
4982 shape.peak = Some(peak);
4983 shape.ramp_up_millis = Some(ramp_up_millis);
4984 shape.hold_millis = Some(hold_millis);
4985 shape.ramp_down_millis = Some(ramp_down_millis);
4986 shape
4987 }
4988
4989 pub fn stairs(start: f64, step: f64, steps: u32, step_duration_millis: u64) -> Self {
4992 let mut shape = Self::base(LoadShapeType::Stairs);
4993 shape.start = Some(start);
4994 shape.step = Some(step);
4995 shape.steps = Some(steps);
4996 shape.step_duration_millis = Some(step_duration_millis);
4997 shape
4998 }
4999
5000 pub fn ramp_hold(target: f64, ramp_millis: u64, hold_millis: u64) -> Self {
5003 let mut shape = Self::base(LoadShapeType::RampHold);
5004 shape.target = Some(target);
5005 shape.ramp_millis = Some(ramp_millis);
5006 shape.hold_millis = Some(hold_millis);
5007 shape
5008 }
5009
5010 pub fn metric(mut self, metric: LoadShapeMetric) -> Self {
5012 self.metric = Some(metric);
5013 self
5014 }
5015
5016 pub fn curve(mut self, curve: RampCurve) -> Self {
5018 self.curve = Some(curve);
5019 self
5020 }
5021
5022 pub fn recovery_hold_millis(mut self, recovery_hold_millis: u64) -> Self {
5025 self.recovery_hold_millis = Some(recovery_hold_millis);
5026 self
5027 }
5028}
5029
5030#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5033#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5034pub enum LoadThresholdMetric {
5035 LatencyP50,
5037 LatencyP95,
5039 LatencyP99,
5041 LatencyP999,
5043 ErrorRate,
5045 ThroughputRps,
5047}
5048
5049#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5052#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5053pub enum LoadComparator {
5054 LessThan,
5056 LessThanOrEqual,
5058 GreaterThan,
5060 GreaterThanOrEqual,
5062}
5063
5064#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5068#[serde(rename_all = "camelCase")]
5069pub struct LoadThreshold {
5070 pub metric: LoadThresholdMetric,
5072
5073 pub comparator: LoadComparator,
5075
5076 pub threshold: f64,
5079}
5080
5081impl LoadThreshold {
5082 pub fn new(metric: LoadThresholdMetric, comparator: LoadComparator, threshold: f64) -> Self {
5084 Self {
5085 metric,
5086 comparator,
5087 threshold,
5088 }
5089 }
5090}
5091
5092#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5095#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5096pub enum LoadPacingMode {
5097 None,
5099 ConstantPacing,
5101 ConstantThroughput,
5103}
5104
5105#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5109#[serde(rename_all = "camelCase")]
5110pub struct LoadPacing {
5111 pub mode: LoadPacingMode,
5113
5114 pub value: f64,
5118}
5119
5120impl LoadPacing {
5121 pub fn new(mode: LoadPacingMode, value: f64) -> Self {
5123 Self { mode, value }
5124 }
5125
5126 pub fn constant_pacing(cycle_millis: f64) -> Self {
5128 Self::new(LoadPacingMode::ConstantPacing, cycle_millis)
5129 }
5130
5131 pub fn constant_throughput(iterations_per_second: f64) -> Self {
5133 Self::new(LoadPacingMode::ConstantThroughput, iterations_per_second)
5134 }
5135}
5136
5137#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5140#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5141pub enum LoadFeederFormat {
5142 Csv,
5144 Json,
5146}
5147
5148#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5151#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5152pub enum LoadFeederStrategy {
5153 Circular,
5155 Random,
5157 Sequential,
5159}
5160
5161#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5166#[serde(rename_all = "camelCase")]
5167pub struct LoadFeeder {
5168 #[serde(skip_serializing_if = "Vec::is_empty")]
5170 pub rows: Vec<HashMap<String, String>>,
5171
5172 #[serde(skip_serializing_if = "Option::is_none")]
5174 pub data: Option<String>,
5175
5176 #[serde(skip_serializing_if = "Option::is_none")]
5178 pub format: Option<LoadFeederFormat>,
5179
5180 #[serde(skip_serializing_if = "Option::is_none")]
5182 pub strategy: Option<LoadFeederStrategy>,
5183}
5184
5185impl LoadFeeder {
5186 pub fn rows(rows: Vec<HashMap<String, String>>) -> Self {
5188 Self {
5189 rows,
5190 ..Self::default()
5191 }
5192 }
5193
5194 pub fn data(data: impl Into<String>, format: LoadFeederFormat) -> Self {
5196 Self {
5197 data: Some(data.into()),
5198 format: Some(format),
5199 ..Self::default()
5200 }
5201 }
5202
5203 pub fn strategy(mut self, strategy: LoadFeederStrategy) -> Self {
5205 self.strategy = Some(strategy);
5206 self
5207 }
5208}
5209
5210#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5213#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5214pub enum LoadCaptureSource {
5215 BodyJsonpath,
5217 Header,
5219 BodyRegex,
5221}
5222
5223#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5228#[serde(rename_all = "camelCase")]
5229pub struct LoadCapture {
5230 pub name: String,
5232
5233 pub source: LoadCaptureSource,
5235
5236 pub expression: String,
5238
5239 #[serde(skip_serializing_if = "Option::is_none")]
5241 pub default_value: Option<String>,
5242}
5243
5244impl LoadCapture {
5245 pub fn new(
5248 name: impl Into<String>,
5249 source: LoadCaptureSource,
5250 expression: impl Into<String>,
5251 ) -> Self {
5252 Self {
5253 name: name.into(),
5254 source,
5255 expression: expression.into(),
5256 default_value: None,
5257 }
5258 }
5259
5260 pub fn default_value(mut self, default_value: impl Into<String>) -> Self {
5262 self.default_value = Some(default_value.into());
5263 self
5264 }
5265}
5266
5267#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
5270#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
5271pub enum LoadStepSelection {
5272 Sequential,
5274 Weighted,
5276}
5277
5278#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5286#[serde(rename_all = "camelCase")]
5287pub struct LoadProfile {
5288 #[serde(skip_serializing_if = "Vec::is_empty")]
5291 pub stages: Vec<LoadStage>,
5292
5293 #[serde(skip_serializing_if = "Option::is_none")]
5296 pub shape: Option<LoadShape>,
5297}
5298
5299impl LoadProfile {
5300 pub fn of(stages: Vec<LoadStage>) -> Self {
5302 Self {
5303 stages,
5304 shape: None,
5305 }
5306 }
5307
5308 pub fn shaped(shape: LoadShape) -> Self {
5310 Self {
5311 stages: Vec::new(),
5312 shape: Some(shape),
5313 }
5314 }
5315
5316 pub fn constant(vus: u32, duration_millis: u64) -> Self {
5318 Self::of(vec![LoadStage::vu_hold(vus, duration_millis)])
5319 }
5320
5321 pub fn linear(start_vus: u32, end_vus: u32, duration_millis: u64) -> Self {
5324 Self::of(vec![LoadStage::vu_ramp(
5325 start_vus,
5326 end_vus,
5327 duration_millis,
5328 RampCurve::Linear,
5329 )])
5330 }
5331
5332 pub fn add_stage(mut self, stage: LoadStage) -> Self {
5334 self.stages.push(stage);
5335 self
5336 }
5337}
5338
5339#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5342#[serde(rename_all = "camelCase")]
5343pub struct LoadStep {
5344 pub request: HttpRequest,
5346
5347 #[serde(skip_serializing_if = "Option::is_none")]
5349 pub think_time: Option<Delay>,
5350
5351 #[serde(skip_serializing_if = "Vec::is_empty")]
5355 pub captures: Vec<LoadCapture>,
5356
5357 #[serde(skip_serializing_if = "Option::is_none")]
5361 pub weight: Option<f64>,
5362}
5363
5364impl LoadStep {
5365 pub fn new(request: HttpRequest) -> Self {
5367 Self {
5368 request,
5369 think_time: None,
5370 captures: Vec::new(),
5371 weight: None,
5372 }
5373 }
5374
5375 pub fn think_time(mut self, delay: Delay) -> Self {
5377 self.think_time = Some(delay);
5378 self
5379 }
5380
5381 pub fn capture(mut self, capture: LoadCapture) -> Self {
5383 self.captures.push(capture);
5384 self
5385 }
5386
5387 pub fn weight(mut self, weight: f64) -> Self {
5390 self.weight = Some(weight);
5391 self
5392 }
5393}
5394
5395#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5402#[serde(rename_all = "camelCase")]
5403pub struct LoadScenario {
5404 pub name: String,
5406
5407 #[serde(skip_serializing_if = "Option::is_none")]
5410 pub template_type: Option<String>,
5411
5412 #[serde(skip_serializing_if = "Option::is_none")]
5414 pub max_requests: Option<u64>,
5415
5416 #[serde(skip_serializing_if = "Option::is_none")]
5420 pub start_delay_millis: Option<u64>,
5421
5422 #[serde(skip_serializing_if = "Vec::is_empty")]
5425 pub thresholds: Vec<LoadThreshold>,
5426
5427 #[serde(skip_serializing_if = "std::ops::Not::not")]
5429 pub abort_on_fail: bool,
5430
5431 #[serde(skip_serializing_if = "Option::is_none")]
5434 pub abort_grace_millis: Option<u64>,
5435
5436 #[serde(skip_serializing_if = "Option::is_none")]
5438 pub pacing: Option<LoadPacing>,
5439
5440 #[serde(skip_serializing_if = "Option::is_none")]
5442 pub feeder: Option<LoadFeeder>,
5443
5444 #[serde(skip_serializing_if = "Option::is_none")]
5447 pub step_selection: Option<LoadStepSelection>,
5448
5449 pub profile: LoadProfile,
5451
5452 pub steps: Vec<LoadStep>,
5454}
5455
5456impl LoadScenario {
5457 pub fn new(name: impl Into<String>, profile: LoadProfile, steps: Vec<LoadStep>) -> Self {
5459 Self {
5460 name: name.into(),
5461 template_type: None,
5462 max_requests: None,
5463 start_delay_millis: None,
5464 thresholds: Vec::new(),
5465 abort_on_fail: false,
5466 abort_grace_millis: None,
5467 pacing: None,
5468 feeder: None,
5469 step_selection: None,
5470 profile,
5471 steps,
5472 }
5473 }
5474
5475 pub fn threshold(mut self, threshold: LoadThreshold) -> Self {
5477 self.thresholds.push(threshold);
5478 self
5479 }
5480
5481 pub fn abort_on_fail(mut self, abort_on_fail: bool) -> Self {
5483 self.abort_on_fail = abort_on_fail;
5484 self
5485 }
5486
5487 pub fn abort_grace_millis(mut self, abort_grace_millis: u64) -> Self {
5489 self.abort_grace_millis = Some(abort_grace_millis);
5490 self
5491 }
5492
5493 pub fn pacing(mut self, pacing: LoadPacing) -> Self {
5495 self.pacing = Some(pacing);
5496 self
5497 }
5498
5499 pub fn feeder(mut self, feeder: LoadFeeder) -> Self {
5501 self.feeder = Some(feeder);
5502 self
5503 }
5504
5505 pub fn step_selection(mut self, step_selection: LoadStepSelection) -> Self {
5507 self.step_selection = Some(step_selection);
5508 self
5509 }
5510
5511 pub fn template_type(mut self, template_type: impl Into<String>) -> Self {
5513 self.template_type = Some(template_type.into());
5514 self
5515 }
5516
5517 pub fn max_requests(mut self, max_requests: u64) -> Self {
5519 self.max_requests = Some(max_requests);
5520 self
5521 }
5522
5523 pub fn start_delay_millis(mut self, start_delay_millis: u64) -> Self {
5526 self.start_delay_millis = Some(start_delay_millis);
5527 self
5528 }
5529}
5530
5531#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5538#[serde(rename_all = "camelCase")]
5539pub struct SloObjective {
5540 pub sli: String,
5543
5544 pub comparator: String,
5548
5549 pub threshold: f64,
5552
5553 #[serde(skip_serializing_if = "Option::is_none")]
5556 pub scope: Option<String>,
5557}
5558
5559impl SloObjective {
5560 pub fn new(sli: impl Into<String>, comparator: impl Into<String>, threshold: f64) -> Self {
5562 Self {
5563 sli: sli.into(),
5564 comparator: comparator.into(),
5565 threshold,
5566 scope: None,
5567 }
5568 }
5569
5570 pub fn scope(mut self, scope: impl Into<String>) -> Self {
5572 self.scope = Some(scope.into());
5573 self
5574 }
5575}
5576
5577#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
5580#[serde(rename_all = "camelCase")]
5581pub struct SloWindow {
5582 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
5584 pub window_type: Option<String>,
5585
5586 #[serde(skip_serializing_if = "Option::is_none")]
5588 pub lookback_millis: Option<u64>,
5589
5590 #[serde(skip_serializing_if = "Option::is_none")]
5592 pub from_epoch_millis: Option<u64>,
5593
5594 #[serde(skip_serializing_if = "Option::is_none")]
5596 pub to_epoch_millis: Option<u64>,
5597}
5598
5599impl SloWindow {
5600 pub fn lookback(millis: u64) -> Self {
5602 Self {
5603 window_type: Some("LOOKBACK".to_string()),
5604 lookback_millis: Some(millis),
5605 ..Default::default()
5606 }
5607 }
5608
5609 pub fn explicit(from_epoch_millis: u64, to_epoch_millis: u64) -> Self {
5611 Self {
5612 window_type: Some("EXPLICIT".to_string()),
5613 from_epoch_millis: Some(from_epoch_millis),
5614 to_epoch_millis: Some(to_epoch_millis),
5615 ..Default::default()
5616 }
5617 }
5618}
5619
5620#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5623#[serde(rename_all = "camelCase")]
5624pub struct SloCriteria {
5625 #[serde(skip_serializing_if = "Option::is_none")]
5627 pub name: Option<String>,
5628
5629 #[serde(skip_serializing_if = "Option::is_none")]
5631 pub window: Option<SloWindow>,
5632
5633 #[serde(skip_serializing_if = "Option::is_none")]
5636 pub minimum_sample_count: Option<u64>,
5637
5638 #[serde(skip_serializing_if = "Option::is_none")]
5640 pub upstream_hosts: Option<Vec<String>>,
5641
5642 pub objectives: Vec<SloObjective>,
5644}
5645
5646impl SloCriteria {
5647 pub fn new(objectives: Vec<SloObjective>) -> Self {
5649 Self {
5650 name: None,
5651 window: None,
5652 minimum_sample_count: None,
5653 upstream_hosts: None,
5654 objectives,
5655 }
5656 }
5657
5658 pub fn name(mut self, name: impl Into<String>) -> Self {
5660 self.name = Some(name.into());
5661 self
5662 }
5663
5664 pub fn window(mut self, window: SloWindow) -> Self {
5666 self.window = Some(window);
5667 self
5668 }
5669
5670 pub fn minimum_sample_count(mut self, count: u64) -> Self {
5672 self.minimum_sample_count = Some(count);
5673 self
5674 }
5675
5676 pub fn upstream_hosts(mut self, hosts: Vec<String>) -> Self {
5678 self.upstream_hosts = Some(hosts);
5679 self
5680 }
5681}
5682
5683#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5686#[serde(rename_all = "camelCase")]
5687pub struct SloObjectiveResult {
5688 #[serde(skip_serializing_if = "Option::is_none")]
5689 pub sli: Option<String>,
5690 #[serde(skip_serializing_if = "Option::is_none")]
5691 pub comparator: Option<String>,
5692 #[serde(skip_serializing_if = "Option::is_none")]
5693 pub threshold: Option<f64>,
5694 #[serde(skip_serializing_if = "Option::is_none")]
5695 pub observed_value: Option<f64>,
5696 #[serde(skip_serializing_if = "Option::is_none")]
5698 pub result: Option<String>,
5699 #[serde(skip_serializing_if = "Option::is_none")]
5700 pub detail: Option<String>,
5701}
5702
5703#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5706#[serde(rename_all = "camelCase")]
5707pub struct SloVerdict {
5708 #[serde(skip_serializing_if = "Option::is_none")]
5709 pub name: Option<String>,
5710 #[serde(skip_serializing_if = "Option::is_none")]
5712 pub result: Option<String>,
5713 #[serde(skip_serializing_if = "Option::is_none")]
5714 pub window_from_epoch_millis: Option<u64>,
5715 #[serde(skip_serializing_if = "Option::is_none")]
5716 pub window_to_epoch_millis: Option<u64>,
5717 #[serde(skip_serializing_if = "Option::is_none")]
5718 pub sample_count: Option<u64>,
5719 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5720 pub objective_results: Vec<SloObjectiveResult>,
5721}
5722
5723impl SloVerdict {
5724 pub fn is_pass(&self) -> bool {
5726 self.result.as_deref() == Some("PASS")
5727 }
5728
5729 pub fn is_fail(&self) -> bool {
5731 self.result.as_deref() == Some("FAIL")
5732 }
5733
5734 pub fn is_inconclusive(&self) -> bool {
5736 self.result.as_deref() == Some("INCONCLUSIVE")
5737 }
5738}
5739
5740#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
5747#[serde(rename_all = "camelCase")]
5748pub struct PreemptionRequest {
5749 #[serde(skip_serializing_if = "Option::is_none")]
5752 pub mode: Option<String>,
5753
5754 #[serde(skip_serializing_if = "Option::is_none")]
5756 pub drain_millis: Option<u64>,
5757
5758 #[serde(skip_serializing_if = "Option::is_none")]
5761 pub ttl_millis: Option<u64>,
5762
5763 #[serde(skip_serializing_if = "Option::is_none")]
5766 pub last_stream_id: Option<i64>,
5767}
5768
5769impl PreemptionRequest {
5770 pub fn new() -> Self {
5772 Self::default()
5773 }
5774
5775 pub fn mode(mut self, mode: impl Into<String>) -> Self {
5777 self.mode = Some(mode.into());
5778 self
5779 }
5780
5781 pub fn drain_millis(mut self, millis: u64) -> Self {
5783 self.drain_millis = Some(millis);
5784 self
5785 }
5786
5787 pub fn ttl_millis(mut self, millis: u64) -> Self {
5789 self.ttl_millis = Some(millis);
5790 self
5791 }
5792
5793 pub fn last_stream_id(mut self, id: i64) -> Self {
5795 self.last_stream_id = Some(id);
5796 self
5797 }
5798}
5799
5800#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
5803#[serde(rename_all = "camelCase")]
5804pub struct PreemptionStatus {
5805 #[serde(skip_serializing_if = "Option::is_none")]
5807 pub state: Option<String>,
5808
5809 #[serde(skip_serializing_if = "Option::is_none")]
5811 pub in_flight: Option<u64>,
5812
5813 #[serde(skip_serializing_if = "Option::is_none")]
5815 pub drain_remaining_millis: Option<u64>,
5816
5817 #[serde(skip_serializing_if = "Option::is_none")]
5819 pub mode: Option<String>,
5820}
5821
5822#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5830#[serde(rename_all = "camelCase")]
5831pub struct HttpChaosProfile {
5832 #[serde(skip_serializing_if = "Option::is_none")]
5834 pub error_status: Option<u16>,
5835
5836 #[serde(skip_serializing_if = "Option::is_none")]
5838 pub error_probability: Option<f64>,
5839
5840 #[serde(skip_serializing_if = "Option::is_none")]
5842 pub latency: Option<Delay>,
5843
5844 #[serde(skip_serializing_if = "Option::is_none")]
5846 pub connection_drop: Option<bool>,
5847
5848 #[serde(skip_serializing_if = "Option::is_none")]
5850 pub seed: Option<i64>,
5851
5852 #[serde(skip_serializing_if = "Option::is_none")]
5854 pub retry_after: Option<String>,
5855
5856 #[serde(skip_serializing_if = "Option::is_none")]
5858 pub drop_connection_probability: Option<f64>,
5859
5860 #[serde(skip_serializing_if = "Option::is_none")]
5862 pub succeed_first: Option<i64>,
5863
5864 #[serde(skip_serializing_if = "Option::is_none")]
5866 pub fail_request_count: Option<i64>,
5867
5868 #[serde(skip_serializing_if = "Option::is_none")]
5870 pub outage_after_millis: Option<i64>,
5871
5872 #[serde(skip_serializing_if = "Option::is_none")]
5874 pub outage_duration_millis: Option<i64>,
5875
5876 #[serde(skip_serializing_if = "Option::is_none")]
5878 pub truncate_body_at_fraction: Option<f64>,
5879
5880 #[serde(skip_serializing_if = "Option::is_none")]
5882 pub malformed_body: Option<bool>,
5883
5884 #[serde(skip_serializing_if = "Option::is_none")]
5886 pub slow_response_chunk_size: Option<i64>,
5887
5888 #[serde(skip_serializing_if = "Option::is_none")]
5890 pub slow_response_chunk_delay: Option<Delay>,
5891
5892 #[serde(skip_serializing_if = "Option::is_none")]
5894 pub quota_name: Option<String>,
5895
5896 #[serde(skip_serializing_if = "Option::is_none")]
5898 pub quota_limit: Option<i64>,
5899
5900 #[serde(skip_serializing_if = "Option::is_none")]
5902 pub quota_window_millis: Option<i64>,
5903
5904 #[serde(skip_serializing_if = "Option::is_none")]
5906 pub quota_error_status: Option<u16>,
5907
5908 #[serde(skip_serializing_if = "Option::is_none")]
5910 pub degradation_ramp_millis: Option<i64>,
5911
5912 #[serde(skip_serializing_if = "Option::is_none")]
5914 pub graphql_errors: Option<bool>,
5915
5916 #[serde(skip_serializing_if = "Option::is_none")]
5918 pub graphql_error_message: Option<String>,
5919
5920 #[serde(skip_serializing_if = "Option::is_none")]
5922 pub graphql_error_code: Option<String>,
5923
5924 #[serde(skip_serializing_if = "Option::is_none")]
5926 pub graphql_nullify_data: Option<bool>,
5927
5928 #[serde(flatten)]
5930 pub extra: HashMap<String, serde_json::Value>,
5931}
5932
5933impl HttpChaosProfile {
5934 pub fn new() -> Self {
5936 Self::default()
5937 }
5938
5939 pub fn error_status(mut self, status: u16) -> Self {
5941 self.error_status = Some(status);
5942 self
5943 }
5944
5945 pub fn error_probability(mut self, probability: f64) -> Self {
5947 self.error_probability = Some(probability);
5948 self
5949 }
5950
5951 pub fn latency(mut self, latency: Delay) -> Self {
5953 self.latency = Some(latency);
5954 self
5955 }
5956
5957 pub fn connection_drop(mut self, drop: bool) -> Self {
5959 self.connection_drop = Some(drop);
5960 self
5961 }
5962
5963 pub fn seed(mut self, seed: i64) -> Self {
5965 self.seed = Some(seed);
5966 self
5967 }
5968}
5969
5970#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
5977#[serde(rename_all = "camelCase")]
5978pub struct ChaosStage {
5979 pub duration_millis: u64,
5981
5982 pub profiles: HashMap<String, HttpChaosProfile>,
5984}
5985
5986impl ChaosStage {
5987 pub fn new(duration_millis: u64) -> Self {
5989 Self {
5990 duration_millis,
5991 profiles: HashMap::new(),
5992 }
5993 }
5994
5995 pub fn profile(mut self, host: impl Into<String>, profile: HttpChaosProfile) -> Self {
5997 self.profiles.insert(host.into(), profile);
5998 self
5999 }
6000}
6001
6002#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
6005#[serde(rename_all = "camelCase")]
6006pub struct ChaosExperiment {
6007 #[serde(skip_serializing_if = "Option::is_none")]
6009 pub name: Option<String>,
6010
6011 #[serde(rename = "loop", skip_serializing_if = "Option::is_none")]
6014 pub loop_back: Option<bool>,
6015
6016 pub stages: Vec<ChaosStage>,
6018}
6019
6020impl ChaosExperiment {
6021 pub fn new(stages: Vec<ChaosStage>) -> Self {
6023 Self {
6024 name: None,
6025 loop_back: None,
6026 stages,
6027 }
6028 }
6029
6030 pub fn name(mut self, name: impl Into<String>) -> Self {
6032 self.name = Some(name.into());
6033 self
6034 }
6035
6036 pub fn loop_back(mut self, loop_back: bool) -> Self {
6038 self.loop_back = Some(loop_back);
6039 self
6040 }
6041}
6042
6043#[cfg(test)]
6048mod tests {
6049 use super::*;
6050
6051 #[test]
6052 fn test_grpc_services_deserialize_from_server_wire_shape() {
6053 let wire = r#"[
6056 {
6057 "name": "helloworld.Greeter",
6058 "methods": [
6059 {
6060 "name": "SayHello",
6061 "inputType": "helloworld.HelloRequest",
6062 "outputType": "helloworld.HelloReply",
6063 "clientStreaming": false,
6064 "serverStreaming": false
6065 },
6066 {
6067 "name": "LotsOfReplies",
6068 "inputType": "helloworld.HelloRequest",
6069 "outputType": "helloworld.HelloReply",
6070 "clientStreaming": false,
6071 "serverStreaming": true
6072 }
6073 ]
6074 }
6075 ]"#;
6076
6077 let services: Vec<GrpcService> = serde_json::from_str(wire).unwrap();
6078 assert_eq!(services.len(), 1);
6079 let svc = &services[0];
6080 assert_eq!(svc.name, "helloworld.Greeter");
6081 assert_eq!(svc.methods.len(), 2);
6082
6083 let unary = &svc.methods[0];
6084 assert_eq!(unary.name, "SayHello");
6085 assert_eq!(unary.input_type, "helloworld.HelloRequest");
6086 assert_eq!(unary.output_type, "helloworld.HelloReply");
6087 assert!(!unary.client_streaming);
6088 assert!(!unary.server_streaming);
6089
6090 let server_stream = &svc.methods[1];
6091 assert_eq!(server_stream.name, "LotsOfReplies");
6092 assert!(!server_stream.client_streaming);
6093 assert!(server_stream.server_streaming);
6094 }
6095
6096 #[test]
6097 fn test_grpc_method_serializes_with_camel_case_keys() {
6098 let method = GrpcMethod {
6099 name: "BidiChat".into(),
6100 input_type: "chat.Message".into(),
6101 output_type: "chat.Message".into(),
6102 client_streaming: true,
6103 server_streaming: true,
6104 };
6105 let value = serde_json::to_value(&method).unwrap();
6106 assert_eq!(value["name"], "BidiChat");
6107 assert_eq!(value["inputType"], "chat.Message");
6108 assert_eq!(value["outputType"], "chat.Message");
6109 assert_eq!(value["clientStreaming"], true);
6110 assert_eq!(value["serverStreaming"], true);
6111 }
6112
6113 #[test]
6114 fn test_grpc_services_empty_array() {
6115 let services: Vec<GrpcService> = serde_json::from_str("[]").unwrap();
6116 assert!(services.is_empty());
6117 }
6118
6119 #[test]
6120 fn test_grpc_service_round_trips() {
6121 let original = GrpcService {
6122 name: "helloworld.Greeter".into(),
6123 methods: vec![GrpcMethod {
6124 name: "SayHello".into(),
6125 input_type: "helloworld.HelloRequest".into(),
6126 output_type: "helloworld.HelloReply".into(),
6127 client_streaming: false,
6128 server_streaming: false,
6129 }],
6130 };
6131 let json = serde_json::to_string(&original).unwrap();
6132 let parsed: GrpcService = serde_json::from_str(&json).unwrap();
6133 assert_eq!(original, parsed);
6134 }
6135}