@fincity/kirun-js 2.4.0 → 2.7.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 (39) hide show
  1. package/__tests__/engine/function/system/math/MathFunctionRepositoryTest.ts +9 -18
  2. package/__tests__/engine/function/system/math/MaximumTest.ts +3 -2
  3. package/__tests__/engine/function/system/math/MinimumTest.ts +18 -20
  4. package/__tests__/engine/json/schema/covnertor/BooleanConvertorTest.ts +55 -0
  5. package/__tests__/engine/json/schema/covnertor/NullConvertorTest.ts +47 -0
  6. package/__tests__/engine/json/schema/covnertor/NumberConvertorTest.ts +154 -0
  7. package/__tests__/engine/json/schema/covnertor/StringConvertorTest.ts +56 -0
  8. package/__tests__/engine/json/schema/validator/AnyOfAllOfOneOfValidatorTest.ts +9 -7
  9. package/__tests__/engine/json/schema/validator/NotValidatorTest.ts +3 -3
  10. package/__tests__/engine/json/schema/validator/StringValidatorTest.ts +1 -9
  11. package/__tests__/engine/json/schema/validator/TypeValidatorTest.ts +290 -0
  12. package/__tests__/engine/runtime/expression/ExpressionEvaluationTest.ts +80 -0
  13. package/__tests__/engine/runtime/expression/ExpressionEvaluatorStringLiteralTest.ts +73 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/module.js +1 -1
  17. package/dist/module.js.map +1 -1
  18. package/dist/types.d.ts +14 -8
  19. package/dist/types.d.ts.map +1 -1
  20. package/generator/generateValidationCSV.ts +6 -1
  21. package/generator/validation-js.csv +0 -0
  22. package/package.json +4 -1
  23. package/src/engine/function/system/object/ObjectConvert.ts +73 -0
  24. package/src/engine/function/system/object/ObjectPutValue.ts +0 -2
  25. package/src/engine/json/schema/convertor/BooleanConvertor.ts +76 -0
  26. package/src/engine/json/schema/convertor/NullConvertor.ts +31 -0
  27. package/src/engine/json/schema/convertor/NumberConvertor.ts +117 -0
  28. package/src/engine/json/schema/convertor/StringConvertor.ts +41 -0
  29. package/src/engine/json/schema/convertor/enums/ConversionMode.ts +11 -0
  30. package/src/engine/json/schema/convertor/exception/SchemaConversionException.ts +39 -0
  31. package/src/engine/json/schema/validator/AnyOfAllOfOneOfValidator.ts +96 -36
  32. package/src/engine/json/schema/validator/ArrayValidator.ts +15 -7
  33. package/src/engine/json/schema/validator/ObjectValidator.ts +24 -13
  34. package/src/engine/json/schema/validator/SchemaValidator.ts +74 -22
  35. package/src/engine/json/schema/validator/TypeValidator.ts +136 -23
  36. package/src/engine/runtime/expression/tokenextractor/TokenValueExtractor.ts +63 -59
  37. package/src/engine/util/json/ConvertorUtil.ts +51 -0
  38. package/src/engine/util/json/ValidatorUtil.ts +29 -0
  39. package/tsconfig.json +2 -1
@@ -7,8 +7,22 @@ import { SchemaUtil } from '../SchemaUtil';
7
7
  import { AnyOfAllOfOneOfValidator } from './AnyOfAllOfOneOfValidator';
8
8
  import { SchemaValidationException } from './exception/SchemaValidationException';
9
9
  import { TypeValidator } from './TypeValidator';
10
+ import { ConversionMode } from '../convertor/enums/ConversionMode';
11
+ import { SchemaType } from '../type/SchemaType';
10
12
 
