ondc-code-generator 0.8.4 → 0.8.6

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.
@@ -176,73 +176,73 @@
176
176
  "$.message.catalog['bpp/fulfillments'][*].contact.phone",
177
177
  "$.message.catalog['bpp/fulfillments'][*].contact.email"
178
178
  ],
179
- "item": [
180
- "$.items[*].id",
181
- "$.items[*].parent_item_id",
182
- "$.items[*].descriptor.name",
183
- "$.items[*].descriptor.code",
184
- "$.items[*].descriptor.symbol",
185
- "$.items[*].descriptor.short_desc",
186
- "$.items[*].descriptor.long_desc",
187
- "$.items[*].descriptor.images[*]",
188
- "$.items[*].descriptor.tags[*].code",
189
- "$.items[*].descriptor.tags[*].list[*].code",
190
- "$.items[*].descriptor.tags[*].list[*].name",
191
- "$.items[*].descriptor.tags[*].list[*].value",
192
- "$.items[*].descriptor.additional_desc.content_type",
193
- "$.items[*].price.currency",
194
- "$.items[*].price.value",
195
- "$.items[*].price.maximum_value",
196
- "$.items[*].price.tags[*].code",
197
- "$.items[*].price.tags[*].list[*].code",
198
- "$.items[*].price.tags[*].list[*].name",
199
- "$.items[*].price.tags[*].list[*].value",
200
- "$.items[*].quantity.available.count",
201
- "$.items[*].quantity.maximum.count",
202
- "$.items[*].quantity.unitized.count",
203
- "$.items[*].quantity.unitized.measure.unit",
204
- "$.items[*].quantity.unitized.measure.value",
205
- "$.items[*].quantity.count",
206
- "$.items[*].category_id",
207
- "$.items[*].category_ids[*]",
208
- "$.items[*].fulfillment_id",
209
- "$.items[*].fulfillment_ids[*]",
210
- "$.items[*].replacement_terms[*].replace_within.duration",
211
- "$.items[*].rating",
212
- "$.items[*].location_id",
213
- "$.items[*].time.label",
214
- "$.items[*].time.timestamp",
215
- "$.items[*].time.range.start",
216
- "$.items[*].time.range.end",
217
- "$.items[*].time.days",
218
- "$.items[*].time.schedule.frequency",
219
- "$.items[*].time.schedule.holidays[*]",
220
- "$.items[*].time.schedule.times[*]",
221
- "$.items[*].related",
222
- "$.items[*].recommended",
223
- "$.items[*]['@ondc/org/returnable']",
224
- "$.items[*]['@ondc/org/seller_pickup_return']",
225
- "$.items[*]['@ondc/org/return_window']",
226
- "$.items[*]['@ondc/org/cancellable']",
227
- "$.items[*]['@ondc/org/time_to_ship']",
228
- "$.items[*]['@ondc/org/available_on_cod']",
229
- "$.items[*]['@ondc/org/contact_details_consumer_care']",
230
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].common_or_generic_name_of_commodity",
231
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].month_year_of_manufacture_packing_import",
232
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].manufacturer_or_packer_name",
233
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].manufacturer_or_packer_address",
234
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].mfg_license_no",
235
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].multiple_products_name_number_or_qty",
236
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].net_quantity_or_measure_of_commodity_in_pkg",
237
- "$.items[*]['@ondc/org/statutory_reqs_packaged_commodities'].expiry_date",
238
- "$.items[*]['@ondc/org/statutory_reqs_prepackaged_food'].nutritional_info",
239
- "$.items[*]['@ondc/org/statutory_reqs_prepackaged_food'].additives_info",
240
- "$.items[*]['@ondc/org/statutory_reqs_prepackaged_food'].brand_owner_FSSAI_license_no",
241
- "$.items[*]['@ondc/org/statutory_reqs_prepackaged_food'].other_FSSAI_license_no",
242
- "$.items[*]['@ondc/org/statutory_reqs_prepackaged_food'].importer_FSSAI_license_no",
243
- "$.items[*].tags[*].code",
244
- "$.items[*].tags[*].list[*].code",
245
- "$.items[*].tags[*].list[*].name",
246
- "$.items[*].tags[*].list[*].value"
179
+ "items": [
180
+ "$.id",
181
+ "$.parent_item_id",
182
+ "$.descriptor.name",
183
+ "$.descriptor.code",
184
+ "$.descriptor.symbol",
185
+ "$.descriptor.short_desc",
186
+ "$.descriptor.long_desc",
187
+ "$.descriptor.images[*]",
188
+ "$.descriptor.tags[*].code",
189
+ "$.descriptor.tags[*].list[*].code",
190
+ "$.descriptor.tags[*].list[*].name",
191
+ "$.descriptor.tags[*].list[*].value",
192
+ "$.descriptor.additional_desc.content_type",
193
+ "$.price.currency",
194
+ "$.price.value",
195
+ "$.price.maximum_value",
196
+ "$.price.tags[*].code",
197
+ "$.price.tags[*].list[*].code",
198
+ "$.price.tags[*].list[*].name",
199
+ "$.price.tags[*].list[*].value",
200
+ "$.quantity.available.count",
201
+ "$.quantity.maximum.count",
202
+ "$.quantity.unitized.count",
203
+ "$.quantity.unitized.measure.unit",
204
+ "$.quantity.unitized.measure.value",
205
+ "$.quantity.count",
206
+ "$.category_id",
207
+ "$.category_ids[*]",
208
+ "$.fulfillment_id",
209
+ "$.fulfillment_ids[*]",
210
+ "$.replacement_terms[*].replace_within.duration",
211
+ "$.rating",
212
+ "$.location_id",
213
+ "$.time.label",
214
+ "$.time.timestamp",
215
+ "$.time.range.start",
216
+ "$.time.range.end",
217
+ "$.time.days",
218
+ "$.time.schedule.frequency",
219
+ "$.time.schedule.holidays[*]",
220
+ "$.time.schedule.times[*]",
221
+ "$.related",
222
+ "$.recommended",
223
+ "$['@ondc/org/returnable']",
224
+ "$['@ondc/org/seller_pickup_return']",
225
+ "$['@ondc/org/return_window']",
226
+ "$['@ondc/org/cancellable']",
227
+ "$['@ondc/org/time_to_ship']",
228
+ "$['@ondc/org/available_on_cod']",
229
+ "$['@ondc/org/contact_details_consumer_care']",
230
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].common_or_generic_name_of_commodity",
231
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].month_year_of_manufacture_packing_import",
232
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].manufacturer_or_packer_name",
233
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].manufacturer_or_packer_address",
234
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].mfg_license_no",
235
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].multiple_products_name_number_or_qty",
236
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].net_quantity_or_measure_of_commodity_in_pkg",
237
+ "$['@ondc/org/statutory_reqs_packaged_commodities'].expiry_date",
238
+ "$['@ondc/org/statutory_reqs_prepackaged_food'].nutritional_info",
239
+ "$['@ondc/org/statutory_reqs_prepackaged_food'].additives_info",
240
+ "$['@ondc/org/statutory_reqs_prepackaged_food'].brand_owner_FSSAI_license_no",
241
+ "$['@ondc/org/statutory_reqs_prepackaged_food'].other_FSSAI_license_no",
242
+ "$['@ondc/org/statutory_reqs_prepackaged_food'].importer_FSSAI_license_no",
243
+ "$.tags[*].code",
244
+ "$.tags[*].list[*].code",
245
+ "$.tags[*].list[*].name",
246
+ "$.tags[*].list[*].value"
247
247
  ]
