ondc-code-generator 0.6.22 → 0.7.3

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 (42) hide show
  1. package/.github/copilot-instructions.md +128 -0
  2. package/GOLANG_IMPLEMENTATION.md +156 -0
  3. package/README.md +10 -1
  4. package/dist/bin/cli-tool.d.ts +70 -0
  5. package/dist/bin/cli-tool.js +310 -0
  6. package/dist/bin/cli.d.ts +2 -0
  7. package/dist/bin/cli.js +80 -0
  8. package/dist/generator/config-compiler.d.ts +1 -0
  9. package/dist/generator/config-compiler.js +14 -0
  10. package/dist/generator/generators/go/go-ast.d.ts +1 -0
  11. package/dist/generator/generators/go/go-ast.js +60 -0
  12. package/dist/generator/generators/go/go-generator.d.ts +13 -0
  13. package/dist/generator/generators/go/go-generator.js +322 -0
  14. package/dist/generator/generators/go/templates/api-tests.mustache +65 -0
  15. package/dist/generator/generators/go/templates/go-mod.mustache +3 -0
  16. package/dist/generator/generators/go/templates/index.mustache +34 -0
  17. package/dist/generator/generators/go/templates/json-normalizer.mustache +155 -0
  18. package/dist/generator/generators/go/templates/json-path-utils.mustache +63 -0
  19. package/dist/generator/generators/go/templates/storage-templates/api-save-utils.mustache +84 -0
  20. package/dist/generator/generators/go/templates/storage-templates/api-save.mustache +44 -0
  21. package/dist/generator/generators/go/templates/storage-templates/index.mustache +72 -0
  22. package/dist/generator/generators/go/templates/storage-templates/save-utils.mustache +75 -0
  23. package/dist/generator/generators/go/templates/storage-templates/storage-interface.mustache +107 -0
  24. package/dist/generator/generators/go/templates/test-config.mustache +62 -0
  25. package/dist/generator/generators/go/templates/test-object.mustache +52 -0
  26. package/dist/generator/generators/go/templates/validation-code.mustache +66 -0
  27. package/dist/generator/generators/go/templates/validation-utils.mustache +321 -0
  28. package/dist/generator/generators/typescript/templates/index.mustache +1 -1
  29. package/dist/generator/generators/typescript/ts-generator.js +2 -2
  30. package/dist/index.d.ts +7 -1
  31. package/dist/index.js +6 -1
  32. package/dist/index.test.js +8 -1
  33. package/dist/types/build.d.ts +2 -0
  34. package/dist/types/compiler-types.d.ts +2 -1
  35. package/dist/types/compiler-types.js +1 -0
  36. package/dist/utils/fs-utils.d.ts +1 -0
  37. package/dist/utils/fs-utils.js +24 -0
  38. package/dist/utils/general-utils/string-utils.d.ts +1 -0
  39. package/dist/utils/general-utils/string-utils.js +11 -0
  40. package/package.json +5 -1
  41. package/sample.md +273 -0
  42. package/test-python-session.js +0 -0
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import fs from "fs/promises";
4
+ import { fileURLToPath } from "url";
5
+ import { program } from "commander";
6
+ import { ConfigCompiler, SupportedLanguages } from "../index.js";
7
+ import Cli from "./cli-tool.js";
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
+ // interface CLIOptions {
10
+ // config: string;
11
+ // output: string;
12
+ // lang: string;
13
+ // }
14
+ program
15
+ .name("ondc-code-generator")
16
+ .description("Ondc Validation Code Generator")
17
+ .version("0.0.1");
18
+ program
19
+ .command("ondc-validation-gen")
20
+ .alias("xval")
21
+ .option("-c, --config <path>", "Path to build.yaml file")
22
+ .option("-f, --function-name <name>", "Name of the validation function to generate")
23
+ .option("-o, --output <directory>", "Output directory for generated code")
24
+ .option("-l, --lang <language>", "Target programming language (typescript, python, javascript, go)")
25
+ .description("Generate validation code")
26
+ .action(async (options) => {
27
+ const { config, output, lang, functionName } = options;
28
+ console.log(Cli.title("Ondc Validation Code Generator"));
29
+ if (!config || !output || !lang) {
30
+ console.log(Cli.description.error("Please provide all required options: --config, --output, --lang"));
31
+ process.exit(1);
32
+ }
33
+ try {
34
+ console.log(Cli.description.info(`Generating validation code for language: ${lang}`));
35
+ const functionName = options.functionName || "L1validations";
36
+ const language = getSupportedLanguage(lang);
37
+ const compiler = new ConfigCompiler(language);
38
+ const buildPath = path.resolve(process.cwd(), config);
39
+ console.log(Cli.description.info(`Reading build file from ${buildPath}...`));
40
+ const buildYaml = await fs.readFile(buildPath, "utf-8");
41
+ console.log(Cli.description.info("Initializing compiler..."));
42
+ await compiler.initialize(buildYaml);
43
+ console.log(Cli.description.info("Generating validation code..."));
44
+ await compiler.generateValidationFromBuild(functionName, output);
45
+ console.log(Cli.description.success(`Validation code generated successfully in ${output} for language ${lang}`));
46
+ }
47
+ catch (error) {
48
+ const message = error instanceof Error ? error.message : String(error);
49
+ console.error(Cli.description.error(`Error: ${message}`));
50
+ process.exit(1);
51
+ }
52
+ });
53
+ program
54
+ .command("schema-gen")
55
+ .alias("schema")
56
+ .option("-c, --config <path>", "Path to build.yaml file")
57
+ .option("-o, --output <directory>", "Output directory for generated schema")
58
+ .option("-f, --format <format>", "Output format (json, yaml,typescript)")
59
+ .description("Generate L0 schema")
60
+ .action(async () => {
61
+ console.log("Schema generation command invoked");
62
+ });
63
+ program.parse();
64
+ function getSupportedLanguage(lang) {
65
+ switch (lang.toLowerCase()) {
66
+ case "typescript":
67
+ return SupportedLanguages.Typescript;
68
+ case "python":
69
+ return SupportedLanguages.Python;
70
+ case "javascript":
71
+ return SupportedLanguages.Javascript;
72
+ case "go":
73
+ return SupportedLanguages.Golang;
74
+ default:
75
+ throw new Error(`Unsupported language: ${lang}. Supported languages are: ${getValidLanguageOptions()}`);
76
+ }
77
+ }
78
+ function getValidLanguageOptions() {
79
+ return Object.values(SupportedLanguages).join(", ");
80
+ }
@@ -23,5 +23,6 @@ export declare class ConfigCompiler {
23
23
  generateCode: (valConfig: ValidationConfig, codeName?: string, minimal?: boolean, outputPath?: string) => Promise<void>;
24
24
  generateL0Schema: (outputPath?: string, type?: "json" | "typescript") => Promise<void>;
25
25
  generateValidPaths: () => Promise<Record<string, string[]>>;
26
+ generateValidationFromBuild: (codeName: string, outputPath: string) => Promise<void>;
26
27
  }
