Skip to content

Commit 275c226

Browse files
committed
Fixed automatic array expansion based on input variables
1 parent 9635dd9 commit 275c226

File tree

1 file changed

+194
-16
lines changed

1 file changed

+194
-16
lines changed

translate.go

Lines changed: 194 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ import (
1414
"math/rand"
1515
"os"
1616
"reflect"
17-
"regexp"
1817
"sort"
1918
"strings"
2019
"sync"
2120
"time"
21+
"regexp"
2222

2323
"encoding/base64"
2424
"gopkg.in/yaml.v3"
@@ -59,6 +59,8 @@ func getRootFolder() string {
5959
return rootFolder
6060
}
6161

62+
63+
6264
func SaveQuery(inputStandard, gptTranslated string, shuffleConfig ShuffleConfig) error {
6365
if len(shuffleConfig.URL) > 0 {
6466
//return nil
@@ -95,8 +97,10 @@ func GptTranslate(keyTokenFile, standardFormat, inputDataFormat string, shuffleC
9597
systemMessage := fmt.Sprintf(`Translate the given user input JSON structure to the provided standard format in the jq format. Use the values from the standard to guide you what to look for. Ensure the output is valid JSON, and does NOT add more keys to the standard. Make sure each important key from the user input is in the standard. Empty fields in the standard are ok. If values are nested, ALWAYS add the nested value in jq format such as 'secret.version.value'. %sExample: If the standard is '{"id": "The id of the ticket", "title": "The ticket title"}', and the user input is '{"key": "12345", "fields:": {"id": "1234", "summary": "The title of the ticket"}}', the output should be '{"id": "key", "title": "fields.summary"}'. ALWAYS go deeper than the top level of the User Input and choose accurate values like "fields.id" instead of just "fields" where it fits.
9698
9799
Additional formatting rules:
100+
- jq formatting for loops is ok
98101
- Add a dollar sign in front of every translation: $key.subkey.subsubkey
99102
- If it makes sense, you can add multiple variables in the middle of descriptive text such as 'The ticket $data.id with title $data.title has been created'
103+
- If the type is an Array, make it an actual JSON array with all the relevant keys. Example: Array type 'firstname & lastname' becomes [{"firstname": "$data[].firstname", "lastname": "$data[].lastname"}]
100104
`, additionalCondition)
101105
// If translation is needed, you may use Liquid.
102106

@@ -236,6 +240,105 @@ func LiquidTranslate(ctx context.Context, userInput, translatedInput []byte) ([]
236240
return []byte(out), nil
237241
}
238242

243+
type Valuereplace struct {
244+
Key string `json:"key" datastore:"key" yaml:"key"`
245+
Value string `json:"value" datastore:"value,noindex" yaml:"value"`
246+
247+
// Used for e.g. user input storage
248+
Answer string `json:"answer,omitempty" datastore:"answer,noindex" yaml:"answer,omitempty"`
249+
}
250+
251+
// Fixes potential decision return or reference problems:
252+
// {{list_tickets}} -> $list_tickets
253+
// {{list_tickets[0].description}} -> $list_tickets.#0.description
254+
// {{ticket.description}} -> $ticket.description
255+
func TranslateBadFieldFormats(fields []Valuereplace, skipLiquid ...bool) []Valuereplace {
256+
skipLiquidCheck := false
257+
if len(skipLiquid) > 0 && skipLiquid[0] {
258+
skipLiquidCheck = true
259+
}
260+
261+
for fieldIndex, _ := range fields {
262+
field := fields[fieldIndex]
263+
if !skipLiquidCheck && (!strings.Contains(field.Value, "{{") || !strings.Contains(field.Value, "}}")) {
264+
log.Printf("[DEBUG] Schemaless: No Liquid format found in field value '%s', skipping.", field.Value)
265+
continue
266+
}
267+
268+
// App SDK pattern
269+
//matchPattern := `([$]{1}([a-zA-Z0-9_@-]+\.?){1}([a-zA-Z0-9#_@-]+\.?){0,})`
270+
271+
// Used for testing
272+
re := regexp.MustCompile(`{{\s*([a-zA-Z0-9_\.]+)(\[[0-9]*\])?(\.[a-zA-Z0-9_]+)?\s*}}`)
273+
if skipLiquidCheck {
274+
//re = regexp.MustCompile(`\s*([a-zA-Z0-9_\.]+)(\[[0-9]*\])?(\.[a-zA-Z0-9_]+)?\s*`)
275+
}
276+
277+
matches := re.FindAllStringSubmatch(field.Value, -1)
278+
if len(matches) == 0 {
279+
continue
280+
}
281+
282+
stringBuild := "$"
283+
for _, match := range matches {
284+
log.Printf("MATCH: %#v", match)
285+
286+
for i, matchValue := range match {
287+
if i == 0 {
288+
continue
289+
}
290+
291+
if i != 1 {
292+
if len(matchValue) > 0 && !strings.HasPrefix(matchValue, ".") {
293+
stringBuild += "."
294+
}
295+
}
296+
297+
if strings.HasPrefix(matchValue, "[") && strings.HasSuffix(matchValue, "]") {
298+
// Find the formats:
299+
// [] -> #
300+
// [:] -> #
301+
// [0] -> #0
302+
// [0:1] -> #0-1
303+
// [0:] -> #0-max
304+
if matchValue == "[]" || matchValue == "[:]" {
305+
stringBuild += "#"
306+
} else if strings.Contains(matchValue, ":") {
307+
parts := strings.Split(matchValue, ":")
308+
if len(parts) == 2 {
309+
stringBuild += fmt.Sprintf("#%s-%s", parts[0], parts[1])
310+
} else {
311+
stringBuild += fmt.Sprintf("#%s-max", parts[0])
312+
}
313+
314+
stringBuild += fmt.Sprintf("#%s", matchValue)
315+
} else {
316+
// Remove the brackets
317+
matchValue = strings.ReplaceAll(matchValue, "[", "")
318+
matchValue = strings.ReplaceAll(matchValue, "]", "")
319+
stringBuild += fmt.Sprintf("#%s", matchValue)
320+
}
321+
322+
continue
323+
}
324+
325+
stringBuild += matchValue
326+
}
327+
328+
log.Printf("VALUE: %#v", field.Value)
329+
if len(match) > 1 {
330+
field.Value = strings.ReplaceAll(field.Value, match[0], stringBuild)
331+
fields[fieldIndex].Value = field.Value
332+
//log.Printf("VALUE: %#v", field.Value)
333+
}
334+
335+
stringBuild = "$"
336+
}
337+
}
338+
339+
return fields
340+
}
341+
239342
func GetStructureFromCache(ctx context.Context, inputKeyToken string) (map[string]interface{}, error) {
240343
// Making sure it's not too long
241344
inputKeyTokenMd5 := fmt.Sprintf("%x", md5.Sum([]byte(inputKeyToken)))
@@ -811,6 +914,7 @@ func recurseFindKey(input map[string]interface{}, key string, depth int) (string
811914
log.Printf("[ERROR] Schemaless reverse (11): Error marshalling list value: %v", err)
812915
}
813916

917+
814918
// Checks if we are supposed to check the list or not
815919
if len(parsedKey) > 0 && string(parsedKey[0]) == "#" {
816920
// Trim until first dot (.)
@@ -860,7 +964,8 @@ func recurseFindKey(input map[string]interface{}, key string, depth int) (string
860964

861965
// If we get them recursed with .#.#
862966
if len(marshalled) > 2 {
863-
return "schemaless_list" + string(marshalled), nil
967+
toReturn := fmt.Sprintf("schemaless_list%s", string(marshalled))
968+
return toReturn, nil
864969
} else {
865970
return "", nil
866971
}
@@ -976,12 +1081,17 @@ func handleMultiListItems(translatedInput []interface{}, parentKey string, parse
9761081
// strings -> build it out.
9771082
//for childKey, v := range parsedValues {
9781083

979-
log.Printf("\n\n\nSTARTING NEW LIST\n\n\n")
1084+
if debug {
1085+
log.Printf("\n\n[DEBUG] STARTING NEW LIST\n\n")
1086+
}
1087+
9801088
newParsedValues := parsedValues
9811089
for childKey, _ := range newParsedValues {
982-
log.Printf("\n\nCHILDKEY START (%d): %#v\n\n", childIndex, childKey)
983-
v := parsedValues[childKey]
1090+
if debug {
1091+
log.Printf("[DEBUG] CHILDKEY START (%d): %#v", childIndex, childKey)
1092+
}
9841093

1094+
v := parsedValues[childKey]
9851095
if val, ok := v.(map[string]interface{}); ok {
9861096
// By passing in translatedInput we allow child objects to modify the parent?
9871097
newKey := fmt.Sprintf("%s.%s", parentKey, childKey)
@@ -1000,17 +1110,48 @@ func handleMultiListItems(translatedInput []interface{}, parentKey string, parse
10001110
}
10011111

10021112
} else if val, ok := v.(string); ok {
1113+
1114+
// Handle list expansion
10031115
if strings.Contains(val, "schemaless_list[") {
1004-
//if val == "schemaless_list[]" || val == "schemaless_list" {
1005-
//}
10061116

10071117
foundList := strings.Split(val, "schemaless_list")
10081118
if len(foundList) >= 2 {
10091119

10101120
// Somehow ALWAYS look for the first INNER list
10111121
// This makes it so that extrapolation can be done well across all fields everywhere
1012-
10131122
matchingList := strings.Join(foundList[1:], "schemaless_list")
1123+
newListStr := ""
1124+
1125+
recording := false
1126+
bracketCount := 0
1127+
for _, matchChar := range matchingList {
1128+
if matchChar == '[' {
1129+
recording = true
1130+
bracketCount += 1
1131+
if bracketCount == 1 {
1132+
//continue
1133+
}
1134+
}
1135+
1136+
if recording {
1137+
newListStr += string(matchChar)
1138+
}
1139+
1140+
if matchChar == ']' {
1141+
bracketCount -= 1
1142+
if bracketCount == 0 {
1143+
recording = false
1144+
break
1145+
}
1146+
}
1147+
1148+
1149+
}
1150+
1151+
if strings.HasPrefix(newListStr, "[") && strings.HasSuffix(newListStr, "]") {
1152+
matchingList = newListStr
1153+
}
1154+
10141155
unmarshalledList := []string{}
10151156
err := json.Unmarshal([]byte(matchingList), &unmarshalledList)
10161157
if err != nil {
@@ -1061,17 +1202,30 @@ func handleMultiListItems(translatedInput []interface{}, parentKey string, parse
10611202
newModList := modificationList[cnt].(map[string]interface{})
10621203
marshalledMap, err := json.Marshal(newModList)
10631204
if err == nil {
1064-
comparisonString := fmt.Sprintf(`"%s":"schemaless_list[`, newKey)
1065-
if !strings.Contains(string(marshalledMap), comparisonString) {
1066-
continue
1205+
// FIXME: This part is going wrong with additional items in there
1206+
//comparisonString := fmt.Sprintf(`"%s":"schemaless_list[`, newKey)
1207+
//if !strings.Contains(string(marshalledMap), comparisonString) {
1208+
1209+
if strings.Contains(string(marshalledMap), fmt.Sprintf(`"%s":"`, newKey)) && strings.Contains(string(marshalledMap), `schemaless_list[`) {
1210+
// Replace the schemaless_list[...] part with the listValue
1211+
listValue = strings.Replace(val, fmt.Sprintf("schemaless_list%s", matchingList), listValue, 1)
10671212

1213+
} else {
1214+
continue
10681215
}
10691216

1070-
log.Printf("Listvalue to put '%s' in key '%s': %s", listValue, newKey, string(marshalledMap))
1217+
if debug {
1218+
log.Printf("[DEBUG] Listvalue to put '%s' in key '%s': %s", listValue, newKey, string(marshalledMap))
1219+
}
1220+
} else {
1221+
log.Printf("[ERROR] Schemaless: Error marshalling map during list handling: %v", err)
10711222
}
10721223

10731224
newModList, _ = setNestedMap(newModList, newKey, listValue)
1074-
log.Printf("NEW VALUE: %#v", newModList)
1225+
if debug {
1226+
log.Printf("[DEBUG] NEW VALUE: %#v", newModList)
1227+
}
1228+
10751229
modificationList[cnt] = newModList
10761230
updated = true
10771231
//break
@@ -1084,7 +1238,9 @@ func handleMultiListItems(translatedInput []interface{}, parentKey string, parse
10841238
}
10851239

10861240
marshalled, _ := json.MarshalIndent(modificationList, "", "\t")
1087-
log.Printf("MARSHALLED (%d): %s.", listDepth, string(marshalled))
1241+
if debug {
1242+
log.Printf("[DEBUG] MARSHALLED (%d): %s.", listDepth, string(marshalled))
1243+
}
10881244

10891245
if listDepth > 0 {
10901246
// Updates the child & here
@@ -1276,7 +1432,6 @@ func runJsonTranslation(ctx context.Context, inputValue []byte, translation map[
12761432
continue
12771433
}
12781434

1279-
// Runs an actual translation
12801435
output, _, err := runJsonTranslation(ctx, inputValue, newValue)
12811436
if err != nil {
12821437
log.Printf("[ERROR] Schemaless: Error in runJsonTranslation for key '%s': %v", translationKey, err)
@@ -1298,6 +1453,10 @@ func runJsonTranslation(ctx context.Context, inputValue []byte, translation map[
12981453
// Hard to optimise for subkeys -> parent control tho
12991454
if strings.Contains(string(output), "schemaless_list[") {
13001455
//newTranslatedInput := handleMultiListItems(newOutput, translationKey, outputParsed)
1456+
if debug {
1457+
log.Printf("\n\n\nLIST: %s\n\n\n", string(output))
1458+
}
1459+
13011460
newTranslatedInput := handleMultiListItems(newOutput, "", outputParsed, 0, 0)
13021461
translationValue = newTranslatedInput
13031462

@@ -1347,7 +1506,21 @@ func runJsonTranslation(ctx context.Context, inputValue []byte, translation map[
13471506
translatedInput[translationKey] = translationValueParsed
13481507

13491508
} else if val, ok := translationValue.(string); ok {
1350-
log.Printf("[DEBUG] Schemaless: Looking for field %#v in input field %#v", translationValue, translationKey)
1509+
//log.Printf("[DEBUG] Schemaless: Looking for field %#v in input field %#v", translationValue, translationKey)
1510+
1511+
// Basic, default translator
1512+
if strings.Contains(val, "[") {
1513+
fields := []Valuereplace{
1514+
Valuereplace{
1515+
Value: val,
1516+
},
1517+
}
1518+
1519+
fields = TranslateBadFieldFormats(fields, true)
1520+
if len(fields) == 1 {
1521+
val = fields[0].Value
1522+
}
1523+
}
13511524

13521525
if strings.Contains(val, ".") {
13531526
//if debug {
@@ -1381,6 +1554,11 @@ func runJsonTranslation(ctx context.Context, inputValue []byte, translation map[
13811554
log.Printf("[ERROR] Schemaless: Error in RecurseFindKey for match %#v: %v", match, err)
13821555
}
13831556

1557+
newOutput = strings.ReplaceAll(newOutput, match, recursed)
1558+
if strings.Contains(match, ".#") {
1559+
match = strings.ReplaceAll(match, ".#", "[]")
1560+
}
1561+
13841562
newOutput = strings.ReplaceAll(newOutput, match, recursed)
13851563
}
13861564

0 commit comments

Comments
 (0)