ballerina-core 1.0.49 → 1.0.51
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/package.json +1 -1
- package/src/collections/domains/valueOrErrors/state.ts +24 -23
- package/src/forms/domains/launcher/coroutines/runner.ts +4 -3
- package/src/forms/domains/launcher/domains/edit/coroutines/runner.ts +21 -9
- package/src/forms/domains/launcher/domains/edit/state.ts +3 -1
- package/src/forms/domains/parser/coroutines/runner.ts +3 -3
- package/src/forms/domains/parser/domains/built-ins/state.ts +46 -64
- package/src/forms/domains/parser/domains/validator/state.ts +142 -127
- package/src/forms/domains/parser/state.tsx +14 -23
- package/src/forms/domains/primitives/domains/list/template.tsx +17 -17
package/package.json
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
import { List } from "immutable";
|
|
2
2
|
import { BasicFun, BasicFun2, Fun } from "../../../fun/state";
|
|
3
3
|
import { Value } from "../../../value/state";
|
|
4
|
+
import { Updater } from "../../../../main";
|
|
4
5
|
|
|
5
6
|
export type ValueOrErrors<v, e> = (
|
|
6
7
|
| (Value<v> & { kind: "value" })
|
|
7
8
|
| { errors: List<e>; kind: "errors" }
|
|
8
9
|
) & {
|
|
9
|
-
|
|
10
|
+
Map: <a, b, e>(
|
|
10
11
|
this: ValueOrErrors<a, e>,
|
|
11
12
|
f: BasicFun<a, b>
|
|
12
13
|
) => ValueOrErrors<b, e>;
|
|
13
|
-
|
|
14
|
+
MapErrors: <a, e, e2>(
|
|
14
15
|
this: ValueOrErrors<a, e>,
|
|
15
16
|
f: BasicFun<List<e>, List<e2>>
|
|
16
17
|
) => ValueOrErrors<a, e2>;
|
|
17
|
-
|
|
18
|
+
Flatten: <a, e>(
|
|
18
19
|
this: ValueOrErrors<ValueOrErrors<a, e>, e>
|
|
19
20
|
) => ValueOrErrors<a, e>;
|
|
20
|
-
|
|
21
|
+
Then: <a, b, e>(
|
|
21
22
|
this: ValueOrErrors<a, e>,
|
|
22
23
|
k: BasicFun<a, ValueOrErrors<b, e>>
|
|
23
24
|
) => ValueOrErrors<b, e>;
|
|
24
25
|
};
|
|
25
26
|
|
|
26
27
|
const operations = {
|
|
27
|
-
|
|
28
|
+
Map: function <a, b, e>(
|
|
28
29
|
this: ValueOrErrors<a, e>,
|
|
29
30
|
f: BasicFun<a, b>
|
|
30
31
|
): ValueOrErrors<b, e> {
|
|
@@ -33,7 +34,7 @@ const operations = {
|
|
|
33
34
|
}
|
|
34
35
|
return ValueOrErrors.Default.return(f(this.value));
|
|
35
36
|
},
|
|
36
|
-
|
|
37
|
+
MapErrors: function <a, e, e2>(
|
|
37
38
|
this: ValueOrErrors<a, e>,
|
|
38
39
|
f: BasicFun<List<e>, List<e2>>
|
|
39
40
|
): ValueOrErrors<a, e2> {
|
|
@@ -42,7 +43,7 @@ const operations = {
|
|
|
42
43
|
}
|
|
43
44
|
return this;
|
|
44
45
|
},
|
|
45
|
-
|
|
46
|
+
Flatten: function <a, e>(
|
|
46
47
|
this: ValueOrErrors<ValueOrErrors<a, e>, e>
|
|
47
48
|
): ValueOrErrors<a, e> {
|
|
48
49
|
if (this.kind == "errors") {
|
|
@@ -53,11 +54,11 @@ const operations = {
|
|
|
53
54
|
return this.value;
|
|
54
55
|
}
|
|
55
56
|
},
|
|
56
|
-
|
|
57
|
+
Then: function <a, b, e>(
|
|
57
58
|
this: ValueOrErrors<a, e>,
|
|
58
59
|
k: BasicFun<a, ValueOrErrors<b, e>>
|
|
59
60
|
): ValueOrErrors<b, e> {
|
|
60
|
-
return this.
|
|
61
|
+
return this.Map(k).Flatten();
|
|
61
62
|
},
|
|
62
63
|
};
|
|
63
64
|
|
|
@@ -75,23 +76,23 @@ export const ValueOrErrors = {
|
|
|
75
76
|
}),
|
|
76
77
|
},
|
|
77
78
|
Operations: {
|
|
78
|
-
|
|
79
|
+
Return: <v, e>(_: v): ValueOrErrors<v, e> =>
|
|
79
80
|
ValueOrErrors.Default.return(_),
|
|
80
|
-
|
|
81
|
-
ValueOrErrors.Default.throw(
|
|
82
|
-
|
|
81
|
+
Throw: <v, e>(_: List<e>): ValueOrErrors<v, e> =>
|
|
82
|
+
ValueOrErrors.Default.throw(_),
|
|
83
|
+
Map: <a, b, e>(
|
|
83
84
|
f: BasicFun<a, b>
|
|
84
85
|
): Fun<ValueOrErrors<a, e>, ValueOrErrors<b, e>> =>
|
|
85
86
|
Fun((_) =>
|
|
86
87
|
_.kind == "errors" ? _ : ValueOrErrors.Default.return(f(_.value))
|
|
87
88
|
),
|
|
88
|
-
|
|
89
|
+
MapErrors: <a, e, e2>(
|
|
89
90
|
f: BasicFun<List<e>, List<e2>>
|
|
90
91
|
): BasicFun<ValueOrErrors<a, e>, ValueOrErrors<a, e2>> =>
|
|
91
92
|
Fun((_) =>
|
|
92
93
|
_.kind == "errors" ? ValueOrErrors.Default.throw(f(_.errors)) : _
|
|
93
94
|
),
|
|
94
|
-
|
|
95
|
+
Flatten: <a, e>(): Fun<
|
|
95
96
|
ValueOrErrors<ValueOrErrors<a, e>, e>,
|
|
96
97
|
ValueOrErrors<a, e>
|
|
97
98
|
> =>
|
|
@@ -104,30 +105,30 @@ export const ValueOrErrors = {
|
|
|
104
105
|
return _.value;
|
|
105
106
|
}
|
|
106
107
|
}),
|
|
107
|
-
|
|
108
|
+
Then: <a, b, e>(
|
|
108
109
|
k: BasicFun<a, ValueOrErrors<b, e>>
|
|
109
110
|
): Fun<ValueOrErrors<a, e>, ValueOrErrors<b, e>> =>
|
|
110
111
|
Fun((_: ValueOrErrors<a, e>) =>
|
|
111
|
-
ValueOrErrors.Operations.
|
|
112
|
-
ValueOrErrors.Operations.
|
|
112
|
+
ValueOrErrors.Operations.Flatten<b, e>()(
|
|
113
|
+
ValueOrErrors.Operations.Map<a, ValueOrErrors<b, e>, e>(k)(_)
|
|
113
114
|
)
|
|
114
115
|
),
|
|
115
|
-
|
|
116
|
+
Fold: <v, e, c>(
|
|
116
117
|
l: BasicFun<v, c>,
|
|
117
118
|
r: BasicFun<List<e>, c>
|
|
118
119
|
): Fun<ValueOrErrors<v, e>, c> =>
|
|
119
120
|
Fun((_) => (_.kind == "value" ? l(_.value) : r(_.errors))),
|
|
120
|
-
|
|
121
|
+
All: <v, e>(_: List<ValueOrErrors<v, e>>): ValueOrErrors<List<v>, e> =>
|
|
121
122
|
_.reduce(
|
|
122
123
|
(reduction, value) =>
|
|
123
|
-
ValueOrErrors.Operations.
|
|
124
|
+
ValueOrErrors.Operations.Fold<v, e, ValueOrErrors<List<v>, e>>(
|
|
124
125
|
(v: v) =>
|
|
125
126
|
reduction.kind == "errors"
|
|
126
127
|
? reduction
|
|
127
|
-
: reduction.
|
|
128
|
+
: reduction.Map((_) => _.concat(v)),
|
|
128
129
|
(es: List<e>) =>
|
|
129
130
|
reduction.kind == "errors"
|
|
130
|
-
? reduction.
|
|
131
|
+
? reduction.MapErrors((_) => _.concat(es))
|
|
131
132
|
: ValueOrErrors.Default.throw(es)
|
|
132
133
|
)(value),
|
|
133
134
|
ValueOrErrors.Default.return(List<v>())
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { List } from "immutable"
|
|
1
2
|
import { FormRunnerErrorsTemplate, id, Mapping, replaceWith, Sum, unit } from "../../../../../main"
|
|
2
3
|
import { AsyncState } from "../../../../async/state"
|
|
3
4
|
import { CoTypedFactory } from "../../../../coroutines/builder"
|
|
@@ -27,7 +28,7 @@ export const FormRunnerLoader = () => {
|
|
|
27
28
|
return FormRunnerState.Updaters.form(
|
|
28
29
|
replaceWith(
|
|
29
30
|
Sum.Default.left(
|
|
30
|
-
FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
|
|
31
|
+
FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
|
|
31
32
|
)
|
|
32
33
|
)
|
|
33
34
|
)
|
|
@@ -47,7 +48,7 @@ export const FormRunnerLoader = () => {
|
|
|
47
48
|
return FormRunnerState.Updaters.form(
|
|
48
49
|
replaceWith(
|
|
49
50
|
Sum.Default.left(
|
|
50
|
-
FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
|
|
51
|
+
FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
|
|
51
52
|
)
|
|
52
53
|
)
|
|
53
54
|
)
|
|
@@ -67,7 +68,7 @@ export const FormRunnerLoader = () => {
|
|
|
67
68
|
return FormRunnerState.Updaters.form(
|
|
68
69
|
replaceWith(
|
|
69
70
|
Sum.Default.left(
|
|
70
|
-
FormRunnerErrorsTemplate(Sum.Default.right([`Cannot find form '${formRef.formName}'`]))
|
|
71
|
+
FormRunnerErrorsTemplate(Sum.Default.right(List([`Cannot find form '${formRef.formName}'`])))
|
|
71
72
|
)
|
|
72
73
|
)
|
|
73
74
|
)
|
|
@@ -37,15 +37,27 @@ export const editFormRunner = <E, FS>() => {
|
|
|
37
37
|
EditFormState<E, FS>().Updaters.Template.toUnchecked()
|
|
38
38
|
),
|
|
39
39
|
Debounce<Synchronized<Unit, ApiErrors>, EditFormWritableState<E, FS>>(
|
|
40
|
-
|
|
41
|
-
(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
(() => {
|
|
41
|
+
if(current.entity.sync.kind != "loaded") return Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
|
|
42
|
+
(_) => Promise.resolve([]),
|
|
43
|
+
(_) => "transient failure",
|
|
44
|
+
5,
|
|
45
|
+
50
|
|
46
|
+
)
|
|
47
|
+
const parsed = current.parser(current.entity.sync.value, current.formState)
|
|
48
|
+
|
|
49
|
+
return Synchronize<Unit, ApiErrors, EditFormWritableState<E, FS>>(
|
|
50
|
+
(_) =>
|
|
51
|
+
{
|
|
52
|
+
if(parsed.kind == "errors") return Promise.reject(parsed.errors)
|
|
53
|
+
return current.api.update(current.entityId, parsed)
|
|
54
|
+
},
|
|
55
|
+
(_) => "transient failure",
|
|
56
|
+
parsed.kind == "errors" ? 1 : 5,
|
|
57
|
+
50
|
|
58
|
+
)
|
|
59
|
+
})(),
|
|
60
|
+
|
|
49
61
|
15
|
|
50
62
|
).embed(
|
|
51
63
|
(_) => ({ ..._, ..._.apiRunner }),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApiResponseChecker, AsyncState, BasicUpdater, Debounced, ForeignMutationsInput, Guid, id, SimpleCallback, simpleUpdater, Synchronized, Template, unit, Unit, Updater, Value } from "../../../../../../main"
|
|
2
|
+
import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state"
|
|
2
3
|
import { BasicFun } from "../../../../../fun/state"
|
|
3
4
|
|
|
4
5
|
export type ApiErrors = Array<string>
|
|
@@ -7,8 +8,9 @@ export type EditFormContext<E,FS> = {
|
|
|
7
8
|
entityId:string,
|
|
8
9
|
api:{
|
|
9
10
|
get:(id: Guid) => Promise<E>,
|
|
10
|
-
update:(id: Guid,
|
|
11
|
+
update:(id: Guid, raw: any) => Promise<ApiErrors>
|
|
11
12
|
},
|
|
13
|
+
parser: (entity:E, formstate: FS) => ValueOrErrors<E, ApiErrors>
|
|
12
14
|
actualForm:Template<Value<E> & FS, FS, { onChange:SimpleCallback<BasicUpdater<E>>}>
|
|
13
15
|
}
|
|
14
16
|
|
|
@@ -12,9 +12,9 @@ export const LoadValidateAndParseFormsConfig = <T extends {[key in keyof T] : {t
|
|
|
12
12
|
const formsConfig = replaceKeywords(rawFormsConfig, "from api")
|
|
13
13
|
const builtIns = builtInsFromFieldViews(current.fieldViews)
|
|
14
14
|
const injectedPrimitives = current.injectedPrimitives ? injectablesFromFieldViews(current.fieldViews, current.injectedPrimitives) : undefined
|
|
15
|
-
const validationResult = FormsConfig.Default.
|
|
16
|
-
if (validationResult.kind == "
|
|
17
|
-
return Sum.Default.right(validationResult.
|
|
15
|
+
const validationResult = FormsConfig.Default.validateAndParseFormConfig(builtIns, current.fieldTypeConverters, injectedPrimitives)(formsConfig)
|
|
16
|
+
if (validationResult.kind == "errors")
|
|
17
|
+
return Sum.Default.right(validationResult.errors)
|
|
18
18
|
return parseForms(
|
|
19
19
|
builtIns,
|
|
20
20
|
injectedPrimitives,
|
|
@@ -2,7 +2,7 @@ import { Map, List, Set, OrderedMap } from "immutable"
|
|
|
2
2
|
import { CollectionReference } from "../../../collection/domains/reference/state";
|
|
3
3
|
import { CollectionSelection } from "../../../collection/domains/selection/state";
|
|
4
4
|
import { BasicFun } from "../../../../../fun/state";
|
|
5
|
-
import { InjectedPrimitives, Maybe, replaceKeyword, replaceKeywords, revertKeyword, Type, TypeDefinition, TypeName, Unit, Value } from "../../../../../../main";
|
|
5
|
+
import { InjectedPrimitives, Maybe, replaceKeyword, replaceKeywords, revertKeyword, Sum, Type, TypeDefinition, TypeName, Unit, Value } from "../../../../../../main";
|
|
6
6
|
import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state";
|
|
7
7
|
|
|
8
8
|
export const PrimitiveTypes =
|
|
@@ -152,15 +152,6 @@ export const defaultValue = <T>(types: Map<TypeName, TypeDefinition>, builtIns:
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
|
|
156
|
-
const parseTypeIShouldBePartOfFormValidation = (t:any) : TypeName | Type => {
|
|
157
|
-
if (typeof t == "string") return t
|
|
158
|
-
if ("fun" in t && "args" in t && Array.isArray(t.args)) {
|
|
159
|
-
return { kind:"application", value:t.fun, args:t.args }
|
|
160
|
-
}
|
|
161
|
-
return null!
|
|
162
|
-
}
|
|
163
|
-
|
|
164
155
|
export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReplaced: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any): any => {
|
|
165
156
|
// alert(JSON.stringify(t))
|
|
166
157
|
if (raw == undefined) {
|
|
@@ -196,25 +187,25 @@ export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>
|
|
|
196
187
|
}
|
|
197
188
|
if (t.value == "Map" && t.args.length == 2) {
|
|
198
189
|
let result = converters[t.value].fromAPIRawValue(obj)
|
|
199
|
-
|
|
200
|
-
const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
201
|
-
const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
|
|
190
|
+
|
|
191
|
+
const isKeyPrimitive = typeof t.args[0] == "string" && PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
192
|
+
const isValuePrimitive = typeof t.args[1] == "string" && PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
|
|
202
193
|
result = result.map(keyValue => ([
|
|
203
194
|
fromAPIRawValue(
|
|
204
|
-
typeof
|
|
195
|
+
typeof t.args[0] == "string" ?
|
|
205
196
|
isKeyPrimitive ?
|
|
206
|
-
{ kind: "primitive", value:
|
|
207
|
-
: { kind: "lookup", name:
|
|
197
|
+
{ kind: "primitive", value: t.args[0] as PrimitiveType }
|
|
198
|
+
: { kind: "lookup", name: t.args[0] }
|
|
208
199
|
:
|
|
209
|
-
|
|
200
|
+
t.args[0],
|
|
210
201
|
types, builtIns, converters, true, injectedPrimitives)(keyValue[0]),
|
|
211
202
|
fromAPIRawValue(
|
|
212
|
-
typeof
|
|
203
|
+
typeof t.args[1] == "string" ?
|
|
213
204
|
isValuePrimitive ?
|
|
214
|
-
{ kind: "primitive", value:
|
|
215
|
-
: { kind: "lookup", name:
|
|
205
|
+
{ kind: "primitive", value: t.args[1] as PrimitiveType }
|
|
206
|
+
: { kind: "lookup", name: t.args[1] }
|
|
216
207
|
:
|
|
217
|
-
|
|
208
|
+
t.args[1],
|
|
218
209
|
types, builtIns, converters, true, injectedPrimitives)(keyValue[1]),
|
|
219
210
|
])
|
|
220
211
|
)
|
|
@@ -237,24 +228,24 @@ export const fromAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>
|
|
|
237
228
|
export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>, builtIns: BuiltIns, converters: BuiltInApiConverters, isKeywordsReverted: boolean = false, injectedPrimitives?: InjectedPrimitives<T>) => (raw: any, formState: any) : ValueOrErrors<any, string> => {
|
|
238
229
|
const obj = !isKeywordsReverted ? replaceKeywords(raw, "to api") : raw
|
|
239
230
|
if (t.kind == "primitive") {
|
|
240
|
-
return ValueOrErrors.Operations.
|
|
231
|
+
return ValueOrErrors.Operations.Return(converters[t.value].toAPIRawValue([obj, formState.modifiedByUser] as never))
|
|
241
232
|
} else if (t.kind == "application") { // application here means "generic type application"
|
|
242
233
|
if (t.value == "SingleSelection" && t.args.length == 1) {
|
|
243
234
|
const result = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
244
|
-
if(typeof result != "object") return ValueOrErrors.Operations.
|
|
235
|
+
if(typeof result != "object") return ValueOrErrors.Operations.Return(result)
|
|
245
236
|
|
|
246
237
|
return toAPIRawValue({ kind:"lookup", name:t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(result, formState)
|
|
247
238
|
}
|
|
248
239
|
if ((t.value == "Multiselection" || t.value == "MultiSelection") && t.args.length == 1) {
|
|
249
240
|
const result = converters["MultiSelection"].toAPIRawValue([obj, formState.modifiedByUser])
|
|
250
241
|
|
|
251
|
-
return ValueOrErrors.Operations.
|
|
252
|
-
typeof _ == "object" ? toAPIRawValue({ kind:"lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(_, formState) : ValueOrErrors.Operations.
|
|
242
|
+
return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(result.map((_:any) =>
|
|
243
|
+
typeof _ == "object" ? toAPIRawValue({ kind:"lookup", name: t.args[0] }, types, builtIns, converters, true, injectedPrimitives)(_, formState) : ValueOrErrors.Operations.Return(_))))
|
|
253
244
|
}
|
|
254
245
|
if (t.value == "List" && t.args.length == 1) {
|
|
255
246
|
const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
256
247
|
const isPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
257
|
-
return ValueOrErrors.Operations.
|
|
248
|
+
return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(converterResult.map((item: any, index: number) =>
|
|
258
249
|
toAPIRawValue(
|
|
259
250
|
isPrimitive ?
|
|
260
251
|
{ kind:"primitive", value:t.args[0] as PrimitiveType }
|
|
@@ -264,62 +255,53 @@ export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>,
|
|
|
264
255
|
))))
|
|
265
256
|
}
|
|
266
257
|
if (t.value == "Map" && t.args.length == 2) {
|
|
267
|
-
const converterResult = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
258
|
+
const [converterResult, toIdentiferAndDisplayName] = converters[t.value].toAPIRawValue([obj, formState.modifiedByUser])
|
|
268
259
|
const isKeyPrimitive = PrimitiveTypes.some(_ => _ == t.args[0]) || injectedPrimitives?.injectedPrimitives.has(t.args[0] as keyof T)
|
|
269
260
|
const isValuePrimitive = PrimitiveTypes.some(_ => _ == t.args[1]) || injectedPrimitives?.injectedPrimitives.has(t.args[1] as keyof T)
|
|
270
|
-
let t_args = t.args.map(parseTypeIShouldBePartOfFormValidation)
|
|
271
261
|
|
|
272
|
-
const parsedMap: ValueOrErrors<{key: ValueOrErrors<any, any>, value: ValueOrErrors<any, any>}, any
|
|
273
|
-
const
|
|
274
|
-
typeof
|
|
262
|
+
const parsedMap: List<ValueOrErrors<{key: ValueOrErrors<any, any>, value: ValueOrErrors<any, any>}, any>> = converterResult.map((keyValue: any, index: number) => {
|
|
263
|
+
const possiblyUndefinedKey = toAPIRawValue(
|
|
264
|
+
typeof t.args[0] == "string" ?
|
|
275
265
|
isKeyPrimitive ?
|
|
276
|
-
{ kind: "primitive", value:
|
|
277
|
-
: { kind: "lookup", name:
|
|
266
|
+
{ kind: "primitive", value: t.args[0] as PrimitiveType }
|
|
267
|
+
: { kind: "lookup", name: t.args[0] }
|
|
278
268
|
:
|
|
279
|
-
|
|
269
|
+
t.args[0],
|
|
280
270
|
types, builtIns, converters, true, injectedPrimitives)(keyValue[0], formState.elementFormStates.get(index).KeyFormState
|
|
281
271
|
)
|
|
282
272
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
273
|
+
const key: ValueOrErrors<any, string> = (() => {
|
|
274
|
+
if(possiblyUndefinedKey.kind == "value" && (possiblyUndefinedKey.value == undefined || possiblyUndefinedKey.value == null || possiblyUndefinedKey.value == "" || (typeof possiblyUndefinedKey.value == "object" && Object.keys(possiblyUndefinedKey.value).length == 0))) {
|
|
275
|
+
return ValueOrErrors.Operations.Throw(List([`A mapped key is undefined for type ${JSON.stringify(t.args[0])}`]))
|
|
276
|
+
}
|
|
277
|
+
return possiblyUndefinedKey
|
|
278
|
+
})()
|
|
288
279
|
|
|
289
280
|
const value = toAPIRawValue(
|
|
290
|
-
typeof
|
|
281
|
+
typeof t.args[1] == "string" ?
|
|
291
282
|
isValuePrimitive ?
|
|
292
|
-
{ kind: "primitive", value:
|
|
293
|
-
: { kind: "lookup", name:
|
|
283
|
+
{ kind: "primitive", value: t.args[1] as PrimitiveType }
|
|
284
|
+
: { kind: "lookup", name: t.args[1] }
|
|
294
285
|
:
|
|
295
|
-
|
|
286
|
+
t.args[1],
|
|
296
287
|
types, builtIns, converters, true, injectedPrimitives)(keyValue[1], formState.elementFormStates.get(index).ValueFormState)
|
|
297
288
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
return ValueOrErrors.Operations.return({key, value})
|
|
289
|
+
return key.kind == "errors" || value.kind == "errors" ? ValueOrErrors.Operations.All(List([key, value])) : ValueOrErrors.Default.return({key: key.value, value: value.value})
|
|
301
290
|
}
|
|
302
291
|
)
|
|
303
|
-
|
|
304
|
-
if(parsedMap.length > 0 && parsedMap.some((_: ValueOrErrors<any, any>) => _.kind == "errors")) {
|
|
305
|
-
return ValueOrErrors.Operations.all(List(parsedMap))
|
|
306
|
-
}
|
|
307
|
-
// TODO this needs improvement
|
|
308
|
-
const allKeysStringified = parsedMap.map((_) => _.kind == "value" ? JSON.stringify((_.value.key as any).value) : "")
|
|
309
|
-
const allKeysUnique = Set(allKeysStringified).size == allKeysStringified.length
|
|
310
|
-
|
|
311
|
-
if(allKeysStringified.length > 0 && !allKeysUnique) {
|
|
312
|
-
return ValueOrErrors.Operations.throw(`Keys in the map are not unique: ${JSON.stringify(allKeysStringified)}`)
|
|
313
|
-
}
|
|
314
292
|
|
|
315
|
-
|
|
316
|
-
|
|
293
|
+
const nonUniqueKeyErrors = parsedMap.filter(_ => _.kind == "value").reduce((acc, _) => {
|
|
294
|
+
const [id, displayName] = toIdentiferAndDisplayName(_.value.key)
|
|
295
|
+
acc.ids.contains(id) ? acc.errors = acc.errors.push(ValueOrErrors.Default.throw(List([`Keys in the map are not unique: ${displayName}`]))) : acc.ids = acc.ids.push(id)
|
|
296
|
+
return acc
|
|
297
|
+
}, {ids: List<string>(), errors: List<ValueOrErrors<any, string>>()}).errors
|
|
317
298
|
|
|
299
|
+
return ValueOrErrors.Operations.All(parsedMap.concat(nonUniqueKeyErrors))
|
|
318
300
|
}
|
|
319
301
|
} else { // t.kind == lookup: we are dealing with a record/object or extended type
|
|
320
302
|
const tDef = types.get(t.name)!
|
|
321
303
|
if("extends" in tDef && tDef.extends.length == 1) {
|
|
322
|
-
return ValueOrErrors.Operations.
|
|
304
|
+
return ValueOrErrors.Operations.Return(converters[(tDef.extends[0] as keyof BuiltInApiConverters)].toAPIRawValue([obj, formState.modifiedByUser] as never))
|
|
323
305
|
}
|
|
324
306
|
const convertedMap = tDef.fields.mapEntries(([fieldName, fieldType] ) => {
|
|
325
307
|
const revertedFieldName = revertKeyword(fieldName)
|
|
@@ -329,10 +311,10 @@ export const toAPIRawValue = <T>(t: Type, types: Map<TypeName, TypeDefinition>,
|
|
|
329
311
|
})
|
|
330
312
|
if(convertedMap.some((valueOrError) => valueOrError.kind == "errors")) {
|
|
331
313
|
const propertiesWithErrors = convertedMap.filter((valueOrError) => valueOrError.kind == "errors")
|
|
332
|
-
const namedErrors = propertiesWithErrors.map((value, key) => value.
|
|
333
|
-
return ValueOrErrors.Operations.
|
|
314
|
+
const namedErrors = propertiesWithErrors.map((value, key) => value.MapErrors(_ => _.map((_: string) => `${key}: ${_}`)))
|
|
315
|
+
return ValueOrErrors.Operations.All(List<ValueOrErrors<any, string>>(namedErrors.valueSeq().toList()))
|
|
334
316
|
}
|
|
335
|
-
return ValueOrErrors.Operations.
|
|
317
|
+
return ValueOrErrors.Operations.Return(convertedMap.map(valueOrError => valueOrError.kind == "value" ? valueOrError.value : valueOrError.errors).toJS())
|
|
336
318
|
}
|
|
337
|
-
return ValueOrErrors.Operations.
|
|
319
|
+
return ValueOrErrors.Operations.Return(defaultValue(types, builtIns, injectedPrimitives)(t.value))
|
|
338
320
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Set, Map, OrderedMap } from "immutable";
|
|
2
|
-
import { ApiConverters, BoolExpr, BuiltIns, FieldName, FormsConfigMerger, InjectedPrimitives, MappingPaths, revertKeyword, Sum, Type, TypeDefinition, TypeName } from "../../../../../../main";
|
|
1
|
+
import { Set, Map, OrderedMap, List } from "immutable";
|
|
2
|
+
import { ApiConverters, BoolExpr, BuiltIns, FieldName, FormsConfigMerger, InjectedPrimitives, MappingPaths, revertKeyword, Sum, Type, TypeDefinition, TypeName, unit, Value } from "../../../../../../main";
|
|
3
|
+
import { ValueOrErrors } from "../../../../../collections/domains/valueOrErrors/state";
|
|
3
4
|
|
|
4
5
|
export type FieldConfig = {
|
|
5
6
|
renderer: string;
|
|
@@ -67,23 +68,46 @@ export type FormsConfig = {
|
|
|
67
68
|
};
|
|
68
69
|
export type FormValidationError = string;
|
|
69
70
|
|
|
70
|
-
|
|
71
|
+
|
|
72
|
+
export type FormConfigValidationAndParseResult = ValueOrErrors<FormsConfig, FormValidationError>
|
|
71
73
|
export const FormsConfig = {
|
|
72
74
|
Default: {
|
|
73
|
-
|
|
74
|
-
let errors:
|
|
75
|
+
validateAndParseFormConfig: <T extends {[key in keyof T]: {type: any, state: any}}>(builtIns: BuiltIns, apiConverters: ApiConverters<T>, injectedPrimitives?: InjectedPrimitives<T>) => (fc: any): FormConfigValidationAndParseResult => {
|
|
76
|
+
let errors: List<FormValidationError> = List();
|
|
75
77
|
const formsConfig = Array.isArray(fc) ? FormsConfigMerger.Default.merge(fc) : fc;
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
78
|
+
|
|
79
|
+
const hasApis = "apis" in formsConfig;
|
|
80
|
+
const apiProps = List(["enumOptions", "searchableStreams", "entities"]);
|
|
81
|
+
const formPropertyChecks = List<[string, boolean]>(
|
|
82
|
+
[
|
|
83
|
+
['types', "types" in formsConfig],
|
|
84
|
+
['forms', "forms" in formsConfig],
|
|
85
|
+
['apis', hasApis],
|
|
86
|
+
['enumOptions', hasApis && "enumOptions" in formsConfig.apis],
|
|
87
|
+
['searchableStreams', hasApis && "searchableStreams" in formsConfig.apis],
|
|
88
|
+
['entities', hasApis && "entities" in formsConfig.apis],
|
|
89
|
+
['mappings', "mappings" in formsConfig],
|
|
90
|
+
['launchers', "launchers" in formsConfig]
|
|
91
|
+
]
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
if(formPropertyChecks.some(([_, hasProp]) => !hasProp)){
|
|
95
|
+
const formPropertyErrors = formPropertyChecks.filter(([_, hasProp]) => !hasProp)
|
|
96
|
+
.map(([prop, _]) => apiProps.includes(prop) ?
|
|
97
|
+
`the formsConfig.apis does not contain a '${prop}' field` :
|
|
98
|
+
`the formsConfig does not contain a '${prop}' field`);
|
|
99
|
+
return ValueOrErrors.Default.throw(formPropertyErrors);
|
|
80
100
|
}
|
|
101
|
+
|
|
81
102
|
if(injectedPrimitives){
|
|
82
103
|
injectedPrimitives?.injectedPrimitives.keySeq().toArray().some((injectedPrimitiveName) => {
|
|
83
104
|
if(!Object.keys(apiConverters).includes(injectedPrimitiveName as string)){
|
|
84
|
-
errors.push(`the formsConfig does not contain an Api Converter for injected primitive: ${injectedPrimitiveName as string}`);
|
|
105
|
+
errors = errors.push(`the formsConfig does not contain an Api Converter for injected primitive: ${injectedPrimitiveName as string}`);
|
|
106
|
+
|
|
85
107
|
}})
|
|
86
108
|
}
|
|
109
|
+
|
|
110
|
+
let types: Map<TypeName, TypeDefinition> = Map();
|
|
87
111
|
Object.keys(formsConfig["types"]).forEach((typeName: any) => {
|
|
88
112
|
let typeDef: TypeDefinition = { name: typeName, extends: [], fields: OrderedMap() };
|
|
89
113
|
types = types.set(typeName, typeDef);
|
|
@@ -93,17 +117,17 @@ export const FormsConfig = {
|
|
|
93
117
|
typeDef.extends.push(...configTypeDef["extends"]);
|
|
94
118
|
|
|
95
119
|
else
|
|
96
|
-
errors.push(`invalid 'extends' clause in type ${typeName}: expected string[]`);
|
|
120
|
+
errors = errors.push(`invalid 'extends' clause in type ${typeName}: expected string[]`);
|
|
97
121
|
}
|
|
98
122
|
if ("fields" in configTypeDef == false)
|
|
99
|
-
errors.push(`missing 'fields' in type ${typeName}: expected object`);
|
|
123
|
+
errors = errors.push(`missing 'fields' in type ${typeName}: expected object`);
|
|
100
124
|
|
|
101
125
|
Object.keys(configTypeDef["fields"]).forEach((fieldName: any) => {
|
|
102
126
|
let configFieldType = configTypeDef["fields"][fieldName];
|
|
103
127
|
if (typeof configFieldType == "string") {
|
|
104
128
|
if (injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T) &&
|
|
105
129
|
(builtIns.primitives.has(configFieldType) || builtIns.generics.has(configFieldType))) {
|
|
106
|
-
errors.push(`field ${fieldName} in type ${typeName}: injectedPrimitive cannot have same name as builtIn primitive`);
|
|
130
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: injectedPrimitive cannot have same name as builtIn primitive`);
|
|
107
131
|
} else {
|
|
108
132
|
if (builtIns.primitives.has(configFieldType) || injectedPrimitives?.injectedPrimitives.has(configFieldType as keyof T))
|
|
109
133
|
typeDef.fields = typeDef.fields.set(fieldName, { kind: "primitive", value: configFieldType as any });
|
|
@@ -114,97 +138,93 @@ export const FormsConfig = {
|
|
|
114
138
|
if ("fun" in configFieldType && "args" in configFieldType &&
|
|
115
139
|
typeof configFieldType["fun"] == "string" &&
|
|
116
140
|
Array.isArray(configFieldType["args"])
|
|
117
|
-
// &&
|
|
118
|
-
// configFieldType["args"].every(_ => typeof (_) == "string")
|
|
119
141
|
) {
|
|
142
|
+
const args = configFieldType["fun"] == "Map" ?
|
|
143
|
+
configFieldType["args"].map((arg:any) => (typeof arg == "string" ? arg : { kind:"application", value: arg.fun, args: arg.args })) as any :
|
|
144
|
+
configFieldType["args"] as any;
|
|
120
145
|
const fieldType: Type = {
|
|
121
146
|
kind: "application",
|
|
122
147
|
value: configFieldType["fun"] as any,
|
|
123
|
-
args
|
|
148
|
+
args,
|
|
124
149
|
}
|
|
125
150
|
typeDef.fields = typeDef.fields.set(fieldName, fieldType);
|
|
126
151
|
}
|
|
127
152
|
else
|
|
128
|
-
errors.push(`field ${fieldName} in type ${typeName}: expected application, found ${JSON.stringify(configFieldType)}`);
|
|
153
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: expected application, found ${JSON.stringify(configFieldType)}`);
|
|
129
154
|
}
|
|
130
155
|
});
|
|
131
156
|
});
|
|
157
|
+
|
|
132
158
|
types.forEach((typeDef, typeName) => {
|
|
133
159
|
typeDef.extends.forEach(extendedTypeName => {
|
|
134
160
|
if ((!builtIns.primitives.has(extendedTypeName) && !injectedPrimitives?.injectedPrimitives.has(extendedTypeName as keyof T)) && !types.has(extendedTypeName))
|
|
135
|
-
errors.push(`type ${typeName} extends non-existent type ${extendedTypeName}`);
|
|
161
|
+
errors = errors.push(`type ${typeName} extends non-existent type ${extendedTypeName}`);
|
|
136
162
|
});
|
|
137
163
|
typeDef.fields.forEach((fieldDef, fieldName) => {
|
|
138
164
|
if (fieldDef.kind == "primitive" && (!builtIns.primitives.has(fieldDef.value) && !injectedPrimitives?.injectedPrimitives.has(fieldDef.value as keyof T) ))
|
|
139
|
-
errors.push(`field ${fieldName} of type ${typeName} is non-existent primitive type ${fieldDef.value}`);
|
|
165
|
+
errors = errors.push(`field ${fieldName} of type ${typeName} is non-existent primitive type ${fieldDef.value}`);
|
|
140
166
|
if (fieldDef.kind == "lookup" && !types.has(fieldDef.name))
|
|
141
|
-
errors.push(`field ${fieldName} of type ${typeName} is non-existent type ${fieldDef.name}`);
|
|
142
|
-
// if (fieldDef.kind == "application" && !builtIns.generics.has(fieldDef.value))
|
|
143
|
-
// errors.push(`field ${fieldName} of type ${typeName} applies non-existent generic type ${fieldDef.value}`);
|
|
144
|
-
// if (fieldDef.kind == "application" && fieldDef.args.some(argType => !builtIns.primitives.has(argType) && !types.has(argType)))
|
|
145
|
-
// errors.push(`field ${fieldName} of type ${typeName} applies non-existent type arguments ${JSON.stringify(fieldDef.args.filter(argType => !builtIns.primitives.has(argType) && !types.has(argType)))}`);
|
|
167
|
+
errors = errors.push(`field ${fieldName} of type ${typeName} is non-existent type ${fieldDef.name}`);
|
|
146
168
|
if (fieldDef.kind == "application" && fieldDef.value == "SingleSelection") {
|
|
147
169
|
if (fieldDef.args.length != 1)
|
|
148
|
-
errors.push(`field ${fieldName} in type ${typeName}: SingleSelection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
|
|
170
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: SingleSelection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
|
|
149
171
|
else {
|
|
150
172
|
const argType = types.get(fieldDef.args[0])!
|
|
173
|
+
if(argType == undefined){
|
|
174
|
+
errors = errors.push(`arg ${fieldDef.args[0]} in type ${typeName} references non existent type`);
|
|
175
|
+
return
|
|
176
|
+
}
|
|
151
177
|
if (argType.extends.length != 1 || argType.extends[0] != "CollectionReference")
|
|
152
|
-
errors.push(`field ${fieldName} in type ${typeName}: SingleSelection requires ${argType.name} to 'extend CollectionReference'`);
|
|
178
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: SingleSelection requires ${argType.name} to 'extend CollectionReference'`);
|
|
153
179
|
}
|
|
154
180
|
}
|
|
155
181
|
if (fieldDef.kind == "application" && fieldDef.value == "Multiselection") {
|
|
156
182
|
if (fieldDef.args.length != 1)
|
|
157
|
-
errors.push(`field ${fieldName} in type ${typeName}: Multiselection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
|
|
183
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: Multiselection should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`);
|
|
158
184
|
else {
|
|
159
185
|
const argType = types.get(fieldDef.args[0])!
|
|
186
|
+
if(argType == undefined){
|
|
187
|
+
errors = errors.push(`arg ${fieldDef.args[0]} in type ${typeName} references non existent type`);
|
|
188
|
+
return errors
|
|
189
|
+
}
|
|
160
190
|
if (argType.extends.length != 1 || argType.extends[0] != "CollectionReference")
|
|
161
|
-
errors.push(`field ${fieldName} in type ${typeName}: Multiselection requires ${argType.name} to 'extend CollectionReference'`);
|
|
191
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: Multiselection requires ${argType.name} to 'extend CollectionReference'`);
|
|
162
192
|
}
|
|
163
193
|
}
|
|
194
|
+
if (fieldDef.kind == "application" && fieldDef.value == "List") {
|
|
195
|
+
if (fieldDef.args.length != 1)
|
|
196
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: List should have exactly one type argument, found ${JSON.stringify(fieldDef.args)}`)
|
|
197
|
+
}
|
|
198
|
+
if (fieldDef.kind == "application" && fieldDef.value == "Map") {
|
|
199
|
+
if (fieldDef.args.length != 2)
|
|
200
|
+
errors = errors.push(`field ${fieldName} in type ${typeName}: Map should have exactly two type arguments, found ${JSON.stringify(fieldDef.args)}`)
|
|
201
|
+
}
|
|
164
202
|
});
|
|
165
203
|
});
|
|
166
204
|
|
|
167
|
-
if ("forms" in formsConfig == false) {
|
|
168
|
-
errors.push("the formsConfig does not contain a 'forms' field");
|
|
169
|
-
return Sum.Default.right(errors);
|
|
170
|
-
}
|
|
171
|
-
if ("apis" in formsConfig == false) {
|
|
172
|
-
errors.push("the formsConfig does not contain an 'apis' field");
|
|
173
|
-
return Sum.Default.right(errors);
|
|
174
|
-
}
|
|
175
|
-
if ("mappings" in formsConfig == false) {
|
|
176
|
-
errors.push("the formsConfig does not contain a 'mappings' field");
|
|
177
|
-
return Sum.Default.right(errors);
|
|
178
|
-
}
|
|
179
|
-
if ("enumOptions" in formsConfig["apis"] == false) {
|
|
180
|
-
errors.push("formsConfig.apis does not contain an 'enumOptions' field");
|
|
181
|
-
return Sum.Default.right(errors);
|
|
182
|
-
}
|
|
183
|
-
if ("entities" in formsConfig["apis"] == false) {
|
|
184
|
-
errors.push("formsConfig.apis does not contain an 'entities' field");
|
|
185
|
-
return Sum.Default.right(errors);
|
|
186
|
-
}
|
|
187
205
|
let enums: Map<string, TypeName> = Map();
|
|
188
206
|
Object.keys(formsConfig["apis"]["enumOptions"]).forEach((enumOptionsName: any) => {
|
|
189
207
|
if (!types.has(formsConfig["apis"]["enumOptions"][enumOptionsName])) {
|
|
190
|
-
errors.push(`formsConfig.apis.enumOptions refers to non-existent type ${formsConfig['apis']['enumOptions'][enumOptionsName]}`);
|
|
208
|
+
errors = errors.push(`formsConfig.apis.enumOptions refers to non-existent type ${formsConfig['apis']['enumOptions'][enumOptionsName]}`);
|
|
191
209
|
} else {
|
|
192
210
|
enums = enums.set(enumOptionsName, formsConfig["apis"]["enumOptions"][enumOptionsName])
|
|
193
211
|
}
|
|
194
212
|
})
|
|
213
|
+
|
|
195
214
|
let streams: Map<string, TypeName> = Map();
|
|
196
215
|
Object.keys(formsConfig["apis"]["searchableStreams"]).forEach((searchableStreamName: any) => {
|
|
197
216
|
if (!types.has(formsConfig["apis"]["searchableStreams"][searchableStreamName])) {
|
|
198
|
-
errors.push(`formsConfig.apis.searchableStreams refers to non-existent type ${formsConfig['apis']['searchableStreams'][searchableStreamName]}`);
|
|
217
|
+
errors = errors.push(`formsConfig.apis.searchableStreams refers to non-existent type ${formsConfig['apis']['searchableStreams'][searchableStreamName]}`);
|
|
199
218
|
} else {
|
|
200
219
|
streams = streams.set(searchableStreamName, formsConfig["apis"]["searchableStreams"][searchableStreamName])
|
|
201
220
|
}
|
|
202
221
|
})
|
|
222
|
+
|
|
203
223
|
let entities: Map<string, EntityApi> = Map();
|
|
204
224
|
Object.keys(formsConfig["apis"]["entities"]).forEach((entityApiName: any) => {
|
|
205
225
|
const entityApiConfig = formsConfig["apis"]["entities"][entityApiName]
|
|
206
226
|
if (!types.has(formsConfig["apis"]["entities"][entityApiName]["type"])) {
|
|
207
|
-
errors.push(`formsConfig.apis.entities refers to non-existent type ${formsConfig['apis']['entities'][entityApiName]["type"]}`);
|
|
227
|
+
errors = errors.push(`formsConfig.apis.entities refers to non-existent type ${formsConfig['apis']['entities'][entityApiName]["type"]}`);
|
|
208
228
|
} else {
|
|
209
229
|
entities = entities.set(entityApiName, {
|
|
210
230
|
type: entityApiConfig["type"],
|
|
@@ -222,23 +242,23 @@ export const FormsConfig = {
|
|
|
222
242
|
Object.keys(formsConfig["mappings"]).forEach((mappingName: any) => {
|
|
223
243
|
const mapping = formsConfig["mappings"][mappingName]
|
|
224
244
|
if (!("source" in mapping && "target" in mapping && "paths" in mapping))
|
|
225
|
-
errors.push(`formsConfig.mappings.${mappingName} does not have all the required fields ('source', 'target', and 'path')`);
|
|
245
|
+
errors = errors.push(`formsConfig.mappings.${mappingName} does not have all the required fields ('source', 'target', and 'path')`);
|
|
226
246
|
else {
|
|
227
247
|
const source = mapping.source as string
|
|
228
248
|
const target = mapping.target as string
|
|
229
249
|
const paths = mapping.paths
|
|
230
250
|
if (!types.has(source))
|
|
231
|
-
errors.push(`formsConfig.mappings.source refers to non-existent type ${source}`)
|
|
251
|
+
errors = errors.push(`formsConfig.mappings.source refers to non-existent type ${source}`)
|
|
232
252
|
if (!types.has(target))
|
|
233
|
-
errors.push(`formsConfig.mappings.target refers to non-existent type ${target}`)
|
|
253
|
+
errors = errors.push(`formsConfig.mappings.target refers to non-existent type ${target}`)
|
|
234
254
|
const lookupPath = (source: Type, path: Array<string>): Type | undefined => {
|
|
235
255
|
if (path.length <= 0) return source
|
|
236
256
|
if (source.kind != "lookup" || !types.has(source.name)) {
|
|
237
|
-
errors.push(`formsConfig.mappings ${JSON.stringify(source)} is not a lookup or does not exist`)
|
|
257
|
+
errors = errors.push(`formsConfig.mappings ${JSON.stringify(source)} is not a lookup or does not exist`)
|
|
238
258
|
} else {
|
|
239
259
|
const sourceTypeDef = types.get(source.name)!
|
|
240
260
|
if (!sourceTypeDef.fields.has(path[0])) {
|
|
241
|
-
errors.push(`path ${JSON.stringify(path)} refers to non-existent field ${path[0]}`)
|
|
261
|
+
errors = errors.push(`path ${JSON.stringify(path)} refers to non-existent field ${path[0]}`)
|
|
242
262
|
}
|
|
243
263
|
return lookupPath(sourceTypeDef.fields.get(path[0])!, path.slice(1))
|
|
244
264
|
}
|
|
@@ -248,21 +268,21 @@ export const FormsConfig = {
|
|
|
248
268
|
if (Array.isArray(path)) {
|
|
249
269
|
const sourceType = lookupPath(source, path)
|
|
250
270
|
if (!sourceType || Type.Operations.Equals(sourceType, target) == false) {
|
|
251
|
-
errors.push(`path ${JSON.stringify(path)} connects different types ${JSON.stringify(sourceType)} and ${JSON.stringify(target)}`)
|
|
271
|
+
errors = errors.push(`path ${JSON.stringify(path)} connects different types ${JSON.stringify(sourceType)} and ${JSON.stringify(target)}`)
|
|
252
272
|
}
|
|
253
273
|
} else {
|
|
254
274
|
if (target.kind != "lookup" || !types.has(target.name)) {
|
|
255
|
-
errors.push(`path ${JSON.stringify(path)} refers to an object but ${JSON.stringify(target)} is not`)
|
|
275
|
+
errors = errors.push(`path ${JSON.stringify(path)} refers to an object but ${JSON.stringify(target)} is not`)
|
|
256
276
|
} else {
|
|
257
277
|
const targetTypeDef = types.get(target.name)!
|
|
258
278
|
const pathFieldNames = Set(Object.keys(path))
|
|
259
279
|
const targetFieldNames = targetTypeDef.fields.keySeq().toSet()
|
|
260
280
|
if (!pathFieldNames.equals(targetFieldNames))
|
|
261
|
-
errors.push(`path ${JSON.stringify(pathFieldNames.toArray())} does not cover all fields of target ${JSON.stringify(targetFieldNames.toArray())}, ${JSON.stringify(targetFieldNames.subtract(pathFieldNames).toArray())} are missing`)
|
|
281
|
+
errors = errors.push(`path ${JSON.stringify(pathFieldNames.toArray())} does not cover all fields of target ${JSON.stringify(targetFieldNames.toArray())}, ${JSON.stringify(targetFieldNames.subtract(pathFieldNames).toArray())} are missing`)
|
|
262
282
|
else
|
|
263
283
|
Object.keys(path).forEach(fieldName => {
|
|
264
284
|
if (!targetTypeDef.fields.has(fieldName)) {
|
|
265
|
-
errors.push(`path ${JSON.stringify(path)} refers to non-existing field ${fieldName} on ${JSON.stringify(target)} is not`)
|
|
285
|
+
errors = errors.push(`path ${JSON.stringify(path)} refers to non-existing field ${fieldName} on ${JSON.stringify(target)} is not`)
|
|
266
286
|
}
|
|
267
287
|
mappingPathValidator(source, targetTypeDef.fields.get(fieldName)!, path[fieldName])
|
|
268
288
|
})
|
|
@@ -280,33 +300,33 @@ export const FormsConfig = {
|
|
|
280
300
|
forms = forms.set(formName, formDef);
|
|
281
301
|
const configFormDef = formsConfig["forms"][formName];
|
|
282
302
|
if ("type" in configFormDef == false) {
|
|
283
|
-
errors.push(`form ${formName} is missing the required 'type' attribute`);
|
|
303
|
+
errors = errors.push(`form ${formName} is missing the required 'type' attribute`);
|
|
284
304
|
} else {
|
|
285
305
|
if (types.has(configFormDef["type"])) {
|
|
286
306
|
formDef.type = configFormDef["type"];
|
|
287
307
|
formDef.typeDef = types.get(configFormDef["type"])!
|
|
288
308
|
} else
|
|
289
|
-
errors.push(`form ${formName} references non-existing type ${configFormDef["type"]}`);
|
|
309
|
+
errors = errors.push(`form ${formName} references non-existing type ${configFormDef["type"]}`);
|
|
290
310
|
}
|
|
291
311
|
const formTypeDef = types.get(configFormDef["type"])
|
|
292
312
|
if ("fields" in configFormDef == false) {
|
|
293
|
-
errors.push(`form ${formName} is missing the required 'fields' attribute`);
|
|
313
|
+
errors = errors.push(`form ${formName} is missing the required 'fields' attribute`);
|
|
294
314
|
} else {
|
|
295
315
|
formTypeDef?.fields.forEach((fieldType, fieldName) => {
|
|
296
316
|
if (!Object.keys(configFormDef["fields"]).includes(fieldName))
|
|
297
|
-
errors.push(`form ${formName} is missing a renderer for field ${fieldName} which is defined on type ${formDef.type}`);
|
|
317
|
+
errors = errors.push(`form ${formName} is missing a renderer for field ${fieldName} which is defined on type ${formDef.type}`);
|
|
298
318
|
})
|
|
299
319
|
Object.keys(configFormDef["fields"]).forEach(fieldName => {
|
|
300
320
|
const fieldConfig = configFormDef["fields"][fieldName]
|
|
301
321
|
if (!formTypeDef?.fields.has(fieldName)) {
|
|
302
|
-
errors.push(`form ${formName} references field ${fieldName} which does not exist on type ${formDef.type}`);
|
|
322
|
+
errors = errors.push(`form ${formName} references field ${fieldName} which does not exist on type ${formDef.type}`);
|
|
303
323
|
} else {
|
|
304
324
|
if ("renderer" in fieldConfig == false) {
|
|
305
|
-
errors.push(`field ${fieldName} of form ${formName} has no 'renderer' attribute`);
|
|
325
|
+
errors = errors.push(`field ${fieldName} of form ${formName} has no 'renderer' attribute`);
|
|
306
326
|
}
|
|
307
327
|
const fieldTypeDef = formTypeDef?.fields.get(fieldName)
|
|
308
328
|
if (!fieldTypeDef)
|
|
309
|
-
errors.push(`field ${fieldName} of form ${formName} has no corresponding type `);
|
|
329
|
+
errors = errors.push(`field ${fieldName} of form ${formName} has no corresponding type `);
|
|
310
330
|
}
|
|
311
331
|
})
|
|
312
332
|
}
|
|
@@ -316,66 +336,64 @@ export const FormsConfig = {
|
|
|
316
336
|
if (fieldTypeDef?.kind == "primitive") {
|
|
317
337
|
if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
|
|
318
338
|
if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
|
|
319
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
339
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
320
340
|
}
|
|
321
341
|
else if (fieldTypeDef.value == "maybeBoolean") {
|
|
322
|
-
// alert(JSON.stringify(fieldConfig["renderer"]))
|
|
323
|
-
// alert(JSON.stringify(builtIns.renderers.MaybeBooleanViews))
|
|
324
342
|
if (!builtIns.renderers.maybeBoolean.has(fieldConfig["renderer"]))
|
|
325
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
343
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
326
344
|
} else if (fieldTypeDef.value == "boolean") {
|
|
327
345
|
if (!builtIns.renderers.boolean.has(fieldConfig["renderer"]))
|
|
328
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
346
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
329
347
|
} else if (fieldTypeDef.value == "number") {
|
|
330
348
|
if (!builtIns.renderers.number.has(fieldConfig["renderer"]))
|
|
331
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
349
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
332
350
|
} else if (fieldTypeDef.value == "string") {
|
|
333
351
|
if (!builtIns.renderers.string.has(fieldConfig["renderer"]))
|
|
334
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
352
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
335
353
|
} else if (fieldTypeDef.value == "Date") {
|
|
336
354
|
if (!builtIns.renderers.date.has(fieldConfig["renderer"])) {
|
|
337
|
-
|
|
338
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
355
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
339
356
|
}
|
|
340
357
|
} else {
|
|
341
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
358
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
342
359
|
}
|
|
343
360
|
if(injectedPrimitives?.injectedPrimitives.has(fieldTypeDef.value as keyof T)){
|
|
344
361
|
if (!injectedPrimitives.renderers[fieldTypeDef.value as keyof T].has(fieldConfig["renderer"]))
|
|
345
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
362
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing injected primitive 'renderer' ${fieldConfig["renderer"]}`);
|
|
346
363
|
}
|
|
347
364
|
} else if (fieldTypeDef?.kind == "application") {
|
|
348
365
|
if (fieldTypeDef?.value == "SingleSelection") {
|
|
349
366
|
if (!builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"]) &&
|
|
350
367
|
!builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"]))
|
|
351
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
368
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
352
369
|
} else if (fieldTypeDef?.value == "Multiselection") {
|
|
353
370
|
if (!builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) &&
|
|
354
371
|
!builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"]))
|
|
355
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
372
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
356
373
|
} else if (fieldTypeDef?.value == "List") {
|
|
357
374
|
if (!builtIns.renderers.list.has(fieldConfig["renderer"]) ){
|
|
358
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
|
|
375
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`)
|
|
359
376
|
}
|
|
360
377
|
} else if (fieldTypeDef?.value == "Map") {
|
|
361
378
|
if (!builtIns.renderers.map.has(fieldConfig["renderer"]))
|
|
362
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
379
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing ${fieldTypeDef.value} 'renderer' ${fieldConfig["renderer"]}`);
|
|
363
380
|
if ("keyRenderer" in fieldConfig != true || "valueRenderer" in fieldConfig != true)
|
|
364
|
-
errors.push(`field ${fieldName} of form ${formName} must have both a keyRenderer and a valueRenderer`);
|
|
381
|
+
errors = errors.push(`field ${fieldName} of form ${formName} must have both a keyRenderer and a valueRenderer`);
|
|
365
382
|
if (fieldTypeDef.args.length != 2)
|
|
366
|
-
errors.push(`field ${fieldName} of form ${formName} should have exactly two type arguments`);
|
|
383
|
+
errors = errors.push(`field ${fieldName} of form ${formName} should have exactly two type arguments`);
|
|
367
384
|
else {
|
|
368
385
|
const typeDefToType = (typeDef:any) : Type | undefined =>
|
|
369
|
-
"
|
|
386
|
+
"kind" in typeDef == false ? undefined
|
|
387
|
+
: "value" in typeDef == false ? undefined
|
|
370
388
|
: "args" in typeDef == false ? undefined
|
|
371
389
|
: Array.isArray(typeDef.args) == false ? undefined
|
|
372
|
-
: Type.Default.application(typeDef.fun, typeDef.args)
|
|
390
|
+
: Type.Default.application(typeDef.fun, typeDef.args.map((arg:any) => (typeof arg == "string" ? arg : { kind:"application", value: arg.fun, args: arg.args })) as any)
|
|
373
391
|
const keyType:Type | undefined = typeof fieldTypeDef.args[0] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[0]) : typeDefToType(fieldTypeDef.args[0] as any)
|
|
374
392
|
const valueType:Type | undefined = typeof fieldTypeDef.args[1] == "string" ? Type.Operations.FromName(types, builtIns, injectedPrimitives)(fieldTypeDef.args[1]) : typeDefToType(fieldTypeDef.args[1] as any)
|
|
375
393
|
if (!keyType) {
|
|
376
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing key type ${JSON.stringify(fieldTypeDef.args[0])}`);
|
|
394
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing key type ${JSON.stringify(fieldTypeDef.args[0])}`);
|
|
377
395
|
} else if (!valueType) {
|
|
378
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing value type ${JSON.stringify(fieldTypeDef.args[1])}`);
|
|
396
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing value type ${JSON.stringify(fieldTypeDef.args[1])}`);
|
|
379
397
|
} else {
|
|
380
398
|
rendererMatchesType(formName, fieldName)(keyType, fieldConfig.keyRenderer)
|
|
381
399
|
rendererMatchesType(formName, fieldName)(valueType, fieldConfig.valueRenderer)
|
|
@@ -383,42 +401,41 @@ export const FormsConfig = {
|
|
|
383
401
|
}
|
|
384
402
|
}
|
|
385
403
|
if (fieldTypeDef.args.length < 1)
|
|
386
|
-
errors.push(`field ${fieldName} of form ${formName} should have one type argument}`);
|
|
404
|
+
errors = errors.push(`field ${fieldName} of form ${formName} should have one type argument}`);
|
|
387
405
|
else if (
|
|
388
406
|
(builtIns.renderers.list.has(fieldConfig["renderer"])) && "elementRenderer" in fieldConfig != true)
|
|
389
|
-
errors.push(`field ${fieldName} of form ${formName} is missing the 'elementRenderer' property`);
|
|
407
|
+
errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'elementRenderer' property`);
|
|
390
408
|
else if (
|
|
391
409
|
(builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) ||
|
|
392
410
|
builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"])) && "options" in fieldConfig != true)
|
|
393
|
-
errors.push(`field ${fieldName} of form ${formName} is missing the 'options' property`);
|
|
411
|
+
errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'options' property`);
|
|
394
412
|
else if (
|
|
395
413
|
(builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"]) ||
|
|
396
414
|
builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"])) && "stream" in fieldConfig != true)
|
|
397
|
-
errors.push(`field ${fieldName} of form ${formName} is missing the 'stream' property`);
|
|
415
|
+
errors = errors.push(`field ${fieldName} of form ${formName} is missing the 'stream' property`);
|
|
398
416
|
else if ((builtIns.renderers.enumMultiSelection.has(fieldConfig["renderer"]) ||
|
|
399
417
|
builtIns.renderers.enumSingleSelection.has(fieldConfig["renderer"])) && (enums.get(fieldConfig["options"])) != fieldTypeDef.args[0]) {
|
|
400
418
|
if (enums.has(fieldConfig["options"]))
|
|
401
|
-
errors.push(`field ${fieldName} of form ${formName} references an enum api with type ${enums.get(fieldConfig["options"])} when ${fieldTypeDef.args[0]} was expected`);
|
|
419
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references an enum api with type ${enums.get(fieldConfig["options"])} when ${fieldTypeDef.args[0]} was expected`);
|
|
402
420
|
else
|
|
403
|
-
errors.push(`field ${fieldName} of form ${formName} references a non-existing enum api`);
|
|
421
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing enum api`);
|
|
404
422
|
} else if ((builtIns.renderers.streamMultiSelection.has(fieldConfig["renderer"]) ||
|
|
405
423
|
builtIns.renderers.streamSingleSelection.has(fieldConfig["renderer"])) && (streams.get(fieldConfig["stream"])) != fieldTypeDef.args[0]) {
|
|
406
424
|
if (streams.has(fieldConfig["stream"]))
|
|
407
|
-
errors.push(`field ${fieldName} of form ${formName} references an api with type ${streams.get(fieldConfig["stream"])} when ${fieldTypeDef.args[0]} was expected`);
|
|
425
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references an api with type ${streams.get(fieldConfig["stream"])} when ${fieldTypeDef.args[0]} was expected`);
|
|
408
426
|
else
|
|
409
|
-
errors.push(`field ${fieldName} of form ${formName} references a non-existing stream api`);
|
|
427
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing stream api`);
|
|
410
428
|
}
|
|
411
429
|
} else {
|
|
412
430
|
const formTypeDef = types.get(fieldTypeDef.name)
|
|
413
431
|
if (!formTypeDef) {
|
|
414
|
-
|
|
415
|
-
errors.push(`field ${fieldName} of form ${formName} references a non-existing type ${fieldTypeDef.name}`);
|
|
432
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references a non-existing type ${fieldTypeDef.name}`);
|
|
416
433
|
} else {
|
|
417
434
|
const form = forms.get(fieldConfig.renderer ?? "")
|
|
418
435
|
if (!form) {
|
|
419
|
-
errors.push(`field ${fieldName} of form ${formName} references non-existing form ${fieldConfig.renderer}`);
|
|
436
|
+
errors = errors.push(`field ${fieldName} of form ${formName} references non-existing form ${fieldConfig.renderer}`);
|
|
420
437
|
} else if (fieldTypeDef.name != form.typeDef.name) {
|
|
421
|
-
errors.push(`field ${fieldName} of form ${formName} expected renderer for ${fieldTypeDef.name} but instead found a renderer for ${form.typeDef.name}`);
|
|
438
|
+
errors = errors.push(`field ${fieldName} of form ${formName} expected renderer for ${fieldTypeDef.name} but instead found a renderer for ${form.typeDef.name}`);
|
|
422
439
|
}
|
|
423
440
|
}
|
|
424
441
|
}
|
|
@@ -464,15 +481,15 @@ export const FormsConfig = {
|
|
|
464
481
|
}
|
|
465
482
|
let elementErrors = rendererHasType(elementRenderer, elementType)
|
|
466
483
|
if (elementErrors.length > 0)
|
|
467
|
-
errors.push(...elementErrors)
|
|
484
|
+
errors = errors.push(...elementErrors)
|
|
468
485
|
}
|
|
469
486
|
if (fieldTypeDef?.kind == "lookup") {
|
|
470
487
|
if (!forms.has(fieldConfig.renderer))
|
|
471
|
-
errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
|
|
488
|
+
errors = errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references non-existing form ${fieldConfig["renderer"]}`);
|
|
472
489
|
else {
|
|
473
490
|
const otherForm = forms.get(fieldConfig.renderer)!
|
|
474
491
|
if (otherForm.type != fieldTypeDef.name)
|
|
475
|
-
errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
|
|
492
|
+
errors = errors.push(`field ${revertKeyword(fieldName)} of form ${formName} references form ${fieldConfig["renderer"]}, which has type ${otherForm.type} whereas ${fieldTypeDef.name} was expected`);
|
|
476
493
|
}
|
|
477
494
|
}
|
|
478
495
|
formDef.fields = formDef.fields.set(
|
|
@@ -498,23 +515,22 @@ export const FormsConfig = {
|
|
|
498
515
|
|
|
499
516
|
Object.keys(formsConfig["forms"]).forEach((formName: any) => {
|
|
500
517
|
let formDef = forms.get(formName)!
|
|
501
|
-
// const formTypeDef = types.get(formDef.type)
|
|
502
518
|
const configFormDef = formsConfig["forms"][formName];
|
|
503
519
|
if ("tabs" in configFormDef == false)
|
|
504
|
-
errors.push(`form ${formName} is missing required attribute 'tabs'`);
|
|
520
|
+
errors = errors.push(`form ${formName} is missing required attribute 'tabs'`);
|
|
505
521
|
else {
|
|
506
522
|
let tabs: FormLayout = OrderedMap()
|
|
507
523
|
Object.keys(configFormDef.tabs).forEach(tabName => {
|
|
508
524
|
const tabConfig = configFormDef.tabs[tabName]
|
|
509
525
|
if ("columns" in tabConfig == false)
|
|
510
|
-
errors.push(`tab ${tabName} in form ${formName} is missing required attribute 'columns'`);
|
|
526
|
+
errors = errors.push(`tab ${tabName} in form ${formName} is missing required attribute 'columns'`);
|
|
511
527
|
else {
|
|
512
528
|
let cols: TabLayout = { columns: OrderedMap() }
|
|
513
529
|
tabs = tabs.set(tabName, cols)
|
|
514
530
|
Object.keys(tabConfig.columns).forEach(colName => {
|
|
515
531
|
const colConfig = tabConfig.columns[colName]
|
|
516
532
|
if ("groups" in colConfig == false)
|
|
517
|
-
errors.push(`column ${colName} in tab ${tabName} in form ${formName} is missing required attribute 'groups'`);
|
|
533
|
+
errors = errors.push(`column ${colName} in tab ${tabName} in form ${formName} is missing required attribute 'groups'`);
|
|
518
534
|
else {
|
|
519
535
|
let column: ColumnLayout = { groups: OrderedMap() }
|
|
520
536
|
cols.columns = cols.columns.set(colName, column)
|
|
@@ -523,11 +539,11 @@ export const FormsConfig = {
|
|
|
523
539
|
let group: GroupLayout = []
|
|
524
540
|
column.groups = column.groups.set(groupName, group)
|
|
525
541
|
if (!Array.isArray(groupConfig))
|
|
526
|
-
errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} should be an array of field names`);
|
|
542
|
+
errors = errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} should be an array of field names`);
|
|
527
543
|
else
|
|
528
544
|
groupConfig.forEach((fieldName: any) => {
|
|
529
545
|
if (!formDef.fields.has(fieldName))
|
|
530
|
-
errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} references non-existing field '${fieldName}'`);
|
|
546
|
+
errors = errors.push(`group ${groupName} in column ${colName} in tab ${tabName} in form ${formName} references non-existing field '${fieldName}'`);
|
|
531
547
|
else {
|
|
532
548
|
group.push(fieldName)
|
|
533
549
|
}
|
|
@@ -541,40 +557,39 @@ export const FormsConfig = {
|
|
|
541
557
|
}
|
|
542
558
|
});
|
|
543
559
|
|
|
544
|
-
if ("launchers" in formsConfig == false) {
|
|
545
|
-
errors.push("the formsConfig does not contain a 'launchers' field");
|
|
546
|
-
return Sum.Default.right(errors);
|
|
547
|
-
}
|
|
548
560
|
let launchers: FormsConfig["launchers"] = {
|
|
549
561
|
create: Map<string, Launcher>(),
|
|
550
562
|
edit: Map<string, Launcher>(),
|
|
551
563
|
mappings: Map<string, MappingLauncher>(),
|
|
552
564
|
}
|
|
553
565
|
Object.keys(formsConfig["launchers"]).forEach((launcherName: any) => {
|
|
554
|
-
// let launcherConfig = formsConfig["launchers"][launcherName]
|
|
555
566
|
const launcherKinds = ["create", "edit", "mapping"]
|
|
556
567
|
if (launcherKinds.includes(formsConfig["launchers"][launcherName]["kind"]) == false) {
|
|
557
|
-
errors.push(`launcher '${launcherName}' has invalid 'kind': expected any of ${JSON.stringify(launcherKinds)}`);
|
|
568
|
+
errors = errors.push(`launcher '${launcherName}' has invalid 'kind': expected any of ${JSON.stringify(launcherKinds)}`);
|
|
558
569
|
return
|
|
559
570
|
}
|
|
560
571
|
const launcherKind = formsConfig["launchers"][launcherName]["kind"] as Launcher["kind"] | MappingLauncher["kind"]
|
|
561
|
-
if (forms.has(formsConfig["launchers"][launcherName]["form"]) == false)
|
|
562
|
-
errors.push(`launcher '${launcherName}' references non-existing form '${formsConfig.launchers[launcherName].form}'`);
|
|
572
|
+
if (forms.has(formsConfig["launchers"][launcherName]["form"]) == false) {
|
|
573
|
+
errors = errors.push(`launcher '${launcherName}' references non-existing form '${formsConfig.launchers[launcherName].form}'`);
|
|
574
|
+
return
|
|
575
|
+
}
|
|
563
576
|
const form = forms.get(formsConfig["launchers"][launcherName]["form"])!
|
|
564
577
|
if (launcherKind != "mapping") {
|
|
565
|
-
if (entities.has(formsConfig["launchers"][launcherName]["api"]) == false)
|
|
566
|
-
errors.push(`launcher '${launcherName}' references non-existing entity api '${formsConfig.launchers[launcherName].api}'`);
|
|
578
|
+
if (entities.has(formsConfig["launchers"][launcherName]["api"]) == false) {
|
|
579
|
+
errors = errors.push(`launcher '${launcherName}' references non-existing entity api '${formsConfig.launchers[launcherName].api}'`);
|
|
580
|
+
return
|
|
581
|
+
}
|
|
567
582
|
const api = entities.get(formsConfig["launchers"][launcherName]["api"])!
|
|
568
583
|
if (form.type != api.type)
|
|
569
|
-
errors.push(`form and api in launcher '${launcherName}' reference different types '${form.type}' and '${api.type}'`);
|
|
584
|
+
errors = errors.push(`form and api in launcher '${launcherName}' reference different types '${form.type}' and '${api.type}'`);
|
|
570
585
|
if (formsConfig["launchers"][launcherName]["kind"] == "create" &&
|
|
571
586
|
!(api.methods.create && api.methods.default)
|
|
572
587
|
)
|
|
573
|
-
errors.push(`launcher '${launcherName}' requires api methods 'create' and 'default'`);
|
|
588
|
+
errors = errors.push(`launcher '${launcherName}' requires api methods 'create' and 'default'`);
|
|
574
589
|
if (formsConfig["launchers"][launcherName]["kind"] == "edit" &&
|
|
575
590
|
!(api.methods.get && api.methods.update)
|
|
576
591
|
)
|
|
577
|
-
errors.push(`launcher '${launcherName}' requires api methods 'get' and 'update'`);
|
|
592
|
+
errors = errors.push(`launcher '${launcherName}' requires api methods 'get' and 'update'`);
|
|
578
593
|
let launcher: Launcher = {
|
|
579
594
|
name: launcherName,
|
|
580
595
|
kind: formsConfig["launchers"][launcherName]["kind"],
|
|
@@ -589,24 +604,24 @@ export const FormsConfig = {
|
|
|
589
604
|
const launcherConfig = formsConfig["launchers"][launcherName]
|
|
590
605
|
const mappingName = launcherConfig["mapping"]
|
|
591
606
|
if (mappingName in formsConfig["mappings"] == false)
|
|
592
|
-
errors.push(`launcher '${launcherName}' references non-existing mapping '${mappingName}'`);
|
|
607
|
+
errors = errors.push(`launcher '${launcherName}' references non-existing mapping '${mappingName}'`);
|
|
593
608
|
else {
|
|
594
609
|
const mapping = formsConfig["mappings"][mappingName]
|
|
595
610
|
if (mapping["target"] != form.type)
|
|
596
|
-
errors.push(`launcher '${launcherName}' has a form over '${form.type}' but a mapping to a different type '${mapping["target"]}'`);
|
|
611
|
+
errors = errors.push(`launcher '${launcherName}' has a form over '${form.type}' but a mapping to a different type '${mapping["target"]}'`);
|
|
597
612
|
else
|
|
598
613
|
launchers.mappings = launchers.mappings.set(launcherName, launcherConfig)
|
|
599
614
|
}
|
|
600
615
|
}
|
|
601
616
|
})
|
|
602
617
|
|
|
603
|
-
if (errors.
|
|
618
|
+
if (errors.size > 0) {
|
|
604
619
|
console.error("parsing errors")
|
|
605
620
|
console.error(errors)
|
|
606
|
-
return
|
|
621
|
+
return ValueOrErrors.Default.throw(errors);
|
|
607
622
|
}
|
|
608
623
|
|
|
609
|
-
return
|
|
624
|
+
return ValueOrErrors.Default.return({
|
|
610
625
|
types: types,
|
|
611
626
|
forms: forms,
|
|
612
627
|
apis: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { List, Map, OrderedMap, OrderedSet, Set } from "immutable";
|
|
2
|
-
import { BoolExpr, Unit, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, Synchronized, simpleUpdater, TypeName, ListFieldState, ListForm, TypeDefinition, BuiltInApiConverters, defaultValue, fromAPIRawValue, toAPIRawValue, EditFormForeignMutationsExpected, MapFieldState, MapForm, Type, FieldConfig, Base64FileForm, SecretForm, InjectedPrimitives, Injectables, ApiConverters, Maybe } from "../../../../main";
|
|
2
|
+
import { BoolExpr, Unit, Guid, LeafPredicatesEvaluators, Predicate, FormsConfig, BuiltIns, FormDef, Sum, BasicFun, Template, unit, EditFormState, EditFormTemplate, ApiErrors, CreateFormTemplate, EntityFormTemplate, SharedFormState, CreateFormState, Entity, EditFormContext, CreateFormContext, MappedEntityFormTemplate, Mapping, Synchronized, simpleUpdater, TypeName, ListFieldState, ListForm, TypeDefinition, BuiltInApiConverters, defaultValue, fromAPIRawValue, toAPIRawValue, EditFormForeignMutationsExpected, MapFieldState, MapForm, Type, FieldConfig, Base64FileForm, SecretForm, InjectedPrimitives, Injectables, ApiConverters, Maybe, FormValidationError, FormConfigValidationAndParseResult } from "../../../../main";
|
|
3
3
|
import { Value } from "../../../value/state";
|
|
4
4
|
import { CollectionReference } from "../collection/domains/reference/state";
|
|
5
5
|
import { CollectionSelection } from "../collection/domains/selection/state";
|
|
@@ -209,7 +209,7 @@ export const ParseForm = <T,>(
|
|
|
209
209
|
.mapContext<any>(_ => ({ ..._, label, tooltip }))
|
|
210
210
|
} else { // the list argument is a primitive
|
|
211
211
|
const elementForm = FieldView(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, elementLabel, elementTooltip, EnumOptionsSources, fieldsOptionsConfig, leafPredicates, injectedPrimitives)
|
|
212
|
-
const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
|
|
212
|
+
const initialFormState = FieldFormState(fieldConfig, fieldViews, fieldNameToElementViewCategory(formFieldElementRenderers)(fieldName) as any, elementRendererName, fieldName, InfiniteStreamSources, fieldsInfiniteStreamsConfig, injectedPrimitives);
|
|
213
213
|
formConfig[fieldName] = ListForm<any, any, any & FormLabel, Unit>(
|
|
214
214
|
{ Default: () => initialFormState },
|
|
215
215
|
{ Default: () => initialElementValue },
|
|
@@ -220,21 +220,12 @@ export const ParseForm = <T,>(
|
|
|
220
220
|
} else {
|
|
221
221
|
if (viewType == "map") {
|
|
222
222
|
const field = type.fields.get(fieldName)!
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (typeof t == "string") return t
|
|
226
|
-
if ("fun" in t && "args" in t && Array.isArray(t.args)) {
|
|
227
|
-
return { kind:"application", value:t.fun, args:t.args }
|
|
228
|
-
}
|
|
229
|
-
return undefined
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const [keyType, valueType] = field.kind == "application" ? [parseType(field.args[0]), parseType(field.args[1])] : (() => {
|
|
233
|
-
throw `error processing field type ${JSON.stringify(field)} of ${fieldName}`
|
|
234
|
-
})()
|
|
235
|
-
if (!keyType || !valueType) {
|
|
223
|
+
// TODO - reconsider error handling approach here in general
|
|
224
|
+
if(field.kind != "application") {
|
|
236
225
|
throw `error processing field type ${JSON.stringify(field)} of ${fieldName}`
|
|
237
226
|
}
|
|
227
|
+
|
|
228
|
+
const [keyType, valueType] = [field.args[0], field.args[1]]
|
|
238
229
|
const initialKeyValue = defaultValue(keyType)
|
|
239
230
|
const initialValueValue = defaultValue(valueType)
|
|
240
231
|
const getFormAndInitialState = (elementRenderers:any, rendererName:any, fieldConfig:FieldConfig) => {
|
|
@@ -314,7 +305,7 @@ export type EditLauncherContext<Entity, FormState, ExtraContext> =
|
|
|
314
305
|
extraContext: ExtraContext,
|
|
315
306
|
containerFormView: any,
|
|
316
307
|
submitButtonWrapper: any
|
|
317
|
-
}, "api" | "actualForm">
|
|
308
|
+
}, "api" | "parser" | "actualForm">
|
|
318
309
|
|
|
319
310
|
export type CreateLauncherContext<Entity, FormState, ExtraContext> =
|
|
320
311
|
Omit<
|
|
@@ -350,7 +341,7 @@ export type ParsedLaunchers = {
|
|
|
350
341
|
}>
|
|
351
342
|
}
|
|
352
343
|
export type ParsedForms = Map<string, ParsedForm & { form: EntityFormTemplate<any, any, any, any, any> }>
|
|
353
|
-
export type FormParsingErrors =
|
|
344
|
+
export type FormParsingErrors = List<string>
|
|
354
345
|
export type FormParsingResult = Sum<ParsedLaunchers, FormParsingErrors>
|
|
355
346
|
export type StreamName = string
|
|
356
347
|
export type InfiniteStreamSources = BasicFun<StreamName, SearchableInfiniteStreamState<CollectionReference>["getChunk"]>
|
|
@@ -379,7 +370,7 @@ export const parseForms =
|
|
|
379
370
|
leafPredicates: LeafPredicates) =>
|
|
380
371
|
(formsConfig: FormsConfig):
|
|
381
372
|
FormParsingResult => {
|
|
382
|
-
let errors: FormParsingErrors =
|
|
373
|
+
let errors: FormParsingErrors = List()
|
|
383
374
|
let seen = Set<string>()
|
|
384
375
|
let formProcessingOrder = OrderedSet<string>()
|
|
385
376
|
|
|
@@ -474,7 +465,7 @@ export const parseForms =
|
|
|
474
465
|
}
|
|
475
466
|
})
|
|
476
467
|
|
|
477
|
-
if (errors.
|
|
468
|
+
if (errors.size > 0) {
|
|
478
469
|
return Sum.Default.right(errors)
|
|
479
470
|
}
|
|
480
471
|
|
|
@@ -486,9 +477,8 @@ export const parseForms =
|
|
|
486
477
|
get: (id: string) => entityApis.get(launcher.api)(id).then((raw: any) => {
|
|
487
478
|
return fromAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(raw)
|
|
488
479
|
}),
|
|
489
|
-
update: (id:
|
|
490
|
-
|
|
491
|
-
return raw.kind == "errors" ? Promise.reject(raw.errors) : entityApis.update(launcher.api)(id, raw.value)
|
|
480
|
+
update: (id: any, parsed: any) => {
|
|
481
|
+
return entityApis.update(launcher.api)(id, parsed)
|
|
492
482
|
}
|
|
493
483
|
}
|
|
494
484
|
parsedLaunchers.edit = parsedLaunchers.edit.set(
|
|
@@ -498,6 +488,7 @@ export const parseForms =
|
|
|
498
488
|
({
|
|
499
489
|
...parentContext,
|
|
500
490
|
api: api,
|
|
491
|
+
parser: (value: any, formState: any) => toAPIRawValue({ kind: "lookup", name: parsedForm.formDef.type }, formsConfig.types, builtIns, apiConverters, false, injectedPrimitives)(value, formState),
|
|
501
492
|
actualForm: form.withView(containerFormView).mapContext((_: any) => ({ ..._, rootValue: _.value, ...parentContext.extraContext }))
|
|
502
493
|
}) as any)
|
|
503
494
|
.withViewFromProps(props => props.context.submitButtonWrapper)
|
|
@@ -565,7 +556,7 @@ export const parseForms =
|
|
|
565
556
|
)
|
|
566
557
|
})
|
|
567
558
|
|
|
568
|
-
if (errors.
|
|
559
|
+
if (errors.size > 0) {
|
|
569
560
|
return Sum.Default.right(errors)
|
|
570
561
|
}
|
|
571
562
|
return Sum.Default.left(parsedLaunchers)
|
|
@@ -8,44 +8,44 @@ import { ListFieldState, ListFieldView } from "./state";
|
|
|
8
8
|
|
|
9
9
|
export const ListForm = <Element, ElementFormState, Context extends FormLabel, ForeignMutationsExpected>(
|
|
10
10
|
ElementFormState: { Default: () => ElementFormState },
|
|
11
|
-
Element: { Default: () => Element },
|
|
11
|
+
Element: { Default: () => Element | undefined},
|
|
12
12
|
elementTemplate: Template<
|
|
13
|
-
Context & Value<Element> & ElementFormState,
|
|
13
|
+
Context & Value<Element | undefined> & ElementFormState,
|
|
14
14
|
ElementFormState,
|
|
15
15
|
ForeignMutationsExpected & {
|
|
16
|
-
onChange: OnChange<Element>;
|
|
16
|
+
onChange: OnChange<Element | undefined>;
|
|
17
17
|
}>,
|
|
18
|
-
validation?: BasicFun<List<Element>, Promise<FieldValidation>>,
|
|
18
|
+
validation?: BasicFun<List<Element | undefined>, Promise<FieldValidation>>,
|
|
19
19
|
) => {
|
|
20
20
|
const embeddedElementTemplate = (elementIndex:number) =>
|
|
21
21
|
elementTemplate
|
|
22
22
|
.mapForeignMutationsFromProps<ForeignMutationsExpected & {
|
|
23
|
-
onChange: OnChange<List<Element>>;
|
|
23
|
+
onChange: OnChange<List<Element| undefined>>;
|
|
24
24
|
add: SimpleCallback<Unit>;
|
|
25
|
-
remove: SimpleCallback<number>;}>((props): ForeignMutationsExpected & {onChange: OnChange<Element>} => ({
|
|
25
|
+
remove: SimpleCallback<number>;}>((props): ForeignMutationsExpected & {onChange: OnChange<Element | undefined>} => ({
|
|
26
26
|
...props.foreignMutations,
|
|
27
27
|
onChange: (elementUpdater, path) => {
|
|
28
|
-
props.foreignMutations.onChange(Updater((elements:List<Element>) =>
|
|
29
|
-
elements.
|
|
28
|
+
props.foreignMutations.onChange(Updater((elements:List<Element | undefined>) =>
|
|
29
|
+
elements.has(elementIndex) ? elements.update(elementIndex, undefined, elementUpdater) : elements), path)
|
|
30
30
|
props.setState(_ => ({..._,
|
|
31
31
|
modifiedByUser:true,
|
|
32
32
|
})) },
|
|
33
33
|
add: (newElement: Element) => { },
|
|
34
34
|
remove: (elementIndex: number) => { }
|
|
35
35
|
}))
|
|
36
|
-
.mapContext((_: Context & Value<List<Element>> & ListFieldState<Element, ElementFormState>) : (Context & Value<Element> & ElementFormState) | undefined => {
|
|
36
|
+
.mapContext((_: Context & Value<List<Element | undefined>> & ListFieldState<Element | undefined, ElementFormState>) : (Context & Value<Element | undefined> & ElementFormState) | undefined => {
|
|
37
|
+
if(!_.value.has(elementIndex)) return undefined
|
|
37
38
|
const element = _.value.get(elementIndex)
|
|
38
|
-
if (element == undefined) return undefined
|
|
39
39
|
const elementFormState = _.elementFormStates.get(elementIndex) || ElementFormState.Default()
|
|
40
|
-
const elementContext : Context & Value<Element> & ElementFormState = ({ ..._, ...elementFormState, value: element })
|
|
40
|
+
const elementContext : Context & Value<Element | undefined> & ElementFormState = ({ ..._, ...elementFormState, value: element })
|
|
41
41
|
return elementContext
|
|
42
42
|
})
|
|
43
|
-
.mapState((_:BasicUpdater<ElementFormState>) : Updater<ListFieldState<Element, ElementFormState>> =>
|
|
44
|
-
ListFieldState<Element, ElementFormState>().Updaters.Core.elementFormStates(
|
|
43
|
+
.mapState((_:BasicUpdater<ElementFormState>) : Updater<ListFieldState<Element | undefined, ElementFormState>> =>
|
|
44
|
+
ListFieldState<Element | undefined, ElementFormState>().Updaters.Core.elementFormStates(
|
|
45
45
|
MapRepo.Updaters.upsert(elementIndex, () => ElementFormState.Default(), _)
|
|
46
46
|
))
|
|
47
|
-
return Template.Default<Context & Value<List<Element>> & { disabled: boolean }, ListFieldState<Element, ElementFormState>, ForeignMutationsExpected & { onChange: OnChange<List<Element>>; },
|
|
48
|
-
ListFieldView<Element, ElementFormState, Context, ForeignMutationsExpected>>(props => <>
|
|
47
|
+
return Template.Default<Context & Value<List<Element | undefined>> & { disabled: boolean }, ListFieldState<Element | undefined, ElementFormState>, ForeignMutationsExpected & { onChange: OnChange<List<Element | undefined>>; },
|
|
48
|
+
ListFieldView<Element | undefined, ElementFormState, Context, ForeignMutationsExpected>>(props => <>
|
|
49
49
|
<props.view {...props}
|
|
50
50
|
context={{
|
|
51
51
|
...props.context,
|
|
@@ -54,7 +54,7 @@ export const ListForm = <Element, ElementFormState, Context extends FormLabel, F
|
|
|
54
54
|
...props.foreignMutations,
|
|
55
55
|
add: (_) => {
|
|
56
56
|
props.foreignMutations.onChange(
|
|
57
|
-
ListRepo.Updaters.push(Element.Default()), List()
|
|
57
|
+
ListRepo.Updaters.push<Element | undefined>(Element.Default()), List()
|
|
58
58
|
)
|
|
59
59
|
},
|
|
60
60
|
remove: (_) => {
|
|
@@ -67,7 +67,7 @@ export const ListForm = <Element, ElementFormState, Context extends FormLabel, F
|
|
|
67
67
|
/>
|
|
68
68
|
</>
|
|
69
69
|
).any([
|
|
70
|
-
ValidateRunner<Context & { disabled: boolean }, ListFieldState<Element, ElementFormState>, ForeignMutationsExpected, List<Element>>(
|
|
70
|
+
ValidateRunner<Context & { disabled: boolean }, ListFieldState<Element | undefined, ElementFormState>, ForeignMutationsExpected, List<Element | undefined>>(
|
|
71
71
|
validation ? _ => validation(_).then(FieldValidationWithPath.Default.fromFieldValidation) : undefined
|
|
72
72
|
),
|
|
73
73
|
]);
|