ondc-code-generator 0.5.51 → 0.6.0

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.
Files changed (31) hide show
  1. package/dist/generator/generators/python/py-generator.d.ts +2 -1
  2. package/dist/generator/generators/python/py-generator.js +83 -3
  3. package/dist/generator/generators/python/templates/requirements.mustache +1 -0
  4. package/dist/generator/generators/python/templates/storage-templates/api-save.mustache +41 -0
  5. package/dist/generator/generators/python/templates/storage-templates/index.mustache +40 -0
  6. package/dist/generator/generators/python/templates/storage-templates/save-utils.mustache +22 -0
  7. package/dist/generator/generators/python/templates/storage-templates/storage-interface.mustache +93 -0
  8. package/dist/generator/generators/python/templates/storage-templates/storage-types.mustache +5 -0
  9. package/dist/generator/generators/python/templates/test-config.mustache +6 -0
  10. package/dist/generator/generators/python/templates/validation-code.mustache +9 -1
  11. package/dist/generator/generators/typescript/templates/storage-templates/api-save.mustache +58 -0
  12. package/dist/generator/generators/typescript/templates/storage-templates/index.mustache +37 -0
  13. package/dist/generator/generators/typescript/templates/storage-templates/save-utils.mustache +39 -0
  14. package/dist/generator/generators/typescript/templates/storage-templates/storage-interface.mustache +121 -0
  15. package/dist/generator/generators/typescript/templates/storage-templates/storage-types.mustache +6 -0
  16. package/dist/generator/generators/typescript/templates/test-config.mustache +16 -9
  17. package/dist/generator/generators/typescript/templates/test-object.mustache +1 -1
  18. package/dist/generator/generators/typescript/templates/validation-code.mustache +11 -1
  19. package/dist/generator/generators/typescript/ts-generator.d.ts +2 -1
  20. package/dist/generator/generators/typescript/ts-generator.js +94 -5
  21. package/dist/generator/validators/config-validator.js +11 -1
  22. package/dist/generator/validators/session-data-config/session-data-validator.d.ts +1 -0
  23. package/dist/generator/validators/session-data-config/session-data-validator.js +10 -0
  24. package/dist/index.js +0 -44
  25. package/dist/test.d.ts +1 -0
  26. package/dist/test.js +32 -0
  27. package/dist/utils/config-utils/load-variables.d.ts +3 -0
  28. package/dist/utils/config-utils/load-variables.js +33 -0
  29. package/package.json +2 -1
  30. package/dist/example.d.ts +0 -0
  31. package/dist/example.js +0 -22
@@ -1,7 +1,8 @@
1
1
  import { TestObject } from "../../../types/config-types.js";
2
2
  import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