248
248
  }
@@ -82,24 +82,24 @@ export class GoGenerator extends CodeGenerator {
82
82
  });
83
83
  for (const action of allActions) {
84
84
  const loadData = relevantSessionData[action] || {};
85
+ console.log("Load data for action", action, loadData);
85
86
  const saveData = sessionData[action] || {};
86
87
  const saveCode = Mustache.render(saveActionTemplate, {
87
- storeActions: Object.keys(saveData)
88
- .filter((f) => f !== "_SELF")
89
- .map((key) => {
90
- return {
91
- key: key,
92
- value: saveData[key],
93
- };
94
- }),
95
- loadActions: Object.keys(loadData)
96
- .filter((f) => f !== "_SELF")
97
- .map((key) => {
98
- console.log(loadData[key]);
99
- return {
100
- key: loadData[key],
101
- };
102
- }),
88
+ storeActions: [
89
+ ...new Set(Object.keys(saveData)
90
+ .filter((f) => f !== "_SELF")
91
+ .map((key) => saveData[key])),
92
+ ].map((value) => ({
93
+ key: Object.keys(saveData).find((k) => saveData[k] === value), // original key
94
+ value: value,
95
+ })),
96
+ loadActions: [
97
+ ...new Set(Object.keys(loadData)
98
+ .filter((f) => f !== "_SELF")
99
+ .map((key) => loadData[key])),
100
+ ].map((value) => ({
101
+ key: value,
102
+ })),
103
103
  action: action,
104
104
  });
105
105
  await writeAndFormatCode(this.rootPath, `./${packageName}/storageutils/${action}.go`, saveCode, "go");
@@ -277,7 +277,8 @@ ${importList.map((imp) => `\t${imp}`).join("\n")}
277
277
  let elementsList = [];
278
278
  // REPLACE ALL special WITH empty AND SPLIT
279
279
  if (typeof returnStatement === "string") {
280
- elementsList = removeAllSpecialCharacters(returnStatement).split(" ");
280
+ elementsList =
281
+ removeAllSpecialCharacters(returnStatement).split(" ");
281
282
  }
282
283
  if (continueStatement) {
283
284
  const contElements = removeAllSpecialCharacters(continueStatement).split(" ");
@@ -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
- resolvedPath, resolvedData, err := resolveJSONPathData(path, payload)
33
- output, err := jsonpath.Retrieve(resolvedPath, resolvedData)
107
+ resolvedPath, resolvedData, err := resolveJSONPathData(path, payload)
34
108
  if err != nil {
35
- fmt.Printf("Error retrieving JSONPath %s: %v\n", path, err)
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
- if input, ok := data.(ValidationInput); ok {
74
- if strings.HasPrefix(path, "$._EXTERNAL._SELF") {
75
- resolvedPath := strings.Replace(path, "$._EXTERNAL._SELF", "$", 1)
76
- return resolvedPath, input.ExternalData["_SELF"], nil
77
- }else if( strings.HasPrefix(path, "$._EXTERNAL")){
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
- return path, data, nil
83
- }
171
+ }
172
+ return path, data, nil
173
+ }
@@ -5,6 +5,8 @@ package validationutils
5
5
  import (
6
6
  "fmt"
7
7
  "strconv"
8
+ "strings"
9
+ "sync"
8
10
  "time"
9
11
 
10
12
  "github.com/dlclark/regexp2"
@@ -34,7 +36,7 @@ func AreUnique(operand []interface{}) bool {
34
36
  if !ok {
35
37
  return false
36
38
  }
37
-
39
+
38
40
  valuesSet := make(map[string]struct{})
39
41
  for _, v := range strs {
40
42
  valuesSet[v] = struct{}{}
@@ -199,7 +201,7 @@ func EqualTo(left []interface{}, right []interface{}) bool {
199
201
  if !ok1 || !ok2 {
200
202
  return false
201
203
  }
202
-
204
+
203
205
  if len(leftStrs) != len(rightStrs) {
204
206
  return false
205
207
  }
@@ -217,7 +219,7 @@ func GreaterThan(left []interface{}, right []interface{}) bool {
217
219
  if !ok1 || !ok2 {
218
220
  return false
219
221
  }
220
-
222
+
221
223
  areAllISO := func(arr []string) bool {
222
224
  for _, v := range arr {
223
225
  if !isISO8601(v) {
@@ -287,7 +289,7 @@ func LessThan(left []interface{}, right []interface{}) bool {
287
289
  if !ok1 || !ok2 {
288
290
  return false
289
291
  }
290
-
292
+
291
293
  areAllISO := func(arr []string) bool {
292
294
  for _, v := range arr {
293
295
  if !isISO8601(v) {
@@ -351,21 +353,36 @@ func LessThan(left []interface{}, right []interface{}) bool {
351
353
  return false
352
354
  }
353
355
 
356
+ var regexCache sync.Map // cache compiled regexes
357
+
354
358
  func FollowRegex(left []interface{}, regexArray []interface{}) bool {
355
359
  leftStrs, ok1 := toStringSlice(left)
356
360
  regexStrs, ok2 := toStringSlice(regexArray)
357
361
  if !ok1 || !ok2 {
358
362
  return false
359
363
  }
360
-
364
+
361
365
  if len(leftStrs) == 0 && len(regexStrs) != 0 {
362
366
  return false
363
367
  }
368
+
364
369
  for _, regexStr := range regexStrs {
365
- re, err := regexp2.Compile(regexStr, 0)
366
- if err != nil {
367
- return false
370
+ var re *regexp2.Regexp
371
+ regexStr = sanitizeRegex(regexStr)
372
+ fmt.Printf("Using regex: %s\n", regexStr)
373
+ // Check cache first
374
+ if cached, ok := regexCache.Load(regexStr); ok {
375
+ re = cached.(*regexp2.Regexp)
376
+ } else {
377
+ compiled, err := regexp2.Compile(regexStr, 0)
378
+ if err != nil {
379
+ fmt.Printf("Invalid regex '%s': %v\n", regexStr, err)
380
+ return false
381
+ }
382
+ regexCache.Store(regexStr, compiled)
383
+ re = compiled
368
384
  }
385
+
369
386
  for _, v := range leftStrs {
370
387
  match, err := re.MatchString(v)
371
388
  if err != nil || !match {
@@ -373,15 +390,23 @@ func FollowRegex(left []interface{}, regexArray []interface{}) bool {
373
390
  }
374
391
  }
375
392
  }
393
+
376
394
  return true
377
395
  }
378
396
 
397
+ // Only fixes escapes that are invalid in regexp2
398
+ func sanitizeRegex(regex string) string {
399
+ regex = strings.ReplaceAll(regex, `\_`, `_`) // escaped underscore → literal
400
+ regex = strings.ReplaceAll(regex, `\-`, `-`) // escaped hyphen → literal
401
+ return regex
402
+ }
403
+
379
404
  func isISO8601(str string) bool {
380
405
  iso8601Regex, err := regexp2.Compile(`^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$`, 0)
381
406
  if err != nil {
382
407
  return false
383
408
  }
384
-
409
+
385
410
  match, err := iso8601Regex.MatchString(str)
386
411
  if err != nil || !match {
387
412
  return false
@@ -401,4 +426,4 @@ func StringSliceToInterface(strs []string) []interface{} {
401
426
  }
402
427
 
403
428
  // UnusedFunction to avoid unused variable errors
404
- func UnusedFunction(item interface{}) {}
429
+ func UnusedFunction(item interface{}) {}
@@ -0,0 +1,132 @@
1
+ package validationutils
2
+
3
+ import "testing"
4
+
5
+ func TestToStringSlice(t *testing.T) {
6
+ got, ok := toStringSlice([]interface{}{"a", 10, true, nil})
7
+ if !ok {
8
+ t.Fatalf("expected conversion to succeed")
9
+ }
10
+ want := []string{"a", "10", "true", "null"}
11
+ if len(got) != len(want) {
12
+ t.Fatalf("length mismatch: got=%d want=%d", len(got), len(want))
13
+ }
14
+ for i := range want {
15
+ if got[i] != want[i] {
16
+ t.Fatalf("unexpected value at %d: got=%q want=%q", i, got[i], want[i])
17
+ }
18
+ }
19
+ }
20
+
21
+ func TestAreUniqueAndPresent(t *testing.T) {
22
+ if !AreUnique([]interface{}{"x", "y"}) {
23
+ t.Fatalf("expected unique slice to pass")
24
+ }
25
+ if AreUnique([]interface{}{"x", "x"}) {
26
+ t.Fatalf("expected duplicate slice to fail")
27
+ }
28
+ if !ArePresent([]interface{}{"value"}) {
29
+ t.Fatalf("expected non-empty non-null values to be present")
30
+ }
31
+ if ArePresent([]interface{}{}) || ArePresent([]interface{}{"null"}) || ArePresent([]interface{}{"undefined"}) {
32
+ t.Fatalf("expected empty/null/undefined slices to fail presence")
33
+ }
34
+ }
35
+
36
+ func TestSetComparators(t *testing.T) {
37
+ tests := []struct {
38
+ name string
39
+ fn func([]interface{}, []interface{}) bool
40
+ left []interface{}
41
+ right []interface{}
42
+ want bool
43
+ }{
44
+ {"allin true", AllIn, []interface{}{"a", "b"}, []interface{}{"a", "b", "c"}, true},
45
+ {"allin false", AllIn, []interface{}{"a", "z"}, []interface{}{"a", "b", "c"}, false},
46
+ {"anyin true", AnyIn, []interface{}{"x", "b"}, []interface{}{"a", "b", "c"}, true},
47
+ {"anyin false", AnyIn, []interface{}{"x", "y"}, []interface{}{"a", "b", "c"}, false},
48
+ {"nonein true", NoneIn, []interface{}{"x", "y"}, []interface{}{"a", "b", "c"}, true},
49
+ {"nonein false", NoneIn, []interface{}{"x", "b"}, []interface{}{"a", "b", "c"}, false},
50
+ {"regression empty-left allin false", AllIn, []interface{}{}, []interface{}{"a"}, false},
51
+ {"regression empty-left anyin false", AnyIn, []interface{}{}, []interface{}{"a"}, false},
52
+ {"empty-left nonein true", NoneIn, []interface{}{}, []interface{}{"a"}, true},
53
+ {"map lookup", AllIn, []interface{}{"k1", "k2"}, []interface{}{map[string]interface{}{"k1": 1, "k2": 2}}, true},
54
+ }
55
+
56
+ for _, tt := range tests {
57
+ t.Run(tt.name, func(t *testing.T) {
58
+ if got := tt.fn(tt.left, tt.right); got != tt.want {
59
+ t.Fatalf("unexpected result: got=%v want=%v", got, tt.want)
60
+ }
61
+ })
62
+ }
63
+ }
64
+
65
+ func TestEqualTo(t *testing.T) {
66
+ if !EqualTo([]interface{}{"1", "2"}, []interface{}{"1", "2"}) {
67
+ t.Fatalf("expected equal slices to match")
68
+ }
69
+ if EqualTo([]interface{}{"1", "2"}, []interface{}{"2", "1"}) {
70
+ t.Fatalf("expected different-order slices to fail")
71
+ }
72
+ if EqualTo([]interface{}{"1"}, []interface{}{"1", "2"}) {
73
+ t.Fatalf("expected different-length slices to fail")
74
+ }
75
+ }
76
+
77
+ func TestGreaterThanAndLessThan(t *testing.T) {
78
+ if !GreaterThan([]interface{}{"10", "20"}, []interface{}{"1", "2"}) {
79
+ t.Fatalf("expected numeric greater-than to pass")
80
+ }
81
+ if GreaterThan([]interface{}{"1"}, []interface{}{"2"}) {
82
+ t.Fatalf("expected numeric greater-than to fail")
83
+ }
84
+ if !LessThan([]interface{}{"1", "2"}, []interface{}{"10", "20"}) {
85
+ t.Fatalf("expected numeric less-than to pass")
86
+ }
87
+ if LessThan([]interface{}{"2"}, []interface{}{"1"}) {
88
+ t.Fatalf("expected numeric less-than to fail")
89
+ }
90
+
91
+ leftDates := []interface{}{"2025-01-02T00:00:00Z", "2025-01-03T00:00:00Z"}
92
+ rightDates := []interface{}{"2025-01-01T00:00:00Z", "2025-01-02T00:00:00Z"}
93
+ if !GreaterThan(leftDates, rightDates) {
94
+ t.Fatalf("expected date greater-than to pass")
95
+ }
96
+
97
+ leftOlderDates := []interface{}{"2025-01-01T00:00:00Z", "2025-01-02T00:00:00Z"}
98
+ rightNewerDates := []interface{}{"2025-01-02T00:00:00Z", "2025-01-03T00:00:00Z"}
99
+ if !LessThan(leftOlderDates, rightNewerDates) {
100
+ t.Fatalf("expected date less-than to pass")
101
+ }
102
+
103
+ if GreaterThan([]interface{}{"abc"}, []interface{}{"1"}) || LessThan([]interface{}{"abc"}, []interface{}{"1"}) {
104
+ t.Fatalf("expected non-comparable values to fail")
105
+ }
106
+ }
107
+
108
+ func TestRegexAndISOHelpers(t *testing.T) {
109
+ if !FollowRegex([]interface{}{"123", "456"}, []interface{}{`^\d+$`}) {
110
+ t.Fatalf("expected all values to match regex")
111
+ }
112
+ if FollowRegex([]interface{}{"123", "abc"}, []interface{}{`^\d+$`}) {
113
+ t.Fatalf("expected mixed values to fail regex check")
114
+ }
115
+ if FollowRegex([]interface{}{"123"}, []interface{}{`[`}) {
116
+ t.Fatalf("expected invalid regex to fail")
117
+ }
118
+
119
+ if !isISO8601("2025-01-02T03:04:05Z") {
120
+ t.Fatalf("expected valid RFC3339 timestamp")
121
+ }
122
+ if isISO8601("2025-01-02") {
123
+ t.Fatalf("expected date-only string to fail")
124
+ }
125
+ }
126
+
127
+ func TestStringSliceToInterface(t *testing.T) {
128
+ got := StringSliceToInterface([]string{"a", "b"})
129
+ if len(got) != 2 || got[0] != "a" || got[1] != "b" {
130
+ t.Fatalf("unexpected conversion result: %#v", got)
131
+ }
132
+ }
@@ -129,6 +129,9 @@ export class VariableValidator extends TestObjectValidator {
129
129
  const replaced = replaceBracketsWithAsteriskNested(path);
130
130
  if (this.minimal)
131
131
  return;
132
+ // skip validation for paths that include both xinput and data as they are dynamic paths that can't be validated against the schema
133
+ if (replaced.includes("xinput") && replaced.includes("data"))
134
+ return;
132
135
  if (!this.possibleJsonPaths.includes(replaced)) {
133
136
  throw new Error(`Variable: ${key} should be a jsonPath that returns a array of strings or the path don't exist in the schema, at ${this.validationPath} found original ${path} replaces: ${replaced}`);
134
137
  }
package/package.json CHANGED
@@ -1,48 +1,48 @@
1
1
  {
2
- "name": "ondc-code-generator",
3
- "version": "0.8.4",
4
- "description": "generate code from build.yaml ",
5
- "main": "dist/index.js",
6
- "type": "module",
7
- "bin": {
8
- "ondc-code-generator": "dist/bin/cli.js"
9
- },
10
- "scripts": {
11
- "build": "tsc && copyfiles -u 1 \"src/**/*.{yaml,html,css,mustache}\" dist/",
12
- "start": "node ./dist/index.js",
13
- "test": "npm run build && node ./dist/index.test.js",
14
- "dev": "nodemon",
15
- "clean": "rm -rf dist",
16
- "prepare": "npm run clean && npm run build"
17
- },
18
- "author": "extedcoud",
19
- "license": "MIT",
20
- "devDependencies": {
21
- "@types/fs-extra": "^11.0.4",
22
- "@types/js-yaml": "^4.0.9",
23
- "@types/json-schema": "^7.0.15",
24
- "@types/jsonpath": "^0.2.4",
25
- "@types/mustache": "^4.2.5",
26
- "@types/node": "^22.10.1",
27
- "copyfiles": "^2.4.1",
28
- "esm-loader-typescript": "^1.0.6",
29
- "nodemon": "^3.1.7",
30
- "ts-node": "^10.9.2",
31
- "typescript": "^5.7.2"
32
- },
33
- "dependencies": {
34
- "@apidevtools/json-schema-ref-parser": "^11.7.2",
35
- "chalk": "^4.1.2",
36
- "chevrotain": "^11.0.3",
37
- "commander": "^14.0.2",
38
- "fs-extra": "^11.2.0",
39
- "js-yaml": "^4.1.0",
40
- "json-schema": "^0.4.0",
41
- "json-to-yaml-ref": "^1.0.0",
42
- "jsonpath": "^1.1.1",
43
- "marked": "^15.0.3",
44
- "mustache": "^4.2.0",
45
- "prettier": "^3.4.2",
46
- "winston": "^3.17.0"
47
- }
2
+ "name": "ondc-code-generator",
3
+ "version": "0.8.6",
4
+ "description": "generate code from build.yaml ",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "bin": {
8
+ "ondc-code-generator": "dist/bin/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc && copyfiles -u 1 \"src/**/*.{yaml,html,css,mustache}\" dist/",
12
+ "start": "node ./dist/index.js",
13
+ "test": "npm run build && node ./dist/index.test.js",
14
+ "dev": "nodemon",
15
+ "clean": "rm -rf dist",
16
+ "prepare": "npm run clean && npm run build"
17
+ },
18
+ "author": "extedcoud",
19
+ "license": "MIT",
20
+ "devDependencies": {
21
+ "@types/fs-extra": "^11.0.4",
22
+ "@types/js-yaml": "^4.0.9",
23
+ "@types/json-schema": "^7.0.15",
24
+ "@types/jsonpath": "^0.2.4",
25
+ "@types/mustache": "^4.2.5",
26
+ "@types/node": "^22.10.1",
27
+ "copyfiles": "^2.4.1",
28
+ "esm-loader-typescript": "^1.0.6",
29
+ "nodemon": "^3.1.7",
30
+ "ts-node": "^10.9.2",
31
+ "typescript": "^5.7.2"
32
+ },
33
+ "dependencies": {
34
+ "@apidevtools/json-schema-ref-parser": "^11.7.2",
35
+ "chalk": "^4.1.2",
36
+ "chevrotain": "^11.0.3",
37
+ "commander": "^14.0.2",
38
+ "fs-extra": "^11.2.0",
39
+ "js-yaml": "^4.1.0",
40
+ "json-schema": "^0.4.0",
41
+ "json-to-yaml-ref": "^1.0.0",
42
+ "jsonpath": "^1.1.1",
43
+ "marked": "^15.0.3",
44
+ "mustache": "^4.2.0",
45
+ "prettier": "^3.4.2",
46
+ "winston": "^3.17.0"
47
+ }
48
48
  }
package/sample.md CHANGED
@@ -312,8 +312,7 @@ This document outlines best practices for writing effective JVAL (JSON Validatio
312
312
 
313
313
  results, runErr := PerformL1_validations(action, payload, cfg, validationutils.ExternalData{
314
314
  "full_ids" : map[string]bool{
315
- "test": true,
316
- "test2": false,
315
+ "10102": true,
317
316
  },
318
317
  })
319
318