ondc-code-generator 0.0.1
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/.idea/code-generator.iml +12 -0
- package/.idea/modules.xml +8 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +21 -0
- package/README.md +0 -0
- package/custom-loader.js +4 -0
- package/dist/Generator/config-compiler.js +53 -0
- package/dist/Generator/config-validator.js +21 -0
- package/dist/Generator/generators/classes/abstract-generator.js +16 -0
- package/dist/Generator/generators/documentation/markdown-message-generator.js +37 -0
- package/dist/Generator/generators/documentation/md-generator.js +57 -0
- package/dist/Generator/generators/markdown-message-generator.js +25 -0
- package/dist/Generator/generators/python/py-generator.js +1 -0
- package/dist/Generator/generators/typescript/templates/json-path-utils.js +15 -0
- package/dist/Generator/generators/typescript/templates/validation-utils.js +91 -0
- package/dist/Generator/generators/typescript/ts-ast.js +50 -0
- package/dist/Generator/generators/typescript/ts-generator.js +184 -0
- package/dist/Generator/pipline.js +1 -0
- package/dist/Generator/validators/abstract-validator.js +6 -0
- package/dist/Generator/validators/config-validator.js +32 -0
- package/dist/Generator/validators/session-data-config/session-data-validator.js +35 -0
- package/dist/Generator/validators/tests-config/sub-validations.js +194 -0
- package/dist/Generator/validators/tests-config/test-list-validator.js +36 -0
- package/dist/Generator/validators/tests-config/test-validator.js +24 -0
- package/dist/Generator/validators/validation-error.js +9 -0
- package/dist/constants/operations.js +17 -0
- package/dist/constants/syntax.js +75 -0
- package/dist/index.js +39 -0
- package/dist/services/rename-later/main.js +1 -0
- package/dist/services/rename-later/parser.js +22 -0
- package/dist/services/rename-later/tokens.js +32 -0
- package/dist/services/rename-later/tokens1.js +33 -0
- package/dist/services/return-complier/ast-functions/compile-to-markdown.js +66 -0
- package/dist/services/return-complier/ast-functions/semantic-validations.js +30 -0
- package/dist/services/return-complier/ast.js +92 -0
- package/dist/services/return-complier/combined.js +7 -0
- package/dist/services/return-complier/parser.js +95 -0
- package/dist/services/return-complier/tokens.js +144 -0
- package/dist/services/schema-service.js +29 -0
- package/dist/types/build.js +1 -0
- package/dist/types/compiler-types.js +4 -0
- package/dist/types/config-types.js +1 -0
- package/dist/types/error-codes.js +1 -0
- package/dist/types/general-types.js +1 -0
- package/dist/utils/config-utils/json-schema-utils.js +91 -0
- package/dist/utils/config-utils/yaml.js +16 -0
- package/dist/utils/file-system.js +1 -0
- package/dist/utils/fs-utils.js +20 -0
- package/dist/utils/general-utils/string-utils.js +56 -0
- package/dist/utils/general-utils/test-object-utils.js +12 -0
- package/dist/utils/general-utils/validation-utils.js +21 -0
- package/dist/utils/json-path-utils/extract-string-paths.js +113 -0
- package/dist/utils/json-path-utils/paths.js +46 -0
- package/dist/utils/logger.js +41 -0
- package/docs/error-gen.md +33 -0
- package/docs/return-grammer.md +23 -0
- package/docs/sampleConfig.md +39 -0
- package/generated/L1-validations/api-tests/cancel.ts +569 -0
- package/generated/L1-validations/api-tests/confirm.ts +1162 -0
- package/generated/L1-validations/api-tests/init.ts +1063 -0
- package/generated/L1-validations/api-tests/on_cancel.ts +2069 -0
- package/generated/L1-validations/api-tests/on_confirm.ts +2219 -0
- package/generated/L1-validations/api-tests/on_init.ts +1949 -0
- package/generated/L1-validations/api-tests/on_search.ts +1574 -0
- package/generated/L1-validations/api-tests/on_select.ts +1723 -0
- package/generated/L1-validations/api-tests/on_status.ts +2221 -0
- package/generated/L1-validations/api-tests/on_update.ts +1969 -0
- package/generated/L1-validations/api-tests/search.ts +695 -0
- package/generated/L1-validations/api-tests/select.ts +994 -0
- package/generated/L1-validations/api-tests/status.ts +443 -0
- package/generated/L1-validations/api-tests/update.ts +898 -0
- package/generated/L1-validations/error.ts +64 -0
- package/generated/L1-validations/index.ts +138 -0
- package/generated/L1-validations/page/index.html +2118 -0
- package/generated/L1-validations/page/style.css +225 -0
- package/generated/L1-validations/readme.md +1779 -0
- package/generated/L1-validations/types/test-config.ts +27 -0
- package/generated/L1-validations/utils/json-path-utils.ts +17 -0
- package/generated/L1-validations/utils/validation-utils.ts +116 -0
- package/generated-structure/api-tests/search.ts +24 -0
- package/generated-structure/error.ts +0 -0
- package/generated-structure/index.ts +0 -0
- package/generated-structure/types/test-config.ts +21 -0
- package/nodemon.json +5 -0
- package/package.json +40 -0
- package/samples/build.yaml +24799 -0
- package/samples/output.md +91 -0
- package/samples/output.ts +27 -0
- package/samples/selections.json +216 -0
- package/samples/validation-config.json +3422 -0
- package/samples/x-validations.yaml +2893 -0
- package/src/constants/operations.ts +19 -0
- package/src/constants/syntax.ts +81 -0
- package/src/example.ts +25 -0
- package/src/generator/config-compiler.ts +122 -0
- package/src/generator/generators/classes/abstract-generator.ts +29 -0
- package/src/generator/generators/documentation/markdown-message-generator.ts +43 -0
- package/src/generator/generators/documentation/md-generator.ts +76 -0
- package/src/generator/generators/documentation/templates/index.mustache +36 -0
- package/src/generator/generators/documentation/templates/style.css +204 -0
- package/src/generator/generators/python/py-generator.ts +0 -0
- package/src/generator/generators/typescript/templates/api-test.mustache +7 -0
- package/src/generator/generators/typescript/templates/json-path-utils.ts +17 -0
- package/src/generator/generators/typescript/templates/schema-template.mustache +18 -0
- package/src/generator/generators/typescript/templates/test-config.mustache +28 -0
- package/src/generator/generators/typescript/templates/test-object.mustache +20 -0
- package/src/generator/generators/typescript/templates/validation-code.mustache +39 -0
- package/src/generator/generators/typescript/templates/validation-utils.ts +117 -0
- package/src/generator/generators/typescript/ts-ast.ts +72 -0
- package/src/generator/generators/typescript/ts-generator.ts +275 -0
- package/src/generator/validators/abstract-validator.ts +23 -0
- package/src/generator/validators/config-validator.ts +55 -0
- package/src/generator/validators/session-data-config/session-data-validator.ts +58 -0
- package/src/generator/validators/tests-config/sub-validations.ts +302 -0
- package/src/generator/validators/tests-config/test-list-validator.ts +59 -0
- package/src/generator/validators/tests-config/test-validator.ts +69 -0
- package/src/index.ts +2 -0
- package/src/services/return-complier/ast-functions/compile-to-markdown.ts +152 -0
- package/src/services/return-complier/ast-functions/semantic-validations.ts +44 -0
- package/src/services/return-complier/ast.ts +147 -0
- package/src/services/return-complier/combined.ts +8 -0
- package/src/services/return-complier/parser.ts +128 -0
- package/src/services/return-complier/tokens.ts +184 -0
- package/src/services/schema-service.ts +42 -0
- package/src/types/build.ts +51 -0
- package/src/types/compiler-types.ts +3 -0
- package/src/types/config-types.ts +27 -0
- package/src/types/error-codes.ts +6 -0
- package/src/types/general-types.ts +2 -0
- package/src/utils/config-utils/json-schema-utils.ts +150 -0
- package/src/utils/config-utils/yaml.ts +17 -0
- package/src/utils/fs-utils.ts +32 -0
- package/src/utils/general-utils/string-utils.ts +76 -0
- package/src/utils/general-utils/test-object-utils.ts +14 -0
- package/src/utils/general-utils/validation-utils.ts +30 -0
- package/src/utils/json-path-utils/extract-string-paths.ts +139 -0
- package/src/utils/json-path-utils/paths.ts +44 -0
- package/src/utils/logger.ts +53 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TestObjectSyntax,
|
|
3
|
+
nodeReservedKeywords,
|
|
4
|
+
ExternalDataSyntax,
|
|
5
|
+
ConfigSyntax,
|
|
6
|
+
} from "../../../constants/syntax.js";
|
|
7
|
+
import { buildAst } from "../../../services/return-complier/ast.js";
|
|
8
|
+
import { checkValidVariables } from "../../../services/return-complier/ast-functions/semantic-validations.js";
|
|
9
|
+
import { parseReturnInput } from "../../../services/return-complier/parser.js";
|
|
10
|
+
import { TestObject } from "../../../types/config-types.js";
|
|
11
|
+
import { ErrorDefinition } from "../../../types/error-codes.js";
|
|
12
|
+
import {
|
|
13
|
+
isSnakeCase,
|
|
14
|
+
isValidVariableName,
|
|
15
|
+
} from "../../../utils/general-utils/string-utils.js";
|
|
16
|
+
import { isValidVariableValueType } from "../../../utils/general-utils/validation-utils.js";
|
|
17
|
+
import {
|
|
18
|
+
isValidJsonPath,
|
|
19
|
+
replaceBracketsWithAsteriskNested,
|
|
20
|
+
} from "../../../utils/json-path-utils/paths.js";
|
|
21
|
+
import {
|
|
22
|
+
TestObjectValidator,
|
|
23
|
+
TestsValidatorDependencies,
|
|
24
|
+
} from "../abstract-validator.js";
|
|
25
|
+
import { TestsValidator } from "./test-list-validator.js";
|
|
26
|
+
import logger from "../../../utils/logger.js";
|
|
27
|
+
|
|
28
|
+
export class RequiredFieldsValidator extends TestObjectValidator {
|
|
29
|
+
validate = async () => {
|
|
30
|
+
if (!this.targetObject[TestObjectSyntax.Name]) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`${TestObjectSyntax.Name} is required at path ${this.validationPath}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
if (!this.targetObject[TestObjectSyntax.Return]) {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`${TestObjectSyntax.Return} is required at path ${this.validationPath}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class NameValidator extends TestObjectValidator {
|
|
44
|
+
validate = async () => {
|
|
45
|
+
if (typeof this.targetObject[TestObjectSyntax.Name] !== "string") {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`${TestObjectSyntax.Name} should be a string at path ${this.validationPath}`
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
const name = this.targetObject[TestObjectSyntax.Name];
|
|
51
|
+
if (name.length < 1) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`${TestObjectSyntax.Name} can't be a non-empty string at path ${this.validationPath}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (nodeReservedKeywords.has(name)) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`${TestObjectSyntax.Name} can't be a reserved keyword at path ${this.validationPath}`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (!isSnakeCase(name)) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`${TestObjectSyntax.Name} must be in snake_case at path ${this.validationPath}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class ScopeValidator extends TestObjectValidator {
|
|
70
|
+
impossiblePaths: string[];
|
|
71
|
+
constructor(testObject: TestObject, path: string, impossiblePaths: string[]) {
|
|
72
|
+
super(testObject, path);
|
|
73
|
+
this.impossiblePaths = impossiblePaths;
|
|
74
|
+
}
|
|
75
|
+
validate = async () => {
|
|
76
|
+
const path = this.targetObject[TestObjectSyntax.Scope];
|
|
77
|
+
if (typeof path !== "string") {
|
|
78
|
+
throw new Error(
|
|
79
|
+
`${TestObjectSyntax.Scope} should be a string at path ${this.validationPath}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
if (!isValidJsonPath(path)) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
`${TestObjectSyntax.Scope} should be a valid json path at path ${this.validationPath}`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
if (!path.startsWith(`$.`)) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`${TestObjectSyntax.Scope} json path should start with $. at ${this.validationPath}`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
if (
|
|
93
|
+
this.impossiblePaths.includes(replaceBracketsWithAsteriskNested(path))
|
|
94
|
+
) {
|
|
95
|
+
throw new Error(
|
|
96
|
+
`${TestObjectSyntax.Scope} can't be a path that returns a array of string it must be a json path which returns a array of objects at path ${this.validationPath}`
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class ErrorCodeValidator extends TestObjectValidator {
|
|
103
|
+
possibleErrorCodes: ErrorDefinition[];
|
|
104
|
+
constructor(
|
|
105
|
+
testObject: TestObject,
|
|
106
|
+
path: string,
|
|
107
|
+
possibleErrorCodes: ErrorDefinition[]
|
|
108
|
+
) {
|
|
109
|
+
super(testObject, path);
|
|
110
|
+
this.possibleErrorCodes = possibleErrorCodes;
|
|
111
|
+
}
|
|
112
|
+
validate = async () => {
|
|
113
|
+
if (!this.targetObject[TestObjectSyntax.ErrorCode]) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (typeof this.targetObject[TestObjectSyntax.Return] !== "string") {
|
|
118
|
+
throw new Error(
|
|
119
|
+
`You can't define a ${TestObjectSyntax.ErrorCode} with nested ${TestObjectSyntax.Return} at path ${this.validationPath}`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof this.targetObject[TestObjectSyntax.ErrorCode] !== "number") {
|
|
124
|
+
throw new Error(
|
|
125
|
+
`${TestObjectSyntax.ErrorCode} should be a number at path ${this.validationPath}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
const errorCode = this.targetObject[TestObjectSyntax.ErrorCode];
|
|
129
|
+
if (!this.possibleErrorCodes.some((code) => code.code === errorCode)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`${TestObjectSyntax.ErrorCode} don't exist in error codes at path ${this.validationPath}`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (!this.targetObject[TestObjectSyntax.SuccessCode]) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (typeof this.targetObject[TestObjectSyntax.SuccessCode] !== "number") {
|
|
139
|
+
throw new Error(
|
|
140
|
+
`${TestObjectSyntax.SuccessCode} should be a number at path ${this.validationPath}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export class VariableValidator extends TestObjectValidator {
|
|
147
|
+
possibleJsonPaths: string[];
|
|
148
|
+
externalVariables: string[];
|
|
149
|
+
constructor(
|
|
150
|
+
testObject: TestObject,
|
|
151
|
+
path: string,
|
|
152
|
+
posibleJsonPaths: string[],
|
|
153
|
+
externalVariables: string[]
|
|
154
|
+
) {
|
|
155
|
+
super(testObject, path);
|
|
156
|
+
this.externalVariables = externalVariables;
|
|
157
|
+
this.possibleJsonPaths = posibleJsonPaths;
|
|
158
|
+
}
|
|
159
|
+
validate = async () => {
|
|
160
|
+
for (const key in this.targetObject) {
|
|
161
|
+
if (Object.values(TestObjectSyntax).includes(key as TestObjectSyntax)) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
this.validateKey(key);
|
|
165
|
+
const value = this.targetObject[key];
|
|
166
|
+
console.log(value);
|
|
167
|
+
if (!isValidVariableValueType(value)) {
|
|
168
|
+
throw new Error(
|
|
169
|
+
`Variable: ${key} should be a string or array of primitives at path ${this.validationPath}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
if (typeof value === "string") {
|
|
173
|
+
if (!isValidJsonPath(value)) {
|
|
174
|
+
throw new Error(
|
|
175
|
+
`Variable: ${key} should be a valid jsonPath at ${this.validationPath}`
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
if (!value.startsWith(`$.`)) {
|
|
179
|
+
throw new Error(
|
|
180
|
+
`Variable: ${key} should start with $. at ${this.validationPath}`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (value.startsWith(`$.${ExternalDataSyntax}`)) {
|
|
184
|
+
this.validateExternalData(value, this.externalVariables);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let path = value;
|
|
189
|
+
if (this.targetObject[TestObjectSyntax.Scope]) {
|
|
190
|
+
const scope = this.targetObject[TestObjectSyntax.Scope];
|
|
191
|
+
const pathWithoutDollar = path.slice(2);
|
|
192
|
+
path = `${scope}.${pathWithoutDollar}`;
|
|
193
|
+
}
|
|
194
|
+
const replaced = replaceBracketsWithAsteriskNested(path);
|
|
195
|
+
if (!this.possibleJsonPaths.includes(replaced)) {
|
|
196
|
+
throw new Error(
|
|
197
|
+
`Variable: ${key} should be a jsonPath that returns a array of strings or the path don't exist in the schema, at ${this.validationPath} found original ${path} replaces: ${replaced}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
validateKey(key: string) {
|
|
205
|
+
if (nodeReservedKeywords.has(key)) {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`${key} can't be a reserved keyword at path ${this.validationPath}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
if (key.includes(" ")) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`${key} can't contain spaces at path ${this.validationPath}`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (key === this.targetObject[TestObjectSyntax.Name]) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`${key} can't be the same as ${TestObjectSyntax.Name} at path ${this.validationPath}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
if (!isValidVariableName(key)) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`${key} is not a valid variable name at path ${this.validationPath}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
validateExternalData(path: string, definedExternalValues: string[]) {
|
|
227
|
+
const externalData = path.split(".")[2];
|
|
228
|
+
if (!definedExternalValues.includes(externalData)) {
|
|
229
|
+
throw new Error(
|
|
230
|
+
`${externalData} is not defined in ${ConfigSyntax.SessionData} data at path ${this.validationPath}`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export class ContinueValidator extends TestObjectValidator {
|
|
237
|
+
definedVariables: string[];
|
|
238
|
+
constructor(testObject: TestObject, path: string) {
|
|
239
|
+
super(testObject, path);
|
|
240
|
+
this.definedVariables = Object.keys(testObject).filter(
|
|
241
|
+
(key) =>
|
|
242
|
+
!Object.values(TestObjectSyntax).includes(key as TestObjectSyntax)
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
validate = async () => {
|
|
246
|
+
try {
|
|
247
|
+
const contStatement = this.targetObject[TestObjectSyntax.Continue];
|
|
248
|
+
if (typeof contStatement === "string") {
|
|
249
|
+
const cst = parseReturnInput(contStatement);
|
|
250
|
+
const ast = buildAst(cst);
|
|
251
|
+
checkValidVariables(ast, this.definedVariables, this.validationPath);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
throw new Error(
|
|
255
|
+
`${TestObjectSyntax.Continue} should be a string at path ${this.validationPath}`
|
|
256
|
+
);
|
|
257
|
+
} catch (err: any) {
|
|
258
|
+
throw new Error(err.message + " at path " + this.validationPath);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
export class ReturnValidator extends TestObjectValidator {
|
|
264
|
+
definedVariables: string[];
|
|
265
|
+
dependencies: TestsValidatorDependencies;
|
|
266
|
+
constructor(
|
|
267
|
+
testObject: TestObject,
|
|
268
|
+
path: string,
|
|
269
|
+
dependencies: TestsValidatorDependencies
|
|
270
|
+
) {
|
|
271
|
+
super(testObject, path);
|
|
272
|
+
this.definedVariables = Object.keys(testObject).filter(
|
|
273
|
+
(key) =>
|
|
274
|
+
!Object.values(TestObjectSyntax).includes(key as TestObjectSyntax)
|
|
275
|
+
);
|
|
276
|
+
this.dependencies = dependencies;
|
|
277
|
+
}
|
|
278
|
+
validate = async () => {
|
|
279
|
+
try {
|
|
280
|
+
const returnStatement = this.targetObject[TestObjectSyntax.Return];
|
|
281
|
+
if (typeof returnStatement === "string") {
|
|
282
|
+
const cst = parseReturnInput(returnStatement);
|
|
283
|
+
const ast = buildAst(cst);
|
|
284
|
+
checkValidVariables(ast, this.definedVariables, this.validationPath);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (Array.isArray(returnStatement)) {
|
|
288
|
+
await new TestsValidator(
|
|
289
|
+
returnStatement,
|
|
290
|
+
this.validationPath,
|
|
291
|
+
this.dependencies
|
|
292
|
+
).validate();
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
throw new Error(
|
|
296
|
+
`${TestObjectSyntax.Return} should be a string or arrays`
|
|
297
|
+
);
|
|
298
|
+
} catch (err: any) {
|
|
299
|
+
throw new Error(err.message + " at path " + this.validationPath);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { TestObjectSyntax } from "../../../constants/syntax.js";
|
|
2
|
+
import { TestObject } from "../../../types/config-types.js";
|
|
3
|
+
import {
|
|
4
|
+
IValidator,
|
|
5
|
+
TestsValidatorDependencies,
|
|
6
|
+
} from "../abstract-validator.js";
|
|
7
|
+
import { CompleteTestObjectValidator } from "./test-validator.js";
|
|
8
|
+
|
|
9
|
+
export class TestsValidator implements IValidator {
|
|
10
|
+
tests: TestObject[];
|
|
11
|
+
validationPath: string;
|
|
12
|
+
dependencies: TestsValidatorDependencies;
|
|
13
|
+
constructor(
|
|
14
|
+
tests: TestObject[],
|
|
15
|
+
configPath: string,
|
|
16
|
+
dependencies: TestsValidatorDependencies
|
|
17
|
+
) {
|
|
18
|
+
this.tests = tests;
|
|
19
|
+
this.validationPath = configPath;
|
|
20
|
+
this.dependencies = dependencies;
|
|
21
|
+
}
|
|
22
|
+
validate = async () => {
|
|
23
|
+
this.validateDuplicateNames();
|
|
24
|
+
let i = 0;
|
|
25
|
+
for (const test of this.tests) {
|
|
26
|
+
const newPath = `${this.validationPath}/${i}/${TestObjectSyntax.Name} = ${
|
|
27
|
+
test[TestObjectSyntax.Name]
|
|
28
|
+
}`;
|
|
29
|
+
await new CompleteTestObjectValidator(
|
|
30
|
+
test,
|
|
31
|
+
newPath,
|
|
32
|
+
this.dependencies
|
|
33
|
+
).validate();
|
|
34
|
+
i++;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
validateDuplicateNames = () => {
|
|
39
|
+
const seen = new Set();
|
|
40
|
+
const duplicates = new Set();
|
|
41
|
+
|
|
42
|
+
this.tests.forEach((test) => {
|
|
43
|
+
const name = test[TestObjectSyntax.Name];
|
|
44
|
+
if (seen.has(name)) {
|
|
45
|
+
duplicates.add(name); // Add to duplicates if already seen
|
|
46
|
+
} else {
|
|
47
|
+
seen.add(name); // Track seen names
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
if (duplicates.size > 0) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Duplicate test names found at ${this.validationPath}: ${[
|
|
54
|
+
...duplicates,
|
|
55
|
+
].join(", ")}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { TestObjectSyntax } from "../../../constants/syntax.js";
|
|
2
|
+
import { TestObject } from "../../../types/config-types.js";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
TestObjectValidator,
|
|
6
|
+
TestsValidatorDependencies,
|
|
7
|
+
} from "../abstract-validator.js";
|
|
8
|
+
import {
|
|
9
|
+
RequiredFieldsValidator,
|
|
10
|
+
NameValidator,
|
|
11
|
+
ScopeValidator,
|
|
12
|
+
ErrorCodeValidator,
|
|
13
|
+
VariableValidator,
|
|
14
|
+
ContinueValidator,
|
|
15
|
+
ReturnValidator,
|
|
16
|
+
} from "./sub-validations.js";
|
|
17
|
+
|
|
18
|
+
export class CompleteTestObjectValidator extends TestObjectValidator {
|
|
19
|
+
dependencies: TestsValidatorDependencies;
|
|
20
|
+
constructor(
|
|
21
|
+
testObject: TestObject,
|
|
22
|
+
path: string,
|
|
23
|
+
dependencies: TestsValidatorDependencies
|
|
24
|
+
) {
|
|
25
|
+
super(testObject, path);
|
|
26
|
+
this.dependencies = dependencies;
|
|
27
|
+
}
|
|
28
|
+
validate = async () => {
|
|
29
|
+
await new RequiredFieldsValidator(
|
|
30
|
+
this.targetObject,
|
|
31
|
+
this.validationPath
|
|
32
|
+
).validate();
|
|
33
|
+
await new NameValidator(this.targetObject, this.validationPath).validate();
|
|
34
|
+
|
|
35
|
+
if (this.targetObject[TestObjectSyntax.Scope]) {
|
|
36
|
+
await new ScopeValidator(
|
|
37
|
+
this.targetObject,
|
|
38
|
+
this.validationPath,
|
|
39
|
+
this.dependencies.stringJsonPaths
|
|
40
|
+
).validate();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (this.targetObject[TestObjectSyntax.ErrorCode]) {
|
|
44
|
+
await new ErrorCodeValidator(
|
|
45
|
+
this.targetObject,
|
|
46
|
+
this.validationPath,
|
|
47
|
+
this.dependencies.errorDefinitions
|
|
48
|
+
).validate();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
await new VariableValidator(
|
|
52
|
+
this.targetObject,
|
|
53
|
+
this.validationPath,
|
|
54
|
+
this.dependencies.stringJsonPaths,
|
|
55
|
+
this.dependencies.externalVariables
|
|
56
|
+
).validate();
|
|
57
|
+
if (this.targetObject[TestObjectSyntax.Continue]) {
|
|
58
|
+
await new ContinueValidator(
|
|
59
|
+
this.targetObject,
|
|
60
|
+
this.validationPath
|
|
61
|
+
).validate();
|
|
62
|
+
}
|
|
63
|
+
await new ReturnValidator(
|
|
64
|
+
this.targetObject,
|
|
65
|
+
this.validationPath,
|
|
66
|
+
this.dependencies
|
|
67
|
+
).validate();
|
|
68
|
+
};
|
|
69
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import logger from "../../../utils/logger.js";
|
|
2
|
+
import {
|
|
3
|
+
AstNode,
|
|
4
|
+
BinaryOperatorNode,
|
|
5
|
+
CustomBinaryFunction,
|
|
6
|
+
CustomUniaryFunction,
|
|
7
|
+
NotOperatorNode,
|
|
8
|
+
ReturnStatementNode,
|
|
9
|
+
} from "../ast.js";
|
|
10
|
+
import {
|
|
11
|
+
AllIn,
|
|
12
|
+
AnyIn,
|
|
13
|
+
AreUnique,
|
|
14
|
+
ArePresent,
|
|
15
|
+
EqualTo,
|
|
16
|
+
FollowRegex,
|
|
17
|
+
GreaterThan,
|
|
18
|
+
LessThan,
|
|
19
|
+
NoneIn,
|
|
20
|
+
} from "../tokens.js";
|
|
21
|
+
|
|
22
|
+
const uniaryMessages = {
|
|
23
|
+
[AreUnique.LABEL ?? "are unique"]: (variable: string, forNot: boolean) =>
|
|
24
|
+
`all values of {{{${variable}}}} must${forNot ? " **not**" : ""} be unique`,
|
|
25
|
+
[ArePresent.LABEL ?? "are present"]: (variable: string, forNot: boolean) =>
|
|
26
|
+
`{{{${variable}}}} must${forNot ? " **not**" : ""} be present in the payload`,
|
|
27
|
+
};
|
|
28
|
+
const binaryMessages = {
|
|
29
|
+
[AllIn.LABEL ?? "all in"]: (lhs: string, rhs: string, forNot: boolean) =>
|
|
30
|
+
`every element of {{{${lhs}}}} must${
|
|
31
|
+
forNot ? " **not**" : ""
|
|
32
|
+
} be in {{{${rhs}}}}`,
|
|
33
|
+
[AnyIn.LABEL ?? "any in"]: (lhs: string, rhs: string, forNot: boolean) =>
|
|
34
|
+
`at least one element of {{{${lhs}}}} must${
|
|
35
|
+
forNot ? " **not**" : ""
|
|
36
|
+
} be in {{{${rhs}}}}`,
|
|
37
|
+
[FollowRegex.LABEL ?? "follow regex"]: (
|
|
38
|
+
lhs: string,
|
|
39
|
+
rhs: string,
|
|
40
|
+
forNot: boolean
|
|
41
|
+
) =>
|
|
42
|
+
`all elements of {{{${lhs}}}} must${
|
|
43
|
+
forNot ? " **not**" : ""
|
|
44
|
+
} follow every regex in {{{${rhs}}}}`,
|
|
45
|
+
[NoneIn.LABEL ?? "none in"]: (lhs: string, rhs: string, forNot: boolean) =>
|
|
46
|
+
`no element of {{{${lhs}}}} must${
|
|
47
|
+
forNot ? " **not**" : ""
|
|
48
|
+
} be in {{{${rhs}}}}`,
|
|
49
|
+
[EqualTo.LABEL ?? "equal to"]: (lhs: string, rhs: string, forNot: boolean) =>
|
|
50
|
+
`{{{${lhs}}}} must${forNot ? " **not**" : ""} be equal to {{{${rhs}}}}`,
|
|
51
|
+
[GreaterThan.LABEL ?? "greater than"]: (
|
|
52
|
+
lhs: string,
|
|
53
|
+
rhs: string,
|
|
54
|
+
forNot: boolean
|
|
55
|
+
) =>
|
|
56
|
+
`{{{${lhs}}}} must${forNot ? " **not**" : ""} be greater than {{{${rhs}}}}`,
|
|
57
|
+
[LessThan.LABEL ?? "less than"]: (
|
|
58
|
+
lhs: string,
|
|
59
|
+
rhs: string,
|
|
60
|
+
forNot: boolean
|
|
61
|
+
) => `{{{${lhs}}}} must${forNot ? " **not**" : ""} be less than {{{${rhs}}}}`,
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export function CompileToMarkdown(
|
|
65
|
+
ast: AstNode,
|
|
66
|
+
pointer: string,
|
|
67
|
+
depth = 0,
|
|
68
|
+
forNot: boolean
|
|
69
|
+
): string {
|
|
70
|
+
const indent = " ".repeat(depth); // 2 spaces per depth level
|
|
71
|
+
|
|
72
|
+
// Helper function to indent multi-line strings
|
|
73
|
+
function indentMultilineString(str: string, indentLevel: number): string {
|
|
74
|
+
const subIndent = " ".repeat(indentLevel);
|
|
75
|
+
return str
|
|
76
|
+
.split("\n")
|
|
77
|
+
.map((line) => subIndent + line)
|
|
78
|
+
.join("\n");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (ast.type === "returnStatement") {
|
|
82
|
+
const returnStatement = ast as ReturnStatementNode;
|
|
83
|
+
const generated = CompileToMarkdown(
|
|
84
|
+
returnStatement.expression,
|
|
85
|
+
`${pointer}`,
|
|
86
|
+
depth,
|
|
87
|
+
forNot
|
|
88
|
+
);
|
|
89
|
+
return `${generated}`;
|
|
90
|
+
}
|
|
91
|
+
if (ast.type === "binaryOperator") {
|
|
92
|
+
const binary = ast as BinaryOperatorNode;
|
|
93
|
+
const subMdLhs = CompileToMarkdown(
|
|
94
|
+
binary.lhs,
|
|
95
|
+
getNextPointer(pointer, 1),
|
|
96
|
+
depth + 1,
|
|
97
|
+
forNot
|
|
98
|
+
);
|
|
99
|
+
const subMdRhs = CompileToMarkdown(
|
|
100
|
+
binary.rhs,
|
|
101
|
+
getNextPointer(pointer, 2),
|
|
102
|
+
depth + 1,
|
|
103
|
+
forNot
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
const indentedSubMdLhs = indentMultilineString(subMdLhs, 0); // LHS already indented
|
|
107
|
+
const indentedSubMdRhs = indentMultilineString(subMdRhs, 0); // RHS already indented
|
|
108
|
+
|
|
109
|
+
if (binary.operator === "&&") {
|
|
110
|
+
return `${indent}- **condition ${pointer}**: all of the following sub conditions must${
|
|
111
|
+
forNot ? "**not**" : ""
|
|
112
|
+
} be met:\n\n${indentedSubMdLhs}\n${indentedSubMdRhs}`;
|
|
113
|
+
}
|
|
114
|
+
if (binary.operator === "||") {
|
|
115
|
+
return `${indent}- **condition ${pointer}**: any one of the following sub conditions must${
|
|
116
|
+
forNot ? "**not**" : ""
|
|
117
|
+
} be met:\n\n${indentedSubMdLhs}\n${indentedSubMdRhs}`;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (ast.type === "notOperator") {
|
|
121
|
+
const not = ast as NotOperatorNode;
|
|
122
|
+
return CompileToMarkdown(not.expression, pointer, depth, !forNot);
|
|
123
|
+
}
|
|
124
|
+
if (ast.type === "customUniaryFunction") {
|
|
125
|
+
const customFunction = ast as CustomUniaryFunction;
|
|
126
|
+
const func = customFunction.customFunction;
|
|
127
|
+
const messageFunction = uniaryMessages[func as keyof typeof uniaryMessages];
|
|
128
|
+
const lhs = customFunction.expression;
|
|
129
|
+
return `${indent}- **condition ${pointer}**: ${messageFunction(
|
|
130
|
+
lhs.name,
|
|
131
|
+
forNot
|
|
132
|
+
)}`;
|
|
133
|
+
}
|
|
134
|
+
if (ast.type === "customBinaryFunction") {
|
|
135
|
+
const customFunction = ast as CustomBinaryFunction;
|
|
136
|
+
const func = customFunction.customFunction;
|
|
137
|
+
const messageFunction = binaryMessages[func as keyof typeof binaryMessages];
|
|
138
|
+
const lhs = customFunction.lhs;
|
|
139
|
+
const rhs = customFunction.rhs;
|
|
140
|
+
return `${indent}- **condition ${pointer}**: ${messageFunction(
|
|
141
|
+
lhs.name,
|
|
142
|
+
rhs.name,
|
|
143
|
+
forNot
|
|
144
|
+
)}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
throw new Error("Invalid AST node:" + JSON.stringify(ast));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getNextPointer(currentPointer: string, nextIndex: number): string {
|
|
151
|
+
return `${currentPointer}.${nextIndex}`;
|
|
152
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AstNode,
|
|
3
|
+
BinaryOperatorNode,
|
|
4
|
+
CustomBinaryFunction,
|
|
5
|
+
CustomUniaryFunction,
|
|
6
|
+
IdentifierNode,
|
|
7
|
+
NotOperatorNode,
|
|
8
|
+
ReturnStatementNode,
|
|
9
|
+
} from "../ast";
|
|
10
|
+
|
|
11
|
+
export function checkValidVariables(
|
|
12
|
+
ast: AstNode,
|
|
13
|
+
validVariables: string[],
|
|
14
|
+
path?: string
|
|
15
|
+
): void {
|
|
16
|
+
if (ast.type === "identifier") {
|
|
17
|
+
const identifier = ast as IdentifierNode;
|
|
18
|
+
if (!validVariables.includes(identifier.name)) {
|
|
19
|
+
throw new Error(`Invalid variable ${identifier.name} at path ${path}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
if (ast.type === "returnStatement") {
|
|
23
|
+
const returnStatement = ast as ReturnStatementNode;
|
|
24
|
+
checkValidVariables(returnStatement.expression, validVariables, path);
|
|
25
|
+
}
|
|
26
|
+
if (ast.type === "binaryOperator") {
|
|
27
|
+
const binaryOperator = ast as BinaryOperatorNode;
|
|
28
|
+
checkValidVariables(binaryOperator.lhs, validVariables, path);
|
|
29
|
+
checkValidVariables(binaryOperator.rhs, validVariables, path);
|
|
30
|
+
}
|
|
31
|
+
if (ast.type === "notOperator") {
|
|
32
|
+
const notOperator = ast as NotOperatorNode;
|
|
33
|
+
checkValidVariables(notOperator.expression, validVariables, path);
|
|
34
|
+
}
|
|
35
|
+
if (ast.type === "customBinaryFunction") {
|
|
36
|
+
const customBinaryFunction = ast as CustomBinaryFunction;
|
|
37
|
+
checkValidVariables(customBinaryFunction.lhs, validVariables, path);
|
|
38
|
+
checkValidVariables(customBinaryFunction.rhs, validVariables, path);
|
|
39
|
+
}
|
|
40
|
+
if (ast.type === "customUniaryFunction") {
|
|
41
|
+
const customUniaryFunction = ast as CustomUniaryFunction;
|
|
42
|
+
checkValidVariables(customUniaryFunction.expression, validVariables, path);
|
|
43
|
+
}
|
|
44
|
+
}
|