3
3
  export declare class PythonGenerator extends CodeGenerator {
4
- generateSessionDataCode: () => Promise<never>;
4
+ codeConfig: CodeGeneratorProps | undefined;
5
+ generateSessionDataCode: () => Promise<void>;
5
6
  generateValidationCode: () => Promise<void>;
6
7
  generateCode: (codeConfig: CodeGeneratorProps) => Promise<void>;
7
8
  generateTestFunction: (testObject: TestObject) => Promise<{
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import path from "path";
3
3
  import { fileURLToPath } from "url";
4
- import { ConfigSyntax, TestObjectSyntax } from "../../../constants/syntax.js";
4
+ import { ConfigSyntax, TestObjectSyntax, ExternalDataSyntax, } from "../../../constants/syntax.js";
5
5
  import Mustache from "mustache";
6
6
  import { markdownMessageGenerator } from "../documentation/markdown-message-generator.js";
7
7
  import { getVariablesFromTest as extractVariablesFromText } from "../../../utils/general-utils/test-object-utils.js";
@@ -9,13 +9,60 @@ import { compileInputToPy } from "./py-ast.js";
9
9
  import { CodeGenerator, } from "../classes/abstract-generator.js";
10
10
  import { writeAndFormatCode } from "../../../utils/fs-utils.js";
11
11
  import { MarkdownDocGenerator } from "../documentation/md-generator.js";
12
+ import { collectLoadData } from "../../../utils/config-utils/load-variables.js";
12
13
  const __filename = fileURLToPath(import.meta.url);
13
14
  const __dirname = path.dirname(__filename);
14
15
  export class PythonGenerator extends CodeGenerator {
15
16
  constructor() {
16
17
  super(...arguments);
17
18
  this.generateSessionDataCode = async () => {
18
- throw new Error("Method not implemented.");
19
+ if (!this.codeConfig) {
20
+ throw new Error("Code config not set. Please call generateCode first.");
21
+ }
22
+ const sessionData = this.validationConfig[ConfigSyntax.SessionData];
23
+ const tests = this.validationConfig[ConfigSyntax.Tests];
24
+ const relevantSessionData = {};
25
+ collectLoadData(tests, relevantSessionData);
26
+ console.log("Relevant Session Data for Loading:", relevantSessionData);
27
+ const sessionDataUtilsTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/save-utils.mustache"), "utf-8");
28
+ const storageInterfaceTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/storage-interface.mustache"), "utf-8");
29
+ const storageTypesTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/storage-types.mustache"), "utf-8");
30
+ const indexTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/index.mustache"), "utf-8");
31
+ const saveActionTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/api-save.mustache"), "utf-8");
32
+ const allActions = Object.keys(tests);
33
+ const indexCode = Mustache.render(indexTemplate, {
34
+ actions: Array.from(allActions).map((action) => {
35
+ return { action: action };
36
+ }),
37
+ functionName: this.codeConfig.codeName.replace(/[^a-zA-Z0-9_]/g, ""),
38
+ });
39
+ // Generate individual action files
40
+ for (const action of allActions) {
41
+ const loadData = relevantSessionData[action] || {};
42
+ const saveData = sessionData[action] || {};
43
+ const saveCode = Mustache.render(saveActionTemplate, {
44
+ storeActions: Object.keys(saveData).map((key) => {
45
+ return {
46
+ key: key,
47
+ value: saveData[key],
48
+ };
49
+ }),
50
+ loadActions: Object.keys(loadData).map((key) => {
51
+ console.log(loadData[key]);
52
+ return {
53
+ key: loadData[key],
54
+ };
55
+ }),
56
+ action: action,
57
+ });
58
+ await writeAndFormatCode(this.rootPath, `./storage_actions/${action}.py`, saveCode, "python");
59
+ }
60
+ // Generate utility and interface files
61
+ await writeAndFormatCode(this.rootPath, "./utils/save_utils.py", sessionDataUtilsTemplate, "python");
62
+ await writeAndFormatCode(this.rootPath, "./types/storage_types.py", storageTypesTemplate, "python");
63
+ await writeAndFormatCode(this.rootPath, "./interfaces/storage_interface.py", storageInterfaceTemplate, "python");
64
+ await writeAndFormatCode(this.rootPath, "./storage_actions/__init__.py", indexCode, "python");
65
+ await writeAndFormatCode(this.rootPath, "./interfaces/__init__.py", "# Interfaces package", "python");
19
66
  };
20
67
  this.generateValidationCode = async () => {
21
68
  const testConfig = this.validationConfig[ConfigSyntax.Tests];
@@ -35,6 +82,7 @@ export class PythonGenerator extends CodeGenerator {
35
82
  }
36
83
  };
37
84
  this.generateCode = async (codeConfig) => {
85
+ this.codeConfig = codeConfig;
38
86
  const jsonPathUtilsCode = readFileSync(path.resolve(__dirname, "./templates/json-path-utils.mustache"), "utf-8");
39
87
  const validationUtils = readFileSync(path.resolve(__dirname, "./templates/validation-utils.mustache"), "utf-8");
40
88
  const typesTemplate = readFileSync(path.resolve(__dirname, "./templates/test-config.mustache"), "utf-8");
@@ -61,6 +109,7 @@ export class PythonGenerator extends CodeGenerator {
61
109
  await writeAndFormatCode(this.rootPath, "error.py", this.generateErrorFile(this.errorCodes), "python");
62
110
  await writeAndFormatCode(this.rootPath, "__init__.py", this.generateIndexFile(apiNames, codeConfig.codeName), "python");
63
111
  await new MarkdownDocGenerator(this.validationConfig, this.errorCodes, this.rootPath).generateCode();
112
+ await this.generateSessionDataCode();
64
113
  };
65
114
  this.generateTestFunction = async (testObject) => {
66
115
  const template = readFileSync(path.resolve(__dirname, "./templates/test-object.mustache"), "utf-8");
@@ -89,8 +138,21 @@ export class PythonGenerator extends CodeGenerator {
89
138
  const skipList = skip ? [skip] : undefined;
90
139
  if (typeof testObject[TestObjectSyntax.Return] === "string") {
91
140
  const returnStatement = compileInputToPy(testObject[TestObjectSyntax.Return]);
141
+ // Check if this is a stateful validation
142
+ let isStateFull = false;
143
+ for (const k in testObject) {
144
+ const value = testObject[k];
145
+ if (typeof value === "string") {
146
+ if (value.includes(`${ExternalDataSyntax}.`) &&
147
+ !value.includes("_SELF")) {
148
+ isStateFull = true;
149
+ break;
150
+ }
151
+ }
152
+ }
92
153
  return Mustache.render(template, {
93
154
  isNested: false,
155
+ isStateFull: isStateFull,
94
156
  returnStatement: returnStatement,
95
157
  errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
96
158
  errorDescription: this.CreateErrorMarkdown(testObject, skipList),
@@ -117,6 +179,7 @@ export class PythonGenerator extends CodeGenerator {
117
179
  nestedFunctions: indentedNestedFunctions,
118
180
  names: names,
119
181
  errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
182
+ testName: testObject[TestObjectSyntax.Name],
120
183
  TEST_OBJECT: `${JSON.stringify(testObject)}`,
121
184
  });
122
185
  }
@@ -203,9 +266,10 @@ ${errorsList}
203
266
  .map((api) => `from .api_tests import ${api}`)
204
267
  .join("\n");
205
268
  importsCode += `\nfrom .types.test_config import ValidationConfig\n`;
269
+ importsCode += `\nfrom .storage_actions import perform_${functionName.toLowerCase()}_save, perform_${functionName.toLowerCase()}_load\n`;
206
270
  const masterDoc = readFileSync(path.resolve(__dirname, "./templates/master-doc.mustache"), "utf-8");
207
271
  const masterFunction = `
208
- def perform_${functionName.toLowerCase()}(action, payload,config: ValidationConfig = None, external_data=None):
272
+ def perform_${functionName.toLowerCase()}(action, payload, config: ValidationConfig = None, external_data=None):
209
273
  ${masterDoc}
210
274
 
211
275
  if external_data is None:
@@ -219,6 +283,7 @@ def perform_${functionName.toLowerCase()}(action, payload,config: ValidationConf
219
283
  "standard_logs": False,
220
284
  "_debug": False,
221
285
  "hide_parent_errors": True,
286
+ "state_full_validations": False,
222
287
  }
223
288
  # Merge user config with default config
224
289
  if config is None:
@@ -226,6 +291,21 @@ def perform_${functionName.toLowerCase()}(action, payload,config: ValidationConf
226
291
  else:
227
292
  config = {**default_config, **config}
228
293
 
294
+ if config.get("state_full_validations") and not config.get("store"):
295
+ raise Exception(
296
+ "State Full validations require a storage interface to be provided in the config."
297
+ )
298
+
299
+ if config.get("state_full_validations") and not config.get("unique_key"):
300
+ raise Exception(
301
+ "State Full validations require a unique_key to be provided in the config."
302
+ )
303
+
304
+ if config.get("state_full_validations"):
305
+ import asyncio
306
+ load_data = asyncio.run(perform_${functionName.toLowerCase()}_load(action, config["unique_key"], config["store"]))
307
+ external_data = {**load_data, **external_data}
308
+
229
309
  input_data = {
230
310
  "payload": normalized_payload,
231
311
  "external_data": external_data,
@@ -1,2 +1,3 @@
1
1
  # requirements.txt
2
2
  jsonpath-ng>=1.6.0
3
+ typing-extensions>=4.0.0
@@ -0,0 +1,41 @@
1
+ from ..utils.json_path_utils import get_json_path
2
+ from ..utils.save_utils import save_data, load_function
3
+ from ..interfaces.storage_interface import StorageInterface
4
+ from ..types.storage_types import StorageConfig
5
+ import json
6
+ from datetime import datetime
7
+
8
+ async def store_{{action}}(unique_prefix: str, payload: dict, store: StorageInterface, config: StorageConfig):
9
+ {{#storeActions}}
10
+ await save_function(payload, unique_prefix, "{{{key}}}", "{{{value}}}", store, config)
11
+ {{/storeActions}}
12
+
13
+ async def load_for_{{action}}(unique_prefix: str, store: StorageInterface):
14
+ return {
15
+ {{#loadActions}}
16
+ "{{{key}}}": await load_function(store, unique_prefix, "{{{key}}}"),
17
+ {{/loadActions}}
18
+ }
19
+
20
+ async def save_function(payload: dict, unique_prefix: str, key: str, path: str, store: StorageInterface, config: StorageConfig):
21
+ if path == "" or key == "_SELF":
22
+ return
23
+
24
+ value = get_json_path(payload, path)
25
+ data = {
26
+ "stored_from": "{{action}}_action",
27
+ "key_path": path,
28
+ "value": value,
29
+ "timestamp": datetime.now().isoformat()
30
+ }
31
+ data_string = json.dumps(data)
32
+ await save_data(unique_prefix, key, data_string, store, config)
33
+
34
+ async def load_function(store: StorageInterface, unique_prefix: str, key: str):
35
+ try:
36
+ value = await store.get_key(unique_prefix, key)
37
+ if not value:
38
+ return []
39
+ return json.loads(value)["value"]
40
+ except Exception:
41
+ return []
@@ -0,0 +1,40 @@
1
+ {{#actions}}
2
+ from .{{{action}}} import store_{{{action}}}, load_for_{{{action}}}
3
+ {{/actions}}
4
+ from ..interfaces.storage_interface import StorageInterface
5
+ from ..types.storage_types import StorageConfig
6
+ from typing import Optional, Dict, Any
7
+
8
+ async def perform_{{functionName}}_save(action: str, unique_prefix: str, payload: Dict[Any, Any], store: StorageInterface, config: Optional[StorageConfig] = None):
9
+ complete_config: StorageConfig = {
10
+ "retry_attempts": 3,
11
+ "retry_delay_ms": 1000,
12
+ **(config or {})
13
+ }
14
+
15
+ {{#actions}}
16
+ {{#-first}}
17
+ if action == "{{{action}}}":
18
+ return await store_{{{action}}}(unique_prefix, payload, store, complete_config)
19
+ {{/-first}}
20
+ {{^-first}}
21
+ elif action == "{{{action}}}":
22
+ return await store_{{{action}}}(unique_prefix, payload, store, complete_config)
23
+ {{/-first}}
24
+ {{/actions}}
25
+ else:
26
+ raise Exception("Action not found")
27
+
28
+ async def perform_{{functionName}}_load(action: str, unique_prefix: str, store: StorageInterface):
29
+ {{#actions}}
30
+ {{#-first}}
31
+ if action == "{{{action}}}":
32
+ return await load_for_{{{action}}}(unique_prefix, store)
33
+ {{/-first}}
34
+ {{^-first}}
35
+ elif action == "{{{action}}}":
36
+ return await load_for_{{{action}}}(unique_prefix, store)
37
+ {{/-first}}
38
+ {{/actions}}
39
+ else:
40
+ raise Exception("Action not found")
@@ -0,0 +1,22 @@
1
+ from ..interfaces.storage_interface import StorageInterface
2
+ from ..types.storage_types import StorageConfig
3
+ import asyncio
4
+
5
+ async def save_data(unique_prefix: str, key: str, save_data: str, store: StorageInterface, config: StorageConfig):
6
+ final_key = f"{unique_prefix}:{key}"
7
+ retry_times = config["retry_attempts"]
8
+ delay_ms = config["retry_delay_ms"]
9
+ attempts = 0
10
+
11
+ while attempts < retry_times:
12
+ try:
13
+ await store.save_key(unique_prefix, final_key, save_data)
14
+ return
15
+ except Exception as e:
16
+ attempts += 1
17
+ if attempts >= retry_times:
18
+ raise e
19
+ await asyncio.sleep(delay_ms / 1000.0) # Convert ms to seconds
20
+
21
+ def create_key(unique_prefix: str, key: str):
22
+ return f"{unique_prefix}:{key}"
@@ -0,0 +1,93 @@
1
+ from abc import ABC, abstractmethod
2
+ from typing import List
3
+
4
+ class StorageInterface(ABC):
5
+ """
6
+ Storage interface for persisting and retrieving key-value data.
7
+
8
+ Purpose — Provides a standardized abstraction layer for storage operations
9
+ that can be implemented by different storage backends (Redis, Memory, File, etc.).
10
+
11
+ Implementation Notes:
12
+ - All operations are asynchronous and return coroutines
13
+ - Keys should be strings and values are stored as strings
14
+ - Implementations should handle serialization/deserialization as needed
15
+ - Prefix-based operations allow for namespacing and bulk operations
16
+ """
17
+
18
+ @abstractmethod
19
+ async def save_key(self, unique_prefix: str, key: str, value: str) -> None:
20
+ """
21
+ Save a key-value pair to storage with a unique prefix for namespacing.
22
+
23
+ Args:
24
+ unique_prefix: A unique identifier/namespace prefix to prevent key collisions
25
+ key: The unique identifier for the stored value within the prefix namespace
26
+ value: The string value to store
27
+ """
28
+ pass
29
+
30
+ @abstractmethod
31
+ async def get_key(self, unique_prefix: str, key: str) -> str:
32
+ """
33
+ Retrieve a value by its key from storage within a specific namespace.
34
+
35
+ Args:
36
+ unique_prefix: The unique identifier/namespace prefix used when storing
37
+ key: The unique identifier for the value to retrieve within the prefix namespace
38
+
39
+ Returns:
40
+ The stored value as a string
41
+
42
+ Raises:
43
+ Exception if the key does not exist
44
+ """
45
+ pass
46
+
47
+ @abstractmethod
48
+ async def delete_key(self, unique_prefix: str, key: str) -> None:
49
+ """
50
+ Remove a key-value pair from storage within a specific namespace.
51
+
52
+ Args:
53
+ unique_prefix: The unique identifier/namespace prefix used when storing
54
+ key: The unique identifier for the value to delete within the prefix namespace
55
+ """
56
+ pass
57
+
58
+ @abstractmethod
59
+ async def list_keys(self, unique_prefix: str) -> List[str]:
60
+ """
61
+ List all keys within a specific namespace/prefix.
62
+
63
+ Args:
64
+ unique_prefix: The unique identifier/namespace prefix to list keys from
65
+
66
+ Returns:
67
+ A list of keys within that namespace
68
+ """
69
+ pass
70
+
71
+ @abstractmethod
72
+ async def clear_storage(self) -> None:
73
+ """
74
+ Clear all stored data from the storage backend.
75
+
76
+ Warning: This operation is destructive and cannot be undone.
77
+ Use with caution, especially in production environments.
78
+ """
79
+ pass
80
+
81
+ @abstractmethod
82
+ async def key_exists(self, unique_prefix: str, key: str) -> bool:
83
+ """
84
+ Check if a key exists in storage within a specific namespace.
85
+
86
+ Args:
87
+ unique_prefix: The unique identifier/namespace prefix to check within
88
+ key: The unique identifier to check for existence within the prefix namespace
89
+
90
+ Returns:
91
+ True if the key exists, False otherwise
92
+ """
93
+ pass
@@ -0,0 +1,5 @@
1
+ from typing import TypedDict
2
+
3
+ class StorageConfig(TypedDict):
4
+ retry_attempts: int
5
+ retry_delay_ms: int
@@ -12,10 +12,16 @@ class ValidationConfig(TypedDict, total=False):
12
12
  only_invalid (bool): If True, only invalid results will be returned.
13
13
  hide_parent_errors (Optional[bool]): If True, parent errors will be hidden.
14
14
  _debug (Optional[bool]): If True, debug mode will be enabled.
15
+ state_full_validations (Optional[bool]): If True, stateful validations will be enabled.
16
+ store (Optional[Any]): Storage interface for stateful validations.
17
+ unique_key (Optional[str]): Unique key for stateful validations.
15
18
  """
16
19
  only_invalid: Optional[bool]
17
20
  hide_parent_errors: Optional[bool]
18
21
  _debug: Optional[bool]
22
+ state_full_validations: Optional[bool]
23
+ store: Optional[Any]
24
+ unique_key: Optional[str]
19
25
 
20
26
  # Input structure for validation functions
21
27
  ValidationInput = Dict[str, Any]
@@ -17,10 +17,18 @@
17
17
  valid = all(r["valid"] for r in sub_results)
18
18
  {{/isNested}}
19
19
  {{^isNested}}
20
+ {{#isStateFull}}
21
+ validate = True
22
+ if input_data["config"].get("state_full_validations") == True:
23
+ validate = {{{returnStatement}}}
24
+ {{/isStateFull}}
25
+
26
+ {{^isStateFull}}
20
27
  validate = {{{returnStatement}}}
28
+ {{/isStateFull}}
21
29
 
22
30
  if not validate:
23
- del {{testName}}_obj["_EXTERNAL"]
31
+ # del {{testName}}_obj["_EXTERNAL"]
24
32
  return [{
25
33
  "test_name": "{{testName}}",
26
34
  "valid": False,
@@ -0,0 +1,58 @@
1
+ import payloadUtils from "../utils/json-path-utils";
2
+ import saveUtils from "../utils/save-utils";
3
+ import StorageInterface from "../interfaces/storage-interface";
4
+ import { StorageConfig } from "../types/storage-types";
5
+
6
+ export async function store_{{action}}(
7
+ uniquePrefix: string,
8
+ payload: any,
9
+ store: StorageInterface,
10
+ config: StorageConfig
11
+ ){
12
+ {{#storeActions}}
13
+ await saveFunction(payload,uniquePrefix,"{{{key}}}", "{{{value}}}", store, config);
14
+ {{/storeActions}}
15
+ }
16
+
17
+ export async function loadFor_{{action}}(
18
+ uniquePrefix: string,
19
+ store: StorageInterface
20
+ ) {
21
+ return {
22
+ {{#loadActions}}
23
+ {{{key}}}: await loadFunction(store, uniquePrefix, "{{{key}}}"),
24
+ {{/loadActions}}
25
+ };
26
+ }
27
+
28
+ async function saveFunction(payload: any,uniquePrefix: string, key: string, path: string, store: StorageInterface,
29
+ config: StorageConfig): Promise<void> {
30
+ if(path === "" || key === "_SELF"){
31
+ return;
32
+ }
33
+ const value = payloadUtils.getJsonPath(payload, path);
34
+ const data = {
35
+ stored_from: "{{action}}_action",
36
+ key_path: path,
37
+ value: value,
38
+ timestamp: new Date().toISOString(),
39
+ };
40
+ const dataString = JSON.stringify(data);
41
+ await saveUtils.saveData(uniquePrefix, key, dataString, store, config);
42
+ }
43
+
44
+ async function loadFunction(
45
+ store: StorageInterface,
46
+ uniquePrefix: string,
47
+ key: string
48
+ ): Promise<any[] | []> {
49
+ try {
50
+ const value = await store.getKey(uniquePrefix, key);
51
+ if (!value) {
52
+ return [];
53
+ }
54
+ return JSON.parse(value).value;
55
+ } catch (err) {
56
+ return [];
57
+ }
58
+ }
@@ -0,0 +1,37 @@
1
+ {{#actions}}
2
+ import { store_{{{action}}}, loadFor_{{{action}}} } from './{{{action}}}'
3
+ {{/actions}}
4
+ import StorageInterface from "../interfaces/storage-interface";
5
+ import { StorageConfig } from "../types/storage-types";
6
+
7
+ export async function perform{{functionName}}Save(action: string,uniquePrefix: string, payload: any, store: StorageInterface,
8
+ config?: Partial<StorageConfig>){
9
+
10
+ const completeConfig : StorageConfig = {
11
+ ...{
12
+ retryAttempts: 3,
13
+ retryDelayMs: 1000,
14
+ },
15
+ ...config
16
+ }
17
+ switch(action){
18
+ {{#actions}}
19
+ case '{{{action}}}':
20
+ return await store_{{{action}}}(uniquePrefix,payload,store,completeConfig);
21
+ {{/actions}}
22
+ default:
23
+ throw new Error('Action not found');
24
+ }
25
+ }
26
+
27
+
28
+ export async function perform{{functionName}}Load(action: string,uniquePrefix: string, store: StorageInterface){
29
+ switch(action){
30
+ {{#actions}}
31
+ case '{{{action}}}':
32
+ return await loadFor_{{{action}}}(uniquePrefix,store);
33
+ {{/actions}}
34
+ default:
35
+ throw new Error('Action not found');
36
+ }
37
+ }
@@ -0,0 +1,39 @@
1
+ import StorageInterface from "../interfaces/storage-interface";
2
+ import { StorageConfig } from "../types/storage-types";
3
+ async function saveData(
4
+ uniquePrefix: string,
5
+ key: string,
6
+ saveData: string,
7
+ store: StorageInterface,
8
+ config: StorageConfig
9
+ ){
10
+ const finalKey = `${uniquePrefix}:${key}`;
11
+ const retryTimes = config.retryAttempts;
12
+ const delayMs = config.retryDelayMs;
13
+ let attempts = 0;
14
+ while(attempts < retryTimes){
15
+ try{
16
+ await store.saveKey(uniquePrefix, finalKey, saveData);
17
+ return;
18
+ }catch(e){
19
+ attempts++;
20
+ if(attempts >= retryTimes){
21
+ throw e;
22
+ }
23
+ await new Promise(res => setTimeout(res,delayMs));
24
+ }
25
+ }
26
+
27
+ }
28
+
29
+ function createKey(
30
+ uniquePrefix: string,
31
+ key: string
32
+ ){
33
+ return `${uniquePrefix}:${key}`;
34
+ }
35
+
36
+ export default {
37
+ saveData,
38
+ createKey
39
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Storage interface for persisting and retrieving key-value data.
3
+ *
4
+ * @remarks
5
+ * **Purpose** — Provides a standardized abstraction layer for storage operations
6
+ * that can be implemented by different storage backends (Redis, Memory, File, etc.).
7
+ *
8
+ * **Implementation Notes**:
9
+ * - All operations are asynchronous and return Promises
10
+ * - Keys should be strings and values are stored as strings
11
+ * - Implementations should handle serialization/deserialization as needed
12
+ * - Prefix-based operations allow for namespacing and bulk operations
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * class RedisStorage implements StorageInterface {
17
+ * async saveKey(uniquePrefix: string, key: string, value: string): Promise<void> {
18
+ * const fullKey = `${uniquePrefix}:${key}`;
19
+ * await this.redisClient.set(fullKey, value);
20
+ * }
21
+ * // ... other methods
22
+ * }
23
+ *
24
+ * const storage = new RedisStorage();
25
+ * await storage.saveKey("app1", "user:123", JSON.stringify(userData));
26
+ * const user = JSON.parse(await storage.getKey("app1", "user:123"));
27
+ * ```
28
+ */
29
+ export default interface StorageInterface {
30
+ /**
31
+ * Save a key-value pair to storage with a unique prefix for namespacing.
32
+ *
33
+ * @param uniquePrefix - A unique identifier/namespace prefix to prevent key collisions
34
+ * @param key - The unique identifier for the stored value within the prefix namespace
35
+ * @param value - The string value to store
36
+ * @returns Promise that resolves when the operation completes
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * await storage.saveKey("app1", "session:abc123", JSON.stringify(sessionData));
41
+ * ```
42
+ */
43
+ saveKey(uniquePrefix: string, key: string, value: string): Promise<void>;
44
+
45
+ /**
46
+ * Retrieve a value by its key from storage within a specific namespace.
47
+ *
48
+ * @param uniquePrefix - The unique identifier/namespace prefix used when storing
49
+ * @param key - The unique identifier for the value to retrieve within the prefix namespace
50
+ * @returns Promise that resolves to the stored value
51
+ * @throws Should throw an error if the key does not exist
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * const sessionData = await storage.getKey("app1", "session:abc123");
56
+ * ```
57
+ */
58
+ getKey(uniquePrefix: string, key: string): Promise<string>;
59
+
60
+ /**
61
+ * Remove a key-value pair from storage within a specific namespace.
62
+ *
63
+ * @param uniquePrefix - The unique identifier/namespace prefix used when storing
64
+ * @param key - The unique identifier for the value to delete within the prefix namespace
65
+ * @returns Promise that resolves when the deletion completes
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * await storage.deleteKey("app1", "session:abc123");
70
+ * ```
71
+ */
72
+ deleteKey(uniquePrefix: string, key: string): Promise<void>;
73
+
74
+ /**
75
+ * List all keys within a specific namespace/prefix.
76
+ *
77
+ * @param uniquePrefix - The unique identifier/namespace prefix to list keys from
78
+ * @returns Promise that resolves to an array of keys within that namespace
79
+ *
80
+ * @example
81
+ * ```ts
82
+ * const app1Keys = await storage.listKeys("app1");
83
+ * // Returns keys stored under the "app1" namespace
84
+ * // e.g., ["session:abc123", "user:456", "config:settings"]
85
+ * ```
86
+ */
87
+ listKeys(uniquePrefix: string): Promise<string[]>;
88
+
89
+ /**
90
+ * Clear all stored data from the storage backend.
91
+ *
92
+ * @remarks
93
+ * **Warning** — This operation is destructive and cannot be undone.
94
+ * Use with caution, especially in production environments.
95
+ *
96
+ * @returns Promise that resolves when all data has been cleared
97
+ *
98
+ * @example
99
+ * ```ts
100
+ * await storage.clearStorage(); // All data is now removed
101
+ * ```
102
+ */
103
+ clearStorage(): Promise<void>;
104
+
105
+ /**
106
+ * Check if a key exists in storage within a specific namespace.
107
+ *
108
+ * @param uniquePrefix - The unique identifier/namespace prefix to check within
109
+ * @param key - The unique identifier to check for existence within the prefix namespace
110
+ * @returns Promise that resolves to true if the key exists, false otherwise
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * const exists = await storage.keyExists("app1", "user:123");
115
+ * if (exists) {
116
+ * const userData = await storage.getKey("app1", "user:123");
117
+ * }
118
+ * ```
119
+ */
120
+ keyExists(uniquePrefix: string, key: string): Promise<boolean>;
121
+ }
@@ -0,0 +1,6 @@
1
+
2
+
3
+ export type StorageConfig = {
4
+ retryAttempts: number;
5
+ retryDelayMs: number;
6
+ }
@@ -1,17 +1,25 @@
1
+ import StorageInterface from "../interfaces/storage-interface";
2
+
3
+
1
4
  /**
2
5
  * Configuration options for running validation routines.
3
6
  *
4
7
  * @property onlyInvalid - If true, only invalid results will be returned.
8
+ * @property stateFullValidations - If true, enables stateful validations that may depend on previous data.
9
+ * @property uniqueKey - Optional. A unique key for the validation instance.
10
+ * @property store - Optional. An implementation of StorageInterface for persisting data across validations.
5
11
  * @property hideParentErrors - Optional. Hides nested error details if set to true.
6
12
  * @property _debug - Optional. Enables debug mode for additional diagnostic information.
7
13
  */
8
14
  export interface ValidationConfig {
9
- onlyInvalid: boolean;
10
- hideParentErrors: boolean;
11
- _debug: boolean;
15
+ stateFullValidations: boolean;
16
+ uniqueKey?: string;
17
+ store?: StorageInterface;
18
+ onlyInvalid: boolean;
19
+ hideParentErrors: boolean;
20
+ _debug: boolean;
12
21
  }
13
22
 
14
-
15
23
  /**
16
24
  * Represents the output of a validation run.
17
25
  *
@@ -49,15 +57,14 @@ export type validationOutput = {
49
57
  }[];
50
58
 
51
59
 
52
- /*
53
- {% comment %} export type ExternalData = {
60
+
61
+ export type ExternalData = {
54
62
  {{#externalData}}
55
63
  {{name}}?: string[];
56
64
  {{/externalData}}
57
- }; {% endcomment %}
58
- */
65
+ _SELF?: any;
66
+ };
59
67
 
60
- export type ExternalData = {}
61
68
 
62
69
  export type validationInput = {
63
70
  payload: any;
@@ -14,7 +14,7 @@ function {{name}}(input: validationInput): validationOutput {
14
14
  {{/hasContinue}}
15
15
 
16
16
  {{{validationCode}}}
17
- delete testObj._EXTERNAL;
17
+ // delete testObj._EXTERNAL;
18
18
  }
19
19
  return [{
20
20
  testName: "{{testName}}",
@@ -20,10 +20,20 @@ valid = subResults.every((r) => r.valid);
20
20
  {{/isNested}}
21
21
 
22
22
  {{^isNested}}
23
+
24
+ {{#isStateFull}}
25
+ let validate = true;
26
+ if(input.config.stateFullValidations === true){
27
+ validate = {{{returnStatement}}}
28
+ }
29
+ {{/isStateFull}}
30
+
31
+ {{^isStateFull}}
23
32
  const validate = {{{returnStatement}}}
33
+ {{/isStateFull}}
24
34
 
25
35
  if(!validate){
26
- delete testObj._EXTERNAL;
36
+ // delete testObj._EXTERNAL;
27
37
  return [{
28
38
  testName: "{{testName}}",
29
39
  valid: false,
@@ -1,7 +1,8 @@
1
1
  import { TestObject } from "../../../types/config-types.js";
2
2
  import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
3
3
  export declare class TypescriptGenerator extends CodeGenerator {
4
- generateSessionDataCode: () => Promise<never>;
4
+ codeConfig: CodeGeneratorProps | undefined;
5
+ generateSessionDataCode: () => Promise<void>;
5
6
  generateValidationCode: () => Promise<void>;
6
7
  generateCode: (codeConfig: CodeGeneratorProps) => Promise<void>;
7
8
  generateTestFunction: (testObject: TestObject) => Promise<{
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "fs";
2
2
  import path from "path";
3
3
  import { fileURLToPath } from "url";
4
- import { ConfigSyntax, TestObjectSyntax } from "../../../constants/syntax.js";
4
+ import { ConfigSyntax, ExternalDataSyntax, TestObjectSyntax, } from "../../../constants/syntax.js";
5
5
  import Mustache from "mustache";
6
6
  import { markdownMessageGenerator } from "../documentation/markdown-message-generator.js";
7
7
  import { getVariablesFromTest as extractVariablesFromText } from "../../../utils/general-utils/test-object-utils.js";
@@ -10,13 +10,64 @@ import { compileInputToTs } from "./ts-ast.js";
10
10
  import { CodeGenerator, } from "../classes/abstract-generator.js";
11
11
  import { writeAndFormatCode } from "../../../utils/fs-utils.js";
12
12
  import { MarkdownDocGenerator } from "../documentation/md-generator.js";
13
+ import { collectLoadData } from "../../../utils/config-utils/load-variables.js";
13
14
  const __filename = fileURLToPath(import.meta.url);
14
15
  const __dirname = path.dirname(__filename);
15
16
  export class TypescriptGenerator extends CodeGenerator {
16
17
  constructor() {
17
18
  super(...arguments);
18
19
  this.generateSessionDataCode = async () => {
19
- throw new Error("Method not implemented.");
20
+ if (!this.codeConfig) {
21
+ throw new Error("Code config not set. Please call generateCode first.");
22
+ }
23
+ const sessionData = this.validationConfig[ConfigSyntax.SessionData];
24
+ const tests = this.validationConfig[ConfigSyntax.Tests];
25
+ const relevantSessionData = {};
26
+ collectLoadData(tests, relevantSessionData);
27
+ console.log("Relevant Session Data for Loading:", relevantSessionData);
28
+ const actions = Object.keys(sessionData);
29
+ const sessionDataUtilsTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/save-utils.mustache"), "utf-8");
30
+ const storageInterfaceTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/storage-interface.mustache"), "utf-8");
31
+ const storageTypesTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/storage-types.mustache"), "utf-8");
32
+ const indexTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/index.mustache"), "utf-8");
33
+ const saveActionTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/api-save.mustache"), "utf-8");
34
+ const allActions = Object.keys(tests);
35
+ const indexCode = Mustache.render(indexTemplate, {
36
+ actions: Array.from(allActions).map((action) => {
37
+ return { action: action };
38
+ }),
39
+ functionName: this.codeConfig.codeName.replace(/[^a-zA-Z0-9_]/g, ""),
40
+ });
41
+ /*
42
+ storeActions: {
43
+ key: string;
44
+ value: string;
45
+ }[]
46
+ */
47
+ for (const action of allActions) {
48
+ const loadData = relevantSessionData[action] || {};
49
+ const saveData = sessionData[action] || {};
50
+ const saveCode = Mustache.render(saveActionTemplate, {
51
+ storeActions: Object.keys(saveData).map((key) => {
52
+ return {
53
+ key: key,
54
+ value: saveData[key],
55
+ };
56
+ }),
57
+ loadActions: Object.keys(loadData).map((key) => {
58
+ console.log(loadData[key]);
59
+ return {
60
+ key: loadData[key],
61
+ };
62
+ }),
63
+ action: action,
64
+ });
65
+ await writeAndFormatCode(this.rootPath, `./storage-actions/${action}.ts`, saveCode, "typescript");
66
+ }
67
+ await writeAndFormatCode(this.rootPath, "./utils/save-utils.ts", sessionDataUtilsTemplate, "typescript");
68
+ await writeAndFormatCode(this.rootPath, "./types/storage-types.ts", storageTypesTemplate, "typescript");
69
+ await writeAndFormatCode(this.rootPath, "./interfaces/storage-interface.ts", storageInterfaceTemplate, "typescript");
70
+ await writeAndFormatCode(this.rootPath, "./storage-actions/index.ts", indexCode, "typescript");
20
71
  };
21
72
  this.generateValidationCode = async () => {
22
73
  const testConfig = this.validationConfig[ConfigSyntax.Tests];
@@ -36,6 +87,7 @@ export class TypescriptGenerator extends CodeGenerator {
36
87
  }
37
88
  };
38
89
  this.generateCode = async (codeConfig) => {
90
+ this.codeConfig = codeConfig;
39
91
  const jsonPathUtilsCode = readFileSync(path.resolve(__dirname, "./templates/json-path-utils.mustache"), "utf-8");
40
92
  const validtionUtils = readFileSync(path.resolve(__dirname, "./templates/validation-utils.mustache"), "utf-8");
41
93
  const typesTemplate = readFileSync(path.resolve(__dirname, "./templates/test-config.mustache"), "utf-8");
@@ -51,6 +103,7 @@ export class TypescriptGenerator extends CodeGenerator {
51
103
  await writeAndFormatCode(this.rootPath, "error.ts", this.generateErrorFile(this.errorCodes), "typescript");
52
104
  await writeAndFormatCode(this.rootPath, "index.ts", this.generateIndexFile(Object.keys(this.validationConfig[ConfigSyntax.Tests]), codeConfig.codeName), "typescript");
53
105
  await new MarkdownDocGenerator(this.validationConfig, this.errorCodes, this.rootPath).generateCode();
106
+ await this.generateSessionDataCode();
54
107
  };
55
108
  this.generateTestFunction = async (testObject) => {
56
109
  const template = readFileSync(path.resolve(__dirname, "./templates/test-object.mustache"), "utf-8");
@@ -79,8 +132,20 @@ export class TypescriptGenerator extends CodeGenerator {
79
132
  const skipList = skip ? [skip] : undefined;
80
133
  if (typeof testObject[TestObjectSyntax.Return] === "string") {
81
134
  const returnStatement = compileInputToTs(testObject[TestObjectSyntax.Return]);
135
+ let isStateFull = false;
136
+ for (const k in testObject) {
137
+ const value = testObject[k];
138
+ if (typeof value === "string") {
139
+ if (value.includes(`${ExternalDataSyntax}.`) &&
140
+ !value.includes("_SELF")) {
141
+ isStateFull = true;
142
+ break;
143
+ }
144
+ }
145
+ }
82
146
  return Mustache.render(template, {
83
147
  isNested: false,
148
+ isStateFull: isStateFull,
84
149
  returnStatement: returnStatement,
85
150
  errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
86
151
  errorDescription: this.CreateErrorMarkdown(testObject, skipList),
@@ -155,13 +220,14 @@ export class TypescriptGenerator extends CodeGenerator {
155
220
  }
156
221
  getExternalKeys() {
157
222
  const apis = Object.keys(this.validationConfig[ConfigSyntax.SessionData]);
158
- const result = [];
223
+ let result = [];
159
224
  for (const api of apis) {
160
225
  const keys = Object.keys(this.validationConfig[ConfigSyntax.SessionData][api]);
161
226
  for (const key of keys) {
162
227
  result.push({ name: key });
163
228
  }
164
229
  }
230
+ result = result.filter((v) => v.name !== "_SELF");
165
231
  return result;
166
232
  }
167
233
  generateIndexFile(apis, functionName = "L1Validations") {
@@ -171,15 +237,34 @@ export class TypescriptGenerator extends CodeGenerator {
171
237
  .join("\n");
172
238
  importsCode += `\nimport { ValidationConfig,validationOutput } from "./types/test-config";`;
173
239
  importsCode += `\nimport normalizeKeys from "./utils/json-normalizer";`;
240
+ importsCode += `\nimport { perform${functionName}Save, perform${functionName}Load} from "./storage-actions";`;
241
+ importsCode += `\nimport StorageInterface from "./interfaces/storage-interface";`;
174
242
  const masterTemplate = readFileSync(path.resolve(__dirname, "./templates/index.mustache"), "utf-8");
175
243
  const masterFunction = `
176
244
  export function perform${functionName}(action: string, payload: any, config?: Partial<ValidationConfig>, externalData: any = {}) {
177
245
  const completeConfig: ValidationConfig = {
178
- ...{ onlyInvalid: true, standardLogs: false, hideParentErrors: true, _debug: false },
246
+ ...{ onlyInvalid: true, standardLogs: false, hideParentErrors: true, stateFullValidations: false, _debug: false },
179
247
  ...config,
180
248
  };
249
+
250
+ if (completeConfig.stateFullValidations && !completeConfig.store) {
251
+ throw new Error(
252
+ "State Full validations require a storage interface to be provided in the config."
253
+ );
254
+ }
255
+ if( completeConfig.stateFullValidations && !completeConfig.uniqueKey) {
256
+ throw new Error(
257
+ "State Full validations require a uniqueKey to be provided in the config."
258
+ );
259
+ }
181
260
  const normalizedPayload = normalizeKeys(JSON.parse(JSON.stringify(payload)));
182
261
  externalData._SELF = normalizedPayload;
262
+ if (completeConfig.stateFullValidations) {
263
+ externalData = {
264
+ ...performL1_validationsLoad(action, completeConfig.uniqueKey!, completeConfig.store!),
265
+ ...externalData,
266
+ };
267
+ }
183
268
  switch (action) {
184
269
  ${apis
185
270
  .map((api) => `case "${api}": return ${api}({
@@ -191,7 +276,11 @@ export class TypescriptGenerator extends CodeGenerator {
191
276
  default:
192
277
  throw new Error("Action not found");
193
278
  }
194
- }`;
279
+ }
280
+
281
+ export {perform${functionName}Save, perform${functionName}Load, StorageInterface};
282
+
283
+ `;
195
284
  return Mustache.render(masterTemplate, {
196
285
  importsCode: importsCode,
197
286
  masterFunction: masterFunction,
@@ -11,7 +11,17 @@ export class ConfigValidator {
11
11
  throw new Error(`SessionData not found in config`);
12
12
  const sessionData = this.config[ConfigSyntax.SessionData];
13
13
  const tests = this.config[ConfigSyntax.Tests];
14
- await new SessionDataValidator(`${this.validationPath}/${ConfigSyntax.SessionData}`, sessionData).validate();
14
+ const sessionDataValidator = new SessionDataValidator(`${this.validationPath}/${ConfigSyntax.SessionData}`, sessionData);
15
+ await sessionDataValidator.validate();
16
+ for (const api in sessionData) {
17
+ const paths = this.stringJsonPaths[api];
18
+ for (const key in sessionData[api]) {
19
+ const value = sessionData[api][key];
20
+ if (typeof value === "string") {
21
+ sessionDataValidator.validateApiPath(paths, value, api, key);
22
+ }
23
+ }
24
+ }
15
25
  const externalVariables = getExternalVariables(sessionData);
16
26
  for (const api in tests) {
17
27
  const testList = tests[api];
@@ -6,4 +6,5 @@ export declare class SessionDataValidator implements IValidator {
6
6
  constructor(validtionPath: string, sessionData: Record<string, SessionDataApi>);
7
7
  validate: () => Promise<void>;
8
8
  private validateSessionData;
9
+ validateApiPath(apiPaths: string[], path: string, api: string, key: string): void;
9
10
  }
@@ -1,4 +1,6 @@
1
+ import { ConfigSyntax } from "../../../constants/syntax.js";
1
2
  import { isPrimitive } from "../../../utils/general-utils/validation-utils.js";
3
+ import { isValidJsonPath } from "../../../utils/json-path-utils/paths.js";
2
4
  export class SessionDataValidator {
3
5
  constructor(validtionPath, sessionData) {
4
6
  this.validate = async () => {
@@ -32,4 +34,12 @@ export class SessionDataValidator {
32
34
  }
33
35
  });
34
36
  }
37
+ validateApiPath(apiPaths, path, api, key) {
38
+ if (!isValidJsonPath(path)) {
39
+ throw new Error(`Invalid ${ConfigSyntax.SessionData} path at ${path}: Path should be a valid JSONPath expression for ${api} and key: ${key}`);
40
+ }
41
+ if (!apiPaths.includes(path)) {
42
+ throw new Error(`Invalid ${ConfigSyntax.SessionData} path at ${path}: Path should be a valid path which returns a linear array for ${api} and key: ${key}`);
43
+ }
44
+ }
35
45
  }
package/dist/index.js CHANGED
@@ -1,46 +1,2 @@
1
1
  import { ConfigCompiler } from "./generator/config-compiler.js";
2
2
  export { ConfigCompiler };
3
- // import { readFileSync } from "fs";
4
- // import path from "path";
5
- // import { fileURLToPath } from "url";
6
- // const __filename = fileURLToPath(import.meta.url);
7
- // const __dirname = path.dirname(__filename);
8
- // import { SupportedLanguages } from "./types/compiler-types.js";
9
- // const main = async () => {
10
- // const compiler = new ConfigCompiler(SupportedLanguages.Python);
11
- // const buildPath = path.resolve(__dirname, "../samples/build.yaml");
12
- // const valConfigPath = path.resolve(
13
- // __dirname,
14
- // "../samples/validation-config.json"
15
- // );
16
- // const buildYaml = readFileSync(buildPath, "utf-8");
17
- // const valConfig = JSON.parse(readFileSync(valConfigPath, "utf-8"));
18
- // // await compiler.initialize(buildYaml);
19
- // // await compiler.generateCode(
20
- // // valConfig,
21
- // // "L1_validations",
22
- // // false,
23
- // // "./alpha/python/"
24
- // // );
25
- // // const compilerTy = new ConfigCompiler(SupportedLanguages.Typescript);
26
- // // await compilerTy.initialize(buildYaml);
27
- // // await compilerTy.generateCode(
28
- // // valConfig,
29
- // // "L1_validations",
30
- // // false,
31
- // // "./alpha/typescript/"
32
- // // );
33
- // // JavaScript generation example
34
- // const compilerJs = new ConfigCompiler(SupportedLanguages.Javascript);
35
- // await compilerJs.initialize(buildYaml);
36
- // await compilerJs.generateCode(
37
- // valConfig,
38
- // "L1_validations",
39
- // false,
40
- // "./alpha/javascriptNative/"
41
- // );
42
- // };
43
- // (async () => {
44
- // await main();
45
- // console.log("========== Code generation completed. ==========");
46
- // })();
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,32 @@
1
+ import { readFileSync } from "fs";
2
+ import path from "path";
3
+ import { fileURLToPath } from "url";
4
+ import { ConfigCompiler } from "./generator/config-compiler.js";
5
+ import { SupportedLanguages } from "./types/compiler-types.js";
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ const main = async () => {
9
+ const compiler = new ConfigCompiler(SupportedLanguages.Python);
10
+ const buildPath = path.resolve(__dirname, "../samples/build.yaml");
11
+ const valConfigPath = path.resolve(__dirname, "../samples/validation-config.json");
12
+ const buildYaml = readFileSync(buildPath, "utf-8");
13
+ const valConfig = JSON.parse(readFileSync(valConfigPath, "utf-8"));
14
+ await compiler.initialize(buildYaml);
15
+ await compiler.generateCode(valConfig, "L1_validations", false, "./alpha/python/");
16
+ const compilerTy = new ConfigCompiler(SupportedLanguages.Typescript);
17
+ await compilerTy.initialize(buildYaml);
18
+ await compilerTy.generateCode(valConfig, "L1_validations", false, "./alpha/typescript/");
19
+ // JavaScript generation example
20
+ // const compilerJs = new ConfigCompiler(SupportedLanguages.Javascript);
21
+ // await compilerJs.initialize(buildYaml);
22
+ // await compilerJs.generateCode(
23
+ // valConfig,
24
+ // "L1_validations",
25
+ // false,
26
+ // "./alpha/javascriptNative/"
27
+ // );
28
+ };
29
+ (async () => {
30
+ await main();
31
+ console.log("========== Code generation completed. ==========");
32
+ })();
@@ -0,0 +1,3 @@
1
+ import { ConfigSyntax } from "../../constants/syntax.js";
2
+ import { ValidationConfig } from "../../types/config-types.js";
3
+ export declare function collectLoadData(tests: ValidationConfig[ConfigSyntax.Tests], relevantSessionData: Record<string, Record<string, string>>): void;
@@ -0,0 +1,33 @@
1
+ import { ExternalDataSyntax, TestObjectSyntax, } from "../../constants/syntax.js";
2
+ import { getVariablesFromTest } from "../general-utils/test-object-utils.js";
3
+ export function collectLoadData(tests, relevantSessionData) {
4
+ function processTestObjects(api, testObjects) {
5
+ for (const testObject of testObjects) {
6
+ // Extract variables from this testObject
7
+ const varNames = getVariablesFromTest(testObject);
8
+ for (const varName of varNames) {
9
+ if (typeof testObject[varName] === "string") {
10
+ const path = testObject[varName];
11
+ if (path.includes("_SELF"))
12
+ continue;
13
+ if (path.startsWith(`$.${ExternalDataSyntax}`)) {
14
+ relevantSessionData[api] = relevantSessionData[api] || {};
15
+ // avoid duplication
16
+ const value = path.split(`$.${ExternalDataSyntax}.`)[1];
17
+ if (relevantSessionData[api][varName] !== value) {
18
+ relevantSessionData[api][varName] = value;
19
+ }
20
+ }
21
+ }
22
+ }
23
+ // Recursively process return objects (if array)
24
+ const returnObj = testObject[TestObjectSyntax.Return];
25
+ if (Array.isArray(returnObj)) {
26
+ processTestObjects(api, returnObj);
27
+ }
28
+ }
29
+ }
30
+ for (const api in tests) {
31
+ processTestObjects(api, tests[api]);
32
+ }
33
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "ondc-code-generator",
3
- "version": "0.5.51",
3
+ "version": "0.6.0",
4
4
  "description": "generate code from build.yaml ",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "build": "tsc && copyfiles -u 1 \"src/**/*.{yaml,html,css,mustache}\" dist/",
9
9
  "start": "node ./dist/index.js",
10
+ "test": "npm run build && node ./dist/test.js",
10
11
  "dev": "nodemon",
11
12
  "clean": "rm -rf dist",
12
13
  "prepare": "npm run clean && npm run build"
package/dist/example.d.ts DELETED
File without changes
package/dist/example.js DELETED
@@ -1,22 +0,0 @@
1
- "use strict";
2
- // import { readFileSync } from "fs";
3
- // import path from "path";
4
- // import { fileURLToPath } from "url";
5
- // const __filename = fileURLToPath(import.meta.url);
6
- // const __dirname = path.dirname(__filename);
7
- // import { SupportedLanguages } from "./types/compiler-types.js";
8
- // const main = async () => {
9
- // const compiler = new ConfigCompiler(SupportedLanguages.Typescript);
10
- // const buildPath = path.resolve(__dirname, "../samples/build.yaml");
11
- // const valConfigPath = path.resolve(
12
- // __dirname,
13
- // "../samples/validation-config.json"
14
- // );
15
- // const buildYaml = readFileSync(buildPath, "utf-8");
16
- // const valConfig = JSON.parse(readFileSync(valConfigPath, "utf-8"));
17
- // await compiler.initialize(buildYaml);
18
- // await compiler.generateCode(valConfig);
19
- // };
20
- // (async () => {
21
- // await main();
22
- // })();