27
28
  export {};
@@ -11,6 +11,7 @@ import path from "path";
11
11
  import { duplicateVariablesInChildren } from "../utils/config-utils/duplicateVariables.js";
12
12
  import { PythonGenerator } from "./generators/python/py-generator.js";
13
13
  import { JavascriptGenerator } from "./generators/javascript/js-generator.js";
14
+ import { GoGenerator } from "./generators/go/go-generator.js";
14
15
  const __filename = fileURLToPath(import.meta.url);
15
16
  const __dirname = path.dirname(__filename);
16
17
  const defaultConfig = {
@@ -85,6 +86,11 @@ export class ConfigCompiler {
85
86
  codeName: codeName,
86
87
  });
87
88
  break;
89
+ case SupportedLanguages.Golang:
90
+ await new GoGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
91
+ codeName: codeName,
92
+ });
93
+ break;
88
94
  default:
89
95
  throw new Error("Language not supported");
90
96
  }
@@ -123,6 +129,14 @@ export class ConfigCompiler {
123
129
  // );
124
130
  return this.possibleJsonPaths;
125
131
  };
132
+ this.generateValidationFromBuild = async (codeName, outputPath) => {
133
+ if (!this.buildData)
134
+ throw new Error("Build data not initialized");
135
+ const valConfig = this.buildData["x-validations"];
136
+ if (!valConfig)
137
+ throw new Error("No validation config found in build data");
138
+ await this.generateCode(valConfig, codeName, false, outputPath);
139
+ };
126
140
  this.language = language;
