ondc-code-generator 0.8.4 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -5,10 +5,85 @@ package validationutils
|
|
|
5
5
|
import (
|
|
6
6
|
"fmt"
|
|
7
7
|
"strings"
|
|
8
|
+
"sync"
|
|
8
9
|
|
|
9
10
|
"github.com/AsaiYusuke/jsonpath"
|
|
10
11
|
)
|
|
11
12
|
|
|
13
|
+
// Global cache for parsed JSONPath functions
|
|
14
|
+
var (
|
|
15
|
+
pathParserCache = make(map[string]func(interface{}) ([]interface{}, error))
|
|
16
|
+
parserCacheMu sync.RWMutex
|
|
17
|
+
cacheHits uint64
|
|
18
|
+
cacheMisses uint64
|
|
19
|
+
statsMu sync.Mutex
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// getOrParseJSONPath retrieves a cached parser or parses and caches a new one
|
|
23
|
+
func getOrParseJSONPath(path string) (func(interface{}) ([]interface{}, error), error) {
|
|
24
|
+
// Try to get from cache first (read lock for fast concurrent reads)
|
|
25
|
+
parserCacheMu.RLock()
|
|
26
|
+
parser, exists := pathParserCache[path]
|
|
27
|
+
parserCacheMu.RUnlock()
|
|
28
|
+
|
|
29
|
+
if exists {
|
|
30
|
+
// Cache hit - use existing parser
|
|
31
|
+
statsMu.Lock()
|
|
32
|
+
cacheHits++
|
|
33
|
+
statsMu.Unlock()
|
|
34
|
+
return parser, nil
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Cache miss - need to parse
|
|
38
|
+
statsMu.Lock()
|
|
39
|
+
cacheMisses++
|
|
40
|
+
statsMu.Unlock()
|
|
41
|
+
|
|
42
|
+
// Acquire write lock to add to cache
|
|
43
|
+
parserCacheMu.Lock()
|
|
44
|
+
defer parserCacheMu.Unlock()
|
|
45
|
+
|
|
46
|
+
// Double-check after acquiring write lock (another goroutine might have added it)
|
|
47
|
+
if parser, exists := pathParserCache[path]; exists {
|
|
48
|
+
return parser, nil
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Parse the JSONPath expression
|
|
52
|
+
parser, err := jsonpath.Parse(path)
|
|
53
|
+
if err != nil {
|
|
54
|
+
return nil, fmt.Errorf("failed to parse JSONPath %s: %w", path, err)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Cache it for future use
|
|
58
|
+
pathParserCache[path] = parser
|
|
59
|
+
return parser, nil
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// GetParserCacheStats returns cache statistics
|
|
63
|
+
func GetParserCacheStats() (hits, misses uint64, size int) {
|
|
64
|
+
statsMu.Lock()
|
|
65
|
+
h, m := cacheHits, cacheMisses
|
|
66
|
+
statsMu.Unlock()
|
|
67
|
+
|
|
68
|
+
parserCacheMu.RLock()
|
|
69
|
+
s := len(pathParserCache)
|
|
70
|
+
parserCacheMu.RUnlock()
|
|
71
|
+
|
|
72
|
+
return h, m, s
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// ClearParserCache clears the cache (useful for testing)
|
|
76
|
+
func ClearParserCache() {
|
|
77
|
+
parserCacheMu.Lock()
|
|
78
|
+
pathParserCache = make(map[string]func(interface{}) ([]interface{}, error))
|
|
79
|
+
parserCacheMu.Unlock()
|
|
80
|
+
|
|
81
|
+
statsMu.Lock()
|
|
82
|
+
cacheHits = 0
|
|
83
|
+
cacheMisses = 0
|
|
84
|
+
statsMu.Unlock()
|
|
85
|
+
}
|
|
86
|
+
|
|
12
87
|
// isListOfStringsOrNull checks if the variable is a slice containing only strings or nil values
|
|
13
88
|
func isListOfStringsOrNull(variable interface{}) bool {
|
|
14
89
|
slice, ok := variable.([]interface{})
|
|
@@ -29,10 +104,30 @@ func isListOfStringsOrNull(variable interface{}) bool {
|
|
|
29
104
|
|
|
30
105
|
// GetJsonPath queries a payload using JSONPath and returns the results
|
|
31
106
|
func GetJsonPath(payload interface{}, path string, flattenResult bool) []interface{} {
|
|
32
|
-
|
|
33
|
-
output, err := jsonpath.Retrieve(resolvedPath, resolvedData)
|
|
107
|
+
resolvedPath, resolvedData, err := resolveJSONPathData(path, payload)
|
|
34
108
|
if err != nil {
|
|
35
|
-
fmt.Printf("Error
|
|
109
|
+
// fmt.Printf("Error resolving JSONPath %s: %v\n", path, err)
|
|
110
|
+
return []interface{}{}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Get or parse the JSONPath - uses cache if available
|
|
114
|
+
parser, err := getOrParseJSONPath(resolvedPath)
|
|
115
|
+
if err != nil {
|
|
116
|
+
// fmt.Printf("Error parsing JSONPath %s: %v\n", resolvedPath, err)
|
|
117
|
+
return []interface{}{}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Execute the parsed JSONPath function
|
|
121
|
+
result, err := parser(resolvedData)
|
|
122
|
+
if err != nil {
|
|
123
|
+
// fmt.Printf("Error retrieving JSONPath %s: %v\n", resolvedPath, err)
|
|
124
|
+
return []interface{}{}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Result is already []interface{}
|
|
128
|
+
output := result
|
|
129
|
+
|
|
130
|
+
if len(output) == 0 {
|
|
36
131
|
return []interface{}{}
|
|
37
132
|
}
|
|
38
133
|
|
|
@@ -48,9 +143,6 @@ func GetJsonPath(payload interface{}, path string, flattenResult bool) []interfa
|
|
|
48
143
|
output = flattenSlice(output)
|
|
49
144
|
}
|
|
50
145
|
|
|
51
|
-
if len(output) == 0 {
|
|
52
|
-
return []interface{}{}
|
|
53
|
-
}
|
|
54
146
|
return output
|
|
55
147
|
}
|
|
56
148
|
|
|
@@ -67,17 +159,15 @@ func flattenSlice(slice []interface{}) []interface{} {
|
|
|
67
159
|
return result
|
|
68
160
|
}
|
|
69
161
|
|
|
70
|
-
|
|
71
|
-
|
|
72
162
|
func resolveJSONPathData(path string, data interface{}) (string, interface{}, error) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
163
|
+
if input, ok := data.(ValidationInput); ok {
|
|
164
|
+
if strings.HasPrefix(path, "$._EXTERNAL._SELF") {
|
|
165
|
+
resolvedPath := strings.Replace(path, "$._EXTERNAL._SELF", "$", 1)
|
|
166
|
+
return resolvedPath, input.ExternalData["_SELF"], nil
|
|
167
|
+
} else if strings.HasPrefix(path, "$._EXTERNAL") {
|
|
78
168
|
resolvedPath := strings.Replace(path, "$._EXTERNAL", "$", 1)
|
|
79
169
|
return resolvedPath, input.ExternalData, nil
|
|
80
170
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
171
|
+
}
|
|
172
|
+
return path, data, nil
|
|
173
|
+
}
|