11
13
  export class SchemaValidator {
14
+ private static readonly ORDER: Record<SchemaType, number> = {
15
+ [SchemaType.OBJECT]: 0,
16
+ [SchemaType.ARRAY]: 1,
17
+ [SchemaType.DOUBLE]: 2,
18
+ [SchemaType.FLOAT]: 3,
19
+ [SchemaType.LONG]: 4,
20
+ [SchemaType.INTEGER]: 5,
21
+ [SchemaType.STRING]: 6,
22
+ [SchemaType.BOOLEAN]: 7,
23
+ [SchemaType.NULL]: 8,
24
+ };
25
+
12
26
  public static path(parents: Schema[] | undefined): string {
13
27
  if (!parents) return '';
14
28
 
@@ -23,6 +37,8 @@ export class SchemaValidator {
23
37
  schema: Schema | undefined,
24
38
  repository: Repository<Schema> | undefined,
25
39
  element: any,
40
+ convert?: boolean,
41
+ mode?: ConversionMode,
26
42
  ): Promise<any> {
27
43
  if (!schema) {
28
44
  throw new SchemaValidationException(
@@ -32,7 +48,7 @@ export class SchemaValidator {
32
48
  }
33
49
 
34
50
  if (!parents) {
35
- parents = new Array();
51
+ parents = [];
36
52
  }
37
53
  parents.push(schema);
38
54
 
@@ -56,8 +72,22 @@ export class SchemaValidator {
56
72
  ' format.',
57
73
  );
58
74
 
75
+ if (convert === true && isNullValue(schema.getType())) {
76
+ throw new SchemaValidationException(
77
+ this.path(parents),
78
+ 'Type is missing in schema for declared ' + mode,
79
+ );
80
+ }
81
+
59
82
  if (schema.getType()) {
60
- await SchemaValidator.typeValidation(parents, schema, repository, element);
83
+ element = await SchemaValidator.typeValidation(
84
+ parents,
85
+ schema,
86
+ repository,
87
+ element,
88
+ convert,
89
+ mode,
90
+ );
61
91
  }
62
92
 
63
93
  if (!StringUtil.isNullOrBlank(schema.getRef())) {
@@ -66,21 +96,32 @@ export class SchemaValidator {
66
96
  await SchemaUtil.getSchemaFromRef(parents[0], repository, schema.getRef()),
67
97
  repository,
68
98
  element,
99
+ convert,
100
+ mode,
69
101
  );
70
102
  }
71
103
 
72
104
  if (schema.getOneOf() || schema.getAllOf() || schema.getAnyOf()) {
73
- AnyOfAllOfOneOfValidator.validate(parents, schema, repository, element);
105
+ element = await AnyOfAllOfOneOfValidator.validate(
106
+ parents,
107
+ schema,
108
+ repository,
109
+ element,
110
+ convert,
111
+ mode,
112
+ );
74
113
  }
75
114
 
76
115
  if (schema.getNot()) {
77
- let flag: boolean = false;
116
+ let flag: boolean;
78
117
  try {
79
- let x = await SchemaValidator.validate(
118
+ await SchemaValidator.validate(
80
119
  parents,
81
120
  schema.getNot(),
82
121
  repository,
83
122
  element,
123
+ convert,
124
+ mode,
84
125
  );
85
126
  flag = true;
86
127
  } catch (err) {
@@ -129,28 +170,39 @@ export class SchemaValidator {
129
170
  schema: Schema,
130
171
  repository: Repository<Schema> | undefined,
131
172
  element: any,
132
- ) {
133
- let valid: boolean = false;
134
- let list: SchemaValidationException[] = [];
135
- let type;
136
- for (type of Array.from(schema.getType()?.getAllowedSchemaTypes()?.values() ?? [])) {
173
+ convert?: boolean,
174
+ mode?: ConversionMode,
175
+ ): Promise<any> {
176
+ const allowedTypes: SchemaType[] = Array.from(
177
+ schema.getType()?.getAllowedSchemaTypes()?.values() ?? [],
178
+ ).sort(
179
+ (a: SchemaType, b: SchemaType) =>
180
+ (this.ORDER[a] ?? Infinity) - (this.ORDER[b] ?? Infinity),
181
+ );
182
+
183
+ let errors: SchemaValidationException[] = [];
184
+
185
+ for (const type of allowedTypes) {
137
186
  try {
138
- await TypeValidator.validate(parents, type, schema, repository, element);
139
- valid = true;
140
- break;
187
+ return await TypeValidator.validate(
188
+ parents,
189
+ type,
190
+ schema,
191
+ repository,
192
+ element,
193
+ convert,
194
+ mode,
195
+ );
141
196
  } catch (err: any) {
142
- valid = false;
143
- list.push(err);
197
+ errors.push(err);
144
198
  }
145
199
  }
146
200
 
147
- if (!valid) {
148
- throw new SchemaValidationException(
149
- SchemaValidator.path(parents),
150
- 'Value ' + JSON.stringify(element) + ' is not of valid type(s)',
151
- list,
152
- );
153
- }
201
+ throw new SchemaValidationException(
202
+ SchemaValidator.path(parents),
203
+ 'Value ' + JSON.stringify(element) + ' is not of valid type(s)',
204
+ errors,
205
+ );
154
206
  }
155
207
 
156
208
  private constructor() {}
@@ -3,12 +3,19 @@ import { Schema } from '../Schema';
3
3
  import { SchemaType } from '../type/SchemaType';
4
4
  import { ArrayValidator } from './ArrayValidator';
5
5
  import { BooleanValidator } from './BooleanValidator';
6
- import { SchemaValidationException } from './exception/SchemaValidationException';
7
6
  import { NullValidator } from './NullValidator';
8
7
  import { NumberValidator } from './NumberValidator';
9
8
  import { ObjectValidator } from './ObjectValidator';
10
- import { SchemaValidator } from './SchemaValidator';
11
9
  import { StringValidator } from './StringValidator';
10
+ import { ConversionMode } from '../convertor/enums/ConversionMode';
11
+ import { ConvertorUtil } from '../../../util/json/ConvertorUtil';
12
+ import { StringFormatter } from '../../../util/string/StringFormatter';
13
+ import { StringConvertor } from '../convertor/StringConvertor';
14
+ import { NumberConvertor } from '../convertor/NumberConvertor';
15
+ import { BooleanConvertor } from '../convertor/BooleanConvertor';
16
+ import { NullConvertor } from '../convertor/NullConvertor';
17
+ import { isNullValue } from '../../../util/NullCheck';
18
+ import { ValidatorUtil } from '../../../util/json/ValidatorUtil';
12
19
 
13
20
  export class TypeValidator {
14
21
  public static async validate(
@@ -17,32 +24,138 @@ export class TypeValidator {
17
24
  schema: Schema,
18
25
  repository: Repository<Schema> | undefined,
19
26
  element: any,
27
+ convert?: boolean,
28
+ mode?: ConversionMode,
20
29
  ): Promise<any> {
21
- if (type == SchemaType.STRING) {
22
- StringValidator.validate(parents, schema, element);
23
- } else if (
24
- type == SchemaType.LONG ||
25
- type == SchemaType.INTEGER ||
26
- type == SchemaType.DOUBLE ||
27
- type == SchemaType.FLOAT
28
- ) {
29
- NumberValidator.validate(type, parents, schema, element);
30
- } else if (type == SchemaType.BOOLEAN) {
31
- BooleanValidator.validate(parents, schema, element);
32
- } else if (type == SchemaType.OBJECT) {
33
- await ObjectValidator.validate(parents, schema, repository, element);
30
+ if (type == SchemaType.OBJECT) {
31
+ return await ObjectValidator.validate(
32
+ parents,
33
+ schema,
34
+ repository,
35
+ element,
36
+ convert,
37
+ mode,
38
+ );
34
39
  } else if (type == SchemaType.ARRAY) {
35
- await ArrayValidator.validate(parents, schema, repository, element);
36
- } else if (type == SchemaType.NULL) {
37
- NullValidator.validate(parents, schema, element);
38
- } else {
39
- throw new SchemaValidationException(
40
- SchemaValidator.path(parents),
41
- type + ' is not a valid type.',
40
+ return await ArrayValidator.validate(
41
+ parents,
42
+ schema,
43
+ repository,
44
+ element,
45
+ convert,
46
+ mode,
42
47
  );
43
48
  }
44
49
 
45
- return element;
50
+ return this.handleTypeValidationAndConversion(
51
+ parents,
52
+ type,
53
+ schema,
54
+ element,
55
+ convert,
56
+ mode,
57
+ );
58
+ }
59
+
60
+ private static async handleTypeValidationAndConversion(
61
+ parents: Schema[],
62
+ type: SchemaType,
63
+ schema: Schema,
64
+ element: any,
65
+ convert?: boolean,
66
+ mode?: ConversionMode,
67
+ ): Promise<any> {
68
+ const cElement = convert
69
+ ? this.convertElement(parents, type, schema, element, mode ?? ConversionMode.STRICT)
70
+ : element;
71
+
72
+ return await this.validateElement(
73
+ parents,
74
+ type,
75
+ schema,
76
+ cElement,
77
+ mode ?? ConversionMode.STRICT,
78
+ );
79
+ }
80
+
81
+ private static convertElement(
82
+ parents: Schema[],
83
+ type: SchemaType,
84
+ schema: Schema,
85
+ element: any,
86
+ mode: ConversionMode,
87
+ ): any | null {
88
+ if (isNullValue(type)) {
89
+ return ConvertorUtil.handleUnConvertibleValueWithDefault(
90
+ parents,
91
+ mode,
92
+ element,
93
+ schema.getDefaultValue() ?? null,
94
+ StringFormatter.format('$ is not a valid type for conversion.', type),
95
+ );
96
+ }
97
+
98
+ switch (type) {
99
+ case SchemaType.STRING:
100
+ return StringConvertor.convert(parents, schema, mode, element);
101
+ case SchemaType.INTEGER:
102
+ case SchemaType.LONG:
103
+ case SchemaType.DOUBLE:
104
+ case SchemaType.FLOAT:
105
+ return NumberConvertor.convert(parents, type, schema, mode, element);
106
+ case SchemaType.BOOLEAN:
107
+ return BooleanConvertor.convert(parents, schema, mode, element);
108
+ case SchemaType.NULL:
109
+ return NullConvertor.convert(parents, schema, mode, element);
110
+ default:
111
+ return ConvertorUtil.handleUnConvertibleValueWithDefault(
112
+ parents,
113
+ mode,
114
+ element,
115
+ schema.getDefaultValue() ?? null,
116
+ StringFormatter.format('$ is not a valid type for conversion.', type),
117
+ );
118
+ }
119
+ }
120
+
121
+ private static validateElement(
122
+ parents: Schema[],
123
+ type: SchemaType,
124
+ schema: Schema,
125
+ element: any,
126
+ mode: ConversionMode,
127
+ ): any | null {
128
+ if (isNullValue(type)) {
129
+ return ValidatorUtil.handleValidationError(
130
+ parents,
131
+ mode,
132
+ element,
133
+ schema.getDefaultValue() ?? null,
134
+ StringFormatter.format('$ is not a valid type.', type),
135
+ );
136
+ }
137
+
138
+ switch (type) {
139
+ case SchemaType.STRING:
140
+ return StringValidator.validate(parents, schema, element);
141
+ case SchemaType.INTEGER:
142
+ case SchemaType.LONG:
143
+ case SchemaType.DOUBLE:
144
+ case SchemaType.FLOAT:
145
+ return NumberValidator.validate(type, parents, schema, element);
146
+ case SchemaType.BOOLEAN:
147
+ return BooleanValidator.validate(parents, schema, element);
148
+ case SchemaType.NULL:
149
+ return NullValidator.validate(parents, schema, element);
150
+ default:
151
+ return ValidatorUtil.handleValidationError(
152
+ parents,
153
+ mode,
154
+ element,
155
+ schema.getDefaultValue() ?? null,
156
+ StringFormatter.format('$ is not a valid type.', type),
157
+ );
158
+ }
46
159
  }
47
160
 
48
161
  private constructor() {}
@@ -52,8 +52,8 @@ export abstract class TokenValueExtractor {
52
52
  .map((e) => e.trim())
53
53
  .filter((e) => !StringUtil.isNullOrBlank(e))
54
54
  .reduce(
55
- (a, c, i) =>
56
- this.resolveForEachPartOfTokenWithBrackets(token, parts, partNumber, c, a, i),
55
+ (a, c) =>
56
+ this.resolveForEachPartOfTokenWithBrackets(token, parts, partNumber, c, a),
57
57
  jsonElement,
58
58
  );
59
59
 
@@ -64,74 +64,78 @@ export abstract class TokenValueExtractor {
64
64
  token: string,
65
65
  parts: string[],
66
66
  partNumber: number,
67
- c: string,
68
- a: any,
69
- i: any,
67
+ cPart: string,
68
+ cElement: any
70
69
  ): any {
71
- if (isNullValue(a)) return undefined;
70
+ if (isNullValue(cElement)) return undefined;
72
71
 
73
- if (i === 0) {
74
- if (c === 'length') {
75
- const type = typeof a;
76
- if (type === 'string' || Array.isArray(a)) return a.length;
77
- if (type === 'object') return Object.keys(a).length;
78
- }
79
- if (Array.isArray(a)) {
80
- try {
81
- let index: number = parseInt(c);
82
- if (isNaN(index)) {
83
- throw new Error(StringFormatter.format('$ is not a number', index));
84
- }
85
- if (index >= a.length) return undefined;
86
-
87
- return a[index];
88
- } catch (err: any) {
89
- throw new ExpressionEvaluationException(
90
- token,
91
- StringFormatter.format("$ couldn't be parsed into integer in $", c, token),
92
- err,
93
- );
94
- }
95
- }
72
+ if (cPart === 'length') return this.getLength(token, cElement);
96
73
 
97
- this.checkIfObject(token, parts, partNumber, a);
98
- return a[c];
99
- } else if (c?.startsWith('"')) {
100
- if (!c.endsWith('"') || c.length == 1 || c.length == 2)
101
- throw new ExpressionEvaluationException(
102
- token,
103
- StringFormatter.format('$ is missing a double quote or empty key found', token),
104
- );
74
+ if (Array.isArray(cElement)) return this.handleArrayAccess(token, cPart, cElement);
75
+
76
+ return this.handleObjectAccess(token, parts, partNumber, cPart, cElement);
77
+ }
78
+
79
+ private getLength(
80
+ token: string,
81
+ cElement: any
82
+ ): any {
83
+ const type = typeof cElement;
105
84
 
106
- this.checkIfObject(token, parts, partNumber, a);
107
- return a[c.substring(1, c.length - 1)];
85
+ if (type === 'string' || Array.isArray(cElement)) return cElement.length;
86
+ if (type === 'object') {
87
+ if ('length' in cElement) return cElement['length'];
88
+ else return Object.keys(cElement).length;
108
89
  }
109
90
 
110
- try {
111
- let index: number = parseInt(c);
112
- if (isNaN(index)) {
113
- throw new Error(StringFormatter.format('$ is not a number', index));
114
- }
115
- if (!Array.isArray(a))
116
- throw new ExpressionEvaluationException(
117
- token,
118
- StringFormatter.format(
119
- 'Expecting an array with index $ while processing the expression',
120
- index,
121
- token,
122
- ),
123
- );
91
+ throw new ExpressionEvaluationException(token,
92
+ StringFormatter.format('Length can\'t be found in token $', token))
93
+ }
94
+
95
+ private handleArrayAccess(
96
+ token: string,
97
+ cPart: string,
98
+ cArray: any[]
99
+ ): any {
100
+ const index: number = parseInt(cPart);
124
101
 
125
- if (index >= a.length) return undefined;
102
+ if (isNaN(index)) {
103
+ throw new ExpressionEvaluationException(
104
+ token,
105
+ StringFormatter.format('$ is not a number', cPart)
106
+ );
107
+ }
126
108
 
127
- return a[index];
128
- } catch (err: any) {
109
+ if (index < 0 || index >= cArray.length) {
129
110
  throw new ExpressionEvaluationException(
130
111
  token,
131
- StringFormatter.format("$ couldn't be parsed into integer in $", c, token),
132
- err,
112
+ StringFormatter.format('Index $ is out of bounds for array of length $', index, cArray.length)
133
113
  );
134
114
  }
115
+
116
+ return cArray[index];
117
+ }
118
+
119
+ private handleObjectAccess(
120
+ token: string,
121
+ parts: string[],
122
+ partNumber: number,
123
+ cPart: string,
124
+ cObject: any
125
+ ): any {
126
+ if (cPart.startsWith("\"")) {
127
+ if (!cPart.endsWith("\"") || cPart.length == 1 || cPart.length == 2) {
128
+ throw new ExpressionEvaluationException(
129
+ token,
130
+ StringFormatter.format("$ is missing a double quote or empty key found", token))
131
+ }
132
+
133
+ cPart = cPart.substring(1, parts.length - 2);
134
+ }
135
+
136
+ this.checkIfObject(token, parts, partNumber, cObject);
137
+
138
+ return cObject[cPart];
135
139
  }
136
140
 
137
141
  protected checkIfObject(
@@ -144,7 +148,7 @@ export abstract class TokenValueExtractor {
144
148
  throw new ExpressionEvaluationException(
145
149
  token,
146
150
  StringFormatter.format(
147
- 'Unable to retrive $ from $ in the path $',
151
+ 'Unable to retrieve $ from $ in the path $',
148
152
  parts[partNumber],
149
153
  jsonElement.toString(),
150
154
  token,
@@ -0,0 +1,51 @@
1
+ import { Schema } from '../../json/schema/Schema';
2
+ import { ConversionMode } from '../../json/schema/convertor/enums/ConversionMode';
3
+ import { SchemaConversionException } from '../../json/schema/convertor/exception/SchemaConversionException';
4
+ import { SchemaValidator } from '../../json/schema/validator/SchemaValidator';
5
+
6
+ export class ConvertorUtil {
7
+ public static handleUnConvertibleValue(
8
+ parents: Schema[],
9
+ mode: ConversionMode | null,
10
+ element: any,
11
+ errorMessage: string,
12
+ ): any | null {
13
+ return this.handleUnConvertibleValueWithDefault(parents, mode, element, null, errorMessage);
14
+ }
15
+
16
+ public static handleUnConvertibleValueWithDefault(
17
+ parents: Schema[],
18
+ mode: ConversionMode | null,
19
+ element: any,
20
+ defaultValue: any,
21
+ errorMessage: string,
22
+ ): any | null {
23
+ if (mode === null) {
24
+ mode = ConversionMode.STRICT;
25
+ }
26
+
27
+ switch (mode) {
28
+ case ConversionMode.STRICT:
29
+ throw new SchemaConversionException(
30
+ SchemaValidator.path(parents),
31
+ element,
32
+ errorMessage,
33
+ mode,
34
+ );
35
+ case ConversionMode.LENIENT:
36
+ return null;
37
+ case ConversionMode.USE_DEFAULT:
38
+ return defaultValue;
39
+ case ConversionMode.SKIP:
40
+ return element;
41
+ default:
42
+ throw new SchemaConversionException(
43
+ SchemaValidator.path(parents),
44
+ element,
45
+ 'Invalid conversion mode',
46
+ );
47
+ }
48
+ }
49
+
50
+ private constructor() {}
51
+ }
@@ -0,0 +1,29 @@
1
+ import { Schema } from '../../json/schema/Schema';
2
+ import { ConversionMode } from '../../json/schema/convertor/enums/ConversionMode';
3
+ import { SchemaValidationException } from '../../json/schema/validator/exception/SchemaValidationException';
4
+ import { SchemaValidator } from '../../json/schema/validator/SchemaValidator';
5
+
6
+ export class ValidatorUtil {
7
+ public static handleValidationError(
8
+ parents: Schema[],
9
+ mode: ConversionMode,
10
+ element: any,
11
+ defaultValue: any,
12
+ errorMessage: string,
13
+ ): any | null {
14
+ mode = mode ?? ConversionMode.STRICT;
15
+
16
+ switch (mode) {
17
+ case ConversionMode.STRICT:
18
+ throw new SchemaValidationException(SchemaValidator.path(parents), errorMessage);
19
+ case ConversionMode.LENIENT:
20
+ return null;
21
+ case ConversionMode.USE_DEFAULT:
22
+ return defaultValue;
23
+ case ConversionMode.SKIP:
24
+ return element;
25
+ }
26
+ }
27
+
28
+ private constructor() {}
29
+ }
package/tsconfig.json CHANGED
@@ -5,6 +5,7 @@
5
5
  "esModuleInterop": true,
6
6
  "skipLibCheck": true,
7
7
  "forceConsistentCasingInFileNames": true,
8
- "lib": ["ES2022"]
8
+ "lib": ["ES2022"],
9
+ "target": "ES2022"
9
10
  }
10
11
  }