|
26 | 26 | import com.amazon.aoc.models.ValidationConfig; |
27 | 27 | import com.amazon.aoc.services.XRayService; |
28 | 28 | import com.amazonaws.services.xray.model.Trace; |
| 29 | +import com.github.wnameless.json.flattener.JsonFlattener; |
29 | 30 | import lombok.extern.log4j.Log4j2; |
30 | 31 |
|
31 | | -import java.util.Arrays; |
32 | | -import java.util.Comparator; |
33 | | -import java.util.List; |
34 | | -import java.util.stream.Collectors; |
| 32 | +import java.util.*; |
| 33 | +import java.util.concurrent.atomic.AtomicReference; |
35 | 34 |
|
36 | 35 | @Log4j2 |
37 | 36 | public class TraceValidator implements IValidator { |
38 | | - private MustacheHelper mustacheHelper = new MustacheHelper(); |
39 | | - private static int MAX_RETRY_COUNT = 60; |
40 | | - private Context context; |
41 | | - private XRayService xrayService; |
42 | | - private ICaller caller; |
43 | | - private FileConfig expectedTrace; |
44 | | - |
45 | | - @Override |
46 | | - public void init( |
47 | | - Context context, ValidationConfig validationConfig, ICaller caller, FileConfig expectedTrace) |
48 | | - throws Exception { |
49 | | - this.context = context; |
50 | | - this.xrayService = new XRayService(context.getRegion()); |
51 | | - this.caller = caller; |
52 | | - this.expectedTrace = expectedTrace; |
53 | | - } |
54 | | - |
55 | | - @Override |
56 | | - public void validate() throws Exception { |
57 | | - List<Trace> expectedTraceList = this.getExpectedTrace(); |
58 | | - expectedTraceList.sort(Comparator.comparing(Trace::getId)); |
59 | | - RetryHelper.retry( |
60 | | - MAX_RETRY_COUNT, |
61 | | - () -> { |
62 | | - List<Trace> traceList = |
63 | | - xrayService.listTraceByIds( |
64 | | - expectedTraceList.stream() |
65 | | - .map(trace -> trace.getId()) |
66 | | - .collect(Collectors.toList())); |
67 | | - |
68 | | - traceList.sort(Comparator.comparing(Trace::getId)); |
69 | | - |
70 | | - log.info("expectedTraceList: {}", expectedTraceList); |
71 | | - log.info("traceList got from backend: {}", traceList); |
72 | | - if (expectedTraceList.size() != traceList.size()) { |
73 | | - throw new BaseException(ExceptionCode.TRACE_LIST_NOT_MATCHED); |
74 | | - } |
75 | | - |
76 | | - for (int i = 0; i != expectedTraceList.size(); ++i) { |
77 | | - Trace trace = traceList.get(i); |
78 | | - compareTwoTraces(expectedTraceList.get(i), trace); |
79 | | - } |
| 37 | + private MustacheHelper mustacheHelper = new MustacheHelper(); |
| 38 | + private XRayService xrayService; |
| 39 | + private ICaller caller; |
| 40 | + private Context context; |
| 41 | + private FileConfig expectedTrace; |
| 42 | + |
| 43 | + @Override |
| 44 | + public void init(Context context, ValidationConfig validationConfig, ICaller caller, FileConfig expectedTrace) throws Exception { |
| 45 | + this.xrayService = new XRayService(context.getRegion()); |
| 46 | + this.caller = caller; |
| 47 | + this.context = context; |
| 48 | + this.expectedTrace = expectedTrace; |
| 49 | + } |
| 50 | + |
| 51 | + @Override |
| 52 | + public void validate() throws Exception { |
| 53 | + // get stored trace |
| 54 | + Map<String, Object> storedTrace = this.getStoredTrace(); |
| 55 | + |
| 56 | + // create trace id list to retrieve trace from x-ray service |
| 57 | + String traceId = (String) storedTrace.get("trace_id"); |
| 58 | + List<String> traceIdList = Collections.singletonList(traceId); |
| 59 | + |
| 60 | + // get retrieved trace from x-ray service |
| 61 | + Map<String, Object> retrievedTrace = this.getRetrievedTrace(traceIdList); |
| 62 | + |
| 63 | + // validation of trace id |
| 64 | + if (!storedTrace.get("trace_id").equals(retrievedTrace.get("trace_id"))) { |
| 65 | + log.error("trace id validation failed"); |
| 66 | + throw new BaseException(ExceptionCode.TRACE_ID_NOT_MATCHED); |
| 67 | + } |
| 68 | + |
| 69 | + // data model validation of other fields of segment document |
| 70 | + for (Map.Entry<String, Object> entry : storedTrace.entrySet()) { |
| 71 | + if (!entry.getValue().equals(retrievedTrace.get(entry.getKey()))) { |
| 72 | + log.error("data model validation failed"); |
| 73 | + log.info("mis matched data model field list"); |
| 74 | + log.info("value of stored trace map: {}", entry.getValue()); |
| 75 | + log.info("value of retrieved map: {}",retrievedTrace.get(entry.getKey())); |
| 76 | + log.info("=========================================="); |
| 77 | + throw new BaseException(ExceptionCode.DATA_MODEL_NOT_MATCHED); |
| 78 | + } |
| 79 | + } |
| 80 | + } |
| 81 | + |
| 82 | + // this method will hit get trace from x-ray service and get retrieved trace |
| 83 | + private Map<String, Object> getRetrievedTrace(List<String> traceIdList) throws Exception { |
| 84 | + Map<String, Object> flattenedJsonMapForRetrievedTrace = null; |
| 85 | + AtomicReference<List<Trace>> retrieveTraceListAtomicReference = new AtomicReference<>(); |
| 86 | + int MAX_RETRY_COUNT = 3; |
| 87 | + |
| 88 | + RetryHelper.retry(MAX_RETRY_COUNT, () -> { |
| 89 | + List<Trace> retrieveTraceList = null; |
| 90 | + retrieveTraceList = xrayService.listTraceByIds(traceIdList); |
| 91 | + retrieveTraceListAtomicReference.set(retrieveTraceList); |
| 92 | + |
| 93 | + if (retrieveTraceList == null || retrieveTraceList.isEmpty()) { |
| 94 | + throw new BaseException(ExceptionCode.EMPTY_LIST); |
| 95 | + } |
80 | 96 | }); |
81 | | - } |
82 | 97 |
|
83 | | - private void compareTwoTraces(Trace trace1, Trace trace2) throws BaseException { |
84 | | - // check trace id |
85 | | - if (!trace1.getId().equals(trace2.getId())) { |
86 | | - throw new BaseException(ExceptionCode.TRACE_ID_NOT_MATCHED); |
| 98 | + // flattened JSON object to a map |
| 99 | + if (retrieveTraceListAtomicReference.get() != null && !retrieveTraceListAtomicReference.get().isEmpty()) { |
| 100 | + try { |
| 101 | + flattenedJsonMapForRetrievedTrace = JsonFlattener.flattenAsMap(retrieveTraceListAtomicReference.get().get(0).getSegments().get(0).getDocument()); |
| 102 | + } catch (Exception e) { |
| 103 | + log.error("exception while flattening the retrieved trace: " + e.getMessage()); |
| 104 | + e.printStackTrace(); |
| 105 | + } |
| 106 | + } else { |
| 107 | + log.error("retrieved trace list is empty or null"); |
| 108 | + throw new BaseException(ExceptionCode.EMPTY_LIST); |
| 109 | + } |
| 110 | + |
| 111 | + return flattenedJsonMapForRetrievedTrace; |
87 | 112 | } |
88 | 113 |
|
89 | | - /* |
90 | | - if (trace1.getSegments().size() != trace2.getSegments().size()) { |
91 | | - throw new BaseException(ExceptionCode.TRACE_SPAN_LIST_NOT_MATCHED); |
| 114 | + // this method will hit a http endpoints of sample web apps and get stored trace |
| 115 | + private Map<String, Object> getStoredTrace() throws Exception { |
| 116 | + Map<String, Object> flattenedJsonMapForStoredTraces = null; |
| 117 | + |
| 118 | + SampleAppResponse sampleAppResponse = this.caller.callSampleApp(); |
| 119 | + |
| 120 | + String jsonExpectedTrace = mustacheHelper.render(this.expectedTrace, context); |
| 121 | + |
| 122 | + try { |
| 123 | + // flattened JSON object to a map |
| 124 | + flattenedJsonMapForStoredTraces = JsonFlattener.flattenAsMap(jsonExpectedTrace); |
| 125 | + flattenedJsonMapForStoredTraces.put("trace_id", sampleAppResponse.getTraceId()); |
| 126 | + } catch (Exception e) { |
| 127 | + e.printStackTrace(); |
| 128 | + } |
| 129 | + |
| 130 | + return flattenedJsonMapForStoredTraces; |
92 | 131 | } |
93 | | - trace1.getSegments().sort(Comparator.comparing(Segment::getId)); |
94 | | - trace2.getSegments().sort(Comparator.comparing(Segment::getId)); |
95 | | -
|
96 | | - for (int i = 0; i != trace1.getSegments().size(); ++i) { |
97 | | - // check span id |
98 | | - if (!trace1.getSegments().get(i).getId() |
99 | | - .equals(trace2.getSegments().get(i).getId())) { |
100 | | - throw new BaseException(ExceptionCode.TRACE_SPAN_NOT_MATCHED); |
101 | | - } |
102 | | - }*/ |
103 | | - } |
104 | | - |
105 | | - // this endpoint will be a http endpoint including the path with get method |
106 | | - private List<Trace> getExpectedTrace() throws Exception { |
107 | | - SampleAppResponse sampleAppResponse = this.caller.callSampleApp(); |
108 | | - |
109 | | - // convert the trace data into xray format |
110 | | - Trace trace = new Trace(); |
111 | | - trace.setId(sampleAppResponse.getTraceId()); |
112 | | - |
113 | | - // todo: construct the trace data from the template file |
114 | | - |
115 | | - // we can support multi expected trace id to validate if need |
116 | | - return Arrays.asList(trace); |
117 | | - } |
118 | 132 | } |
0 commit comments