github.com/mweagle/Sparta@v1.15.0/archetype/xformer/xformer.go (about) 1 package xformer 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "regexp" 7 8 awsEvents "github.com/aws/aws-lambda-go/events" 9 jmesPath "github.com/jmespath/go-jmespath" 10 ) 11 12 type xformRegexpResults struct { 13 re *regexp.Regexp 14 reErr error 15 capturedNames map[string]string 16 } 17 18 // XFormableData is the struct that supports transforming the record 19 type XFormableData struct { 20 rawData []byte 21 stringData string 22 jsonData map[string]interface{} 23 xformErr error 24 regexps map[string]*xformRegexpResults 25 } 26 27 func (xf *XFormableData) regexpGroupValue(regexpString string, groupName string) interface{} { 28 results, resultsExist := xf.regexps[regexpString] 29 if !resultsExist { 30 if xf.stringData == "" { 31 xf.stringData = string(xf.rawData) 32 } 33 // Run it... 34 re, reErr := regexp.Compile(regexpString) 35 xformResults := &xformRegexpResults{ 36 re: re, 37 reErr: reErr, 38 } 39 // Parse it... 40 if reErr == nil { 41 xformResults.capturedNames = make(map[string]string) 42 matches := xformResults.re.FindStringSubmatch(xf.stringData) 43 if len(matches) > 0 { 44 for i, eachName := range xformResults.re.SubexpNames() { 45 // Ignore the whole regexp match and unnamed groups 46 if i == 0 || eachName == "" { 47 continue 48 } 49 xformResults.capturedNames[eachName] = matches[i] 50 } 51 } 52 } 53 xf.regexps[regexpString] = xformResults 54 results = xformResults 55 } 56 if results == nil || results.reErr != nil { 57 return nil 58 } 59 return results.capturedNames[groupName] 60 } 61 62 // RegExpGroup captures the output as a String 63 func (xf *XFormableData) RegExpGroup(regexpString string, groupName string) interface{} { 64 value := xf.regexpGroupValue(regexpString, groupName) 65 if value == nil { 66 return "" 67 } 68 return xf.RegExpGroupAsFormattedString(regexpString, groupName, "%s") 69 } 70 71 // RegExpGroupAsFormattedString captures the output as a String 72 func (xf *XFormableData) RegExpGroupAsFormattedString(regexpString string, 73 groupName string, 74 formatSpecifier string) interface{} { 75 value := xf.regexpGroupValue(regexpString, groupName) 76 if value == nil { 77 return "" 78 } 79 return fmt.Sprintf(formatSpecifier, value) 80 } 81 82 // RegExpGroupAsJSON captures the output as a JSON blob 83 func (xf *XFormableData) RegExpGroupAsJSON(regexpString string, groupName string) interface{} { 84 value := xf.regexpGroupValue(regexpString, groupName) 85 if value == nil { 86 return nil 87 } 88 marshalResult, marshalResultErr := json.Marshal(value) 89 if marshalResultErr != nil { 90 xf.xformErr = marshalResultErr 91 return nil 92 } 93 return string(marshalResult) 94 } 95 96 func (xf *XFormableData) jmesValue(pathSpec string) interface{} { 97 if xf.jsonData == nil { 98 xf.jsonData = make(map[string]interface{}) 99 unmarshalErr := json.Unmarshal(xf.rawData, &xf.jsonData) 100 if unmarshalErr != nil { 101 xf.xformErr = unmarshalErr 102 return nil 103 } 104 } 105 106 precompiled, compileErr := jmesPath.Compile(pathSpec) 107 if compileErr != nil { 108 xf.xformErr = compileErr 109 return nil 110 } 111 result, resultErr := precompiled.Search(xf.jsonData) 112 if resultErr != nil { 113 xf.xformErr = compileErr 114 return nil 115 } 116 return result 117 } 118 119 // JMESPath runs the query 120 func (xf *XFormableData) JMESPath(pathSpec string) interface{} { 121 value := xf.jmesValue(pathSpec) 122 if value == nil { 123 return nil 124 } 125 marshalResult, marshalResultErr := json.Marshal(value) 126 if marshalResultErr != nil { 127 xf.xformErr = marshalResultErr 128 return nil 129 } 130 return string(marshalResult) 131 } 132 133 // JMESPathAsString returns the value at pathSpec using a string formatter 134 func (xf *XFormableData) JMESPathAsString(pathSpec string) interface{} { 135 return xf.JMESPathAsFormattedString(pathSpec, "%s") 136 } 137 138 // JMESPathAsFormattedString returns the value at pathSpec using the provided go 139 // format specifier 140 func (xf *XFormableData) JMESPathAsFormattedString(pathSpec string, formatSpecifier string) interface{} { 141 value := xf.jmesValue(pathSpec) 142 if value == nil { 143 return nil 144 } 145 return fmt.Sprintf(formatSpecifier, value) 146 } 147 148 // KinesisEventHeaderInfo is the common header info in the KinesisFirehoseInput event 149 type KinesisEventHeaderInfo struct { 150 InvocationID string 151 DeliveryStreamArn string 152 SourceKinesisStreamArn string 153 Region string 154 } 155 156 // XFormer is the transformer 157 type XFormer struct { 158 Data *XFormableData 159 KinesisEventHeader *KinesisEventHeaderInfo 160 RecordID string 161 Metadata *awsEvents.KinesisFirehoseRecordMetadata 162 ApproximateArrivalTimestamp awsEvents.MilliSecondsEpochTime 163 } 164 165 // Error returns the potentially nil error from applying the 166 // transform 167 func (xf *XFormer) Error() error { 168 return xf.Data.xformErr 169 } 170 171 // NewKinesisFirehoseEventXFormer returns a new XFormer pointer initialized with the 172 // awsEvents.KinesisFirehoseEventRecord data 173 func NewKinesisFirehoseEventXFormer(kinesisEventHeader *KinesisEventHeaderInfo, 174 record *awsEvents.KinesisFirehoseEventRecord) (*XFormer, error) { 175 return &XFormer{ 176 Data: &XFormableData{ 177 rawData: record.Data, 178 regexps: make(map[string]*xformRegexpResults), 179 }, 180 KinesisEventHeader: kinesisEventHeader, 181 RecordID: record.RecordID, 182 Metadata: &record.KinesisFirehoseRecordMetadata, 183 ApproximateArrivalTimestamp: record.ApproximateArrivalTimestamp, 184 }, nil 185 }