nesoi 3.0.20 → 3.0.21
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/lib/elements/entities/bucket/model/bucket_model.convert.js +1 -1
- package/lib/elements/entities/message/message.builder.js +2 -2
- package/lib/elements/entities/message/message_parser.d.ts +0 -1
- package/lib/elements/entities/message/message_parser.js +1 -68
- package/lib/elements/entities/message/template/message_template.schema.d.ts +11 -1
- package/lib/elements/entities/message/template/message_template.schema.js +7 -1
- package/lib/elements/entities/message/template/message_template_field.builder.d.ts +11 -0
- package/lib/elements/entities/message/template/message_template_field.builder.js +21 -5
- package/lib/elements/entities/message/template/message_template_parser.d.ts +2 -2
- package/lib/elements/entities/message/template/message_template_parser.js +126 -45
- package/lib/engine/data/error.d.ts +12 -11
- package/lib/engine/data/error.js +7 -7
- package/lib/engine/util/parse.d.ts +14 -14
- package/lib/engine/util/parse.js +37 -42
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -36,7 +36,7 @@ function convertToView(model, name, fields = model.fields, path) {
|
|
|
36
36
|
* */
|
|
37
37
|
function convertToMessage(module, model, name, alias, include = [], exclude = []) {
|
|
38
38
|
const convertField = (field) => {
|
|
39
|
-
return new message_template_schema_1.$MessageTemplateField(field.type, field.name, field.alias, field.path, field.path, field.array, field.required, undefined, false, [], {
|
|
39
|
+
return new message_template_schema_1.$MessageTemplateField(field.type, field.name, field.alias, field.alias, field.path, field.path, field.array, field.required, undefined, false, [], [], {
|
|
40
40
|
enum: field.meta?.enum ? {
|
|
41
41
|
options: field.meta.enum.options,
|
|
42
42
|
dep: field.meta.enum.dep ? new dependency_1.$Dependency(module, 'constants', `${field.meta.enum.dep.module}::${field.meta.enum.dep.name}`) : undefined
|
|
@@ -21,8 +21,8 @@ class MessageBuilder {
|
|
|
21
21
|
return this;
|
|
22
22
|
}
|
|
23
23
|
template($) {
|
|
24
|
-
const
|
|
25
|
-
const fields = $(
|
|
24
|
+
const factory = new message_template_field_builder_1.MessageTemplateFieldFactory(this.module);
|
|
25
|
+
const fields = $(factory);
|
|
26
26
|
this._template.fields(fields);
|
|
27
27
|
return this;
|
|
28
28
|
}
|
|
@@ -11,6 +11,5 @@ export declare class MessageParser<$ extends $Message> {
|
|
|
11
11
|
constructor(schema: $);
|
|
12
12
|
static parseWithTrxModule(trx: AnyTrxNode, raw: RawMessageInput<any, any>, sigKey?: string): Promise<any>;
|
|
13
13
|
parse(trx: AnyTrxNode, raw: $['#raw'], sigKey?: string): Promise<Message<$>>;
|
|
14
|
-
private sanitize;
|
|
15
14
|
}
|
|
16
15
|
export type AnyMessageParser = MessageParser<$Message>;
|
|
@@ -28,78 +28,11 @@ class MessageParser {
|
|
|
28
28
|
}
|
|
29
29
|
return parser.parse(trx, raw, sigKey);
|
|
30
30
|
}
|
|
31
|
-
// TODO: OPTIMIZATION
|
|
32
|
-
// Parse everything that's static first, then move on to
|
|
33
|
-
// parsing ids etc.
|
|
34
31
|
async parse(trx, raw, sigKey) {
|
|
35
32
|
log_1.Log.debug('trx', trx.globalId, `${(0, log_1.scopeTag)('message', this.schema.name)} Parse${sigKey ? ' (signed)' : ''}`, raw);
|
|
36
|
-
const applyFieldRules = async (field, parent, value, path) => {
|
|
37
|
-
// Apply rules to the field
|
|
38
|
-
// If field is an array, the value passed to the rule is the array itself
|
|
39
|
-
for (const r in field.rules) {
|
|
40
|
-
const rule = field.rules[r];
|
|
41
|
-
const res = await rule({ field, value, path: path.join('.'), msg: parsed });
|
|
42
|
-
if (typeof res === 'object') {
|
|
43
|
-
parent[path.at(-1)] = res.set;
|
|
44
|
-
}
|
|
45
|
-
else if (res !== true) {
|
|
46
|
-
throw error_1.NesoiError.Message.RuleFailed({ rule, error: res });
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
if (!value)
|
|
50
|
-
return;
|
|
51
|
-
if (field.type === 'obj') {
|
|
52
|
-
if (field.array) {
|
|
53
|
-
for (let i = 0; i < value?.length; i++) {
|
|
54
|
-
await applyRules(field.children, value[i] || {}, [...path, i.toString()]);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
else {
|
|
58
|
-
await applyRules(field.children, value || {}, path);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
else if (field.type === 'dict') {
|
|
62
|
-
if (field.array) {
|
|
63
|
-
for (let i = 0; i < value?.length; i++) {
|
|
64
|
-
for (const k in value[i]) {
|
|
65
|
-
await applyFieldRules(field.children.__dict, value[i], value[i][k], [...path, i.toString(), k]);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
for (const k in value) {
|
|
71
|
-
await applyFieldRules(field.children.__dict, value, value[k], [...path, k]);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
const applyRules = async (fields, parent, path = []) => {
|
|
77
|
-
for (const f in fields) {
|
|
78
|
-
const field = fields[f];
|
|
79
|
-
const value = parent[field.name];
|
|
80
|
-
const _path = [...path, field.name];
|
|
81
|
-
await applyFieldRules(field, parent, value, _path);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
33
|
const fields = this.schema.template.fields;
|
|
85
|
-
const parsed =
|
|
86
|
-
for (const k in fields) {
|
|
87
|
-
const field = fields[k];
|
|
88
|
-
const key_raw = field.path_raw.split('.')[0];
|
|
89
|
-
const key_parsed = field.path_parsed.split('.')[0];
|
|
90
|
-
const value = raw[key_raw];
|
|
91
|
-
this.sanitize(value);
|
|
92
|
-
parsed[key_parsed] = await (0, message_template_parser_1.MessageTemplateFieldParser)(raw, trx, field, value);
|
|
93
|
-
}
|
|
94
|
-
await applyRules(this.schema.template.fields, parsed);
|
|
34
|
+
const parsed = await (0, message_template_parser_1.MessageTemplateFieldParser)(trx, fields, raw);
|
|
95
35
|
return message_1.Message.new(this.schema.name, parsed, sigKey);
|
|
96
36
|
}
|
|
97
|
-
sanitize(value) {
|
|
98
|
-
if (typeof value === 'function') {
|
|
99
|
-
throw error_1.NesoiError.Message.UnsanitaryValue({
|
|
100
|
-
details: 'Functions not allowed as message inputs.'
|
|
101
|
-
});
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
37
|
}
|
|
105
38
|
exports.MessageParser = MessageParser;
|
|
@@ -6,6 +6,7 @@ export type $MessageTemplateRule = (def: {
|
|
|
6
6
|
path: string;
|
|
7
7
|
value: any;
|
|
8
8
|
msg: $Message['#raw'];
|
|
9
|
+
inject: Record<string, any>;
|
|
9
10
|
}) => {
|
|
10
11
|
set: any;
|
|
11
12
|
} | true | string | Promise<{
|
|
@@ -46,6 +47,10 @@ export declare class $MessageTemplateField {
|
|
|
46
47
|
name: string;
|
|
47
48
|
/** A human name for the field */
|
|
48
49
|
alias: string;
|
|
50
|
+
/** A human name for the field union, assigned before the type.
|
|
51
|
+
* This is only relevant when using .obj.or, to allow for different
|
|
52
|
+
* aliases for the union and the root object */
|
|
53
|
+
preAlias: string;
|
|
49
54
|
/** The absolute path for reading the field value on the raw object */
|
|
50
55
|
path_raw: string;
|
|
51
56
|
/** The absolute path for writing the field value on the parsed object */
|
|
@@ -55,6 +60,7 @@ export declare class $MessageTemplateField {
|
|
|
55
60
|
defaultValue: any;
|
|
56
61
|
nullable: boolean;
|
|
57
62
|
rules: $MessageTemplateRule[];
|
|
63
|
+
arrayRules: $MessageTemplateRule[];
|
|
58
64
|
meta: $MessageTemplateFieldMeta;
|
|
59
65
|
children?: $MessageTemplateFields | undefined;
|
|
60
66
|
or?: $MessageTemplateField | undefined;
|
|
@@ -71,10 +77,14 @@ export declare class $MessageTemplateField {
|
|
|
71
77
|
name: string,
|
|
72
78
|
/** A human name for the field */
|
|
73
79
|
alias: string,
|
|
80
|
+
/** A human name for the field union, assigned before the type.
|
|
81
|
+
* This is only relevant when using .obj.or, to allow for different
|
|
82
|
+
* aliases for the union and the root object */
|
|
83
|
+
preAlias: string,
|
|
74
84
|
/** The absolute path for reading the field value on the raw object */
|
|
75
85
|
path_raw: string,
|
|
76
86
|
/** The absolute path for writing the field value on the parsed object */
|
|
77
|
-
path_parsed: string, array: boolean, required: boolean, defaultValue: any, nullable: boolean, rules: $MessageTemplateRule[], meta: $MessageTemplateFieldMeta, children?: $MessageTemplateFields | undefined, or?: $MessageTemplateField | undefined);
|
|
87
|
+
path_parsed: string, array: boolean, required: boolean, defaultValue: any, nullable: boolean, rules: $MessageTemplateRule[], arrayRules: $MessageTemplateRule[], meta: $MessageTemplateFieldMeta, children?: $MessageTemplateFields | undefined, or?: $MessageTemplateField | undefined);
|
|
78
88
|
}
|
|
79
89
|
export type $MessageTemplateFields = {
|
|
80
90
|
[x: string]: $MessageTemplateField;
|
|
@@ -16,13 +16,18 @@ class $MessageTemplateField {
|
|
|
16
16
|
name,
|
|
17
17
|
/** A human name for the field */
|
|
18
18
|
alias,
|
|
19
|
+
/** A human name for the field union, assigned before the type.
|
|
20
|
+
* This is only relevant when using .obj.or, to allow for different
|
|
21
|
+
* aliases for the union and the root object */
|
|
22
|
+
preAlias,
|
|
19
23
|
/** The absolute path for reading the field value on the raw object */
|
|
20
24
|
path_raw,
|
|
21
25
|
/** The absolute path for writing the field value on the parsed object */
|
|
22
|
-
path_parsed, array, required, defaultValue, nullable, rules, meta, children, or) {
|
|
26
|
+
path_parsed, array, required, defaultValue, nullable, rules, arrayRules, meta, children, or) {
|
|
23
27
|
this.type = type;
|
|
24
28
|
this.name = name;
|
|
25
29
|
this.alias = alias;
|
|
30
|
+
this.preAlias = preAlias;
|
|
26
31
|
this.path_raw = path_raw;
|
|
27
32
|
this.path_parsed = path_parsed;
|
|
28
33
|
this.array = array;
|
|
@@ -30,6 +35,7 @@ class $MessageTemplateField {
|
|
|
30
35
|
this.defaultValue = defaultValue;
|
|
31
36
|
this.nullable = nullable;
|
|
32
37
|
this.rules = rules;
|
|
38
|
+
this.arrayRules = arrayRules;
|
|
33
39
|
this.meta = meta;
|
|
34
40
|
this.children = children;
|
|
35
41
|
this.or = or;
|
|
@@ -18,6 +18,13 @@ export declare class MessageTemplateFieldFactory<Space extends $Space, Module ex
|
|
|
18
18
|
private module;
|
|
19
19
|
private alias?;
|
|
20
20
|
constructor(module: string);
|
|
21
|
+
/**
|
|
22
|
+
* Specifies an alias for the field.
|
|
23
|
+
* - If this field is a union (.or), this alias is used when
|
|
24
|
+
* referring to the union and it's first option.
|
|
25
|
+
* - You can specify a different alias for the first options
|
|
26
|
+
* by also using the .as() after the type
|
|
27
|
+
*/
|
|
21
28
|
as(alias: string): Omit<typeof this, "as">;
|
|
22
29
|
get any(): MessageTemplateFieldBuilder<Module, Message, {
|
|
23
30
|
'': any;
|
|
@@ -227,7 +234,9 @@ Nullable extends [null | never, null | never] = [never, never]> {
|
|
|
227
234
|
private _defaultValue?;
|
|
228
235
|
private _nullable;
|
|
229
236
|
private _rules;
|
|
237
|
+
private _arrayRules;
|
|
230
238
|
private _or?;
|
|
239
|
+
private preAlias?;
|
|
231
240
|
constructor(type: $MessageTemplateFieldType, value: $MessageTemplateFieldMeta, alias?: string | undefined, children?: Children | undefined);
|
|
232
241
|
as(alias: string): this;
|
|
233
242
|
get optional(): MessageTemplateFieldBuilder<Module, Message, DefinedInput, DefinedOutput, Children, [undefined, undefined], Nullable>;
|
|
@@ -245,8 +254,10 @@ export type MessageTemplateFieldBuilders = {
|
|
|
245
254
|
export type AnyMessageTemplateFieldBuilder = MessageTemplateFieldBuilder<any, any, any, any, any>;
|
|
246
255
|
export type MessageTemplateRuleDef<I, Msg> = (def: {
|
|
247
256
|
field: $MessageTemplateField;
|
|
257
|
+
path: string;
|
|
248
258
|
value: I;
|
|
249
259
|
msg: Msg;
|
|
260
|
+
inject: Record<string, any>;
|
|
250
261
|
}) => {
|
|
251
262
|
set: I;
|
|
252
263
|
} | true | string | Promise<{
|
|
@@ -12,9 +12,17 @@ class MessageTemplateFieldFactory {
|
|
|
12
12
|
constructor(module) {
|
|
13
13
|
this.module = module;
|
|
14
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Specifies an alias for the field.
|
|
17
|
+
* - If this field is a union (.or), this alias is used when
|
|
18
|
+
* referring to the union and it's first option.
|
|
19
|
+
* - You can specify a different alias for the first options
|
|
20
|
+
* by also using the .as() after the type
|
|
21
|
+
*/
|
|
15
22
|
as(alias) {
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
const chain = new MessageTemplateFieldFactory(this.module);
|
|
24
|
+
chain.alias = alias;
|
|
25
|
+
return chain;
|
|
18
26
|
}
|
|
19
27
|
get any() {
|
|
20
28
|
return new MessageTemplateFieldBuilder('unknown', {}, this.alias);
|
|
@@ -106,6 +114,8 @@ class MessageTemplateFieldBuilder {
|
|
|
106
114
|
this._defaultValue = undefined;
|
|
107
115
|
this._nullable = true;
|
|
108
116
|
this._rules = [];
|
|
117
|
+
this._arrayRules = [];
|
|
118
|
+
this.preAlias = alias;
|
|
109
119
|
}
|
|
110
120
|
as(alias) {
|
|
111
121
|
this.alias = alias;
|
|
@@ -136,7 +146,12 @@ class MessageTemplateFieldBuilder {
|
|
|
136
146
|
return this;
|
|
137
147
|
}
|
|
138
148
|
rule(rule) {
|
|
139
|
-
this.
|
|
149
|
+
if (this._array) {
|
|
150
|
+
this._arrayRules.push(rule);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this._rules.push(rule);
|
|
154
|
+
}
|
|
140
155
|
return this;
|
|
141
156
|
}
|
|
142
157
|
get array() {
|
|
@@ -149,11 +164,12 @@ class MessageTemplateFieldBuilder {
|
|
|
149
164
|
}
|
|
150
165
|
or(def) {
|
|
151
166
|
this._or = def;
|
|
167
|
+
this._or.preAlias = this.preAlias;
|
|
152
168
|
this._or._array = this._array;
|
|
153
169
|
this._or._defaultValue = this._defaultValue;
|
|
154
170
|
this._or._nullable = this._nullable;
|
|
155
171
|
this._or._required = this._required;
|
|
156
|
-
this._or.
|
|
172
|
+
this._or._arrayRules = this._arrayRules;
|
|
157
173
|
return this;
|
|
158
174
|
}
|
|
159
175
|
// Build
|
|
@@ -171,7 +187,7 @@ class MessageTemplateFieldBuilder {
|
|
|
171
187
|
const bucket = tree.getSchema(builder.value.id.bucket);
|
|
172
188
|
builder.value.id.type = bucket.model.fields.id.type;
|
|
173
189
|
}
|
|
174
|
-
return new message_template_schema_1.$MessageTemplateField(builder.type, name, builder.alias || name, pathRaw, pathParsed, builder._array, builder._required, builder._defaultValue, builder._nullable, builder._rules, builder.value, builder.children ? MessageTemplateFieldBuilder.buildChildren(builder.children, tree, module, childrenBasePathRaw, childrenBasePathParsed) : undefined, or);
|
|
190
|
+
return new message_template_schema_1.$MessageTemplateField(builder.type, name, builder.alias || name, builder.preAlias || name, pathRaw, pathParsed, builder._array, builder._required, builder._defaultValue, builder._nullable, builder._rules, builder._arrayRules, builder.value, builder.children ? MessageTemplateFieldBuilder.buildChildren(builder.children, tree, module, childrenBasePathRaw, childrenBasePathParsed) : undefined, or);
|
|
175
191
|
}
|
|
176
192
|
static buildChildren(fields, tree, module, basePathRaw = '', basePathParsed = '') {
|
|
177
193
|
const schema = {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { $
|
|
1
|
+
import { $MessageTemplateFields } from './message_template.schema';
|
|
2
2
|
import { AnyTrxNode } from "../../../../engine/transaction/trx_node";
|
|
3
|
-
export declare function MessageTemplateFieldParser(
|
|
3
|
+
export declare function MessageTemplateFieldParser(trx: AnyTrxNode, fields: $MessageTemplateFields, raw: Record<string, any>): Promise<Record<string, any>>;
|
|
4
4
|
/**
|
|
5
5
|
* Empty values: `{}`, `[]`, `''`, `null`, `undefined`
|
|
6
6
|
*/
|
|
@@ -4,19 +4,34 @@ exports.MessageTemplateFieldParser = MessageTemplateFieldParser;
|
|
|
4
4
|
exports.isEmpty = isEmpty;
|
|
5
5
|
const parse_1 = require("../../../../engine/util/parse");
|
|
6
6
|
const error_1 = require("../../../../engine/data/error");
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
// TODO: OPTIMIZATION
|
|
8
|
+
// Parse everything that's static first, then move on to
|
|
9
|
+
// parsing ids etc.
|
|
10
|
+
async function MessageTemplateFieldParser(trx, fields, raw) {
|
|
11
|
+
const parsed = {};
|
|
12
|
+
const inject = {};
|
|
13
|
+
for (const k in fields) {
|
|
14
|
+
const field = fields[k];
|
|
15
|
+
const key_raw = field.path_raw.split('.')[0];
|
|
16
|
+
const key_parsed = field.path_parsed.split('.')[0];
|
|
17
|
+
const value = raw[key_raw];
|
|
18
|
+
parsed[key_parsed] = await parseFieldValue(trx, field, [field.name], raw, value, inject);
|
|
19
|
+
}
|
|
20
|
+
Object.assign(parsed, inject);
|
|
21
|
+
return parsed;
|
|
9
22
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
/**
|
|
24
|
+
* [Parser Step 1]
|
|
25
|
+
*
|
|
26
|
+
* - Check for empty fields ({}, [], '', null, undefined)
|
|
27
|
+
* - If it's array, run step 2 for each value (with this field and path+i)
|
|
28
|
+
* - If not, run step 2 for the original value (with this field and path)
|
|
29
|
+
*/
|
|
30
|
+
async function parseFieldValue(trx, field, path, raw, value, inject) {
|
|
31
|
+
sanitize(field, path, value);
|
|
17
32
|
if (isEmpty(value)) {
|
|
18
33
|
if (field.required) {
|
|
19
|
-
throw error_1.NesoiError.Message.FieldIsRequired({
|
|
34
|
+
throw error_1.NesoiError.Message.FieldIsRequired({ alias: field.alias, path: path.join('.'), value });
|
|
20
35
|
}
|
|
21
36
|
else if (field.defaultValue !== undefined) {
|
|
22
37
|
return field.defaultValue;
|
|
@@ -25,95 +40,132 @@ async function parseFieldValue(trx, field, raw, value, path_idx) {
|
|
|
25
40
|
return undefined;
|
|
26
41
|
}
|
|
27
42
|
}
|
|
43
|
+
let output;
|
|
28
44
|
if (field.array) {
|
|
29
45
|
if (!Array.isArray(value)) {
|
|
30
|
-
throw error_1.NesoiError.Message.InvalidFieldType({
|
|
31
|
-
}
|
|
32
|
-
if (field.required && !value.length) {
|
|
33
|
-
throw error_1.NesoiError.Message.FieldIsRequired({ field: field.alias, path: field.path_raw, value });
|
|
46
|
+
throw error_1.NesoiError.Message.InvalidFieldType({ alias: field.alias, path: path.join('.'), value, type: 'list' });
|
|
34
47
|
}
|
|
35
|
-
|
|
48
|
+
output = [];
|
|
36
49
|
for (let i = 0; i < value.length; i++) {
|
|
37
50
|
const v = value[i];
|
|
38
|
-
const
|
|
39
|
-
|
|
51
|
+
const parsedValue = await _attemptUnion(trx, field, [...path, i.toString()], raw, v, inject);
|
|
52
|
+
output.push(parsedValue);
|
|
40
53
|
}
|
|
41
|
-
|
|
54
|
+
output = await applyFieldRules('array', field, path, raw, output, inject);
|
|
42
55
|
}
|
|
43
|
-
|
|
56
|
+
else {
|
|
57
|
+
output = await _attemptUnion(trx, field, path, raw, value, inject);
|
|
58
|
+
}
|
|
59
|
+
return output;
|
|
44
60
|
}
|
|
45
|
-
|
|
61
|
+
/**
|
|
62
|
+
* [Parser Step 2]
|
|
63
|
+
*
|
|
64
|
+
* - Attempt to run parse method (step 3) for field.
|
|
65
|
+
* - If it fails, attempt other union options (step 2) (if available), with same path
|
|
66
|
+
* - If it works, apply field rules.
|
|
67
|
+
*/
|
|
68
|
+
async function _attemptUnion(trx, field, path, raw, value, inject, unionErrors = []) {
|
|
69
|
+
let output = undefined;
|
|
46
70
|
try {
|
|
47
|
-
|
|
71
|
+
output = await _runParseMethod(trx, field, path, raw, value, inject);
|
|
48
72
|
}
|
|
49
73
|
catch (e) {
|
|
74
|
+
const ue = [
|
|
75
|
+
...unionErrors,
|
|
76
|
+
{
|
|
77
|
+
option: field.alias,
|
|
78
|
+
name: e.name,
|
|
79
|
+
status: e.status,
|
|
80
|
+
message: e.message,
|
|
81
|
+
data: e.data,
|
|
82
|
+
}
|
|
83
|
+
];
|
|
50
84
|
// If failed and there's a second option, atempt it
|
|
51
85
|
if (field.or) {
|
|
52
|
-
return await _attemptUnion(trx, field.or, raw, value,
|
|
86
|
+
return await _attemptUnion(trx, field.or, path, raw, value, inject, ue);
|
|
53
87
|
}
|
|
54
88
|
// If this error was not the first attempt, and we have no other option
|
|
55
89
|
// we throw a specific error
|
|
56
90
|
// This avoid confusion for the client when parsing unions
|
|
57
91
|
if (unionErrors.length) {
|
|
58
|
-
throw error_1.NesoiError.Message.ValueDoesntMatchUnion({
|
|
92
|
+
throw error_1.NesoiError.Message.ValueDoesntMatchUnion({ alias: field.preAlias, path: path.join('.'), value, unionErrors: ue });
|
|
59
93
|
}
|
|
60
94
|
throw e;
|
|
61
95
|
}
|
|
96
|
+
output = await applyFieldRules('item', field, path, raw, output, inject);
|
|
97
|
+
return output;
|
|
62
98
|
}
|
|
63
|
-
|
|
99
|
+
/**
|
|
100
|
+
* [Parser Step 3]
|
|
101
|
+
*
|
|
102
|
+
* - Run a specific parsing method based on the field type
|
|
103
|
+
*/
|
|
104
|
+
async function _runParseMethod(trx, field, path, raw, value, inject) {
|
|
64
105
|
switch (field.type) {
|
|
65
106
|
case 'obj':
|
|
66
107
|
case 'dict':
|
|
67
|
-
return await parseParentField(trx, field, raw, value,
|
|
108
|
+
return await parseParentField(trx, field, path, raw, value, inject);
|
|
68
109
|
case 'unknown':
|
|
69
110
|
return value;
|
|
70
111
|
case 'boolean':
|
|
71
|
-
return (0, parse_1.parseBoolean)(field, value);
|
|
112
|
+
return (0, parse_1.parseBoolean)(field, path, value);
|
|
72
113
|
case 'date':
|
|
73
|
-
return (0, parse_1.parseDate)(field, value);
|
|
114
|
+
return (0, parse_1.parseDate)(field, path, value);
|
|
74
115
|
case 'datetime':
|
|
75
|
-
return (0, parse_1.parseDatetime)(field, value);
|
|
116
|
+
return (0, parse_1.parseDatetime)(field, path, value);
|
|
76
117
|
case 'duration':
|
|
77
|
-
return (0, parse_1.parseDuration)(field, value);
|
|
118
|
+
return (0, parse_1.parseDuration)(field, path, value);
|
|
78
119
|
case 'decimal':
|
|
79
|
-
return (0, parse_1.parseDecimal)(field, value);
|
|
120
|
+
return (0, parse_1.parseDecimal)(field, path, value);
|
|
80
121
|
case 'enum':
|
|
81
|
-
return (0, parse_1.parseEnum)(raw, field, value, field.meta.enum.options, trx);
|
|
122
|
+
return (0, parse_1.parseEnum)(raw, field, path, value, field.meta.enum.options, trx);
|
|
82
123
|
case 'file':
|
|
83
|
-
return (0, parse_1.parseFile)(field, value, field.meta.file);
|
|
124
|
+
return (0, parse_1.parseFile)(field, path, value, field.meta.file);
|
|
84
125
|
case 'float':
|
|
85
|
-
return (0, parse_1.parseFloat_)(field, value);
|
|
126
|
+
return (0, parse_1.parseFloat_)(field, path, value);
|
|
86
127
|
case 'int':
|
|
87
|
-
return (0, parse_1.parseInt_)(field, value);
|
|
128
|
+
return (0, parse_1.parseInt_)(field, path, value);
|
|
88
129
|
case 'string':
|
|
89
|
-
return (0, parse_1.parseString)(field, value);
|
|
130
|
+
return (0, parse_1.parseString)(field, path, value);
|
|
90
131
|
case 'string_or_number':
|
|
91
|
-
return (0, parse_1.parseStringOrNumber)(field, value);
|
|
132
|
+
return (0, parse_1.parseStringOrNumber)(field, path, value);
|
|
92
133
|
case 'id':
|
|
93
|
-
return await parseIdField(trx, field, value);
|
|
134
|
+
return await parseIdField(trx, field, path, value);
|
|
94
135
|
}
|
|
95
136
|
throw error_1.NesoiError.Builder.Message.UnknownTemplateFieldType(field.type);
|
|
96
137
|
}
|
|
97
|
-
|
|
138
|
+
/**
|
|
139
|
+
* [Parser Step 3-b]: 'obj' or 'dict'
|
|
140
|
+
*
|
|
141
|
+
* - The parser methods only return a tuple of field and value, to be parsed again by (step 1)
|
|
142
|
+
* - When calling step 1, the child property name is appended to the path
|
|
143
|
+
*/
|
|
144
|
+
async function parseParentField(trx, field, path, raw, value, inject) {
|
|
98
145
|
let children;
|
|
99
146
|
if (field.type === 'obj') {
|
|
100
|
-
children = (0, parse_1.parseObj)(field,
|
|
147
|
+
children = (0, parse_1.parseObj)(field, path, value);
|
|
101
148
|
}
|
|
102
149
|
else {
|
|
103
|
-
children = (0, parse_1.parseDict)(field, value);
|
|
150
|
+
children = (0, parse_1.parseDict)(field, path, value);
|
|
104
151
|
}
|
|
105
|
-
const
|
|
152
|
+
const parsedParent = {};
|
|
106
153
|
for (const key in children) {
|
|
107
154
|
const child = children[key];
|
|
108
|
-
|
|
155
|
+
parsedParent[key] = await parseFieldValue(trx, child.field, [...path, key], raw, child.value, inject);
|
|
109
156
|
}
|
|
110
|
-
return
|
|
157
|
+
return parsedParent;
|
|
111
158
|
}
|
|
112
|
-
|
|
159
|
+
/**
|
|
160
|
+
* [Parser Step 3-b]: 'id'
|
|
161
|
+
*
|
|
162
|
+
* - Gathers the data for parsing a id
|
|
163
|
+
*/
|
|
164
|
+
async function parseIdField(trx, field, path, value) {
|
|
113
165
|
const bucket = field.meta.id.bucket;
|
|
114
166
|
const type = field.meta.id.type;
|
|
115
167
|
const view = field.meta.id.view;
|
|
116
|
-
const parsed = await (0, parse_1.parseId)(field, value, trx, bucket.refName, type, view);
|
|
168
|
+
const parsed = await (0, parse_1.parseId)(field, path, value, trx, bucket.refName, type, view);
|
|
117
169
|
if (field.array) {
|
|
118
170
|
return parsed.map((p) => p.obj);
|
|
119
171
|
}
|
|
@@ -121,6 +173,13 @@ async function parseIdField(trx, field, value) {
|
|
|
121
173
|
return parsed.obj;
|
|
122
174
|
}
|
|
123
175
|
}
|
|
176
|
+
function sanitize(field, path, value) {
|
|
177
|
+
if (typeof value === 'function') {
|
|
178
|
+
throw error_1.NesoiError.Message.UnsanitaryValue({
|
|
179
|
+
alias: field.alias, path: path.join('.'), details: 'Functions not allowed as message inputs.'
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
124
183
|
/**
|
|
125
184
|
* Empty values: `{}`, `[]`, `''`, `null`, `undefined`
|
|
126
185
|
*/
|
|
@@ -139,3 +198,25 @@ function isEmpty(value) {
|
|
|
139
198
|
}
|
|
140
199
|
return false;
|
|
141
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* Rules
|
|
203
|
+
*/
|
|
204
|
+
async function applyFieldRules(mode, field, path, raw, value, inject) {
|
|
205
|
+
let output = value;
|
|
206
|
+
// If mode is item, the value received is not an array
|
|
207
|
+
// - This comes from a .rule *before* .array
|
|
208
|
+
// If mode is array, the value received is an array
|
|
209
|
+
// - This comes from a .rule *after* .array
|
|
210
|
+
const rules = mode === 'item' ? field.rules : field.arrayRules;
|
|
211
|
+
for (const r in rules) {
|
|
212
|
+
const rule = rules[r];
|
|
213
|
+
const res = await rule({ field, value, path: path.join('.'), msg: raw, inject });
|
|
214
|
+
if (typeof res === 'object') {
|
|
215
|
+
output = res.set;
|
|
216
|
+
}
|
|
217
|
+
else if (res !== true) {
|
|
218
|
+
throw error_1.NesoiError.Message.RuleFailed({ alias: field.alias, path: path.join('.'), rule, error: res });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return output;
|
|
222
|
+
}
|
|
@@ -169,53 +169,54 @@ export declare namespace NesoiError {
|
|
|
169
169
|
module: string;
|
|
170
170
|
}): BaseError;
|
|
171
171
|
function InvalidEnumScope($: {
|
|
172
|
+
alias: string;
|
|
172
173
|
path: string;
|
|
173
|
-
name: string;
|
|
174
|
-
alias?: string;
|
|
175
174
|
value: any;
|
|
176
175
|
fieldpath: string;
|
|
177
176
|
}): BaseError;
|
|
178
177
|
function InvalidFieldEnumValue($: {
|
|
179
|
-
|
|
178
|
+
alias: string;
|
|
180
179
|
path: string;
|
|
181
180
|
value: any;
|
|
182
181
|
type: string;
|
|
183
182
|
options: string[];
|
|
184
183
|
}): BaseError;
|
|
185
184
|
function InvalidFieldType($: {
|
|
186
|
-
|
|
185
|
+
alias: string;
|
|
187
186
|
path: string;
|
|
188
187
|
value: any;
|
|
189
188
|
type: string;
|
|
190
189
|
}): BaseError;
|
|
191
190
|
function ValueDoesntMatchUnion($: {
|
|
192
|
-
|
|
191
|
+
alias: string;
|
|
193
192
|
path: string;
|
|
194
193
|
value: any;
|
|
195
|
-
unionErrors: string[];
|
|
194
|
+
unionErrors: Record<string, any>[];
|
|
196
195
|
}): BaseError;
|
|
197
196
|
function UnsanitaryValue($: {
|
|
197
|
+
alias: string;
|
|
198
|
+
path: string;
|
|
198
199
|
details: string;
|
|
199
200
|
}): BaseError;
|
|
200
201
|
function FieldIsRequired($: {
|
|
201
|
-
|
|
202
|
+
alias: string;
|
|
202
203
|
path: string;
|
|
203
204
|
value: any;
|
|
204
205
|
}): BaseError;
|
|
205
206
|
function RuleFailed($: {
|
|
207
|
+
alias: string;
|
|
208
|
+
path: string;
|
|
206
209
|
rule: $MessageTemplateRule;
|
|
207
210
|
error: string;
|
|
208
211
|
}): BaseError;
|
|
209
212
|
function FileTooBig($: {
|
|
213
|
+
alias: string;
|
|
210
214
|
path: string;
|
|
211
|
-
name: string;
|
|
212
|
-
alias?: string;
|
|
213
215
|
maxsize: number;
|
|
214
216
|
}): BaseError;
|
|
215
217
|
function FileExtNotAllowed($: {
|
|
218
|
+
alias: string;
|
|
216
219
|
path: string;
|
|
217
|
-
name: string;
|
|
218
|
-
alias?: string;
|
|
219
220
|
options: string[];
|
|
220
221
|
}): BaseError;
|
|
221
222
|
}
|
package/lib/engine/data/error.js
CHANGED
|
@@ -302,19 +302,19 @@ var NesoiError;
|
|
|
302
302
|
}
|
|
303
303
|
Message.NotSupportedByModule = NotSupportedByModule;
|
|
304
304
|
function InvalidEnumScope($) {
|
|
305
|
-
return new BaseError('Message.InvalidEnumScope', `${$.alias
|
|
305
|
+
return new BaseError('Message.InvalidEnumScope', `${$.alias} is an enum with dynamic scope, and the path '${$.fieldpath}' of the message has an invalid value '${$.value}'`, Status.BAD_REQUEST, $);
|
|
306
306
|
}
|
|
307
307
|
Message.InvalidEnumScope = InvalidEnumScope;
|
|
308
308
|
function InvalidFieldEnumValue($) {
|
|
309
|
-
return new BaseError('Message.InvalidFieldEnumValue', `Message field '${$.
|
|
309
|
+
return new BaseError('Message.InvalidFieldEnumValue', `Message field '${$.alias}' value '${$.value}' should be one of the following: ${$.options?.join(',')}`, Status.BAD_REQUEST, $);
|
|
310
310
|
}
|
|
311
311
|
Message.InvalidFieldEnumValue = InvalidFieldEnumValue;
|
|
312
312
|
function InvalidFieldType($) {
|
|
313
|
-
return new BaseError('Message.InvalidFieldType', `Message field '${$.
|
|
313
|
+
return new BaseError('Message.InvalidFieldType', `Message field '${$.alias}' value '${$.value}' is not of type '${$.type}'`, Status.BAD_REQUEST, $);
|
|
314
314
|
}
|
|
315
315
|
Message.InvalidFieldType = InvalidFieldType;
|
|
316
316
|
function ValueDoesntMatchUnion($) {
|
|
317
|
-
return new BaseError('Message.ValueDoesntMatchUnion', `Message field '${$.
|
|
317
|
+
return new BaseError('Message.ValueDoesntMatchUnion', `Message field '${$.alias}' (${$.path}) value '${$.value}' doesn't match any of the union options'`, Status.BAD_REQUEST, $);
|
|
318
318
|
}
|
|
319
319
|
Message.ValueDoesntMatchUnion = ValueDoesntMatchUnion;
|
|
320
320
|
function UnsanitaryValue($) {
|
|
@@ -322,7 +322,7 @@ var NesoiError;
|
|
|
322
322
|
}
|
|
323
323
|
Message.UnsanitaryValue = UnsanitaryValue;
|
|
324
324
|
function FieldIsRequired($) {
|
|
325
|
-
return new BaseError('Message.FieldIsRequired', `Field ${$.
|
|
325
|
+
return new BaseError('Message.FieldIsRequired', `Field ${$.alias} (${$.path}) is required`, Status.BAD_REQUEST, $);
|
|
326
326
|
}
|
|
327
327
|
Message.FieldIsRequired = FieldIsRequired;
|
|
328
328
|
function RuleFailed($) {
|
|
@@ -330,11 +330,11 @@ var NesoiError;
|
|
|
330
330
|
}
|
|
331
331
|
Message.RuleFailed = RuleFailed;
|
|
332
332
|
function FileTooBig($) {
|
|
333
|
-
return new BaseError('Message.FileTooBig', `${$.alias
|
|
333
|
+
return new BaseError('Message.FileTooBig', `${$.alias} size exceeds max (${$.maxsize})`, Status.BAD_REQUEST, $);
|
|
334
334
|
}
|
|
335
335
|
Message.FileTooBig = FileTooBig;
|
|
336
336
|
function FileExtNotAllowed($) {
|
|
337
|
-
return new BaseError('Message.FileExtNotAllowed', `${$.alias
|
|
337
|
+
return new BaseError('Message.FileExtNotAllowed', `${$.alias} extension not allowed. Options: ${$.options}`, Status.BAD_REQUEST, $);
|
|
338
338
|
}
|
|
339
339
|
Message.FileExtNotAllowed = FileExtNotAllowed;
|
|
340
340
|
})(Message = NesoiError.Message || (NesoiError.Message = {}));
|