ondc-code-generator 0.8.7 → 0.8.9
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.
- package/README.md +1 -1
- package/alpha/docs/page/index.html +6137 -0
- package/alpha/docs/page/style.css +204 -0
- package/alpha/docs/readme.md +5939 -0
- package/alpha/docs/validPaths.json +14351 -0
- package/alpha/page/index.html +6137 -0
- package/alpha/page/style.css +204 -0
- package/alpha/readme.md +5939 -0
- package/alpha/validPaths.json +11393 -34240
- package/alpha/validationpkg/examples/search.json +143 -0
- package/alpha/validationpkg/examples_output/search/case-001/output.json +12 -0
- package/alpha/validationpkg/go.mod +8 -0
- package/alpha/validationpkg/go.sum +4 -0
- package/alpha/validationpkg/jsonvalidations/cancel.go +1289 -0
- package/alpha/validationpkg/jsonvalidations/confirm.go +9121 -0
- package/alpha/validationpkg/jsonvalidations/init.go +4864 -0
- package/alpha/validationpkg/jsonvalidations/issue.go +4868 -0
- package/alpha/validationpkg/jsonvalidations/on_cancel.go +7111 -0
- package/alpha/validationpkg/jsonvalidations/on_confirm.go +8903 -0
- package/alpha/validationpkg/jsonvalidations/on_init.go +4445 -0
- package/alpha/validationpkg/jsonvalidations/on_issue.go +2828 -0
- package/alpha/validationpkg/jsonvalidations/on_issue_status.go +1938 -0
- package/alpha/validationpkg/jsonvalidations/on_search.go +3356 -0
- package/alpha/validationpkg/jsonvalidations/on_status.go +8129 -0
- package/alpha/validationpkg/jsonvalidations/on_track.go +1415 -0
- package/alpha/validationpkg/jsonvalidations/on_update.go +8700 -0
- package/alpha/validationpkg/jsonvalidations/search.go +3585 -0
- package/alpha/validationpkg/jsonvalidations/status.go +1073 -0
- package/alpha/validationpkg/jsonvalidations/track.go +1073 -0
- package/alpha/validationpkg/jsonvalidations/update.go +3012 -0
- package/alpha/validationpkg/main-validator.go +196 -0
- package/alpha/validationpkg/main-validator_test.go +165 -0
- package/alpha/validationpkg/storageutils/api_save_utils.go +83 -0
- package/alpha/validationpkg/storageutils/cancel.go +30 -0
- package/alpha/validationpkg/storageutils/confirm.go +30 -0
- package/alpha/validationpkg/storageutils/index.go +132 -0
- package/alpha/validationpkg/storageutils/init.go +30 -0
- package/alpha/validationpkg/storageutils/issue.go +30 -0
- package/alpha/validationpkg/storageutils/on_cancel.go +30 -0
- package/alpha/validationpkg/storageutils/on_confirm.go +30 -0
- package/alpha/validationpkg/storageutils/on_init.go +30 -0
- package/alpha/validationpkg/storageutils/on_issue.go +30 -0
- package/alpha/validationpkg/storageutils/on_issue_status.go +30 -0
- package/alpha/validationpkg/storageutils/on_search.go +30 -0
- package/alpha/validationpkg/storageutils/on_status.go +30 -0
- package/alpha/validationpkg/storageutils/on_track.go +30 -0
- package/alpha/validationpkg/storageutils/on_update.go +30 -0
- package/alpha/validationpkg/storageutils/save_utils.go +75 -0
- package/alpha/validationpkg/storageutils/search.go +30 -0
- package/alpha/validationpkg/storageutils/status.go +30 -0
- package/alpha/validationpkg/storageutils/track.go +30 -0
- package/alpha/validationpkg/storageutils/update.go +30 -0
- package/alpha/validationpkg/validationutils/json_normalizer.go +152 -0
- package/alpha/validationpkg/validationutils/json_path_utils.go +173 -0
- package/alpha/validationpkg/validationutils/storage-interface.go +107 -0
- package/alpha/validationpkg/validationutils/test-config.go +69 -0
- package/alpha/validationpkg/validationutils/validation_utils.go +429 -0
- package/dist/bin/cli.js +6 -2
- package/dist/generator/config-compiler.d.ts +2 -2
- package/dist/generator/config-compiler.js +10 -4
- package/dist/generator/generators/classes/abstract-generator.d.ts +1 -0
- package/dist/generator/generators/go/go-generator.js +34 -17
- package/dist/generator/generators/go/templates/api-tests.mustache +1 -1
- package/dist/generator/generators/go/templates/go-mod.mustache +1 -1
- package/dist/generator/generators/go/templates/index.mustache +1 -1
- package/dist/generator/generators/go/templates/storage-templates/api-save-utils.mustache +1 -1
- package/dist/generator/generators/go/templates/storage-templates/api-save.mustache +1 -1
- package/dist/generator/generators/go/templates/storage-templates/index.mustache +1 -1
- package/dist/generator/generators/go/templates/storage-templates/save-utils.mustache +1 -1
- package/dist/generator/generators/go/templates/test-templates/validator-test.mustache +6 -6
- package/dist/types/compiler-types.d.ts +2 -1
- package/dist/types/compiler-types.js +1 -0
- package/docs/jval-dsl.md +913 -0
- package/package.json +1 -1
- package/alpha/possible-json-paths.json +0 -248
package/docs/jval-dsl.md
ADDED
|
@@ -0,0 +1,913 @@
|
|
|
1
|
+
# JVAL — Full DSL Reference
|
|
2
|
+
|
|
3
|
+
**JVAL** (JSON Validation Language) is a declarative, JSON/YAML-based DSL for writing API payload validation rules. A JVAL config is compiled by the code generator into executable validation functions in TypeScript, Python, JavaScript, or Go.
|
|
4
|
+
|
|
5
|
+
This document covers the complete DSL — the config structure, every field, variable binding, scoping, conditional logic, session/stateful data, nested tests, and how it all maps to generated code.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [Top-Level Config Structure](#1-top-level-config-structure)
|
|
12
|
+
2. [Test Sets](#2-test-sets)
|
|
13
|
+
3. [Test Object — field reference](#3-test-object--field-reference)
|
|
14
|
+
4. [Variables](#4-variables)
|
|
15
|
+
5. [Scope](#5-scope)
|
|
16
|
+
6. [The `_RETURN_` field](#6-the-_return_-field)
|
|
17
|
+
7. [The `_CONTINUE_` field (conditional skip)](#7-the-_continue_-field-conditional-skip)
|
|
18
|
+
8. [Nested `_RETURN_` (grouped tests)](#8-nested-_return_-grouped-tests)
|
|
19
|
+
9. [Session Data & Cross-Request State](#9-session-data--cross-request-state)
|
|
20
|
+
10. [External Data & `$._EXTERNAL`](#10-external-data--_external)
|
|
21
|
+
11. [Generated Output Shape](#11-generated-output-shape)
|
|
22
|
+
12. [Runtime ValidationConfig](#12-runtime-validationconfig)
|
|
23
|
+
13. [Complete Annotated Examples](#13-complete-annotated-examples)
|
|
24
|
+
14. [Common Patterns Cookbook](#14-common-patterns-cookbook)
|
|
25
|
+
15. [Reserved Keywords](#15-reserved-keywords)
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 1. Top-Level Config Structure
|
|
30
|
+
|
|
31
|
+
A JVAL config is a JSON object with exactly two top-level keys:
|
|
32
|
+
|
|
33
|
+
```json
|
|
34
|
+
{
|
|
35
|
+
"_TESTS_": {
|
|
36
|
+
"<action_name>": [ <test_object>, ... ],
|
|
37
|
+
"<action_name_2>": [ <test_object>, ... ]
|
|
38
|
+
},
|
|
39
|
+
"_SESSION_DATA_": {
|
|
40
|
+
"<action_name>": {
|
|
41
|
+
"<key>": "<json_path_expression>"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
| Key | Type | Required | Description |
|
|
48
|
+
| ---------------- | ------ | -------- | ------------------------------------------------------------------------------------ |
|
|
49
|
+
| `_TESTS_` | Object | Yes | All validation test sets, keyed by API action name |
|
|
50
|
+
| `_SESSION_DATA_` | Object | Yes | Declares what values to save after each action for stateful cross-request validation |
|
|
51
|
+
|
|
52
|
+
The action names under `_TESTS_` and `_SESSION_DATA_` are arbitrary strings that correspond to API operations (e.g., `search`, `on_search`, `init`, `confirm`, `cancel`, etc.). The code generator emits one validation function per action.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 2. Test Sets
|
|
57
|
+
|
|
58
|
+
Each value under `_TESTS_` is an **ordered array of test objects**. The generated function for that action runs every test object in sequence.
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"_TESTS_": {
|
|
63
|
+
"search": [
|
|
64
|
+
{ /* test object 1 */ },
|
|
65
|
+
{ /* test object 2 */ },
|
|
66
|
+
...
|
|
67
|
+
],
|
|
68
|
+
"on_confirm": [
|
|
69
|
+
{ /* test object 1 */ },
|
|
70
|
+
...
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
- Tests within a set are **independent** — a failure in one does not stop subsequent tests from running.
|
|
77
|
+
- The generated function collects all results and returns them together.
|
|
78
|
+
- Test names must be **unique within a test set** (they become function names in generated code).
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 3. Test Object — field reference
|
|
83
|
+
|
|
84
|
+
A test object is a JSON object with a mix of **reserved system fields** (prefixed and suffixed with `_`) and **user-defined variable fields**.
|
|
85
|
+
|
|
86
|
+
### Full schema
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"_NAME_": "<string>",
|
|
91
|
+
"_DESCRIPTION_": "<string>",
|
|
92
|
+
"_SCOPE_": "<json_path>",
|
|
93
|
+
"_CONTINUE_": "<jval_expression>",
|
|
94
|
+
"_ERROR_CODE_": <number>,
|
|
95
|
+
"_SUCCESS_CODE_": <number>,
|
|
96
|
+
"_RETURN_": "<jval_expression>" | [ <test_object>, ... ],
|
|
97
|
+
|
|
98
|
+
"<varName1>": "<json_path>" | ["<string>", ...],
|
|
99
|
+
"<varName2>": "<json_path>" | ["<string>", ...]
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Reserved fields
|
|
104
|
+
|
|
105
|
+
| Field | Type | Required | Default | Description |
|
|
106
|
+
| ---------------- | --------------- | -------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
107
|
+
| `_NAME_` | string | **Yes** | — | Unique identifier for this test. Becomes the generated function name. Use `UPPER_SNAKE_CASE` or `snake_case`. No spaces or hyphens. |
|
|
108
|
+
| `_DESCRIPTION_` | string | No | Auto-generated | Human-readable description shown in error output when the test fails. |
|
|
109
|
+
| `_SCOPE_` | JSONPath | No | `"$"` | Narrows the JSON subtree that tests run against. The path must resolve to an **array**; the test logic runs once per element. |
|
|
110
|
+
| `_CONTINUE_` | JVAL expression | No | — | If this expression evaluates to `true` for a scope element, **that element is skipped** (the test is not run for it). Used for conditional/optional validation. |
|
|
111
|
+
| `_ERROR_CODE_` | number | No | `30000` | Error code returned when the test fails. |
|
|
112
|
+
| `_SUCCESS_CODE_` | number | No | `200` | Success code returned when the test passes. |
|
|
113
|
+
| `_RETURN_` | string or array | **Yes** | — | Either a JVAL expression string (leaf test) or an array of nested test objects (grouped tests). |
|
|
114
|
+
|
|
115
|
+
### User-defined variable fields
|
|
116
|
+
|
|
117
|
+
Any key that is **not** one of the reserved fields above is treated as a variable declaration. See [Section 4](#4-variables).
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 4. Variables
|
|
122
|
+
|
|
123
|
+
Variables are the bridge between the JSON payload and the JVAL expression in `_RETURN_`.
|
|
124
|
+
|
|
125
|
+
### Variable value types
|
|
126
|
+
|
|
127
|
+
#### A. JSONPath string
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
"attr": "$.context.action"
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
At runtime, the generator extracts all matching values from the current scope object using this path. The variable `attr` becomes an array of extracted values (even if only one matches).
|
|
134
|
+
|
|
135
|
+
```json
|
|
136
|
+
"tags": "$.message.order.tags[*].descriptor.code"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Multiple-match paths (using `[*]`, `..`, filter expressions) produce arrays of all matching values.
|
|
140
|
+
|
|
141
|
+
#### B. String array literal
|
|
142
|
+
|
|
143
|
+
```json
|
|
144
|
+
"validActions": ["search", "select", "init"]
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The variable `validActions` is a fixed list of strings. This is the primary way to supply enum allowlists inline.
|
|
148
|
+
|
|
149
|
+
#### C. External/session data reference
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
"prevTransactionId": "$._EXTERNAL.transaction_id"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
The `$._EXTERNAL` prefix accesses data from outside the current payload — either from previous API calls (via `_SESSION_DATA_`) or from externally injected data passed at runtime. See [Section 10](#10-external-data--_external).
|
|
156
|
+
|
|
157
|
+
### Variable name rules
|
|
158
|
+
|
|
159
|
+
- Must match `[a-zA-Z_][a-zA-Z0-9_]*` (same as a standard identifier).
|
|
160
|
+
- Case-sensitive.
|
|
161
|
+
- Must not clash with any reserved field name.
|
|
162
|
+
- Must not use language reserved keywords (JavaScript/TypeScript/Python keywords are all blocked — see [Section 15](#15-reserved-keywords)).
|
|
163
|
+
|
|
164
|
+
### How variables are resolved in generated code (TypeScript)
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// JSONPath variable
|
|
168
|
+
const attr = payloadUtils.getJsonPath(testObj, "$.context.action", true);
|
|
169
|
+
|
|
170
|
+
// Array literal variable
|
|
171
|
+
const validActions = ["search", "select", "init"];
|
|
172
|
+
|
|
173
|
+
// External data variable
|
|
174
|
+
const prevTransactionId = payloadUtils.getJsonPath(
|
|
175
|
+
testObj,
|
|
176
|
+
"$._EXTERNAL.transaction_id",
|
|
177
|
+
true,
|
|
178
|
+
);
|
|
179
|
+
// (testObj._EXTERNAL is injected by the runner before extraction)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 5. Scope
|
|
185
|
+
|
|
186
|
+
`_SCOPE_` is a JSONPath expression that determines **which part of the payload** a test operates on, and **how many times** the test body runs.
|
|
187
|
+
|
|
188
|
+
### Default scope
|
|
189
|
+
|
|
190
|
+
If `_SCOPE_` is omitted, it defaults to `"$"` (the root of the payload). A single virtual scope object containing the full payload is used, so the test runs exactly once.
|
|
191
|
+
|
|
192
|
+
### Array scope
|
|
193
|
+
|
|
194
|
+
When `_SCOPE_` resolves to an array, **the test runs once per element**. Variable paths inside the test object are relative to each element.
|
|
195
|
+
|
|
196
|
+
```json
|
|
197
|
+
{
|
|
198
|
+
"_NAME_": "CHECK_PAYMENT_TRANSACTION",
|
|
199
|
+
"_SCOPE_": "$.message.order.payments[*]",
|
|
200
|
+
"statusPath": "$.status",
|
|
201
|
+
"transactionId": "$.params.transaction_id",
|
|
202
|
+
"paidStatus": ["PAID"],
|
|
203
|
+
"_CONTINUE_": "!(statusPath all in paidStatus)",
|
|
204
|
+
"_RETURN_": "transactionId are present"
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
For each payment object in the array:
|
|
209
|
+
|
|
210
|
+
1. `statusPath` extracts `$.status` from that payment object.
|
|
211
|
+
2. `transactionId` extracts `$.params.transaction_id` from that payment object.
|
|
212
|
+
3. `_CONTINUE_` is checked — if the payment is NOT `PAID`, skip this test for that element.
|
|
213
|
+
4. `_RETURN_` checks `transactionId are present` for PAID payments only.
|
|
214
|
+
|
|
215
|
+
### Scope with JSONPath filter expressions
|
|
216
|
+
|
|
217
|
+
`_SCOPE_` fully supports JSONPath filter expressions:
|
|
218
|
+
|
|
219
|
+
```json
|
|
220
|
+
"_SCOPE_": "$.message.order.tags[?(@.descriptor.code=='BAP_TERMS')]"
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
This iterates only over tags whose `descriptor.code` is `BAP_TERMS`. Within the test object, `$.list[*].descriptor.code` is then relative to that specific tag object.
|
|
224
|
+
|
|
225
|
+
### Key rule
|
|
226
|
+
|
|
227
|
+
**Variable JSONPaths inside a test object are always relative to the current scope element**, not the root payload. This is because before variable extraction the generator sets the scope object as the current context.
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## 6. The `_RETURN_` field
|
|
232
|
+
|
|
233
|
+
`_RETURN_` is the core of a test object. It specifies **what boolean condition must hold** for the test to pass.
|
|
234
|
+
|
|
235
|
+
### Form 1: JVAL expression string (leaf test)
|
|
236
|
+
|
|
237
|
+
```json
|
|
238
|
+
"_RETURN_": "attr are present && attr all in enumList"
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This is a JVAL expression. The full grammar and operator reference is in the `return-complier/docs/` subfolder. Quick summary:
|
|
242
|
+
|
|
243
|
+
- Variables evaluate to arrays extracted from the payload.
|
|
244
|
+
- Unary functions: `<var> are present`, `<var> are unique`
|
|
245
|
+
- Binary functions: `<lhs> all in <rhs>`, `<lhs> any in <rhs>`, `<lhs> none in <rhs>`, `<lhs> follow regex <rhs>`, `<lhs> equal to <rhs>`, `<lhs> greater than <rhs>`, `<lhs> less than <rhs>`
|
|
246
|
+
- Boolean connectives: `&&`, `||`, `!`
|
|
247
|
+
- Parentheses for grouping: `(…)`, with `!` only applicable to parenthesised groups: `!(…)`
|
|
248
|
+
|
|
249
|
+
When `_RETURN_` is a string:
|
|
250
|
+
|
|
251
|
+
- The expression is compiled to a boolean expression in the target language.
|
|
252
|
+
- If the expression evaluates to `false`, the test fails and returns `{ valid: false, code: _ERROR_CODE_, description: "…" }`.
|
|
253
|
+
- If `true`, the test passes and returns `{ valid: true, code: _SUCCESS_CODE_ }`.
|
|
254
|
+
|
|
255
|
+
### Form 2: Array of test objects (nested/grouped tests)
|
|
256
|
+
|
|
257
|
+
```json
|
|
258
|
+
"_RETURN_": [
|
|
259
|
+
{ "_NAME_": "sub_test_1", ... "_RETURN_": "..." },
|
|
260
|
+
{ "_NAME_": "sub_test_2", ... "_RETURN_": "..." }
|
|
261
|
+
]
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
When `_RETURN_` is an array, the parent test object becomes a **group wrapper**. The generator:
|
|
265
|
+
|
|
266
|
+
1. Generates individual sub-test functions for each item.
|
|
267
|
+
2. The parent function runs all sub-functions and aggregates their results.
|
|
268
|
+
3. The parent test object itself does not have a JVAL expression — it acts as a container.
|
|
269
|
+
|
|
270
|
+
This is how large conditional blocks are written, for example validating message body fields only when no error is present:
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"_NAME_": "on_search_Message_TESTS",
|
|
275
|
+
"errorBlock": "$.error.code",
|
|
276
|
+
"_CONTINUE_": "errorBlock are present",
|
|
277
|
+
"_RETURN_": [
|
|
278
|
+
{
|
|
279
|
+
"_NAME_": "REQUIRED_CATALOG_NAME",
|
|
280
|
+
"attr": "$.message.catalog.descriptor.name",
|
|
281
|
+
"_RETURN_": "attr are present"
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"_NAME_": "REQUIRED_PROVIDER_ID",
|
|
285
|
+
"attr": "$.message.catalog.providers[*].id",
|
|
286
|
+
"_RETURN_": "attr are present"
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Here if `errorBlock` (i.e. `$.error.code`) is present in the payload, the whole group is skipped. If it is absent, all sub-tests run.
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## 7. The `_CONTINUE_` field (conditional skip)
|
|
297
|
+
|
|
298
|
+
`_CONTINUE_` is a JVAL expression that acts as a **skip gate**.
|
|
299
|
+
|
|
300
|
+
- Evaluated first, before `_RETURN_`.
|
|
301
|
+
- If `_CONTINUE_` evaluates to **`true`** for a scope element → **skip** (do not run `_RETURN_` for that element, treat it as passing trivially).
|
|
302
|
+
- If `_CONTINUE_` evaluates to **`false`** → proceed to evaluate `_RETURN_`.
|
|
303
|
+
|
|
304
|
+
### Semantics
|
|
305
|
+
|
|
306
|
+
`_CONTINUE_` means: _"Continue past this test (skip) if this condition holds."_
|
|
307
|
+
|
|
308
|
+
This is used for two main patterns:
|
|
309
|
+
|
|
310
|
+
#### Pattern 1: Optional field validation
|
|
311
|
+
|
|
312
|
+
```json
|
|
313
|
+
{
|
|
314
|
+
"_NAME_": "VALID_ENUM_VEHICLE_CATEGORY",
|
|
315
|
+
"enumList": ["AUTO_RICKSHAW", "CAB", "TWO_WHEELER"],
|
|
316
|
+
"enumPath": "$.message.intent.fulfillment.vehicle.category",
|
|
317
|
+
"_CONTINUE_": "!(enumPath are present)",
|
|
318
|
+
"_RETURN_": "enumPath all in enumList"
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
- If `vehicle.category` is **not present** in the payload → `!(enumPath are present)` is `true` → skip. The field is optional, so absence is fine.
|
|
323
|
+
- If `vehicle.category` **is present** → `!(enumPath are present)` is `false` → run `_RETURN_`, validating the value is within the allowed list.
|
|
324
|
+
|
|
325
|
+
#### Pattern 2: Conditional group skipping
|
|
326
|
+
|
|
327
|
+
```json
|
|
328
|
+
{
|
|
329
|
+
"_NAME_": "on_search_Message_TESTS",
|
|
330
|
+
"errorBlock": "$.error.code",
|
|
331
|
+
"_CONTINUE_": "errorBlock are present",
|
|
332
|
+
"_RETURN_": [ ... ]
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
- If the payload contains an `error.code` (i.e. it's an error response) → skip the entire sub-test group.
|
|
337
|
+
- If no error → validate the message body.
|
|
338
|
+
|
|
339
|
+
#### Pattern 3: Scoped conditional
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"_NAME_": "FULFILLMENT_STATE_ENDED",
|
|
344
|
+
"_SCOPE_": "$.message.order.fulfillments[*]",
|
|
345
|
+
"auth": "$.stops[*].authorization.status",
|
|
346
|
+
"authStatus": ["CLAIMED"],
|
|
347
|
+
"state": "$.state.descriptor.code",
|
|
348
|
+
"stateCode": ["RIDE_ENDED"],
|
|
349
|
+
"_CONTINUE_": "!(authStatus all in auth)",
|
|
350
|
+
"_RETURN_": "state all in stateCode"
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
For each fulfillment element:
|
|
355
|
+
|
|
356
|
+
- Skip if its `authorization.status` is NOT `CLAIMED`.
|
|
357
|
+
- If it IS `CLAIMED`, validate that `state.descriptor.code` is `RIDE_ENDED`.
|
|
358
|
+
|
|
359
|
+
---
|
|
360
|
+
|
|
361
|
+
## 8. Nested `_RETURN_` (grouped tests)
|
|
362
|
+
|
|
363
|
+
When `_RETURN_` is an array of test objects, it creates a hierarchical validation structure.
|
|
364
|
+
|
|
365
|
+
### Rules
|
|
366
|
+
|
|
367
|
+
- The **parent test object** has its own `_NAME_`, can have `_SCOPE_`, and **can have `_CONTINUE_`** (applied to the whole group).
|
|
368
|
+
- The parent does NOT have a JVAL expression — it has no `_ERROR_CODE_` or `_SUCCESS_CODE_` that apply to itself as a leaf.
|
|
369
|
+
- Each **child test object** is a fully independent test object with its own `_NAME_`, `_SCOPE_`, `_CONTINUE_`, `_RETURN_`, `_ERROR_CODE_`, etc.
|
|
370
|
+
- Children can themselves have array `_RETURN_`, creating **arbitrary nesting depth**.
|
|
371
|
+
- The parent's result is derived from all child results: `valid = all children valid`.
|
|
372
|
+
|
|
373
|
+
### Generated structure
|
|
374
|
+
|
|
375
|
+
```
|
|
376
|
+
parentFunction(input)
|
|
377
|
+
└─ runs all child functions:
|
|
378
|
+
childFunction1(input) → validationOutput[]
|
|
379
|
+
childFunction2(input) → validationOutput[]
|
|
380
|
+
...
|
|
381
|
+
└─ returns combined array of all child results
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
The parent function's own result entry has `valid: true` only if every child returned `valid: true`.
|
|
385
|
+
|
|
386
|
+
### Common usage: error-gated message validation
|
|
387
|
+
|
|
388
|
+
For `on_*` ONDC API responses, a payload may be an error response (contains `error.code`) or a successful one (contains `message.*`). Nested `_RETURN_` with `_CONTINUE_` on the parent is the standard pattern:
|
|
389
|
+
|
|
390
|
+
```json
|
|
391
|
+
{
|
|
392
|
+
"_NAME_": "on_confirm_Message_TESTS",
|
|
393
|
+
"errorBlock": "$.error.code",
|
|
394
|
+
"_CONTINUE_": "errorBlock are present",
|
|
395
|
+
"_RETURN_": [
|
|
396
|
+
{
|
|
397
|
+
"_NAME_": "REQUIRED_ORDER_ID",
|
|
398
|
+
"attr": "$.message.order.id",
|
|
399
|
+
"_RETURN_": "attr are present"
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
"_NAME_": "REQUIRED_ORDER_STATUS",
|
|
403
|
+
"attr": "$.message.order.status",
|
|
404
|
+
"_RETURN_": "attr are present"
|
|
405
|
+
}
|
|
406
|
+
]
|
|
407
|
+
}
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
---
|
|
411
|
+
|
|
412
|
+
## 9. Session Data & Cross-Request State
|
|
413
|
+
|
|
414
|
+
`_SESSION_DATA_` enables **stateful validation** — saving values from one API call and using them in a later one.
|
|
415
|
+
|
|
416
|
+
### Structure
|
|
417
|
+
|
|
418
|
+
```json
|
|
419
|
+
"_SESSION_DATA_": {
|
|
420
|
+
"<action_name>": {
|
|
421
|
+
"<key>": "<json_path>"
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
- `<action_name>` is the API action after which data is saved (e.g., `"search"`).
|
|
427
|
+
- `<key>` is the name under which the extracted value is stored.
|
|
428
|
+
- `<json_path>` is extracted from the payload of that action.
|
|
429
|
+
|
|
430
|
+
### Example
|
|
431
|
+
|
|
432
|
+
```json
|
|
433
|
+
"_SESSION_DATA_": {
|
|
434
|
+
"search": {
|
|
435
|
+
"transaction_id": "$.context.transaction_id"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
After a `search` call, `$.context.transaction_id` is extracted from the payload and stored in the session under the key `"transaction_id"`.
|
|
441
|
+
|
|
442
|
+
### Reading session data in a test
|
|
443
|
+
|
|
444
|
+
Use the `$._EXTERNAL.<key>` path for a variable:
|
|
445
|
+
|
|
446
|
+
```json
|
|
447
|
+
{
|
|
448
|
+
"_NAME_": "VERIFY_TRANSACTION_CONTINUITY",
|
|
449
|
+
"currentTxnId": "$.context.transaction_id",
|
|
450
|
+
"prevTxnId": "$._EXTERNAL.transaction_id",
|
|
451
|
+
"_RETURN_": "currentTxnId equal to prevTxnId"
|
|
452
|
+
}
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
This test verifies that the `transaction_id` in the current `on_search` payload matches the one saved from the `search` call.
|
|
456
|
+
|
|
457
|
+
### Runtime requirement
|
|
458
|
+
|
|
459
|
+
Stateful validations require enabling `stateFullValidations: true` in the `ValidationConfig` at runtime, plus providing a `store` (a `StorageInterface` implementation) and a `uniqueKey` to namespace the session:
|
|
460
|
+
|
|
461
|
+
```typescript
|
|
462
|
+
await performL1Validations("on_search", payload, {
|
|
463
|
+
stateFullValidations: true,
|
|
464
|
+
uniqueKey: "session-abc-123",
|
|
465
|
+
store: myRedisStorage,
|
|
466
|
+
});
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
If `stateFullValidations` is `false` (default), variables with `$._EXTERNAL.*` paths are ignored and the validation is treated as stateless.
|
|
470
|
+
|
|
471
|
+
---
|
|
472
|
+
|
|
473
|
+
## 10. External Data & `$._EXTERNAL`
|
|
474
|
+
|
|
475
|
+
`$._EXTERNAL` is a special namespace that provides data from **outside the current payload**. It is populated at validation runtime and can come from two sources:
|
|
476
|
+
|
|
477
|
+
### Source 1: Session/stateful data
|
|
478
|
+
|
|
479
|
+
Values saved via `_SESSION_DATA_` from previous API calls (see Section 9).
|
|
480
|
+
|
|
481
|
+
### Source 2: Caller-injected data
|
|
482
|
+
|
|
483
|
+
Arbitrary data passed by the caller at runtime:
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// TypeScript
|
|
487
|
+
await performL1Validations("search", payload, config, {
|
|
488
|
+
full_ids: { "10102": true, "10103": true },
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Go
|
|
492
|
+
results, err := PerformL1_validations(action, payload, cfg, validationutils.ExternalData{
|
|
493
|
+
"full_ids": map[string]bool{ "10102": true },
|
|
494
|
+
})
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
This is used for data that must come from an external database, orchestration layer, or prior business logic — not from the payload itself.
|
|
498
|
+
|
|
499
|
+
### Source 3: `_SELF` — the current payload
|
|
500
|
+
|
|
501
|
+
`$._EXTERNAL._SELF` is always automatically populated with the **full normalised payload** being validated. This allows a test to reference the entire payload from within any scoped context.
|
|
502
|
+
|
|
503
|
+
```json
|
|
504
|
+
{
|
|
505
|
+
"_SCOPE_": "$.message.items[*]",
|
|
506
|
+
"itemId": "$.id",
|
|
507
|
+
"allProviderIds": "$._EXTERNAL._SELF.message.catalog.providers[*].id",
|
|
508
|
+
"_RETURN_": "itemId all in allProviderIds"
|
|
509
|
+
}
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
Variables referencing `_SELF` are **never collected into load/save session operations** — they are always available without stateful storage.
|
|
513
|
+
|
|
514
|
+
### Path syntax
|
|
515
|
+
|
|
516
|
+
```
|
|
517
|
+
$._EXTERNAL.<key>
|
|
518
|
+
$._EXTERNAL._SELF.<further_json_path>
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
In generated code, before variable extraction the `_EXTERNAL` object is injected onto `testObj`:
|
|
522
|
+
|
|
523
|
+
```typescript
|
|
524
|
+
testObj._EXTERNAL = input.externalData;
|
|
525
|
+
const allProviderIds = payloadUtils.getJsonPath(
|
|
526
|
+
testObj,
|
|
527
|
+
"$._EXTERNAL._SELF.message.catalog.providers[*].id",
|
|
528
|
+
true,
|
|
529
|
+
);
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## 11. Generated Output Shape
|
|
535
|
+
|
|
536
|
+
The code generator produces this directory structure (TypeScript example):
|
|
537
|
+
|
|
538
|
+
```
|
|
539
|
+
generated/<codeName>/
|
|
540
|
+
├── index.ts ← Entry: perform<CodeName>(action, payload, config, externalData)
|
|
541
|
+
├── error.ts ← Error code registry
|
|
542
|
+
├── api-tests/
|
|
543
|
+
│ ├── search.ts ← search(input): validationOutput
|
|
544
|
+
│ ├── on_search.ts
|
|
545
|
+
│ ├── init.ts
|
|
546
|
+
│ └── ...
|
|
547
|
+
├── utils/
|
|
548
|
+
│ ├── json-path-utils.ts ← payloadUtils.getJsonPath(...)
|
|
549
|
+
│ ├── validation-utils.ts ← validations.arePresent(...), allIn(...)...
|
|
550
|
+
│ └── json-normalizer.ts ← normalizeKeys(payload)
|
|
551
|
+
├── types/
|
|
552
|
+
│ └── test-config.ts ← ValidationConfig, validationInput, validationOutput, ExternalData
|
|
553
|
+
├── interfaces/
|
|
554
|
+
│ └── storage-interface.ts ← StorageInterface
|
|
555
|
+
└── storage-actions/
|
|
556
|
+
├── index.ts
|
|
557
|
+
├── search.ts ← perform<CodeName>Save / perform<CodeName>Load per action
|
|
558
|
+
└── ...
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
### `validationOutput` type
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
type validationOutput = {
|
|
565
|
+
testName: string; // The _NAME_ of the test object
|
|
566
|
+
valid: boolean; // true = pass, false = fail
|
|
567
|
+
code: number; // _SUCCESS_CODE_ or _ERROR_CODE_
|
|
568
|
+
description?: string; // Only present on failure; markdown message
|
|
569
|
+
_debugInfo?: {
|
|
570
|
+
// Only present when config._debug = true
|
|
571
|
+
fedConfig: any; // The raw test object JSON
|
|
572
|
+
};
|
|
573
|
+
}[];
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
---
|
|
577
|
+
|
|
578
|
+
## 12. Runtime ValidationConfig
|
|
579
|
+
|
|
580
|
+
The generated entry function accepts a `ValidationConfig` object that controls runtime behaviour:
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
interface ValidationConfig {
|
|
584
|
+
onlyInvalid: boolean; // If true, only return failed test results (default: true)
|
|
585
|
+
hideParentErrors: boolean; // If true, suppress parent group results that have no description (default: true)
|
|
586
|
+
stateFullValidations: boolean; // If true, load/save session data (default: false)
|
|
587
|
+
skipTests?: string[]; // Array of _NAME_ values to skip entirely
|
|
588
|
+
uniqueKey?: string; // Session namespace key (required if stateFullValidations = true)
|
|
589
|
+
store?: StorageInterface; // Storage backend (required if stateFullValidations = true)
|
|
590
|
+
_debug?: boolean; // If true, include _debugInfo in output (default: false)
|
|
591
|
+
}
|
|
592
|
+
```
|
|
593
|
+
|
|
594
|
+
### Calling the generated function
|
|
595
|
+
|
|
596
|
+
```typescript
|
|
597
|
+
import { performMyValidations } from "./generated/my-validations";
|
|
598
|
+
|
|
599
|
+
const results = await performMyValidations(
|
|
600
|
+
"search", // action name — must match a key in _TESTS_
|
|
601
|
+
payload, // the raw JSON payload object
|
|
602
|
+
{
|
|
603
|
+
onlyInvalid: true,
|
|
604
|
+
hideParentErrors: true,
|
|
605
|
+
stateFullValidations: false,
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
// optional external data injected as $._EXTERNAL.*
|
|
609
|
+
full_ids: ["id1", "id2"],
|
|
610
|
+
},
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
// results is validationOutput[]
|
|
614
|
+
// Each entry: { testName, valid, code, description? }
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
## 13. Complete Annotated Examples
|
|
620
|
+
|
|
621
|
+
### Example 1: Simple required field + enum check
|
|
622
|
+
|
|
623
|
+
```json
|
|
624
|
+
{
|
|
625
|
+
"_NAME_": "REQUIRED_CONTEXT_ACTION",
|
|
626
|
+
"attr": "$.context.action",
|
|
627
|
+
"enumList": ["search"],
|
|
628
|
+
"_ERROR_CODE_": 30001,
|
|
629
|
+
"_DESCRIPTION_": "context.action must be present and must be 'search'",
|
|
630
|
+
"_RETURN_": "attr are present && attr all in enumList"
|
|
631
|
+
}
|
|
632
|
+
```
|
|
633
|
+
|
|
634
|
+
- `attr` extracts `$.context.action` (an array, typically single-element: `["search"]`).
|
|
635
|
+
- `enumList` is the literal array `["search"]`.
|
|
636
|
+
- `_RETURN_` checks both presence and value correctness.
|
|
637
|
+
- If either sub-check fails, returns `{ valid: false, code: 30001, description: "…" }`.
|
|
638
|
+
|
|
639
|
+
---
|
|
640
|
+
|
|
641
|
+
### Example 2: Optional field with conditional enum validation
|
|
642
|
+
|
|
643
|
+
```json
|
|
644
|
+
{
|
|
645
|
+
"_NAME_": "VALID_ENUM_VEHICLE_CATEGORY",
|
|
646
|
+
"enumList": ["AUTO_RICKSHAW", "CAB", "TWO_WHEELER"],
|
|
647
|
+
"enumPath": "$.message.order.fulfillments[*].vehicle.category",
|
|
648
|
+
"_CONTINUE_": "!(enumPath are present)",
|
|
649
|
+
"_RETURN_": "enumPath all in enumList"
|
|
650
|
+
}
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
- If `vehicle.category` is absent → `_CONTINUE_` is `true` → skip → no error.
|
|
654
|
+
- If `vehicle.category` is present → `_CONTINUE_` is `false` → validate that every value is in the enum list.
|
|
655
|
+
|
|
656
|
+
---
|
|
657
|
+
|
|
658
|
+
### Example 3: Scoped validation with conditional
|
|
659
|
+
|
|
660
|
+
```json
|
|
661
|
+
{
|
|
662
|
+
"_NAME_": "FULFILLMENT_STATE_ENDED",
|
|
663
|
+
"_SCOPE_": "$.message.order.fulfillments[*]",
|
|
664
|
+
"auth": "$.stops[*].authorization.status",
|
|
665
|
+
"authStatus": ["CLAIMED"],
|
|
666
|
+
"state": "$.state.descriptor.code",
|
|
667
|
+
"stateCode": ["RIDE_ENDED"],
|
|
668
|
+
"_CONTINUE_": "!(authStatus all in auth)",
|
|
669
|
+
"_RETURN_": "state all in stateCode"
|
|
670
|
+
}
|
|
671
|
+
```
|
|
672
|
+
|
|
673
|
+
- Iterates over each fulfillment.
|
|
674
|
+
- For each: extracts `authorization.status` array as `auth`.
|
|
675
|
+
- Skips if it does NOT contain `CLAIMED`.
|
|
676
|
+
- For CLAIMED fulfillments: validates that `state.descriptor.code` is `RIDE_ENDED`.
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
### Example 4: Nested test group (error-gated message validation)
|
|
681
|
+
|
|
682
|
+
```json
|
|
683
|
+
{
|
|
684
|
+
"_NAME_": "on_search_Message_TESTS",
|
|
685
|
+
"errorBlock": "$.error.code",
|
|
686
|
+
"_CONTINUE_": "errorBlock are present",
|
|
687
|
+
"_RETURN_": [
|
|
688
|
+
{
|
|
689
|
+
"_NAME_": "REQUIRED_CATALOG_NAME",
|
|
690
|
+
"attr": "$.message.catalog.descriptor.name",
|
|
691
|
+
"_RETURN_": "attr are present"
|
|
692
|
+
},
|
|
693
|
+
{
|
|
694
|
+
"_NAME_": "REQUIRED_PROVIDER_ID",
|
|
695
|
+
"attr": "$.message.catalog.providers[*].id",
|
|
696
|
+
"_RETURN_": "attr are present"
|
|
697
|
+
}
|
|
698
|
+
]
|
|
699
|
+
}
|
|
700
|
+
```
|
|
701
|
+
|
|
702
|
+
- `errorBlock` extracts `$.error.code`.
|
|
703
|
+
- If error code exists → skip all message validations (error response, not expected to have `message.catalog`).
|
|
704
|
+
- If no error → run all sub-tests checking catalog fields.
|
|
705
|
+
|
|
706
|
+
---
|
|
707
|
+
|
|
708
|
+
### Example 5: Tag code validation with scoped sub-tags
|
|
709
|
+
|
|
710
|
+
```json
|
|
711
|
+
{
|
|
712
|
+
"_NAME_": "validate_BAP_TERMS_tags",
|
|
713
|
+
"validTags": ["BAP_TERMS"],
|
|
714
|
+
"tagPath": "$.message.order.tags[*].descriptor.code",
|
|
715
|
+
"_CONTINUE_": "!(tagPath are present)",
|
|
716
|
+
"_RETURN_": "tagPath all in validTags"
|
|
717
|
+
},
|
|
718
|
+
{
|
|
719
|
+
"_NAME_": "validate_BAP_TERMS_sub_tags",
|
|
720
|
+
"_SCOPE_": "$.message.order.tags[?(@.descriptor.code=='BAP_TERMS')]",
|
|
721
|
+
"subTags": "$.list[*].descriptor.code",
|
|
722
|
+
"validValues": ["BUYER_FINDER_FEES_TYPE", "SETTLEMENT_WINDOW", "SETTLEMENT_TYPE"],
|
|
723
|
+
"_RETURN_": "subTags all in validValues"
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
Two-phase tag validation pattern:
|
|
728
|
+
|
|
729
|
+
1. First test: if any top-level tag is present, verify it is from the allowed set.
|
|
730
|
+
2. Second test: scope into specific tag objects (via filter expression), then validate their `list[*].descriptor.code` against the expected sub-tag names.
|
|
731
|
+
|
|
732
|
+
---
|
|
733
|
+
|
|
734
|
+
### Example 6: Cross-request state (transaction ID continuity)
|
|
735
|
+
|
|
736
|
+
Config:
|
|
737
|
+
|
|
738
|
+
```json
|
|
739
|
+
{
|
|
740
|
+
"_TESTS_": {
|
|
741
|
+
"search": [
|
|
742
|
+
{
|
|
743
|
+
"_NAME_": "REQUIRED_TRANSACTION_ID",
|
|
744
|
+
"attr": "$.context.transaction_id",
|
|
745
|
+
"_RETURN_": "attr are present"
|
|
746
|
+
}
|
|
747
|
+
],
|
|
748
|
+
"on_search": [
|
|
749
|
+
{
|
|
750
|
+
"_NAME_": "TRANSACTION_ID_CONTINUITY",
|
|
751
|
+
"currentId": "$.context.transaction_id",
|
|
752
|
+
"savedId": "$._EXTERNAL.transaction_id",
|
|
753
|
+
"_RETURN_": "currentId equal to savedId"
|
|
754
|
+
}
|
|
755
|
+
]
|
|
756
|
+
},
|
|
757
|
+
"_SESSION_DATA_": {
|
|
758
|
+
"search": {
|
|
759
|
+
"transaction_id": "$.context.transaction_id"
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
```
|
|
764
|
+
|
|
765
|
+
Runtime:
|
|
766
|
+
|
|
767
|
+
```typescript
|
|
768
|
+
const store = new MyRedisStorage();
|
|
769
|
+
const key = "session-xyz";
|
|
770
|
+
|
|
771
|
+
// On search call:
|
|
772
|
+
await performMyValidations("search", searchPayload, {
|
|
773
|
+
stateFullValidations: true,
|
|
774
|
+
store,
|
|
775
|
+
uniqueKey: key,
|
|
776
|
+
});
|
|
777
|
+
// → saves transaction_id from searchPayload to store["session-xyz"]["transaction_id"]
|
|
778
|
+
|
|
779
|
+
// On on_search call:
|
|
780
|
+
await performMyValidations("on_search", onSearchPayload, {
|
|
781
|
+
stateFullValidations: true,
|
|
782
|
+
store,
|
|
783
|
+
uniqueKey: key,
|
|
784
|
+
});
|
|
785
|
+
// → loads transaction_id from store, injects as externalData.transaction_id
|
|
786
|
+
// → test evaluates currentId equal to savedId
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
---
|
|
790
|
+
|
|
791
|
+
## 14. Common Patterns Cookbook
|
|
792
|
+
|
|
793
|
+
### Presence check
|
|
794
|
+
|
|
795
|
+
```json
|
|
796
|
+
{ "attr": "$.some.field", "_RETURN_": "attr are present" }
|
|
797
|
+
```
|
|
798
|
+
|
|
799
|
+
### Enum check (field must be in allowed list)
|
|
800
|
+
|
|
801
|
+
```json
|
|
802
|
+
{
|
|
803
|
+
"attr": "$.field",
|
|
804
|
+
"allowed": ["A", "B", "C"],
|
|
805
|
+
"_RETURN_": "attr all in allowed"
|
|
806
|
+
}
|
|
807
|
+
```
|
|
808
|
+
|
|
809
|
+
### Presence + enum combined
|
|
810
|
+
|
|
811
|
+
```json
|
|
812
|
+
{
|
|
813
|
+
"attr": "$.field",
|
|
814
|
+
"allowed": ["A", "B"],
|
|
815
|
+
"_RETURN_": "attr are present && attr all in allowed"
|
|
816
|
+
}
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
### Optional field enum validation (skip if absent)
|
|
820
|
+
|
|
821
|
+
```json
|
|
822
|
+
{
|
|
823
|
+
"path": "$.optional.field",
|
|
824
|
+
"allowed": ["X", "Y"],
|
|
825
|
+
"_CONTINUE_": "!(path are present)",
|
|
826
|
+
"_RETURN_": "path all in allowed"
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
### Uniqueness check
|
|
831
|
+
|
|
832
|
+
```json
|
|
833
|
+
{ "_SCOPE_": "$.items[*]", "ids": "$.id", "_RETURN_": "ids are unique" }
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
### Regex validation
|
|
837
|
+
|
|
838
|
+
```json
|
|
839
|
+
{
|
|
840
|
+
"phones": "$.contacts[*].phone",
|
|
841
|
+
"pattern": ["^\\+91[0-9]{10}$"],
|
|
842
|
+
"_RETURN_": "phones follow regex pattern"
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
### Cross-field relationship
|
|
847
|
+
|
|
848
|
+
```json
|
|
849
|
+
{
|
|
850
|
+
"itemIds": "$.message.order.items[*].id",
|
|
851
|
+
"catalogIds": "$._EXTERNAL._SELF.message.catalog.providers[*].items[*].id",
|
|
852
|
+
"_RETURN_": "itemIds all in catalogIds"
|
|
853
|
+
}
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
### Error-gated group
|
|
857
|
+
|
|
858
|
+
```json
|
|
859
|
+
{
|
|
860
|
+
"_NAME_": "MESSAGE_TESTS",
|
|
861
|
+
"err": "$.error.code",
|
|
862
|
+
"_CONTINUE_": "err are present",
|
|
863
|
+
"_RETURN_": [ ... sub-tests ... ]
|
|
864
|
+
}
|
|
865
|
+
```
|
|
866
|
+
|
|
867
|
+
### Scoped conditional
|
|
868
|
+
|
|
869
|
+
```json
|
|
870
|
+
{
|
|
871
|
+
"_SCOPE_": "$.payments[*]",
|
|
872
|
+
"status": "$.status",
|
|
873
|
+
"paid": ["PAID"],
|
|
874
|
+
"txnId": "$.params.transaction_id",
|
|
875
|
+
"_CONTINUE_": "!(status all in paid)",
|
|
876
|
+
"_RETURN_": "txnId are present"
|
|
877
|
+
}
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
---
|
|
881
|
+
|
|
882
|
+
## 15. Reserved Keywords
|
|
883
|
+
|
|
884
|
+
The following words **cannot be used as variable names**:
|
|
885
|
+
|
|
886
|
+
- All JavaScript/TypeScript reserved words: `break`, `case`, `catch`, `class`, `const`, `continue`, `debugger`, `default`, `delete`, `do`, `else`, `export`, `extends`, `finally`, `for`, `function`, `if`, `import`, `in`, `instanceof`, `let`, `new`, `return`, `super`, `switch`, `this`, `throw`, `try`, `typeof`, `var`, `void`, `while`, `with`, `yield`, `async`, `await`, `arguments`, `eval`, `of`, `enum`, `implements`, `interface`, `package`, `private`, `protected`, `public`, `static`, `abstract`, `as`, `declare`, `from`, `readonly`, `type`, `namespace`
|
|
887
|
+
- All Python reserved words: `False`, `None`, `True`, `and`, `as`, `assert`, `async`, `await`, `break`, `class`, `continue`, `def`, `del`, `elif`, `else`, `except`, `finally`, `for`, `from`, `global`, `if`, `import`, `in`, `is`, `lambda`, `nonlocal`, `not`, `or`, `pass`, `raise`, `return`, `try`, `while`, `with`, `yield`
|
|
888
|
+
- All JVAL system fields: `_NAME_`, `_RETURN_`, `_SCOPE_`, `_CONTINUE_`, `_ERROR_CODE_`, `_SUCCESS_CODE_`, `_DESCRIPTION_`, `_TESTS_`, `_SESSION_DATA_`
|
|
889
|
+
|
|
890
|
+
---
|
|
891
|
+
|
|
892
|
+
## Appendix: Quick field summary card
|
|
893
|
+
|
|
894
|
+
```
|
|
895
|
+
Test Object
|
|
896
|
+
├── _NAME_ (required) Unique ID / generated function name
|
|
897
|
+
├── _DESCRIPTION_ (optional) Error message on failure
|
|
898
|
+
├── _SCOPE_ (optional, default "$") JSONPath → array to iterate over
|
|
899
|
+
├── _CONTINUE_ (optional) JVAL expression: if true → skip this element
|
|
900
|
+
├── _ERROR_CODE_ (optional, default 30000) Failure code
|
|
901
|
+
├── _SUCCESS_CODE_ (optional, default 200) Success code
|
|
902
|
+
├── _RETURN_ (required) JVAL string expression OR array of test objects
|
|
903
|
+
└── <varName> User variable: JSONPath string or string[] literal
|
|
904
|
+
|
|
905
|
+
Variable value prefixes
|
|
906
|
+
├── "$.some.path" Extract from current scope element
|
|
907
|
+
├── "$._EXTERNAL.<key>" Read from external/session data
|
|
908
|
+
└── "$._EXTERNAL._SELF.*" Access full normalised payload from any scope
|
|
909
|
+
|
|
910
|
+
Config root
|
|
911
|
+
├── _TESTS_ { action: [TestObject, ...] }
|
|
912
|
+
└── _SESSION_DATA_ { action: { key: "$.json.path" } }
|
|
913
|
+
```
|