1616package com .amazon .aoc .validators ;
1717
1818import com .amazon .aoc .callers .ICaller ;
19+ import com .amazon .aoc .enums .GenericConstants ;
1920import com .amazon .aoc .exception .BaseException ;
2021import com .amazon .aoc .exception .ExceptionCode ;
2122import com .amazon .aoc .fileconfigs .FileConfig ;
2223import com .amazon .aoc .helpers .MustacheHelper ;
2324import com .amazon .aoc .helpers .RetryHelper ;
25+ import com .amazon .aoc .helpers .SortUtils ;
2426import com .amazon .aoc .models .Context ;
2527import com .amazon .aoc .models .SampleAppResponse ;
2628import com .amazon .aoc .models .ValidationConfig ;
29+ import com .amazon .aoc .models .xray .Entity ;
2730import com .amazon .aoc .services .XRayService ;
2831import com .amazonaws .services .xray .model .Segment ;
2932import com .amazonaws .services .xray .model .Trace ;
33+ import com .fasterxml .jackson .core .JsonProcessingException ;
3034import com .fasterxml .jackson .databind .ObjectMapper ;
35+ import com .fasterxml .jackson .databind .PropertyNamingStrategy ;
3136import com .github .wnameless .json .flattener .JsonFlattener ;
3237import lombok .extern .log4j .Log4j2 ;
3338
39+ import java .util .ArrayList ;
3440import java .util .Collections ;
3541import java .util .List ;
3642import java .util .Map ;
@@ -44,6 +50,9 @@ public class TraceValidator implements IValidator {
4450 private Context context ;
4551 private FileConfig expectedTrace ;
4652
53+ private static final ObjectMapper MAPPER = new ObjectMapper ()
54+ .setPropertyNamingStrategy (PropertyNamingStrategy .SNAKE_CASE );
55+
4756 @ Override
4857 public void init (
4958 Context context , ValidationConfig validationConfig , ICaller caller , FileConfig expectedTrace )
@@ -64,23 +73,35 @@ public void validate() throws Exception {
6473 List <String > traceIdList = Collections .singletonList (traceId );
6574
6675 // get retrieved trace from x-ray service
67- Map <String , Object > retrievedTrace = this .getRetrievedTrace (traceIdList );
68- log .info ("value of retrieved trace map: {}" , retrievedTrace );
69- // data model validation of other fields of segment document
70- for (Map .Entry <String , Object > entry : storedTrace .entrySet ()) {
71- String targetKey = entry .getKey ();
72- if (retrievedTrace .get (targetKey ) == null ) {
73- log .error ("mis target data: {}" , targetKey );
74- throw new BaseException (ExceptionCode .DATA_MODEL_NOT_MATCHED );
75- }
76- if (!entry .getValue ().toString ().equalsIgnoreCase (retrievedTrace .get (targetKey ).toString ())) {
77- log .error ("data model validation failed" );
78- log .info ("mis matched data model field list" );
79- log .info ("value of stored trace map: {}" , entry .getValue ());
80- log .info ("value of retrieved map: {}" , retrievedTrace .get (entry .getKey ()));
81- log .info ("==========================================" );
82- throw new BaseException (ExceptionCode .DATA_MODEL_NOT_MATCHED );
83- }
76+ boolean isMatched = RetryHelper .retry (10 ,
77+ Integer .parseInt (GenericConstants .SLEEP_IN_MILLISECONDS .getVal ()),
78+ false ,
79+ () -> {
80+ Map <String , Object > retrievedTrace = this .getRetrievedTrace (traceIdList );
81+ log .info ("value of retrieved trace map: {}" , retrievedTrace );
82+ // data model validation of other fields of segment document
83+ for (Map .Entry <String , Object > entry : storedTrace .entrySet ()) {
84+ String targetKey = entry .getKey ();
85+ if (retrievedTrace .get (targetKey ) == null ) {
86+ log .error ("mis target data: {}" , targetKey );
87+ throw new BaseException (ExceptionCode .DATA_MODEL_NOT_MATCHED );
88+ }
89+ if (!entry
90+ .getValue ()
91+ .toString ()
92+ .equalsIgnoreCase (retrievedTrace .get (targetKey ).toString ())) {
93+ log .error ("data model validation failed" );
94+ log .info ("mis matched data model field list" );
95+ log .info ("value of stored trace map: {}" , entry .getValue ());
96+ log .info ("value of retrieved map: {}" , retrievedTrace .get (entry .getKey ()));
97+ log .info ("==========================================" );
98+ throw new BaseException (ExceptionCode .DATA_MODEL_NOT_MATCHED );
99+ }
100+ }
101+ });
102+
103+ if (!isMatched ) {
104+ throw new BaseException (ExceptionCode .DATA_MODEL_NOT_MATCHED );
84105 }
85106
86107 log .info ("validation is passed for path {}" , caller .getCallingPath ());
@@ -113,30 +134,33 @@ private Map<String, Object> getRetrievedTrace(List<String> traceIdList) throws E
113134 }
114135
115136 private Map <String , Object > flattenDocument (List <Segment > segmentList ) {
116- // have to sort the segments by start_time because
117- // 1. we can not get span id from xraysdk today,
118- // 2. the segments come out with different order everytime
119- segmentList .sort (
120- (segment1 , segment2 ) -> {
121- try {
122- Map <String , Object > map1 =
123- new ObjectMapper ().readValue (segment1 .getDocument (), Map .class );
124- Map <String , Object > map2 =
125- new ObjectMapper ().readValue (segment2 .getDocument (), Map .class );
126- return Double .valueOf (map1 .get ("start_time" ).toString ())
127- .compareTo (Double .valueOf (map2 .get ("start_time" ).toString ()));
128- } catch (Exception ex ) {
129- log .error (ex );
130- return 0 ;
131- }
132- });
137+ List <Entity > entityList = new ArrayList <>();
133138
134- // build the segment's document as a jsonarray and flatten it for easy comparison
135- StringBuilder segmentsJson = new StringBuilder ("[" );
139+ // Parse retrieved segment documents into a barebones Entity POJO
136140 for (Segment segment : segmentList ) {
137- segmentsJson .append (segment .getDocument ());
138- segmentsJson .append ("," );
141+ Entity entity ;
142+ try {
143+ entity = MAPPER .readValue (segment .getDocument (), Entity .class );
144+ entityList .add (entity );
145+ } catch (JsonProcessingException e ) {
146+ log .warn ("Error parsing segment JSON" , e );
147+ }
148+ }
149+
150+ // Recursively sort all segments and subsegments so the ordering is always consistent
151+ SortUtils .recursiveEntitySort (entityList );
152+ StringBuilder segmentsJson = new StringBuilder ("[" );
153+
154+ // build the segment's document as a json array and flatten it for easy comparison
155+ for (Entity entity : entityList ) {
156+ try {
157+ segmentsJson .append (MAPPER .writeValueAsString (entity ));
158+ segmentsJson .append ("," );
159+ } catch (JsonProcessingException e ) {
160+ log .warn ("Error serializing segment JSON" , e );
161+ }
139162 }
163+
140164 segmentsJson .replace (segmentsJson .length () - 1 , segmentsJson .length (), "]" );
141165 return JsonFlattener .flattenAsMap (segmentsJson .toString ());
142166 }
0 commit comments