ondc-code-generator 0.7.3 → 0.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/alpha/possible-json-paths.json +3734 -0
- package/dist/bin/cli.js +63 -3
- package/dist/constants/syntax.js +26 -0
- package/dist/generator/config-compiler.d.ts +4 -3
- package/dist/generator/config-compiler.js +16 -7
- package/dist/generator/generators/classes/abstract-generator.d.ts +1 -0
- package/dist/generator/generators/documentation/md-generator.d.ts +1 -0
- package/dist/generator/generators/documentation/md-generator.js +3 -0
- package/dist/generator/generators/go/go-generator.d.ts +1 -0
- package/dist/generator/generators/go/go-generator.js +39 -4
- package/dist/generator/generators/go/templates/api-tests.mustache +0 -1
- package/dist/generator/generators/go/templates/index.mustache +1 -1
- package/dist/generator/generators/go/templates/json-normalizer.mustache +10 -12
- package/dist/generator/generators/go/templates/test-config.mustache +3 -3
- package/dist/generator/generators/go/templates/test-object.mustache +4 -8
- package/dist/generator/generators/go/templates/test-templates/validator-test.mustache +167 -0
- package/dist/generator/generators/go/templates/validation-code.mustache +1 -1
- package/dist/generator/generators/javascript/js-generator.d.ts +1 -0
- package/dist/generator/generators/javascript/js-generator.js +3 -0
- package/dist/generator/generators/python/py-generator.d.ts +1 -0
- package/dist/generator/generators/python/py-generator.js +3 -0
- package/dist/generator/generators/typescript/ts-generator.d.ts +1 -0
- package/dist/generator/generators/typescript/ts-generator.js +3 -0
- package/dist/services/schema-service.d.ts +2 -1
- package/dist/services/schema-service.js +2 -1
- package/dist/types/build.d.ts +1 -0
- package/dist/utils/general-utils/string-utils.d.ts +1 -0
- package/dist/utils/general-utils/string-utils.js +3 -0
- package/package.json +1 -1
package/dist/bin/cli.js
CHANGED
|
@@ -41,7 +41,7 @@ program
|
|
|
41
41
|
console.log(Cli.description.info("Initializing compiler..."));
|
|
42
42
|
await compiler.initialize(buildYaml);
|
|
43
43
|
console.log(Cli.description.info("Generating validation code..."));
|
|
44
|
-
await compiler.generateValidationFromBuild(functionName, output);
|
|
44
|
+
await compiler.generateValidationFromBuild(functionName, output, true);
|
|
45
45
|
console.log(Cli.description.success(`Validation code generated successfully in ${output} for language ${lang}`));
|
|
46
46
|
}
|
|
47
47
|
catch (error) {
|
|
@@ -57,8 +57,56 @@ program
|
|
|
57
57
|
.option("-o, --output <directory>", "Output directory for generated schema")
|
|
58
58
|
.option("-f, --format <format>", "Output format (json, yaml,typescript)")
|
|
59
59
|
.description("Generate L0 schema")
|
|
60
|
-
.action(async () => {
|
|
61
|
-
console.log("Schema
|
|
60
|
+
.action(async (options) => {
|
|
61
|
+
console.log(Cli.title("Ondc Schema Generator"));
|
|
62
|
+
try {
|
|
63
|
+
const { config, output, format } = options;
|
|
64
|
+
if (!config || !output || !format) {
|
|
65
|
+
console.log(Cli.description.error("Please provide all required options: --config, --output, --format"));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
console.log(Cli.description.info(`Generating L0 schema...`));
|
|
69
|
+
const buildPath = path.resolve(process.cwd(), config);
|
|
70
|
+
console.log(Cli.description.info(`Reading build file from ${buildPath}...`));
|
|
71
|
+
const buildYaml = await fs.readFile(buildPath, "utf-8");
|
|
72
|
+
const compiler = new ConfigCompiler(SupportedLanguages.Typescript);
|
|
73
|
+
await compiler.initialize(buildYaml);
|
|
74
|
+
const formatType = getSchemaFormat(format);
|
|
75
|
+
await compiler.generateL0Schema(output, formatType, true);
|
|
76
|
+
}
|
|
77
|
+
catch (error) {
|
|
78
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
79
|
+
console.error(Cli.description.error(`Error: ${message}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
program
|
|
84
|
+
.command("extract-payloads")
|
|
85
|
+
.alias("ext-payloads")
|
|
86
|
+
.description("Extract sample payloads from build.yaml")
|
|
87
|
+
.option("-c, --config <path>", "Path to build.yaml file")
|
|
88
|
+
.option("-o, --output <directory>", "Output directory for extracted payloads")
|
|
89
|
+
.action(async (options) => {
|
|
90
|
+
console.log(Cli.title("Ondc Sample Payload Extractor"));
|
|
91
|
+
try {
|
|
92
|
+
const { config, output } = options;
|
|
93
|
+
if (!config || !output) {
|
|
94
|
+
console.log(Cli.description.error("Please provide all required options: --config, --output"));
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
console.log(Cli.description.info(`Extracting sample payloads...`));
|
|
98
|
+
const buildPath = path.resolve(process.cwd(), config);
|
|
99
|
+
console.log(Cli.description.info(`Reading build file from ${buildPath}...`));
|
|
100
|
+
const buildYaml = await fs.readFile(buildPath, "utf-8");
|
|
101
|
+
const compiler = new ConfigCompiler(SupportedLanguages.Typescript);
|
|
102
|
+
await compiler.initialize(buildYaml);
|
|
103
|
+
await compiler.extractPayloadsFromBuild(output);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
107
|
+
console.error(Cli.description.error(`Error: ${message}`));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
62
110
|
});
|
|
63
111
|
program.parse();
|
|
64
112
|
function getSupportedLanguage(lang) {
|
|
@@ -75,6 +123,18 @@ function getSupportedLanguage(lang) {
|
|
|
75
123
|
throw new Error(`Unsupported language: ${lang}. Supported languages are: ${getValidLanguageOptions()}`);
|
|
76
124
|
}
|
|
77
125
|
}
|
|
126
|
+
function getSchemaFormat(format) {
|
|
127
|
+
switch (format.toLowerCase()) {
|
|
128
|
+
case "json":
|
|
129
|
+
return "json";
|
|
130
|
+
case "typescript":
|
|
131
|
+
return "typescript";
|
|
132
|
+
case "yaml":
|
|
133
|
+
throw new Error("YAML format is not yet supported");
|
|
134
|
+
default:
|
|
135
|
+
throw new Error(`Unsupported format: ${format}. Supported formats are: json, typescript`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
78
138
|
function getValidLanguageOptions() {
|
|
79
139
|
return Object.values(SupportedLanguages).join(", ");
|
|
80
140
|
}
|
package/dist/constants/syntax.js
CHANGED
|
@@ -168,4 +168,30 @@ export const ReservedKeywords = new Set([
|
|
|
168
168
|
"match",
|
|
169
169
|
"case",
|
|
170
170
|
"_",
|
|
171
|
+
// golang keywords
|
|
172
|
+
"break",
|
|
173
|
+
"default",
|
|
174
|
+
"func",
|
|
175
|
+
"interface",
|
|
176
|
+
"select",
|
|
177
|
+
"case",
|
|
178
|
+
"defer",
|
|
179
|
+
"go",
|
|
180
|
+
"map",
|
|
181
|
+
"struct",
|
|
182
|
+
"chan",
|
|
183
|
+
"else",
|
|
184
|
+
"goto",
|
|
185
|
+
"package",
|
|
186
|
+
"switch",
|
|
187
|
+
"const",
|
|
188
|
+
"fallthrough",
|
|
189
|
+
"if",
|
|
190
|
+
"range",
|
|
191
|
+
"type",
|
|
192
|
+
"continue",
|
|
193
|
+
"for",
|
|
194
|
+
"import",
|
|
195
|
+
"return",
|
|
196
|
+
"var",
|
|
171
197
|
]);
|
|
@@ -20,9 +20,10 @@ export declare class ConfigCompiler {
|
|
|
20
20
|
initialize: (buildYaml: string, generatorConfig?: Partial<CodeGeneratorConfig>) => Promise<void>;
|
|
21
21
|
performValidations: (valConfig: ValidationConfig) => Promise<void>;
|
|
22
22
|
withMinimalValidations: (valConfig: ValidationConfig) => Promise<void>;
|
|
23
|
-
generateCode: (valConfig: ValidationConfig, codeName?: string, minimal?: boolean, outputPath?: string) => Promise<void>;
|
|
24
|
-
generateL0Schema: (outputPath?: string, type?: "json" | "typescript") => Promise<void>;
|
|
23
|
+
generateCode: (valConfig: ValidationConfig, codeName?: string, minimal?: boolean, outputPath?: string, absolutePath?: boolean) => Promise<void>;
|
|
24
|
+
generateL0Schema: (outputPath?: string, type?: "json" | "typescript", absolutePath?: boolean) => Promise<void>;
|
|
25
25
|
generateValidPaths: () => Promise<Record<string, string[]>>;
|
|
26
|
-
generateValidationFromBuild: (codeName: string, outputPath: string) => Promise<void>;
|
|
26
|
+
generateValidationFromBuild: (codeName: string, outputPath: string, absolutePath?: boolean) => Promise<void>;
|
|
27
|
+
extractPayloadsFromBuild: (outputPath: string) => Promise<void>;
|
|
27
28
|
}
|
|
28
29
|
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadAndDereferenceYaml } from "../utils/config-utils/yaml.js";
|
|
2
|
-
import {
|
|
2
|
+
import { ExtractionService as SchemaExtractionService } from "../services/schema-service.js";
|
|
3
3
|
import { SupportedLanguages } from "../types/compiler-types.js";
|
|
4
4
|
import { TypescriptGenerator } from "./generators/typescript/ts-generator.js";
|
|
5
5
|
import { ConfigValidator } from "./validators/config-validator.js";
|
|
@@ -57,7 +57,7 @@ export class ConfigCompiler {
|
|
|
57
57
|
// throw new Error("validation failed");
|
|
58
58
|
};
|
|
59
59
|
// };
|
|
60
|
-
this.generateCode = async (valConfig, codeName = "L1-Validations", minimal = false, outputPath = "./") => {
|
|
60
|
+
this.generateCode = async (valConfig, codeName = "L1-Validations", minimal = false, outputPath = "./", absolutePath = false) => {
|
|
61
61
|
valConfig = JSON.parse(JSON.stringify(valConfig));
|
|
62
62
|
if (this.generatorConfig?.duplicateVariablesInChildren) {
|
|
63
63
|
valConfig = duplicateVariablesInChildren(valConfig);
|
|
@@ -69,7 +69,9 @@ export class ConfigCompiler {
|
|
|
69
69
|
await this.performValidations(valConfig);
|
|
70
70
|
}
|
|
71
71
|
// Generate code based on the language
|
|
72
|
-
const targetPath =
|
|
72
|
+
const targetPath = absolutePath
|
|
73
|
+
? outputPath
|
|
74
|
+
: `${outputPath}generated/${codeName}`;
|
|
73
75
|
switch (this.language) {
|
|
74
76
|
case SupportedLanguages.Typescript:
|
|
75
77
|
await new TypescriptGenerator(valConfig, this.errorDefinitions ?? [], targetPath).generateCode({
|
|
@@ -95,11 +97,13 @@ export class ConfigCompiler {
|
|
|
95
97
|
throw new Error("Language not supported");
|
|
96
98
|
}
|
|
97
99
|
};
|
|
98
|
-
this.generateL0Schema = async (outputPath = "./", type = "typescript") => {
|
|
100
|
+
this.generateL0Schema = async (outputPath = "./", type = "typescript", absolutePath = false) => {
|
|
99
101
|
if (!this.jsonSchemas) {
|
|
100
102
|
throw new Error("Schemas not initialized");
|
|
101
103
|
}
|
|
102
|
-
const targetPath =
|
|
104
|
+
const targetPath = absolutePath
|
|
105
|
+
? outputPath
|
|
106
|
+
: `${outputPath}generated/L0-schemas/`;
|
|
103
107
|
for (const schema in this.jsonSchemas) {
|
|
104
108
|
const json = this.jsonSchemas[schema];
|
|
105
109
|
if (type === "typescript") {
|
|
@@ -129,13 +133,18 @@ export class ConfigCompiler {
|
|
|
129
133
|
// );
|
|
130
134
|
return this.possibleJsonPaths;
|
|
131
135
|
};
|
|
132
|
-
this.generateValidationFromBuild = async (codeName, outputPath) => {
|
|
136
|
+
this.generateValidationFromBuild = async (codeName, outputPath, absolutePath = false) => {
|
|
133
137
|
if (!this.buildData)
|
|
134
138
|
throw new Error("Build data not initialized");
|
|
135
139
|
const valConfig = this.buildData["x-validations"];
|
|
136
140
|
if (!valConfig)
|
|
137
141
|
throw new Error("No validation config found in build data");
|
|
138
|
-
await this.generateCode(valConfig, codeName, false, outputPath);
|
|
142
|
+
await this.generateCode(valConfig, codeName, false, outputPath, absolutePath);
|
|
143
|
+
};
|
|
144
|
+
this.extractPayloadsFromBuild = async (outputPath) => {
|
|
145
|
+
if (!this.buildData)
|
|
146
|
+
throw new Error("Build data not initialized");
|
|
147
|
+
const payloads = this.SchemaExtractionService.extractPayloadExamples(this.buildData);
|
|
139
148
|
};
|
|
140
149
|
this.language = language;
|
|
141
150
|
this.SchemaExtractionService = new SchemaExtractionService();
|
|
@@ -11,4 +11,5 @@ export declare abstract class CodeGenerator {
|
|
|
11
11
|
abstract generateSessionDataCode(): Promise<void>;
|
|
12
12
|
abstract generateValidationCode(): Promise<void>;
|
|
13
13
|
abstract generateCode(codeConfig: CodeGeneratorProps): Promise<void>;
|
|
14
|
+
abstract generateUnitTestingCode(): Promise<void>;
|
|
14
15
|
}
|
|
@@ -2,6 +2,7 @@ import { TestObject } from "../../../types/config-types.js";
|
|
|
2
2
|
import { CodeGenerator } from "../classes/abstract-generator.js";
|
|
3
3
|
export declare class MarkdownDocGenerator extends CodeGenerator {
|
|
4
4
|
generateSessionDataCode(): Promise<void>;
|
|
5
|
+
generateUnitTestingCode(): Promise<void>;
|
|
5
6
|
generateValidationCode: () => Promise<void>;
|
|
6
7
|
generateCode: () => Promise<void>;
|
|
7
8
|
generateMarkdownForTest: (testObject: TestObject) => string;
|
|
@@ -8,7 +8,7 @@ import { writeAndFormatCode } from "../../../utils/fs-utils.js";
|
|
|
8
8
|
import { collectLoadData } from "../../../utils/config-utils/load-variables.js";
|
|
9
9
|
import { compileInputToGo } from "./go-ast.js";
|
|
10
10
|
import { getVariablesFromTest } from "../../../utils/general-utils/test-object-utils.js";
|
|
11
|
-
import { ConvertArrayToStringGoStyle } from "../../../utils/general-utils/string-utils.js";
|
|
11
|
+
import { ConvertArrayToStringGoStyle, removeAllSpecialCharacters, } from "../../../utils/general-utils/string-utils.js";
|
|
12
12
|
import { markdownMessageGenerator } from "../documentation/markdown-message-generator.js";
|
|
13
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
14
14
|
const __dirname = path.dirname(__filename);
|
|
@@ -34,6 +34,7 @@ export class GoGenerator extends CodeGenerator {
|
|
|
34
34
|
await writeAndFormatCode(this.rootPath, `./${packageName}/main-validator.go`, this.generateIndexFile(Object.keys(this.validationConfig[ConfigSyntax.Tests]), codeConfig.codeName), "go");
|
|
35
35
|
await writeAndFormatCode(this.rootPath, `./${packageName}/go.mod`, goMod, "text");
|
|
36
36
|
await this.generateSessionDataCode();
|
|
37
|
+
await this.generateUnitTestingCode();
|
|
37
38
|
};
|
|
38
39
|
this.generateTestFunction = async (testObject) => {
|
|
39
40
|
const template = readFileSync(path.resolve(__dirname, "./templates/test-object.mustache"), "utf-8");
|
|
@@ -255,11 +256,38 @@ ${importList.map((imp) => `\t${imp}`).join("\n")}
|
|
|
255
256
|
createVariablesCode(testObject) {
|
|
256
257
|
const variables = [];
|
|
257
258
|
const varNames = getVariablesFromTest(testObject);
|
|
259
|
+
const returnStatement = testObject[TestObjectSyntax.Return];
|
|
260
|
+
const continueStatement = testObject[TestObjectSyntax.Continue];
|
|
261
|
+
let elementsList = [];
|
|
262
|
+
// REPLACE ALL special WITH empty AND SPLIT
|
|
263
|
+
if (typeof returnStatement === "string") {
|
|
264
|
+
elementsList = removeAllSpecialCharacters(returnStatement).split(" ");
|
|
265
|
+
}
|
|
266
|
+
if (continueStatement) {
|
|
267
|
+
const contElements = removeAllSpecialCharacters(continueStatement).split(" ");
|
|
268
|
+
for (const elem of contElements) {
|
|
269
|
+
if (!elementsList.includes(elem)) {
|
|
270
|
+
elementsList.push(elem);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
258
274
|
for (const name of varNames) {
|
|
259
275
|
const value = testObject[name];
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
276
|
+
if (!elementsList.includes(name)) {
|
|
277
|
+
console.log(`Variable ${name} not used in return or continue statements, skipping generation.: \n ${returnStatement} \n ${continueStatement}`);
|
|
278
|
+
console.log(elementsList);
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
let final = "";
|
|
282
|
+
if (value.includes("_EXTERNAL")) {
|
|
283
|
+
final = `validationutils.GetJsonPath(input, "${value}",true)`;
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
final =
|
|
287
|
+
typeof value === "string"
|
|
288
|
+
? `validationutils.GetJsonPath(testObjMap, "${value}",true)`
|
|
289
|
+
: ConvertArrayToStringGoStyle(value);
|
|
290
|
+
}
|
|
263
291
|
variables.push({
|
|
264
292
|
name: name,
|
|
265
293
|
value: final,
|
|
@@ -316,6 +344,13 @@ ${importList.map((imp) => `\t${imp}`).join("\n")}
|
|
|
316
344
|
CreateErrorMarkdown(testObject, skipList) {
|
|
317
345
|
return markdownMessageGenerator(testObject[TestObjectSyntax.Return], testObject, testObject[TestObjectSyntax.Name], skipList);
|
|
318
346
|
}
|
|
347
|
+
async generateUnitTestingCode() {
|
|
348
|
+
const testTemplate = readFileSync(path.resolve(__dirname, "./templates/test-templates/validator-test.mustache"), "utf-8");
|
|
349
|
+
const finalTestCode = Mustache.render(testTemplate, {
|
|
350
|
+
functionName: this.codeConfig?.codeName ?? "L1Validations",
|
|
351
|
+
});
|
|
352
|
+
await writeAndFormatCode(this.rootPath, `./${packageName}/main-validator_test.go`, finalTestCode, "go");
|
|
353
|
+
}
|
|
319
354
|
}
|
|
320
355
|
function stringToCaps(str) {
|
|
321
356
|
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
@@ -2,14 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
package validationutils
|
|
4
4
|
|
|
5
|
-
import (
|
|
6
|
-
"encoding/json"
|
|
7
|
-
)
|
|
8
5
|
|
|
9
6
|
// NormalizeKeys normalizes JSON structures so that:
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
7
|
+
// - All objects with the same property name share the union of keys seen anywhere
|
|
8
|
+
// - All objects inside the same array share the union of keys at that array level
|
|
9
|
+
// - Missing keys are filled with nil
|
|
13
10
|
func NormalizeKeys(input interface{}) interface{} {
|
|
14
11
|
// Step 1: Collect templates by property name
|
|
15
12
|
templatesByPropName := make(map[string]map[string]struct{})
|
|
@@ -67,12 +64,12 @@ func applyTemplates(node interface{}, templates map[string]map[string]struct{})
|
|
|
67
64
|
if obj, ok := item.(map[string]interface{}); ok {
|
|
68
65
|
// Create new object with array union keys
|
|
69
66
|
next := make(map[string]interface{})
|
|
70
|
-
|
|
67
|
+
|
|
71
68
|
// Copy existing keys
|
|
72
69
|
for k, val := range obj {
|
|
73
70
|
next[k] = val
|
|
74
71
|
}
|
|
75
|
-
|
|
72
|
+
|
|
76
73
|
// Add missing keys from array union
|
|
77
74
|
for key := range arrayUnion {
|
|
78
75
|
if _, exists := next[key]; !exists {
|
|
@@ -141,15 +138,16 @@ func fillFromTemplate(propName string, obj map[string]interface{}, templates map
|
|
|
141
138
|
return filled
|
|
142
139
|
}
|
|
143
140
|
|
|
144
|
-
// DeepCloneJSON creates a deep clone of a JSON-serializable structure
|
|
141
|
+
// DeepCloneJSON creates a deep clone of a JSON-serializable structure using sonic
|
|
145
142
|
func DeepCloneJSON(v interface{}) interface{} {
|
|
146
|
-
b, err :=
|
|
143
|
+
/* b, err := sonic.Marshal(v)
|
|
147
144
|
if err != nil {
|
|
148
145
|
panic(err) // or handle error
|
|
149
146
|
}
|
|
150
147
|
var out interface{}
|
|
151
|
-
if err :=
|
|
148
|
+
if err := sonic.Unmarshal(b, &out); err != nil {
|
|
152
149
|
panic(err)
|
|
153
150
|
}
|
|
154
|
-
return out
|
|
151
|
+
return out */
|
|
152
|
+
return v
|
|
155
153
|
}
|
|
@@ -49,9 +49,9 @@ type ExternalData struct {
|
|
|
49
49
|
|
|
50
50
|
// ValidationInput represents the input data for validation functions
|
|
51
51
|
type ValidationInput struct {
|
|
52
|
-
Payload interface{}
|
|
53
|
-
ExternalData ExternalData
|
|
54
|
-
Config ValidationConfig
|
|
52
|
+
Payload interface{} `json:"payload"`
|
|
53
|
+
ExternalData ExternalData `json:"_EXTERNAL,omitempty"`
|
|
54
|
+
Config ValidationConfig `json:"_CONFIG,omitempty"`
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// TestFunction is a function type that takes ValidationInput and returns a slice of ValidationOutput
|
|
@@ -4,13 +4,9 @@ var {{name}} = func (input validationutils.ValidationInput) ([]validationutils.V
|
|
|
4
4
|
subResults := make([]validationutils.ValidationOutput, 0)
|
|
5
5
|
valid := true
|
|
6
6
|
configureDebugInfo := ""
|
|
7
|
-
for _,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return nil, fmt.Errorf("Invalid object structure in scope for test {{name}}")
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
testObjMap["_EXTERNAL"] = validationutils.DeepCloneJSON(input.ExternalData)
|
|
7
|
+
for _, testObjMap := range scope {
|
|
8
|
+
validationutils.UnusedFunction(testObjMap)
|
|
9
|
+
// testObjMap["_EXTERNAL"] = validationutils.DeepCloneJSON(input.ExternalData)
|
|
14
10
|
|
|
15
11
|
{{#variables}}
|
|
16
12
|
{{name}} := {{{value}}}
|
|
@@ -26,7 +22,7 @@ var {{name}} = func (input validationutils.ValidationInput) ([]validationutils.V
|
|
|
26
22
|
|
|
27
23
|
{{{validationCode}}}
|
|
28
24
|
|
|
29
|
-
delete(testObjMap, "_EXTERNAL")
|
|
25
|
+
// delete(testObjMap, "_EXTERNAL")
|
|
30
26
|
}
|
|
31
27
|
|
|
32
28
|
result := validationutils.ValidationOutput{
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
// Code generated by github.com/ONDC-Official/automation-validation-compiler, DO NOT EDIT.
|
|
2
|
+
|
|
3
|
+
package validationpkg
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// How to run:
|
|
7
|
+
//
|
|
8
|
+
// 1) Create example payload files under ./examples (relative to this package):
|
|
9
|
+
// validationpkg/examples/search.json
|
|
10
|
+
// validationpkg/examples/on_search.json
|
|
11
|
+
// Each file must contain a JSON array of payload objects.
|
|
12
|
+
//
|
|
13
|
+
// 2) From the validationpkg module directory, run:
|
|
14
|
+
// go test -run TestPerformL1validations_Examples -v
|
|
15
|
+
// Or from the repo root:
|
|
16
|
+
// go test ./ondcValidator/validationpkg -run TestPerformL1validations_Examples -v
|
|
17
|
+
//
|
|
18
|
+
// Outputs are written under ./examples_output/<action>/case-XXX/output.json.
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import (
|
|
22
|
+
"encoding/json"
|
|
23
|
+
"fmt"
|
|
24
|
+
"os"
|
|
25
|
+
"path/filepath"
|
|
26
|
+
"strings"
|
|
27
|
+
"testing"
|
|
28
|
+
|
|
29
|
+
"validationpkg/validationutils"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
type noopStorage struct{}
|
|
33
|
+
|
|
34
|
+
func (n *noopStorage) SaveKey(uniquePrefix string, key string, value string) error { return nil }
|
|
35
|
+
func (n *noopStorage) GetKey(uniquePrefix string, key string) (string, error) {
|
|
36
|
+
return "", fmt.Errorf("noop storage: key not found")
|
|
37
|
+
}
|
|
38
|
+
func (n *noopStorage) DeleteKey(uniquePrefix string, key string) error { return nil }
|
|
39
|
+
func (n *noopStorage) ListKeys(uniquePrefix string) ([]string, error) {
|
|
40
|
+
return nil, fmt.Errorf("noop storage: list not supported")
|
|
41
|
+
}
|
|
42
|
+
func (n *noopStorage) ClearStorage() error { return nil }
|
|
43
|
+
func (n *noopStorage) KeyExists(uniquePrefix string, key string) (bool, error) { return false, nil }
|
|
44
|
+
|
|
45
|
+
type validationRunOutput struct {
|
|
46
|
+
Action string `json:"action"`
|
|
47
|
+
Case int `json:"case"`
|
|
48
|
+
UniqueKey string `json:"uniqueKey"`
|
|
49
|
+
Error string `json:"error,omitempty"`
|
|
50
|
+
Results []validationutils.ValidationOutput `json:"results,omitempty"`
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
func TestPerformL1validations_Examples(t *testing.T) {
|
|
54
|
+
examplesDir := "examples"
|
|
55
|
+
if _, err := os.Stat(examplesDir); err != nil {
|
|
56
|
+
if os.IsNotExist(err) {
|
|
57
|
+
t.Skipf("%s directory not found; skipping example-based validations", examplesDir)
|
|
58
|
+
}
|
|
59
|
+
t.Fatalf("failed to stat %s: %v", examplesDir, err)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
files, err := filepath.Glob(filepath.Join(examplesDir, "*.json"))
|
|
63
|
+
if err != nil {
|
|
64
|
+
t.Fatalf("failed to glob examples: %v", err)
|
|
65
|
+
}
|
|
66
|
+
if len(files) == 0 {
|
|
67
|
+
t.Skipf("no example files found in %s", examplesDir)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
outputRoot := "examples_output"
|
|
71
|
+
if err := os.MkdirAll(outputRoot, 0o755); err != nil {
|
|
72
|
+
t.Fatalf("failed to create output dir %s: %v", outputRoot, err)
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
store := &noopStorage{}
|
|
76
|
+
|
|
77
|
+
for _, filePath := range files {
|
|
78
|
+
action := strings.TrimSuffix(filepath.Base(filePath), filepath.Ext(filePath))
|
|
79
|
+
|
|
80
|
+
t.Run(action, func(t *testing.T) {
|
|
81
|
+
data, err := os.ReadFile(filePath)
|
|
82
|
+
if err != nil {
|
|
83
|
+
t.Fatalf("failed to read %s: %v", filePath, err)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var payloads []json.RawMessage
|
|
87
|
+
if err := json.Unmarshal(data, &payloads); err != nil {
|
|
88
|
+
t.Fatalf("%s must contain a JSON array of payloads: %v", filePath, err)
|
|
89
|
+
}
|
|
90
|
+
if len(payloads) == 0 {
|
|
91
|
+
t.Skipf("no payloads in %s", filePath)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for i, raw := range payloads {
|
|
95
|
+
caseNum := i + 1
|
|
96
|
+
|
|
97
|
+
var payload interface{}
|
|
98
|
+
if err := json.Unmarshal(raw, &payload); err != nil {
|
|
99
|
+
t.Errorf("case %d: payload is not valid JSON: %v", caseNum, err)
|
|
100
|
+
continue
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
uniqueKey := extractTransactionID(payload)
|
|
104
|
+
if uniqueKey == "" {
|
|
105
|
+
uniqueKey = fmt.Sprintf("%s-case-%03d", action, caseNum)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
cfg := &validationutils.ValidationConfig{
|
|
109
|
+
StateFullValidations: false,
|
|
110
|
+
Debug: false,
|
|
111
|
+
OnlyInvalid: true,
|
|
112
|
+
HideParentErrors: true,
|
|
113
|
+
UniqueKey: &uniqueKey,
|
|
114
|
+
Store: store,
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
results, runErr := Perform{{functionName}}(action, payload, cfg, validationutils.ExternalData{})
|
|
118
|
+
out := validationRunOutput{
|
|
119
|
+
Action: action,
|
|
120
|
+
Case: caseNum,
|
|
121
|
+
UniqueKey: uniqueKey,
|
|
122
|
+
Results: results,
|
|
123
|
+
}
|
|
124
|
+
if runErr != nil {
|
|
125
|
+
out.Error = runErr.Error()
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
caseDir := filepath.Join(outputRoot, action, fmt.Sprintf("case-%03d", caseNum))
|
|
129
|
+
if err := os.MkdirAll(caseDir, 0o755); err != nil {
|
|
130
|
+
t.Errorf("case %d: failed to create output dir %s: %v", caseNum, caseDir, err)
|
|
131
|
+
continue
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
encoded, err := json.MarshalIndent(out, "", " ")
|
|
135
|
+
if err != nil {
|
|
136
|
+
t.Errorf("case %d: failed to marshal output: %v", caseNum, err)
|
|
137
|
+
continue
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if err := os.WriteFile(filepath.Join(caseDir, "output.json"), encoded, 0o644); err != nil {
|
|
141
|
+
t.Errorf("case %d: failed to write output.json: %v", caseNum, err)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if runErr != nil {
|
|
145
|
+
t.Errorf("case %d: PerformL1validations returned error: %v", caseNum, runErr)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
})
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
func extractTransactionID(payload interface{}) string {
|
|
153
|
+
root, ok := payload.(map[string]interface{})
|
|
154
|
+
if !ok {
|
|
155
|
+
return ""
|
|
156
|
+
}
|
|
157
|
+
ctx, ok := root["context"].(map[string]interface{})
|
|
158
|
+
if !ok {
|
|
159
|
+
return ""
|
|
160
|
+
}
|
|
161
|
+
if v, ok := ctx["transaction_id"]; ok {
|
|
162
|
+
if s, ok := v.(string); ok {
|
|
163
|
+
return s
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return ""
|
|
167
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { CodeGenerator, CodeGeneratorProps } from "../classes/abstract-generator.js";
|
|
2
2
|
export declare class JavascriptGenerator extends CodeGenerator {
|
|
3
3
|
private tempTsPath;
|
|
4
|
+
generateUnitTestingCode(): Promise<void>;
|
|
4
5
|
constructor(validationConfig: any, errorCodes: any[], rootPath?: string);
|
|
5
6
|
generateSessionDataCode: () => Promise<never>;
|
|
6
7
|
generateValidationCode: () => Promise<void>;
|
|
@@ -12,6 +12,9 @@ const execAsync = promisify(exec);
|
|
|
12
12
|
const __filename = fileURLToPath(import.meta.url);
|
|
13
13
|
const __dirname = path.dirname(__filename);
|
|
14
14
|
export class JavascriptGenerator extends CodeGenerator {
|
|
15
|
+
generateUnitTestingCode() {
|
|
16
|
+
throw new Error("Method not implemented.");
|
|
17
|
+
}
|
|
15
18
|
constructor(validationConfig, errorCodes, rootPath = "./") {
|
|
16
19
|
super(validationConfig, errorCodes, rootPath);
|
|
17
20
|
this.generateSessionDataCode = async () => {
|
|
@@ -2,6 +2,7 @@ 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
4
|
codeConfig: CodeGeneratorProps | undefined;
|
|
5
|
+
generateUnitTestingCode(): Promise<void>;
|
|
5
6
|
generateSessionDataCode: () => Promise<void>;
|
|
6
7
|
generateValidationCode: () => Promise<void>;
|
|
7
8
|
generateCode: (codeConfig: CodeGeneratorProps) => Promise<void>;
|
|
@@ -185,6 +185,9 @@ export class PythonGenerator extends CodeGenerator {
|
|
|
185
185
|
}
|
|
186
186
|
};
|
|
187
187
|
}
|
|
188
|
+
generateUnitTestingCode() {
|
|
189
|
+
throw new Error("Method not implemented.");
|
|
190
|
+
}
|
|
188
191
|
CreateErrorMarkdown(testObject, skipList) {
|
|
189
192
|
return markdownMessageGenerator(testObject[TestObjectSyntax.Return], testObject, testObject[TestObjectSyntax.Name], skipList);
|
|
190
193
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JSONSchema7 } from "json-schema";
|
|
2
2
|
import { BUILD_TYPE } from "../types/build.js";
|
|
3
|
-
export declare class
|
|
3
|
+
export declare class ExtractionService {
|
|
4
4
|
extractSchemas: (data: BUILD_TYPE, removeRequired: boolean, removeEnums: boolean) => Promise<Record<string, JSONSchema7>>;
|
|
5
5
|
extractPossiblePaths: (schemas: Record<string, JSONSchema7>) => Record<string, string[]>;
|
|
6
|
+
extractPayloadExamples: (data: BUILD_TYPE) => void;
|
|
6
7
|
}
|