1use serde::Serialize;
68
69use crate::{
70 AWSDate, AWSDateTime, AWSEmail, AWSPhone, AWSTime, AWSTimestamp, AWSUrl, AppsyncError, ID,
71};
72
73pub trait IFSValueMarker: private::Sealed + Serialize {
87 fn to_value(&self) -> serde_json::Value {
89 serde_json::to_value(self).expect("cannot fail for IFSValueMarker types")
90 }
91}
92
93pub trait IFSBValueMarker: private::Sealed + Serialize {
105 fn to_value(&self) -> serde_json::Value {
107 serde_json::to_value(self).expect("cannot fail for IFSBValueMarker types")
108 }
109}
110
111mod private {
113 pub trait Sealed {}
114}
115
116macro_rules! impl_markers {
118 (nested $tr:ty, ($($t:ty),+)) => {
119 $(impl $tr for $t {})+
120 };
121 ($($tr:ty),+| $t:tt) => {
122 $(impl_markers!(nested $tr, $t);)+
123 }
124}
125impl_markers!(
126 IFSBValueMarker,
127 private::Sealed
128 | (
129 u8,
130 i8,
131 u16,
132 i16,
133 u32,
134 i32,
135 u64,
136 i64,
137 u128,
138 i128,
139 f32,
140 f64,
141 bool,
142 String,
143 &str,
144 ID,
145 AWSEmail,
146 AWSUrl,
147 AWSDate,
148 AWSTime,
149 AWSPhone,
150 AWSDateTime,
151 AWSTimestamp
152 )
153);
154impl_markers!(
155 IFSValueMarker
156 | (
157 u8,
158 i8,
159 u16,
160 i16,
161 u32,
162 i32,
163 u64,
164 i64,
165 u128,
166 i128,
167 f32,
168 f64,
169 String,
170 &str,
171 ID,
172 AWSEmail,
173 AWSUrl,
174 AWSDate,
175 AWSTime,
176 AWSPhone,
177 AWSDateTime,
178 AWSTimestamp
179 )
180);
181
182#[derive(Debug, Clone, PartialEq)]
184pub struct FixedVec<T, const N: usize>([Option<T>; N]);
185
186impl<T: Serialize, const N: usize> Serialize for FixedVec<T, N> {
187 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
188 where
189 S: serde::Serializer,
190 {
191 serializer.collect_seq(self.0.iter().flatten())
192 }
193}
194impl<T: IFSValueMarker, const N: usize> FixedVec<T, N> {
195 fn to_value(&self) -> serde_json::Value {
196 serde_json::to_value(self).expect("cannot fail for IFSValueMarker types")
197 }
198}
199
200type InVec<T> = FixedVec<T, 5>;
202
203type ContainsAnyVec<T> = FixedVec<T, 20>;
205
206macro_rules! impl_from_array {
208 (none 5) => {
209 [None, None, None, None, None]
210 };
211 (none 10) => {
212 [None, None, None, None, None, None, None, None, None, None]
213 };
214 (none 20) => {
215 [
216 None, None, None, None, None, None, None, None, None, None, None, None, None, None,
217 None, None, None, None, None, None,
218 ]
219 };
220 ($m:tt; $n:literal; $(($idx:tt, $v:ident)),*) => {
221 impl<T> From<[T; $n]> for FixedVec<T, $m> {
222 fn from([$($v),*]: [T; $n]) -> Self {
223 let mut slice = impl_from_array!(none $m);
224 $((slice[$idx]).replace($v);)*
225 Self(slice)
226 }
227 }
228 };
229}
230impl_from_array!(5; 1; (0, v1));
231impl_from_array!(5; 2; (0, v1), (1, v2));
232impl_from_array!(5; 3; (0, v1), (1, v2), (2, v3));
233impl_from_array!(5; 4; (0, v1), (1, v2), (2, v3), (3, v4));
234impl_from_array!(5; 5; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5));
235impl_from_array!(10; 1; (0, v1));
236impl_from_array!(10; 2; (0, v1), (1, v2));
237impl_from_array!(10; 3; (0, v1), (1, v2), (2, v3));
238impl_from_array!(10; 4; (0, v1), (1, v2), (2, v3), (3, v4));
239impl_from_array!(10; 5; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5));
240impl_from_array!(10; 6; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6));
241impl_from_array!(10; 7; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7));
242impl_from_array!(10; 8; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8));
243impl_from_array!(10; 9; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9));
244impl_from_array!(10; 10; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10));
245impl_from_array!(20; 1; (0, v1));
246impl_from_array!(20; 2; (0, v1), (1, v2));
247impl_from_array!(20; 3; (0, v1), (1, v2), (2, v3));
248impl_from_array!(20; 4; (0, v1), (1, v2), (2, v3), (3, v4));
249impl_from_array!(20; 5; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5));
250impl_from_array!(20; 6; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6));
251impl_from_array!(20; 7; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7));
252impl_from_array!(20; 8; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8));
253impl_from_array!(20; 9; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9));
254impl_from_array!(20; 10; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10));
255impl_from_array!(20; 11; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11));
256impl_from_array!(20; 12; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12));
257impl_from_array!(20; 13; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13));
258impl_from_array!(20; 14; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14));
259impl_from_array!(20; 15; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15));
260impl_from_array!(20; 16; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15), (15, v16));
261impl_from_array!(20; 17; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15), (15, v16), (16, v17));
262impl_from_array!(20; 18; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15), (15, v16), (16, v17), (17, v18));
263impl_from_array!(20; 19; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15), (15, v16), (16, v17), (17, v18), (18, v19));
264impl_from_array!(20; 20; (0, v1), (1, v2), (2, v3), (3, v4), (4, v5), (5, v6), (6, v7), (7, v8), (8, v9), (9, v10), (10, v11), (11, v12), (12, v13), (13, v14), (14, v15), (15, v16), (16, v17), (17, v18), (18, v19), (19, v20));
265
266#[derive(Debug, Clone, PartialEq, Serialize)]
268#[serde(transparent)]
269pub struct FieldPath(String);
270
271impl std::fmt::Display for FieldPath {
272 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273 write!(f, "{}", self.0)
274 }
275}
276
277impl FieldPath {
278 pub fn new(path: impl Into<String>) -> Result<Self, AppsyncError> {
295 let path = path.into();
296 if path.len() > 256 {
297 return Err(AppsyncError::new(
298 "ValidationError",
299 "Field path exceeds 256 characters",
300 ));
301 }
302 Ok(Self(path))
304 }
305
306 pub unsafe fn new_unchecked(path: impl Into<String>) -> Self {
320 Self(path.into())
321 }
322
323 pub fn eq<IFSB: IFSBValueMarker>(self, ifsb: IFSB) -> FieldFilter {
326 FieldFilter::new(self, ifsb.to_value(), FilterOp::Eq)
327 }
328
329 pub unsafe fn eq_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
337 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Eq)
338 }
339
340 pub fn ne<IFSB: IFSBValueMarker>(self, ifsb: IFSB) -> FieldFilter {
342 FieldFilter::new(self, ifsb.to_value(), FilterOp::Ne)
343 }
344
345 pub unsafe fn ne_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
353 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Ne)
354 }
355
356 pub fn le<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
359 FieldFilter::new(self, ifs.to_value(), FilterOp::Le)
360 }
361
362 pub unsafe fn le_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
369 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Le)
370 }
371
372 pub fn lt<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
374 FieldFilter::new(self, ifs.to_value(), FilterOp::Lt)
375 }
376
377 pub unsafe fn lt_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
384 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Lt)
385 }
386
387 pub fn ge<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
389 FieldFilter::new(self, ifs.to_value(), FilterOp::Ge)
390 }
391
392 pub unsafe fn ge_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
399 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Ge)
400 }
401
402 pub fn gt<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
404 FieldFilter::new(self, ifs.to_value(), FilterOp::Gt)
405 }
406
407 pub unsafe fn gt_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
414 FieldFilter::new(self, serde_json::to_value(value).unwrap(), FilterOp::Gt)
415 }
416
417 pub fn contains<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
419 FieldFilter::new(self, ifs.to_value(), FilterOp::Contains)
420 }
421
422 pub unsafe fn contains_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
429 FieldFilter::new(
430 self,
431 serde_json::to_value(value).unwrap(),
432 FilterOp::Contains,
433 )
434 }
435
436 pub fn not_contains<IFS: IFSValueMarker>(self, ifs: IFS) -> FieldFilter {
438 FieldFilter::new(self, ifs.to_value(), FilterOp::NotContains)
439 }
440
441 pub unsafe fn not_contains_unchecked<T: Serialize>(self, value: T) -> FieldFilter {
448 FieldFilter::new(
449 self,
450 serde_json::to_value(value).unwrap(),
451 FilterOp::NotContains,
452 )
453 }
454
455 pub fn begins_with(self, value: impl Into<String>) -> FieldFilter {
458 FieldFilter::new(
459 self,
460 serde_json::Value::String(value.into()),
461 FilterOp::BeginsWith,
462 )
463 }
464
465 pub fn in_values<IFS: IFSValueMarker>(self, values: impl Into<InVec<IFS>>) -> FieldFilter {
478 let in_vec = values.into();
479 FieldFilter::new(self, in_vec.to_value(), FilterOp::In)
480 }
481
482 pub unsafe fn in_values_unchecked<T: Serialize>(
489 self,
490 values: impl Into<InVec<T>>,
491 ) -> FieldFilter {
492 let in_vec = values.into();
493 FieldFilter::new(self, serde_json::to_value(in_vec).unwrap(), FilterOp::In)
494 }
495
496 pub fn not_in<IFS: IFSValueMarker>(self, values: impl Into<InVec<IFS>>) -> FieldFilter {
508 let in_vec = values.into();
509 FieldFilter::new(self, in_vec.to_value(), FilterOp::NotIn)
510 }
511
512 pub unsafe fn not_in_unchecked<T: Serialize>(self, values: impl Into<InVec<T>>) -> FieldFilter {
519 let in_vec = values.into();
520 FieldFilter::new(self, serde_json::to_value(in_vec).unwrap(), FilterOp::NotIn)
521 }
522
523 pub fn between<IFS: IFSValueMarker>(self, start: IFS, end: IFS) -> FieldFilter {
525 FieldFilter::new(
526 self,
527 FixedVec([Some(start), Some(end)]).to_value(),
528 FilterOp::Between,
529 )
530 }
531
532 pub unsafe fn between_unchecked<T: Serialize>(self, start: T, end: T) -> FieldFilter {
539 FieldFilter::new(
540 self,
541 serde_json::to_value(FixedVec([Some(start), Some(end)])).unwrap(),
542 FilterOp::Between,
543 )
544 }
545
546 pub fn contains_any<IFS: IFSValueMarker>(
558 self,
559 values: impl Into<ContainsAnyVec<IFS>>,
560 ) -> FieldFilter {
561 let contains_vec = values.into();
562 FieldFilter::new(self, contains_vec.to_value(), FilterOp::ContainsAny)
563 }
564
565 pub unsafe fn contains_any_unchecked<T: Serialize>(
572 self,
573 values: impl Into<ContainsAnyVec<T>>,
574 ) -> FieldFilter {
575 let contains_vec = values.into();
576 FieldFilter::new(
577 self,
578 serde_json::to_value(contains_vec).unwrap(),
579 FilterOp::ContainsAny,
580 )
581 }
582}
583
584#[derive(Debug, Clone, Serialize)]
586#[serde(rename_all = "camelCase")]
587enum FilterOp {
588 Eq,
589 Ne,
590 Le,
591 Lt,
592 Ge,
593 Gt,
594 Contains,
595 NotContains,
596 BeginsWith,
597 In,
598 NotIn,
599 Between,
600 ContainsAny,
601}
602
603#[derive(Debug, Clone, Serialize)]
618pub struct FieldFilter {
619 #[serde(rename = "fieldName")]
620 path: FieldPath,
621 operator: FilterOp,
622 value: serde_json::Value,
623}
624impl FieldFilter {
625 fn new(path: FieldPath, value: serde_json::Value, operator: FilterOp) -> Self {
627 Self {
628 path,
629 value,
630 operator,
631 }
632 }
633}
634#[derive(Debug, Clone, Serialize)]
651pub struct Filter {
652 filters: FixedVec<FieldFilter, 5>,
653}
654
655impl<T> From<T> for Filter
656where
657 T: Into<FixedVec<FieldFilter, 5>>,
658{
659 fn from(filters: T) -> Self {
660 Self {
661 filters: filters.into(),
662 }
663 }
664}
665
666impl From<FieldFilter> for Filter {
667 fn from(value: FieldFilter) -> Self {
668 Filter::from([value])
669 }
670}
671
672#[derive(Debug, Clone, Serialize)]
696pub struct FilterGroup {
697 #[serde(rename = "filterGroup")]
698 filters: FixedVec<Filter, 10>,
699}
700
701impl<T> From<T> for FilterGroup
702where
703 T: Into<FixedVec<Filter, 10>>,
704{
705 fn from(filters: T) -> Self {
706 Self {
707 filters: filters.into(),
708 }
709 }
710}
711
712impl From<FieldFilter> for FilterGroup {
713 fn from(value: FieldFilter) -> Self {
714 FilterGroup::from(Filter::from([value]))
715 }
716}
717impl From<Filter> for FilterGroup {
718 fn from(value: Filter) -> Self {
719 FilterGroup::from([value])
720 }
721}
722
723#[cfg(test)]
724mod tests {
725 use super::*;
726 use serde_json::json;
727
728 fn filter_value(f: &FieldFilter) -> serde_json::Value {
729 serde_json::to_value(f).unwrap()
730 }
731
732 #[test]
733 fn test_create_paths() {
734 let path = FieldPath::new("user.name").unwrap();
735 assert_eq!(path.to_string(), "user.name");
736
737 let path = FieldPath::new("nested.one.two.three.four.five");
738 assert!(path.is_ok());
739
740 let long_path = "a".repeat(257);
741 assert!(FieldPath::new(long_path).is_err());
742 }
743
744 #[test]
745 fn test_eq_operator() {
746 let filter = FieldPath::new("service").unwrap().eq("AWS AppSync");
748 assert_eq!(
749 filter_value(&filter),
750 json!({
751 "fieldName": "service",
752 "operator": "eq",
753 "value": "AWS AppSync"
754 })
755 );
756
757 let filter = FieldPath::new("severity").unwrap().eq(5);
759 assert_eq!(
760 filter_value(&filter),
761 json!({
762 "fieldName": "severity",
763 "operator": "eq",
764 "value": 5
765 })
766 );
767
768 let filter = FieldPath::new("enabled").unwrap().eq(true);
770 assert_eq!(
771 filter_value(&filter),
772 json!({
773 "fieldName": "enabled",
774 "operator": "eq",
775 "value": true
776 })
777 );
778 }
779
780 #[test]
781 fn test_ne_operator() {
782 let filter = FieldPath::new("service").unwrap().ne("AWS AppSync");
784 assert_eq!(
785 filter_value(&filter),
786 json!({
787 "fieldName": "service",
788 "operator": "ne",
789 "value": "AWS AppSync"
790 })
791 );
792
793 let filter = FieldPath::new("severity").unwrap().ne(5);
795 assert_eq!(
796 filter_value(&filter),
797 json!({
798 "fieldName": "severity",
799 "operator": "ne",
800 "value": 5
801 })
802 );
803
804 let filter = FieldPath::new("enabled").unwrap().ne(true);
806 assert_eq!(
807 filter_value(&filter),
808 json!({
809 "fieldName": "enabled",
810 "operator": "ne",
811 "value": true
812 })
813 );
814 }
815
816 #[test]
817 fn test_comparison_operators() {
818 let path = FieldPath::new("size").unwrap();
819
820 let filter = path.clone().le(5);
822 assert_eq!(
823 filter_value(&filter),
824 json!({
825 "fieldName": "size",
826 "operator": "le",
827 "value": 5
828 })
829 );
830
831 let filter = path.clone().lt(5);
833 assert_eq!(
834 filter_value(&filter),
835 json!({
836 "fieldName": "size",
837 "operator": "lt",
838 "value": 5
839 })
840 );
841
842 let filter = path.clone().ge(5);
844 assert_eq!(
845 filter_value(&filter),
846 json!({
847 "fieldName": "size",
848 "operator": "ge",
849 "value": 5
850 })
851 );
852
853 let filter = path.gt(5);
855 assert_eq!(
856 filter_value(&filter),
857 json!({
858 "fieldName": "size",
859 "operator": "gt",
860 "value": 5
861 })
862 );
863 }
864
865 #[test]
866 fn test_contains_operators() {
867 let path = FieldPath::new("seats").unwrap();
868
869 let filter = path.clone().contains(10);
871 assert_eq!(
872 filter_value(&filter),
873 json!({
874 "fieldName": "seats",
875 "operator": "contains",
876 "value": 10
877 })
878 );
879
880 let event_path = FieldPath::new("event").unwrap();
882 let filter = event_path.clone().contains("launch");
883 assert_eq!(
884 filter_value(&filter),
885 json!({
886 "fieldName": "event",
887 "operator": "contains",
888 "value": "launch"
889 })
890 );
891
892 let filter = path.not_contains(10);
894 assert_eq!(
895 filter_value(&filter),
896 json!({
897 "fieldName": "seats",
898 "operator": "notContains",
899 "value": 10
900 })
901 );
902 }
903
904 #[test]
905 fn test_begins_with() {
906 let filter = FieldPath::new("service").unwrap().begins_with("AWS");
907 assert_eq!(
908 filter_value(&filter),
909 json!({
910 "fieldName": "service",
911 "operator": "beginsWith",
912 "value": "AWS"
913 })
914 );
915 }
916
917 #[test]
918 fn test_in_operators() {
919 let path = FieldPath::new("severity").unwrap();
920
921 let filter = path.clone().in_values([1, 2, 3]);
923 assert_eq!(
924 filter_value(&filter),
925 json!({
926 "fieldName": "severity",
927 "operator": "in",
928 "value": [1, 2, 3]
929 })
930 );
931
932 let filter = path.not_in([1, 2, 3]);
934 assert_eq!(
935 filter_value(&filter),
936 json!({
937 "fieldName": "severity",
938 "operator": "notIn",
939 "value": [1, 2, 3]
940 })
941 );
942 }
943
944 #[test]
945 fn test_between() {
946 let filter = FieldPath::new("severity").unwrap().between(1, 5);
947 assert_eq!(
948 filter_value(&filter),
949 json!({
950 "fieldName": "severity",
951 "operator": "between",
952 "value": [1, 5]
953 })
954 );
955 }
956
957 #[test]
958 fn test_contains_any() {
959 let filter = FieldPath::new("seats").unwrap().contains_any([10, 15, 20]);
960 assert_eq!(
961 filter_value(&filter),
962 json!({
963 "fieldName": "seats",
964 "operator": "containsAny",
965 "value": [10, 15, 20]
966 })
967 );
968 }
969
970 #[test]
971 fn test_filter_group() {
972 let group = FilterGroup::from([Filter::from([
973 FieldPath::new("userId").unwrap().eq(1),
974 FieldPath::new("group")
975 .unwrap()
976 .in_values(["Admin", "Developer"]),
977 ])]);
978
979 assert_eq!(
980 serde_json::to_value(group).unwrap(),
981 json!({
982 "filterGroup": [
983 {
984 "filters": [
985 {
986 "fieldName": "userId",
987 "operator": "eq",
988 "value": 1
989 },
990 {
991 "fieldName": "group",
992 "operator": "in",
993 "value": ["Admin", "Developer"]
994 }
995 ]
996 }
997 ]
998 })
999 );
1000 }
1001
1002 #[test]
1003 fn test_complex_filter() {
1004 let group = FilterGroup::from([
1005 Filter::from([
1006 FieldPath::new("severity").unwrap().le(3),
1007 FieldPath::new("type").unwrap().eq("error"),
1008 ]),
1009 Filter::from([
1010 FieldPath::new("service").unwrap().begins_with("AWS"),
1011 FieldPath::new("region")
1012 .unwrap()
1013 .in_values(["us-east-1", "eu-west-1"]),
1014 ]),
1015 ]);
1016
1017 assert_eq!(
1018 serde_json::to_value(group).unwrap(),
1019 json!({
1020 "filterGroup": [
1021 {
1022 "filters": [
1023 {
1024 "fieldName": "severity",
1025 "operator": "le",
1026 "value": 3
1027 },
1028 {
1029 "fieldName": "type",
1030 "operator": "eq",
1031 "value": "error"
1032 }
1033 ]
1034 },
1035 {
1036 "filters": [
1037 {
1038 "fieldName": "service",
1039 "operator": "beginsWith",
1040 "value": "AWS"
1041 },
1042 {
1043 "fieldName": "region",
1044 "operator": "in",
1045 "value": ["us-east-1", "eu-west-1"]
1046 }
1047 ]
1048 }
1049 ]
1050 })
1051 );
1052 }
1053}