127
141
  this.SchemaExtractionService = new SchemaExtractionService();
128
142
  }
@@ -0,0 +1 @@
1
+ export declare const compileInputToGo: (input: string) => string;
@@ -0,0 +1,60 @@
1
+ import { buildAstFromInput } from "../../../services/return-complier/combined.js";
2
+ import { AllIn, AnyIn, AreUnique, EqualTo, FollowRegex, GreaterThan, LessThan, NoneIn, ArePresent, } from "../../../services/return-complier/tokens.js";
3
+ const uniaryFunction = {
4
+ [AreUnique.LABEL ?? "are unique"]: "AreUnique",
5
+ [ArePresent.LABEL ?? "are present"]: "ArePresent",
6
+ };
7
+ const binaryFunction = {
8
+ [AllIn.LABEL ?? "all in"]: "AllIn",
9
+ [AnyIn.LABEL ?? "any in"]: "AnyIn",
10
+ [FollowRegex.LABEL ?? "follow regex"]: "FollowRegex",
11
+ [NoneIn.LABEL ?? "none in"]: "NoneIn",
12
+ [EqualTo.LABEL ?? "equal to"]: "EqualTo",
13
+ [GreaterThan.LABEL ?? "greater than"]: "GreaterThan",
14
+ [LessThan.LABEL ?? "less than"]: "LessThan",
15
+ };
16
+ function getGoOperator(op) {
17
+ switch (op) {
18
+ case "&&":
19
+ return "&&";
20
+ case "||":
21
+ return "||";
22
+ default:
23
+ return op;
24
+ }
25
+ }
26
+ function compileToGo(node) {
27
+ if (node.type === "returnStatement") {
28
+ const returnNode = node;
29
+ return compileToGo(returnNode.expression);
30
+ }
31
+ if (node.type === "binaryOperator") {
32
+ const binaryNode = node;
33
+ const lhs = compileToGo(binaryNode.lhs);
34
+ const rhs = compileToGo(binaryNode.rhs);
35
+ return `(${lhs}) ${getGoOperator(binaryNode.operator)} (${rhs})`;
36
+ }
37
+ if (node.type === "notOperator") {
38
+ const notNode = node;
39
+ const expression = compileToGo(notNode.expression);
40
+ return `!(${expression})`;
41
+ }
42
+ if (node.type === "customUniaryFunction") {
43
+ const unary = node;
44
+ const func = uniaryFunction[unary.customFunction];
45
+ const varName = unary.expression.name;
46
+ return `validationutils.${func}(${varName})`;
47
+ }
48
+ if (node.type === "customBinaryFunction") {
49
+ const binary = node;
50
+ const func = binaryFunction[binary.customFunction];
51
+ const lhs = binary.lhs.name;
52
+ const rhs = binary.rhs.name;
53
+ return `validationutils.${func}(${lhs}, ${rhs})`;
54
+ }
55
+ throw new Error("Unknown node type");
56
+ }
57
+ export const compileInputToGo = (input) => {
58
+ const ast = buildAstFromInput(input);
59
+ return compileToGo(ast);
60
+ };
@@ -0,0 +1,13 @@
1
+ import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
2
+ export declare class GoGenerator extends CodeGenerator {
3
+ codeConfig: CodeGeneratorProps | undefined;
4
+ generateSessionDataCode(): Promise<void>;
5
+ generateValidationCode(): Promise<void>;
6
+ generateCode: (codeConfig: CodeGeneratorProps) => Promise<void>;
7
+ private generateIndexFile;
8
+ private getExternalKeys;
9
+ private generateTestFunction;
10
+ private createVariablesCode;
11
+ private createValidationLogicCode;
12
+ private CreateErrorMarkdown;
13
+ }
@@ -0,0 +1,322 @@
1
+ import { readFileSync } from "fs";
2
+ import { CodeGenerator, } from "../classes/abstract-generator.js";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+ import Mustache from "mustache";
6
+ import { ConfigSyntax, ExternalDataSyntax, TestObjectSyntax, } from "../../../constants/syntax.js";
7
+ import { writeAndFormatCode } from "../../../utils/fs-utils.js";
8
+ import { collectLoadData } from "../../../utils/config-utils/load-variables.js";
9
+ import { compileInputToGo } from "./go-ast.js";
10
+ import { getVariablesFromTest } from "../../../utils/general-utils/test-object-utils.js";
11
+ import { ConvertArrayToStringGoStyle } from "../../../utils/general-utils/string-utils.js";
12
+ import { markdownMessageGenerator } from "../documentation/markdown-message-generator.js";
13
+ const __filename = fileURLToPath(import.meta.url);
14
+ const __dirname = path.dirname(__filename);
15
+ const packageName = "validationpkg";
16
+ export class GoGenerator extends CodeGenerator {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.generateCode = async (codeConfig) => {
20
+ this.codeConfig = codeConfig;
21
+ const jsonPathUtilsCode = readFileSync(path.resolve(__dirname, "./templates/json-path-utils.mustache"), "utf-8");
22
+ const validationUtils = readFileSync(path.resolve(__dirname, "./templates/validation-utils.mustache"), "utf-8");
23
+ const typesTemplate = readFileSync(path.resolve(__dirname, "./templates/test-config.mustache"), "utf-8");
24
+ const normalizerTemplate = readFileSync(path.resolve(__dirname, "./templates/json-normalizer.mustache"), "utf-8");
25
+ const goMod = readFileSync(path.resolve(__dirname, "./templates/go-mod.mustache"), "utf-8");
26
+ const typesCode = Mustache.render(typesTemplate, {
27
+ externalData: this.getExternalKeys(),
28
+ });
29
+ writeAndFormatCode(this.rootPath, `./${packageName}/validationutils/json_path_utils.go`, jsonPathUtilsCode, "go");
30
+ writeAndFormatCode(this.rootPath, `./${packageName}/validationutils/validation_utils.go`, validationUtils, "go");
31
+ writeAndFormatCode(this.rootPath, `./${packageName}/validationutils/test-config.go`, typesCode, "go");
32
+ writeAndFormatCode(this.rootPath, `./${packageName}/validationutils/json_normalizer.go`, normalizerTemplate, "go");
33
+ await this.generateValidationCode();
34
+ await writeAndFormatCode(this.rootPath, `./${packageName}/main-validator.go`, this.generateIndexFile(Object.keys(this.validationConfig[ConfigSyntax.Tests]), codeConfig.codeName), "go");
35
+ await writeAndFormatCode(this.rootPath, `./${packageName}/go.mod`, goMod, "text");
36
+ await this.generateSessionDataCode();
37
+ };
38
+ this.generateTestFunction = async (testObject) => {
39
+ const template = readFileSync(path.resolve(__dirname, "./templates/test-object.mustache"), "utf-8");
40
+ const view = {
41
+ name: stringToCaps(testObject[TestObjectSyntax.Name]),
42
+ scopePath: testObject[TestObjectSyntax.Scope] ?? "$",
43
+ variables: this.createVariablesCode(testObject),
44
+ hasContinue: testObject[TestObjectSyntax.Continue] ? true : false,
45
+ skipCheckStatement: testObject[TestObjectSyntax.Continue]
46
+ ? compileInputToGo(testObject[TestObjectSyntax.Continue])
47
+ : undefined,
48
+ validationCode: await this.createValidationLogicCode(testObject),
49
+ successCode: testObject[TestObjectSyntax.SuccessCode] ?? 200,
50
+ errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
51
+ testName: testObject[TestObjectSyntax.Name],
52
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
53
+ };
54
+ return {
55
+ funcName: testObject[TestObjectSyntax.Name],
56
+ code: Mustache.render(template, view),
57
+ };
58
+ };
59
+ }
60
+ async generateSessionDataCode() {
61
+ if (!this.codeConfig) {
62
+ throw new Error("Code config is not set");
63
+ }
64
+ const sessionData = this.validationConfig[ConfigSyntax.SessionData];
65
+ const tests = this.validationConfig[ConfigSyntax.Tests];
66
+ const relevantSessionData = {};
67
+ collectLoadData(tests, relevantSessionData);
68
+ const sessionDataUtilsTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/save-utils.mustache"), "utf-8");
69
+ const storageInterfaceTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/storage-interface.mustache"), "utf-8");
70
+ const indexTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/index.mustache"), "utf-8");
71
+ const saveActionTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/api-save.mustache"), "utf-8");
72
+ const saveActionUtilsTemplate = readFileSync(path.resolve(__dirname, "./templates/storage-templates/api-save-utils.mustache"), "utf-8");
73
+ const allActions = Object.keys(tests);
74
+ const indexCode = Mustache.render(indexTemplate, {
75
+ actions: Array.from(allActions).map((action) => {
76
+ return { action: action };
77
+ }),
78
+ functionName: this.codeConfig.codeName.replace(/[^a-zA-Z0-9_]/g, ""),
79
+ });
80
+ for (const action of allActions) {
81
+ const loadData = relevantSessionData[action] || {};
82
+ const saveData = sessionData[action] || {};
83
+ const saveCode = Mustache.render(saveActionTemplate, {
84
+ storeActions: Object.keys(saveData).map((key) => {
85
+ return {
86
+ key: key,
87
+ value: saveData[key],
88
+ };
89
+ }),
90
+ loadActions: Object.keys(loadData).map((key) => {
91
+ console.log(loadData[key]);
92
+ return {
93
+ key: loadData[key],
94
+ };
95
+ }),
96
+ action: action,
97
+ });
98
+ await writeAndFormatCode(this.rootPath, `./${packageName}/storageutils/${action}.go`, saveCode, "go");
99
+ }
100
+ await writeAndFormatCode(this.rootPath, `./${packageName}/storageutils/save_utils.go`, sessionDataUtilsTemplate, "go");
101
+ await writeAndFormatCode(this.rootPath, `./${packageName}/validationutils/storage-interface.go`, storageInterfaceTemplate, "go");
102
+ await writeAndFormatCode(this.rootPath, `./${packageName}/storageutils/index.go`, indexCode, "go");
103
+ await writeAndFormatCode(this.rootPath, `./${packageName}/storageutils/api_save_utils.go`, saveActionUtilsTemplate, "go");
104
+ }
105
+ async generateValidationCode() {
106
+ const testConfig = this.validationConfig[ConfigSyntax.Tests];
107
+ for (const key in testConfig) {
108
+ const testObjects = testConfig[key];
109
+ const betaConfig = {
110
+ [TestObjectSyntax.Name]: key + "Validations",
111
+ [TestObjectSyntax.Return]: testObjects,
112
+ };
113
+ const testFunction = await this.generateTestFunction(betaConfig);
114
+ const apiTestTemplate = readFileSync(path.resolve(__dirname, "./templates/api-tests.mustache"), "utf-8");
115
+ const finalCode = Mustache.render(apiTestTemplate, {
116
+ functionCode: testFunction.code,
117
+ apiName: stringToCaps(key),
118
+ });
119
+ await writeAndFormatCode(this.rootPath, `./${packageName}/jsonvalidations/${key}.go`, finalCode, "go");
120
+ }
121
+ }
122
+ generateIndexFile(apis, functionName = "L1Validations") {
123
+ functionName = functionName.replace(/[^a-zA-Z0-9_]/g, "");
124
+ let importList = [
125
+ `"validationpkg/validationutils"`,
126
+ `"validationpkg/jsonvalidations"`,
127
+ `"fmt"`,
128
+ `"encoding/json"`,
129
+ `"validationpkg/storageutils"`,
130
+ ];
131
+ const masterTemplate = readFileSync(path.resolve(__dirname, "./templates/index.mustache"), "utf-8");
132
+ const masterFunction = `func Perform${functionName}(
133
+ action string,
134
+ payload interface{},
135
+ config *validationutils.ValidationConfig,
136
+ externalData validationutils.ExternalData,
137
+ ) ([]validationutils.ValidationOutput, error) {
138
+ completeConfig := getCompleteConfig(config)
139
+
140
+ // Validate stateful requirements
141
+ if completeConfig.StateFullValidations {
142
+ if completeConfig.Store == nil {
143
+ return nil, fmt.Errorf("stateful validations require a storage interface to be provided in the config")
144
+ }
145
+ if completeConfig.UniqueKey == nil || *completeConfig.UniqueKey == "" {
146
+ return nil, fmt.Errorf("stateful validations require a uniqueKey to be provided in the config")
147
+ }
148
+ }
149
+
150
+
151
+ normalizedPayload := validationutils.NormalizeKeys(payload)
152
+
153
+ // Set _SELF
154
+ externalData.Self = normalizedPayload
155
+
156
+ // Load stateful data if needed
157
+ if completeConfig.StateFullValidations {
158
+ loadedData, err := Perform${functionName}Load(action, *completeConfig.UniqueKey, completeConfig.Store)
159
+ if err != nil {
160
+ return nil, fmt.Errorf("failed to load stateful data: %w", err)
161
+ }
162
+ // Merge loaded data with external data
163
+ externalData = mergeExternalData(loadedData, externalData)
164
+ }
165
+
166
+ // Create validation input
167
+ input := validationutils.ValidationInput {
168
+ Payload: normalizedPayload,
169
+ ExternalData: externalData,
170
+ Config: completeConfig,
171
+ }
172
+
173
+ // Route to action-specific validation
174
+ switch action {
175
+ ${apis
176
+ .map((api) => `
177
+ case "${api}":
178
+ return jsonvalidations.${stringToCaps(api)}_Tests(input)
179
+ `)
180
+ .join("\n")}
181
+ default:
182
+ return nil, fmt.Errorf("action not found: %s", action)
183
+ }
184
+ }
185
+
186
+ // getCompleteConfig returns a complete config with defaults
187
+ func getCompleteConfig(config *validationutils.ValidationConfig) validationutils.ValidationConfig {
188
+ if config == nil {
189
+ return validationutils.ValidationConfig{
190
+ OnlyInvalid: true,
191
+ HideParentErrors: true,
192
+ StateFullValidations: false,
193
+ Debug: false,
194
+ }
195
+ }
196
+
197
+ // Return copy with defaults for unset fields
198
+ completeConfig := *config
199
+ // Go doesn't have a clean way to check if bool was explicitly set,
200
+ // so we assume false means "use default true" only if it seems intentional
201
+ // In practice, you might want to use pointers for optional bools
202
+ return completeConfig
203
+ }
204
+
205
+ // mergeExternalData merges loaded data with provided external data
206
+ // using JSON marshal/unmarshal for a generic merge strategy.
207
+ // Non-null fields in provided override loaded fields.
208
+ func mergeExternalData(loaded, provided validationutils.ExternalData) validationutils.ExternalData {
209
+ // Convert to maps
210
+ loadedBytes, _ := json.Marshal(loaded)
211
+ providedBytes, _ := json.Marshal(provided)
212
+
213
+ var loadedMap, providedMap map[string]interface{}
214
+ json.Unmarshal(loadedBytes, &loadedMap)
215
+ json.Unmarshal(providedBytes, &providedMap)
216
+
217
+ // Merge provided into loaded
218
+ for key, value := range providedMap {
219
+ if value != nil {
220
+ loadedMap[key] = value
221
+ }
222
+ }
223
+
224
+ // Convert back
225
+ var result validationutils.ExternalData
226
+ mergedBytes, _ := json.Marshal(loadedMap)
227
+ json.Unmarshal(mergedBytes, &result)
228
+
229
+ return result
230
+ }
231
+
232
+ var Perform${functionName}Load = storageutils.Perform${functionName}Load
233
+ var Perform${functionName}Save = storageutils.Perform${functionName}Save
234
+ `;
235
+ const importCode = `import (
236
+ ${importList.map((imp) => `\t${imp}`).join("\n")}
237
+ )`;
238
+ return Mustache.render(masterTemplate, {
239
+ importCode: importCode,
240
+ masterFunction: masterFunction,
241
+ });
242
+ }
243
+ getExternalKeys() {
244
+ const apis = Object.keys(this.validationConfig[ConfigSyntax.SessionData]);
245
+ let result = [];
246
+ for (const api of apis) {
247
+ const keys = Object.keys(this.validationConfig[ConfigSyntax.SessionData][api]);
248
+ for (const key of keys) {
249
+ result.push({ name: key });
250
+ }
251
+ }
252
+ result = result.filter((v) => v.name !== "_SELF");
253
+ return result;
254
+ }
255
+ createVariablesCode(testObject) {
256
+ const variables = [];
257
+ const varNames = getVariablesFromTest(testObject);
258
+ for (const name of varNames) {
259
+ const value = testObject[name];
260
+ const final = typeof value === "string"
261
+ ? `validationutils.GetJsonPath(testObjMap, "${value}",true)`
262
+ : ConvertArrayToStringGoStyle(value);
263
+ variables.push({
264
+ name: name,
265
+ value: final,
266
+ });
267
+ }
268
+ return variables;
269
+ }
270
+ async createValidationLogicCode(testObject) {
271
+ const template = readFileSync(path.resolve(__dirname, "./templates/validation-code.mustache"), "utf-8");
272
+ const skip = testObject[TestObjectSyntax.Continue];
273
+ const skipList = skip ? [skip] : undefined;
274
+ if (typeof testObject[TestObjectSyntax.Return] === "string") {
275
+ const returnStatement = compileInputToGo(testObject[TestObjectSyntax.Return]);
276
+ let isStateFull = false;
277
+ for (const k in testObject) {
278
+ const value = testObject[k];
279
+ if (typeof value === "string") {
280
+ if (value.includes(`${ExternalDataSyntax}.`) &&
281
+ !value.includes("_SELF")) {
282
+ isStateFull = true;
283
+ break;
284
+ }
285
+ }
286
+ }
287
+ return Mustache.render(template, {
288
+ isNested: false,
289
+ isStateFull: isStateFull,
290
+ returnStatement: returnStatement,
291
+ errorCode: testObject[TestObjectSyntax.ErrorCode] ?? 30000,
292
+ errorDescription: this.CreateErrorMarkdown(testObject, skipList),
293
+ testName: testObject[TestObjectSyntax.Name],
294
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
295
+ });
296
+ }
297
+ else {
298
+ const subObjects = testObject[TestObjectSyntax.Return];
299
+ const functionCodes = [];
300
+ for (const subObject of subObjects) {
301
+ const func = await this.generateTestFunction(subObject);
302
+ functionCodes.push(func);
303
+ }
304
+ const names = functionCodes.map((f) => {
305
+ return { name: f.funcName };
306
+ });
307
+ return Mustache.render(template, {
308
+ isNested: true,
309
+ nestedFunctions: functionCodes.map((f) => f.code).join("\n"),
310
+ names: names,
311
+ testName: testObject[TestObjectSyntax.Name],
312
+ TEST_OBJECT: `${JSON.stringify(testObject)}`,
313
+ });
314
+ }
315
+ }
316
+ CreateErrorMarkdown(testObject, skipList) {
317
+ return markdownMessageGenerator(testObject[TestObjectSyntax.Return], testObject, testObject[TestObjectSyntax.Name], skipList);
318
+ }
319
+ }
320
+ function stringToCaps(str) {
321
+ return str.charAt(0).toUpperCase() + str.slice(1);
322
+ }
@@ -0,0 +1,65 @@
1
+ // Code generated by github.com/ONDC-Official/automation-validation-compiler, DO NOT EDIT.
2
+
3
+ package jsonvalidations
4
+
5
+ import (
6
+ "validationpkg/validationutils"
7
+ "fmt"
8
+ )
9
+
10
+ func {{apiName}}_Tests(input validationutils.ValidationInput) ([]validationutils.ValidationOutput, error) {
11
+ totalResults, err := {{apiName}}Validations(input)
12
+ if err != nil {
13
+ return nil, err
14
+ }
15
+
16
+ if !input.Config.Debug {
17
+ for i := range totalResults {
18
+ totalResults[i].DebugInfo = nil
19
+ }
20
+ }
21
+
22
+ if input.Config.HideParentErrors {
23
+ // Delete results with valid false and no description
24
+ filtered := make([]validationutils.ValidationOutput, 0)
25
+ for _, r := range totalResults {
26
+ if !r.Valid && r.Description == "" {
27
+ continue
28
+ }
29
+ filtered = append(filtered, r)
30
+ }
31
+ totalResults = filtered
32
+ }
33
+
34
+ if input.Config.OnlyInvalid {
35
+ res := make([]validationutils.ValidationOutput, 0)
36
+ for _, r := range totalResults {
37
+ if !r.Valid {
38
+ res = append(res, r)
39
+ }
40
+ }
41
+
42
+ if len(res) == 0 {
43
+ // Find the overall test result
44
+ var targetSuccess *validationutils.ValidationOutput
45
+ for i := range totalResults {
46
+ if totalResults[i].TestName == "{{apiName}}Validations" {
47
+ targetSuccess = &totalResults[i]
48
+ break
49
+ }
50
+ }
51
+
52
+ if targetSuccess == nil {
53
+ panic("Critical: Overall test result not found")
54
+ }
55
+
56
+ return []validationutils.ValidationOutput{*targetSuccess}, nil
57
+ }
58
+
59
+ return res, nil
60
+ }
61
+
62
+ return totalResults, nil
63
+ }
64
+
65
+ {{{functionCode}}}
@@ -0,0 +1,3 @@
1
+ module validationpkg
2
+
3
+ go 1.24.4
@@ -0,0 +1,34 @@
1
+ // Code generated by github.com/ONDC-Official/automation-validation-compiler, DO NOT EDIT.
2
+ package main
3
+
4
+ {{{importCode}}}
5
+
6
+ // PerformL1Validations performs Level-1 validations against a payload for a given action.
7
+ //
8
+ // Output shape - ValidationOutput is a slice of:
9
+ // - TestName: string
10
+ // - Valid: boolean
11
+ // - Code: number
12
+ // - Description: *string (optional)
13
+ // - DebugInfo: *DebugInfo (optional) with FedConfig
14
+ //
15
+ // Config - ValidationConfig (all fields optional):
16
+ // - OnlyInvalid (default true)
17
+ // - HideParentErrors (default true)
18
+ // - Debug (default false)
19
+ // - StateFullValidations (default false)
20
+ // - UniqueKey (optional)
21
+ // - Store (optional)
22
+ //
23
+ // Parameters:
24
+ // - action: The action name to validate against
25
+ // - payload: The JSON payload to validate (map[string]interface{} or struct)
26
+ // - config: Partial ValidationConfig. Merged with defaults
27
+ // - externalData: Extra data accessible to rules (Self will be set to normalized payload)
28
+ //
29
+ // Returns: []ValidationOutput
30
+ //
31
+ // Example:
32
+ // results := PerformL1Validations("search", payload, ValidationConfig{OnlyInvalid: false}, ExternalData{})
33
+ // // results[0] => ValidationOutput{ TestName, Valid, Code, Description, DebugInfo }
34
+ {{{masterFunction}}}