nesoi 3.0.19 → 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/blocks/block.builder.d.ts +10 -1
- package/lib/elements/blocks/block.builder.js +26 -0
- package/lib/elements/blocks/job/job.builder.d.ts +7 -1
- package/lib/elements/blocks/job/job.builder.js +4 -0
- 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 +2 -46
- package/lib/elements/entities/message/template/message_template.schema.d.ts +33 -1
- package/lib/elements/entities/message/template/message_template.schema.js +20 -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 +24 -8
- package/lib/elements/entities/message/template/message_template_parser.d.ts +2 -2
- package/lib/elements/entities/message/template/message_template_parser.js +110 -29
- package/lib/engine/data/error.d.ts +12 -11
- package/lib/engine/data/error.js +7 -7
- package/lib/engine/util/deep.d.ts +2 -0
- package/lib/engine/util/deep.js +30 -0
- package/lib/engine/util/parse.d.ts +28 -18
- package/lib/engine/util/parse.js +31 -34
- package/package.json +1 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $Module, $Space } from "../../schema";
|
|
2
2
|
import { $BlockOutput, $BlockType } from './block.schema';
|
|
3
|
-
import { MultiMessageTemplateDef } from "../entities/message/template/message_template.builder";
|
|
3
|
+
import { MessageTemplateDef, MultiMessageTemplateDef } from "../entities/message/template/message_template.builder";
|
|
4
4
|
import { $Dependency, BuilderNode } from "../../engine/dependency";
|
|
5
5
|
/**
|
|
6
6
|
* @category Builders
|
|
@@ -19,11 +19,20 @@ export declare abstract class BlockBuilder<Space extends $Space, Module extends
|
|
|
19
19
|
/** Block "human" name */
|
|
20
20
|
as(alias: string): this;
|
|
21
21
|
/**
|
|
22
|
+
* Inline messages. This messages is exposed to the module,
|
|
23
|
+
* with a name prefixed by the block name.
|
|
24
|
+
* @param def A method which takes a field factory as input and outputs a template builder
|
|
25
|
+
* @returns The Builder, for call-chaining
|
|
26
|
+
*/
|
|
27
|
+
protected message<Name extends string, Def extends MessageTemplateDef<Space, Module, Name>>(name: Name, def: Def): unknown;
|
|
28
|
+
/**
|
|
29
|
+
* @deprecated
|
|
22
30
|
* Inline messages. These messages are exposed to the module,
|
|
23
31
|
* with a name prefixed by the block name.
|
|
24
32
|
* @param def A method which takes a field factory as input and outputs a template builder
|
|
25
33
|
* @returns The Builder, for call-chaining
|
|
26
34
|
*/
|
|
35
|
+
/** @deprecated Use `.message` instead. Will be removed on 3.1 */
|
|
27
36
|
protected messages<Def extends MultiMessageTemplateDef<Space, Module>>(def: Def): unknown;
|
|
28
37
|
authn(...providers: string[]): unknown;
|
|
29
38
|
protected _input(...names: string[]): unknown;
|
|
@@ -26,11 +26,37 @@ class BlockBuilder {
|
|
|
26
26
|
}
|
|
27
27
|
// Inline Messages
|
|
28
28
|
/**
|
|
29
|
+
* Inline messages. This messages is exposed to the module,
|
|
30
|
+
* with a name prefixed by the block name.
|
|
31
|
+
* @param def A method which takes a field factory as input and outputs a template builder
|
|
32
|
+
* @returns The Builder, for call-chaining
|
|
33
|
+
*/
|
|
34
|
+
message(name, def) {
|
|
35
|
+
const msgName = `${this.name}${name.length ? ('.' + name) : ''}`;
|
|
36
|
+
this._inputMsgs.push(new dependency_1.$Dependency(this.module, 'message', msgName));
|
|
37
|
+
const builder = new message_builder_1.MessageBuilder(this.module, msgName)
|
|
38
|
+
.template(def);
|
|
39
|
+
this._inlineNodes.push(new dependency_1.BuilderNode({
|
|
40
|
+
module: this.module,
|
|
41
|
+
type: 'message',
|
|
42
|
+
name: msgName,
|
|
43
|
+
builder,
|
|
44
|
+
isInline: true,
|
|
45
|
+
filepath: [], // This is added later by Treeshake.blockInlineNodes()
|
|
46
|
+
dependencies: [] // This is added later by Treeshake.*()
|
|
47
|
+
}));
|
|
48
|
+
const dep = new dependency_1.$Dependency(this.module, 'message', msgName);
|
|
49
|
+
this._inputMsgs.push(dep);
|
|
50
|
+
return this;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @deprecated
|
|
29
54
|
* Inline messages. These messages are exposed to the module,
|
|
30
55
|
* with a name prefixed by the block name.
|
|
31
56
|
* @param def A method which takes a field factory as input and outputs a template builder
|
|
32
57
|
* @returns The Builder, for call-chaining
|
|
33
58
|
*/
|
|
59
|
+
/** @deprecated Use `.message` instead. Will be removed on 3.1 */
|
|
34
60
|
messages(def) {
|
|
35
61
|
const factory = new message_template_field_builder_1.MessageTemplateFieldFactory(this.module);
|
|
36
62
|
const schema = def(factory);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $Module, $Space, ScopedMessage, ScopedMessageName } from "../../../schema";
|
|
2
2
|
import { $Job, $JobAssert, $JobMethod } from './job.schema';
|
|
3
3
|
import { BlockBuilder } from '../block.builder';
|
|
4
|
-
import { MultiMessageTemplateDef } from "../../entities/message/template/message_template.builder";
|
|
4
|
+
import { MessageTemplateDef, MultiMessageTemplateDef } from "../../entities/message/template/message_template.builder";
|
|
5
5
|
import { Overlay } from "../../../engine/util/type";
|
|
6
6
|
import { $MessageInfer } from "../../entities/message/message.infer";
|
|
7
7
|
import { TrxNode } from "../../../engine/transaction/trx_node";
|
|
@@ -33,6 +33,12 @@ export declare class JobBuilder<Space extends $Space, Module extends $Module, Jo
|
|
|
33
33
|
from: keyof Machine["states"] & string;
|
|
34
34
|
to: keyof Machine["states"] & string;
|
|
35
35
|
}>;
|
|
36
|
+
message<Name extends string, Def extends MessageTemplateDef<Space, Module, Name>, FullName extends string = `${Job['name']}${Name extends '' ? '' : '.'}${Name & string}`, Msg extends $Message = $MessageInfer<FullName, ($: any) => ReturnType<Def>>>(name: Name, def: Def): JobBuilder<Space, Overlay<Module, {
|
|
37
|
+
messages: Overlay<Module["messages"], { [K in FullName]: Msg; }>;
|
|
38
|
+
}>, Overlay<Job, {
|
|
39
|
+
"#input": Msg;
|
|
40
|
+
}>, Ctx>;
|
|
41
|
+
/** @deprecated Use `.message` instead. Will be removed on 3.1 */
|
|
36
42
|
messages<Def extends MultiMessageTemplateDef<Space, Module>>(def: Def): JobBuilder<Space, Overlay<Module, {
|
|
37
43
|
messages: Overlay<Module["messages"], { [K in keyof ReturnType<Def> as `${Job["name"]}${K extends "" ? "" : "."}${K & string}`]: $MessageInfer<`${Job["name"]}${K extends "" ? "" : "."}${K & string}`, ($: any) => ReturnType<Def>[K], ReturnType<Def>[K], import("../../entities/message/message.infer").$MessageInputInfer<ReturnType<Def>[K], ReturnType<Def>[K] extends infer T ? { [K_1 in keyof T]: ReturnType<Def>[K][K_1] extends import("../../entities/message/template/message_template_field.builder").MessageTemplateFieldBuilder<any, any, infer I, any, any, infer Opt extends [undefined, undefined], infer Nul extends [null, null]> ? {
|
|
38
44
|
path: `${K_1 & string}${keyof I & string}`;
|
|
@@ -31,6 +31,10 @@ class JobBuilder extends block_builder_1.BlockBuilder {
|
|
|
31
31
|
return this;
|
|
32
32
|
}
|
|
33
33
|
/* [Job] */
|
|
34
|
+
message(name, def) {
|
|
35
|
+
return super.message(name, def);
|
|
36
|
+
}
|
|
37
|
+
/** @deprecated Use `.message` instead. Will be removed on 3.1 */
|
|
34
38
|
messages(def) {
|
|
35
39
|
return super.messages(def);
|
|
36
40
|
}
|
|
@@ -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,55 +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
|
|
37
|
-
|
|
38
|
-
for (const k in fields) {
|
|
39
|
-
const field = fields[k];
|
|
40
|
-
parsedObj[k] = await parseField(trx, field, obj[k]);
|
|
41
|
-
}
|
|
42
|
-
return parsedObj;
|
|
43
|
-
};
|
|
44
|
-
const parseField = async (trx, field, value) => {
|
|
45
|
-
// 1. Sanitize input
|
|
46
|
-
this.sanitize(value);
|
|
47
|
-
// 2. Check for required fields
|
|
48
|
-
return (0, message_template_parser_1.MessageTemplateFieldParser)(raw, trx, field, value);
|
|
49
|
-
};
|
|
50
|
-
const applyRules = async (fields, parsed, parsedValue = parsed) => {
|
|
51
|
-
for (const f in fields) {
|
|
52
|
-
const field = fields[f];
|
|
53
|
-
for (const r in field.rules) {
|
|
54
|
-
const rule = field.rules[r];
|
|
55
|
-
const value = parsedValue?.[field.name];
|
|
56
|
-
const res = await rule({ field, value, msg: parsed });
|
|
57
|
-
if (typeof res === 'object') {
|
|
58
|
-
parsedValue ?? (parsedValue = {});
|
|
59
|
-
parsedValue[field.name] = res.set;
|
|
60
|
-
}
|
|
61
|
-
else if (res !== true) {
|
|
62
|
-
throw error_1.NesoiError.Message.RuleFailed({ rule, error: res });
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (field.children) {
|
|
66
|
-
await applyRules(field.children, parsed, parsedValue?.[field.name]);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
const parsed = await parseFields(trx, this.schema.template.fields, raw);
|
|
71
|
-
await applyRules(this.schema.template.fields, parsed);
|
|
33
|
+
const fields = this.schema.template.fields;
|
|
34
|
+
const parsed = await (0, message_template_parser_1.MessageTemplateFieldParser)(trx, fields, raw);
|
|
72
35
|
return message_1.Message.new(this.schema.name, parsed, sigKey);
|
|
73
36
|
}
|
|
74
|
-
sanitize(value) {
|
|
75
|
-
if (typeof value === 'function') {
|
|
76
|
-
throw error_1.NesoiError.Message.UnsanitaryValue({
|
|
77
|
-
details: 'Functions not allowed as message inputs.'
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
37
|
}
|
|
82
38
|
exports.MessageParser = MessageParser;
|
|
@@ -3,8 +3,10 @@ import { $BucketModelFieldType } from "../../bucket/model/bucket_model.schema";
|
|
|
3
3
|
import { $Dependency } from "../../../../engine/dependency";
|
|
4
4
|
export type $MessageTemplateRule = (def: {
|
|
5
5
|
field: $MessageTemplateField;
|
|
6
|
+
path: string;
|
|
6
7
|
value: any;
|
|
7
8
|
msg: $Message['#raw'];
|
|
9
|
+
inject: Record<string, any>;
|
|
8
10
|
}) => {
|
|
9
11
|
set: any;
|
|
10
12
|
} | true | string | Promise<{
|
|
@@ -36,23 +38,53 @@ export type $MessageTemplateFieldType = $BucketModelFieldType | 'string_or_numbe
|
|
|
36
38
|
* @subcategory Entity
|
|
37
39
|
* */
|
|
38
40
|
export declare class $MessageTemplateField {
|
|
41
|
+
/** A string representing the type of data carried by the field */
|
|
39
42
|
type: $MessageTemplateFieldType;
|
|
43
|
+
/**
|
|
44
|
+
* - A machine name for the field, unique inside it's parent (either another field or a message).
|
|
45
|
+
* - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
|
|
46
|
+
*/
|
|
40
47
|
name: string;
|
|
48
|
+
/** A human name for the field */
|
|
41
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;
|
|
54
|
+
/** The absolute path for reading the field value on the raw object */
|
|
42
55
|
path_raw: string;
|
|
56
|
+
/** The absolute path for writing the field value on the parsed object */
|
|
43
57
|
path_parsed: string;
|
|
44
58
|
array: boolean;
|
|
45
59
|
required: boolean;
|
|
46
60
|
defaultValue: any;
|
|
47
61
|
nullable: boolean;
|
|
48
62
|
rules: $MessageTemplateRule[];
|
|
63
|
+
arrayRules: $MessageTemplateRule[];
|
|
49
64
|
meta: $MessageTemplateFieldMeta;
|
|
50
65
|
children?: $MessageTemplateFields | undefined;
|
|
51
66
|
or?: $MessageTemplateField | undefined;
|
|
52
67
|
'#raw': unknown;
|
|
53
68
|
'#parsed': unknown;
|
|
54
69
|
$t: string;
|
|
55
|
-
constructor(
|
|
70
|
+
constructor(
|
|
71
|
+
/** A string representing the type of data carried by the field */
|
|
72
|
+
type: $MessageTemplateFieldType,
|
|
73
|
+
/**
|
|
74
|
+
* - A machine name for the field, unique inside it's parent (either another field or a message).
|
|
75
|
+
* - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
|
|
76
|
+
*/
|
|
77
|
+
name: string,
|
|
78
|
+
/** A human name for the field */
|
|
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,
|
|
84
|
+
/** The absolute path for reading the field value on the raw object */
|
|
85
|
+
path_raw: string,
|
|
86
|
+
/** The absolute path for writing the field value on the parsed object */
|
|
87
|
+
path_parsed: string, array: boolean, required: boolean, defaultValue: any, nullable: boolean, rules: $MessageTemplateRule[], arrayRules: $MessageTemplateRule[], meta: $MessageTemplateFieldMeta, children?: $MessageTemplateFields | undefined, or?: $MessageTemplateField | undefined);
|
|
56
88
|
}
|
|
57
89
|
export type $MessageTemplateFields = {
|
|
58
90
|
[x: string]: $MessageTemplateField;
|
|
@@ -6,10 +6,28 @@ exports.$MessageTemplate = exports.$MessageTemplateField = void 0;
|
|
|
6
6
|
* @subcategory Entity
|
|
7
7
|
* */
|
|
8
8
|
class $MessageTemplateField {
|
|
9
|
-
constructor(
|
|
9
|
+
constructor(
|
|
10
|
+
/** A string representing the type of data carried by the field */
|
|
11
|
+
type,
|
|
12
|
+
/**
|
|
13
|
+
* - A machine name for the field, unique inside it's parent (either another field or a message).
|
|
14
|
+
* - Matches the relative path for writing the field value on the parent parsed object (key_parsed).
|
|
15
|
+
*/
|
|
16
|
+
name,
|
|
17
|
+
/** A human name for the field */
|
|
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,
|
|
23
|
+
/** The absolute path for reading the field value on the raw object */
|
|
24
|
+
path_raw,
|
|
25
|
+
/** The absolute path for writing the field value on the parsed object */
|
|
26
|
+
path_parsed, array, required, defaultValue, nullable, rules, arrayRules, meta, children, or) {
|
|
10
27
|
this.type = type;
|
|
11
28
|
this.name = name;
|
|
12
29
|
this.alias = alias;
|
|
30
|
+
this.preAlias = preAlias;
|
|
13
31
|
this.path_raw = path_raw;
|
|
14
32
|
this.path_parsed = path_parsed;
|
|
15
33
|
this.array = array;
|
|
@@ -17,6 +35,7 @@ class $MessageTemplateField {
|
|
|
17
35
|
this.defaultValue = defaultValue;
|
|
18
36
|
this.nullable = nullable;
|
|
19
37
|
this.rules = rules;
|
|
38
|
+
this.arrayRules = arrayRules;
|
|
20
39
|
this.meta = meta;
|
|
21
40
|
this.children = children;
|
|
22
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
|
|
@@ -163,15 +179,15 @@ class MessageTemplateFieldBuilder {
|
|
|
163
179
|
: undefined;
|
|
164
180
|
const pathParsed = basePathParsed + name;
|
|
165
181
|
const pathRaw = basePathParsed + (builder.type === 'id'
|
|
166
|
-
? builder.
|
|
182
|
+
? builder._array ? `${name}_ids` : `${name}_id`
|
|
167
183
|
: name);
|
|
168
|
-
const childrenBasePathRaw = pathRaw + (builder._array ? '
|
|
169
|
-
const childrenBasePathParsed = pathParsed + (builder._array ? '
|
|
184
|
+
const childrenBasePathRaw = pathRaw + (builder._array ? '.#.' : '.');
|
|
185
|
+
const childrenBasePathParsed = pathParsed + (builder._array ? '.#.' : '.');
|
|
170
186
|
if (builder.value.id) {
|
|
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,46 +40,72 @@ async function parseFieldValue(trx, field, path, raw, value) {
|
|
|
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, 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, path, 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, path, raw, value);
|
|
108
|
+
return await parseParentField(trx, field, path, raw, value, inject);
|
|
68
109
|
case 'unknown':
|
|
69
110
|
return value;
|
|
70
111
|
case 'boolean':
|
|
@@ -94,7 +135,13 @@ async function _runParseMethod(trx, field, path, raw, 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
147
|
children = (0, parse_1.parseObj)(field, path, value);
|
|
@@ -102,13 +149,18 @@ async function parseParentField(trx, field, path, raw, value) {
|
|
|
102
149
|
else {
|
|
103
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
|
}
|
|
159
|
+
/**
|
|
160
|
+
* [Parser Step 3-b]: 'id'
|
|
161
|
+
*
|
|
162
|
+
* - Gathers the data for parsing a id
|
|
163
|
+
*/
|
|
112
164
|
async function parseIdField(trx, field, path, value) {
|
|
113
165
|
const bucket = field.meta.id.bucket;
|
|
114
166
|
const type = field.meta.id.type;
|
|
@@ -121,6 +173,13 @@ async function parseIdField(trx, field, path, 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
|
+
}
|