@yuneta/gobj-ui 7.26.22 → 7.26.24
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/dist/gobj-ui.cjs.js +69 -13
- package/dist/gobj-ui.es.js +69 -13
- package/package.json +1 -1
- package/src/c_yui_schema_editor.js +33 -5
- package/src/schema_editor_reload.wiring.test.js +27 -0
- package/src/schema_import.js +38 -0
- package/src/schema_import.test.js +47 -0
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -73618,6 +73618,11 @@ function plan_import(treedb, incoming, options) {
|
|
|
73618
73618
|
conflicts
|
|
73619
73619
|
};
|
|
73620
73620
|
}
|
|
73621
|
+
if (topic_name_breaks_the_rule(treedb_id)) conflicts.push({
|
|
73622
|
+
code: "treedb name breaks the name rule",
|
|
73623
|
+
topic: "",
|
|
73624
|
+
col: ""
|
|
73625
|
+
});
|
|
73621
73626
|
for (let in_topic of incoming.topics) {
|
|
73622
73627
|
let name = (in_topic || {}).id;
|
|
73623
73628
|
if (name && topic_name_breaks_the_rule(name)) conflicts.push({
|
|
@@ -73625,11 +73630,40 @@ function plan_import(treedb, incoming, options) {
|
|
|
73625
73630
|
topic: name,
|
|
73626
73631
|
col: ""
|
|
73627
73632
|
});
|
|
73628
|
-
|
|
73629
|
-
|
|
73630
|
-
|
|
73631
|
-
|
|
73632
|
-
|
|
73633
|
+
let cols = (in_topic || {}).cols;
|
|
73634
|
+
if (Array.isArray(cols)) {
|
|
73635
|
+
for (let c of cols) if (!c || !c.id) conflicts.push({
|
|
73636
|
+
code: "column has no name",
|
|
73637
|
+
topic: name || "",
|
|
73638
|
+
col: ""
|
|
73639
|
+
});
|
|
73640
|
+
}
|
|
73641
|
+
for (let in_col of incoming_cols(in_topic || {})) {
|
|
73642
|
+
if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
|
|
73643
|
+
code: "column name breaks the name rule",
|
|
73644
|
+
topic: name || "",
|
|
73645
|
+
col: in_col.name
|
|
73646
|
+
});
|
|
73647
|
+
let inner_id = (in_col.col || {}).id;
|
|
73648
|
+
if (typeof inner_id === "string" && inner_id !== in_col.name && col_name_breaks_the_rule(inner_id)) conflicts.push({
|
|
73649
|
+
code: "column name breaks the name rule",
|
|
73650
|
+
topic: name || "",
|
|
73651
|
+
col: inner_id
|
|
73652
|
+
});
|
|
73653
|
+
let hook = (in_col.col || {}).hook;
|
|
73654
|
+
if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
|
|
73655
|
+
if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
|
|
73656
|
+
code: "topic name breaks the name rule",
|
|
73657
|
+
topic: child_topic,
|
|
73658
|
+
col: ""
|
|
73659
|
+
});
|
|
73660
|
+
if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
|
|
73661
|
+
code: "column name breaks the name rule",
|
|
73662
|
+
topic: child_topic,
|
|
73663
|
+
col: String(fkey_col)
|
|
73664
|
+
});
|
|
73665
|
+
}
|
|
73666
|
+
}
|
|
73633
73667
|
}
|
|
73634
73668
|
if (conflicts.length > 0) return {
|
|
73635
73669
|
writes,
|
|
@@ -74436,12 +74470,22 @@ function mt_destroy$1(gobj) {
|
|
|
74436
74470
|
* name field is marked aria-invalid for a reader.
|
|
74437
74471
|
************************************************************/
|
|
74438
74472
|
function refuse_bad_name(gobj, $message) {
|
|
74439
|
-
|
|
74440
|
-
let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
|
|
74441
|
-
if ($value) $value.setAttribute("aria-invalid", "true");
|
|
74473
|
+
mark_name_field(gobj, true);
|
|
74442
74474
|
yui_shell_show_error(yui_shell_of(gobj), $message, { t: i18next.t });
|
|
74443
74475
|
return -1;
|
|
74444
74476
|
}
|
|
74477
|
+
/************************************************************
|
|
74478
|
+
* The name field says whether the LAST save refused its name:
|
|
74479
|
+
* set by every refusal of the name (empty, a rule, a duplicate),
|
|
74480
|
+
* cleared by a save that gets past them.
|
|
74481
|
+
************************************************************/
|
|
74482
|
+
function mark_name_field(gobj, invalid) {
|
|
74483
|
+
let priv = gobj.priv;
|
|
74484
|
+
let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
|
|
74485
|
+
if (!$value) return;
|
|
74486
|
+
if (invalid) $value.setAttribute("aria-invalid", "true");
|
|
74487
|
+
else $value.removeAttribute("aria-invalid");
|
|
74488
|
+
}
|
|
74445
74489
|
/***************************************************************
|
|
74446
74490
|
* The frame: a toolbar that never changes place, a notice line
|
|
74447
74491
|
* for what the whole view has to say, and one body the screens
|
|
@@ -76925,13 +76969,19 @@ function render_import_plan(gobj, plan) {
|
|
|
76925
76969
|
"div",
|
|
76926
76970
|
{ class: "SCHEMA_IMPORT_CONFLICTS notification is-danger is-light p-3" },
|
|
76927
76971
|
plan.conflicts.map((conflict) => {
|
|
76972
|
+
let where = conflict.col ? `${conflict.topic}.${conflict.col}` : conflict.topic;
|
|
76928
76973
|
return [
|
|
76929
76974
|
"p",
|
|
76930
|
-
{
|
|
76931
|
-
|
|
76932
|
-
|
|
76933
|
-
|
|
76934
|
-
|
|
76975
|
+
{ class: "SCHEMA_IMPORT_CONFLICT is-size-7" },
|
|
76976
|
+
[[
|
|
76977
|
+
"span",
|
|
76978
|
+
{ i18n: conflict.code },
|
|
76979
|
+
(0, i18next.t)(conflict.code)
|
|
76980
|
+
], where ? [
|
|
76981
|
+
"code",
|
|
76982
|
+
{ class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
|
|
76983
|
+
text_node(where)
|
|
76984
|
+
] : null].filter(Boolean)
|
|
76935
76985
|
];
|
|
76936
76986
|
})
|
|
76937
76987
|
]));
|
|
@@ -77954,6 +78004,7 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77954
78004
|
}
|
|
77955
78005
|
let values = kw.values || {};
|
|
77956
78006
|
if (kw.creating && (0, _yuneta_gobj_js.empty_string)(String(values.value || "").trim())) {
|
|
78007
|
+
mark_name_field(gobj, true);
|
|
77957
78008
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t: i18next.t });
|
|
77958
78009
|
return -1;
|
|
77959
78010
|
}
|
|
@@ -77963,9 +78014,11 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77963
78014
|
(0, i18next.t)("invalid column name")
|
|
77964
78015
|
]));
|
|
77965
78016
|
if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
78017
|
+
mark_name_field(gobj, true);
|
|
77966
78018
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t: i18next.t });
|
|
77967
78019
|
return -1;
|
|
77968
78020
|
}
|
|
78021
|
+
mark_name_field(gobj, false);
|
|
77969
78022
|
close_dialog(gobj);
|
|
77970
78023
|
return queue_writes(gobj, [{
|
|
77971
78024
|
op: kw.creating ? "create" : "update",
|
|
@@ -78213,6 +78266,7 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78213
78266
|
if (kw.creating) {
|
|
78214
78267
|
let name = String(values.value || "").trim();
|
|
78215
78268
|
if ((0, _yuneta_gobj_js.empty_string)(name)) {
|
|
78269
|
+
mark_name_field(gobj, true);
|
|
78216
78270
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t: i18next.t });
|
|
78217
78271
|
return -1;
|
|
78218
78272
|
}
|
|
@@ -78222,9 +78276,11 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78222
78276
|
(0, i18next.t)("invalid topic name")
|
|
78223
78277
|
]));
|
|
78224
78278
|
if (find_topic(priv.model, priv.treedb_id, name)) {
|
|
78279
|
+
mark_name_field(gobj, true);
|
|
78225
78280
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t: i18next.t });
|
|
78226
78281
|
return -1;
|
|
78227
78282
|
}
|
|
78283
|
+
mark_name_field(gobj, false);
|
|
78228
78284
|
}
|
|
78229
78285
|
close_dialog(gobj);
|
|
78230
78286
|
return queue_writes(gobj, [{
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -73613,6 +73613,11 @@ function plan_import(treedb, incoming, options) {
|
|
|
73613
73613
|
conflicts
|
|
73614
73614
|
};
|
|
73615
73615
|
}
|
|
73616
|
+
if (topic_name_breaks_the_rule(treedb_id)) conflicts.push({
|
|
73617
|
+
code: "treedb name breaks the name rule",
|
|
73618
|
+
topic: "",
|
|
73619
|
+
col: ""
|
|
73620
|
+
});
|
|
73616
73621
|
for (let in_topic of incoming.topics) {
|
|
73617
73622
|
let name = (in_topic || {}).id;
|
|
73618
73623
|
if (name && topic_name_breaks_the_rule(name)) conflicts.push({
|
|
@@ -73620,11 +73625,40 @@ function plan_import(treedb, incoming, options) {
|
|
|
73620
73625
|
topic: name,
|
|
73621
73626
|
col: ""
|
|
73622
73627
|
});
|
|
73623
|
-
|
|
73624
|
-
|
|
73625
|
-
|
|
73626
|
-
|
|
73627
|
-
|
|
73628
|
+
let cols = (in_topic || {}).cols;
|
|
73629
|
+
if (Array.isArray(cols)) {
|
|
73630
|
+
for (let c of cols) if (!c || !c.id) conflicts.push({
|
|
73631
|
+
code: "column has no name",
|
|
73632
|
+
topic: name || "",
|
|
73633
|
+
col: ""
|
|
73634
|
+
});
|
|
73635
|
+
}
|
|
73636
|
+
for (let in_col of incoming_cols(in_topic || {})) {
|
|
73637
|
+
if (col_name_breaks_the_rule(in_col.name)) conflicts.push({
|
|
73638
|
+
code: "column name breaks the name rule",
|
|
73639
|
+
topic: name || "",
|
|
73640
|
+
col: in_col.name
|
|
73641
|
+
});
|
|
73642
|
+
let inner_id = (in_col.col || {}).id;
|
|
73643
|
+
if (typeof inner_id === "string" && inner_id !== in_col.name && col_name_breaks_the_rule(inner_id)) conflicts.push({
|
|
73644
|
+
code: "column name breaks the name rule",
|
|
73645
|
+
topic: name || "",
|
|
73646
|
+
col: inner_id
|
|
73647
|
+
});
|
|
73648
|
+
let hook = (in_col.col || {}).hook;
|
|
73649
|
+
if (hook && typeof hook === "object" && !Array.isArray(hook)) for (let [child_topic, fkey_col] of Object.entries(hook)) {
|
|
73650
|
+
if (topic_name_breaks_the_rule(child_topic)) conflicts.push({
|
|
73651
|
+
code: "topic name breaks the name rule",
|
|
73652
|
+
topic: child_topic,
|
|
73653
|
+
col: ""
|
|
73654
|
+
});
|
|
73655
|
+
if (col_name_breaks_the_rule(fkey_col)) conflicts.push({
|
|
73656
|
+
code: "column name breaks the name rule",
|
|
73657
|
+
topic: child_topic,
|
|
73658
|
+
col: String(fkey_col)
|
|
73659
|
+
});
|
|
73660
|
+
}
|
|
73661
|
+
}
|
|
73628
73662
|
}
|
|
73629
73663
|
if (conflicts.length > 0) return {
|
|
73630
73664
|
writes,
|
|
@@ -74431,12 +74465,22 @@ function mt_destroy$1(gobj) {
|
|
|
74431
74465
|
* name field is marked aria-invalid for a reader.
|
|
74432
74466
|
************************************************************/
|
|
74433
74467
|
function refuse_bad_name(gobj, $message) {
|
|
74434
|
-
|
|
74435
|
-
let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
|
|
74436
|
-
if ($value) $value.setAttribute("aria-invalid", "true");
|
|
74468
|
+
mark_name_field(gobj, true);
|
|
74437
74469
|
yui_shell_show_error(yui_shell_of(gobj), $message, { t });
|
|
74438
74470
|
return -1;
|
|
74439
74471
|
}
|
|
74472
|
+
/************************************************************
|
|
74473
|
+
* The name field says whether the LAST save refused its name:
|
|
74474
|
+
* set by every refusal of the name (empty, a rule, a duplicate),
|
|
74475
|
+
* cleared by a save that gets past them.
|
|
74476
|
+
************************************************************/
|
|
74477
|
+
function mark_name_field(gobj, invalid) {
|
|
74478
|
+
let priv = gobj.priv;
|
|
74479
|
+
let $value = priv.dialog_content ? priv.dialog_content.querySelector("[data-name=\"value\"]") : null;
|
|
74480
|
+
if (!$value) return;
|
|
74481
|
+
if (invalid) $value.setAttribute("aria-invalid", "true");
|
|
74482
|
+
else $value.removeAttribute("aria-invalid");
|
|
74483
|
+
}
|
|
74440
74484
|
/***************************************************************
|
|
74441
74485
|
* The frame: a toolbar that never changes place, a notice line
|
|
74442
74486
|
* for what the whole view has to say, and one body the screens
|
|
@@ -76920,13 +76964,19 @@ function render_import_plan(gobj, plan) {
|
|
|
76920
76964
|
"div",
|
|
76921
76965
|
{ class: "SCHEMA_IMPORT_CONFLICTS notification is-danger is-light p-3" },
|
|
76922
76966
|
plan.conflicts.map((conflict) => {
|
|
76967
|
+
let where = conflict.col ? `${conflict.topic}.${conflict.col}` : conflict.topic;
|
|
76923
76968
|
return [
|
|
76924
76969
|
"p",
|
|
76925
|
-
{
|
|
76926
|
-
|
|
76927
|
-
|
|
76928
|
-
|
|
76929
|
-
|
|
76970
|
+
{ class: "SCHEMA_IMPORT_CONFLICT is-size-7" },
|
|
76971
|
+
[[
|
|
76972
|
+
"span",
|
|
76973
|
+
{ i18n: conflict.code },
|
|
76974
|
+
t(conflict.code)
|
|
76975
|
+
], where ? [
|
|
76976
|
+
"code",
|
|
76977
|
+
{ class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2" },
|
|
76978
|
+
text_node(where)
|
|
76979
|
+
] : null].filter(Boolean)
|
|
76930
76980
|
];
|
|
76931
76981
|
})
|
|
76932
76982
|
]));
|
|
@@ -77949,6 +77999,7 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77949
77999
|
}
|
|
77950
78000
|
let values = kw.values || {};
|
|
77951
78001
|
if (kw.creating && empty_string(String(values.value || "").trim())) {
|
|
78002
|
+
mark_name_field(gobj, true);
|
|
77952
78003
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", { t });
|
|
77953
78004
|
return -1;
|
|
77954
78005
|
}
|
|
@@ -77958,9 +78009,11 @@ function ac_save_column(gobj, event, kw, src) {
|
|
|
77958
78009
|
t("invalid column name")
|
|
77959
78010
|
]));
|
|
77960
78011
|
if (kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
78012
|
+
mark_name_field(gobj, true);
|
|
77961
78013
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", { t });
|
|
77962
78014
|
return -1;
|
|
77963
78015
|
}
|
|
78016
|
+
mark_name_field(gobj, false);
|
|
77964
78017
|
close_dialog(gobj);
|
|
77965
78018
|
return queue_writes(gobj, [{
|
|
77966
78019
|
op: kw.creating ? "create" : "update",
|
|
@@ -78208,6 +78261,7 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78208
78261
|
if (kw.creating) {
|
|
78209
78262
|
let name = String(values.value || "").trim();
|
|
78210
78263
|
if (empty_string(name)) {
|
|
78264
|
+
mark_name_field(gobj, true);
|
|
78211
78265
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", { t });
|
|
78212
78266
|
return -1;
|
|
78213
78267
|
}
|
|
@@ -78217,9 +78271,11 @@ function ac_save_topic(gobj, event, kw, src) {
|
|
|
78217
78271
|
t("invalid topic name")
|
|
78218
78272
|
]));
|
|
78219
78273
|
if (find_topic(priv.model, priv.treedb_id, name)) {
|
|
78274
|
+
mark_name_field(gobj, true);
|
|
78220
78275
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", { t });
|
|
78221
78276
|
return -1;
|
|
78222
78277
|
}
|
|
78278
|
+
mark_name_field(gobj, false);
|
|
78223
78279
|
}
|
|
78224
78280
|
close_dialog(gobj);
|
|
78225
78281
|
return queue_writes(gobj, [{
|
package/package.json
CHANGED
|
@@ -371,15 +371,30 @@ function mt_destroy(gobj)
|
|
|
371
371
|
* name field is marked aria-invalid for a reader.
|
|
372
372
|
************************************************************/
|
|
373
373
|
function refuse_bad_name(gobj, $message)
|
|
374
|
+
{
|
|
375
|
+
mark_name_field(gobj, true);
|
|
376
|
+
yui_shell_show_error(yui_shell_of(gobj), $message, {t: t});
|
|
377
|
+
return -1;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/************************************************************
|
|
381
|
+
* The name field says whether the LAST save refused its name:
|
|
382
|
+
* set by every refusal of the name (empty, a rule, a duplicate),
|
|
383
|
+
* cleared by a save that gets past them.
|
|
384
|
+
************************************************************/
|
|
385
|
+
function mark_name_field(gobj, invalid)
|
|
374
386
|
{
|
|
375
387
|
let priv = gobj.priv;
|
|
376
388
|
let $value = priv.dialog_content?
|
|
377
389
|
priv.dialog_content.querySelector('[data-name="value"]') : null;
|
|
378
|
-
if(
|
|
390
|
+
if(!$value) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if(invalid) {
|
|
379
394
|
$value.setAttribute("aria-invalid", "true");
|
|
395
|
+
} else {
|
|
396
|
+
$value.removeAttribute("aria-invalid");
|
|
380
397
|
}
|
|
381
|
-
yui_shell_show_error(yui_shell_of(gobj), $message, {t: t});
|
|
382
|
-
return -1;
|
|
383
398
|
}
|
|
384
399
|
|
|
385
400
|
/***************************************************************
|
|
@@ -2669,8 +2684,15 @@ function render_import_plan(gobj, plan)
|
|
|
2669
2684
|
$pane.appendChild(createElement2(
|
|
2670
2685
|
["div", {class: "SCHEMA_IMPORT_CONFLICTS notification is-danger is-light p-3"},
|
|
2671
2686
|
plan.conflicts.map((conflict) => {
|
|
2672
|
-
|
|
2673
|
-
|
|
2687
|
+
/* What the conflict is about, said: two bad entries of
|
|
2688
|
+
* one hook map were two identical lines. */
|
|
2689
|
+
let where = conflict.col
|
|
2690
|
+
? `${conflict.topic}.${conflict.col}`
|
|
2691
|
+
: conflict.topic;
|
|
2692
|
+
return ["p", {class: "SCHEMA_IMPORT_CONFLICT is-size-7"}, [
|
|
2693
|
+
["span", {i18n: conflict.code}, t(conflict.code)],
|
|
2694
|
+
where ? ["code", {class: "SCHEMA_IMPORT_CONFLICT_NAME ml-2"}, text_node(where)] : null
|
|
2695
|
+
].filter(Boolean)];
|
|
2674
2696
|
})]
|
|
2675
2697
|
));
|
|
2676
2698
|
if(priv.import_run) {
|
|
@@ -4047,6 +4069,7 @@ function ac_save_column(gobj, event, kw, src)
|
|
|
4047
4069
|
|
|
4048
4070
|
let values = kw.values || {};
|
|
4049
4071
|
if(kw.creating && empty_string(String(values.value || "").trim())) {
|
|
4072
|
+
mark_name_field(gobj, true);
|
|
4050
4073
|
yui_shell_show_error(yui_shell_of(gobj), "a column needs a name", {t: t});
|
|
4051
4074
|
return -1;
|
|
4052
4075
|
}
|
|
@@ -4055,9 +4078,11 @@ function ac_save_column(gobj, event, kw, src)
|
|
|
4055
4078
|
["span", {i18n: "invalid column name"}, t("invalid column name")]));
|
|
4056
4079
|
}
|
|
4057
4080
|
if(kw.creating && find_col(priv.model, priv.treedb_id, kw.topic, String(values.value).trim())) {
|
|
4081
|
+
mark_name_field(gobj, true);
|
|
4058
4082
|
yui_shell_show_error(yui_shell_of(gobj), "that column is already there", {t: t});
|
|
4059
4083
|
return -1;
|
|
4060
4084
|
}
|
|
4085
|
+
mark_name_field(gobj, false);
|
|
4061
4086
|
|
|
4062
4087
|
close_dialog(gobj);
|
|
4063
4088
|
return queue_writes(gobj, [{
|
|
@@ -4358,6 +4383,7 @@ function ac_save_topic(gobj, event, kw, src)
|
|
|
4358
4383
|
if(kw.creating) {
|
|
4359
4384
|
let name = String(values.value || "").trim();
|
|
4360
4385
|
if(empty_string(name)) {
|
|
4386
|
+
mark_name_field(gobj, true);
|
|
4361
4387
|
yui_shell_show_error(yui_shell_of(gobj), "a topic needs a name", {t: t});
|
|
4362
4388
|
return -1;
|
|
4363
4389
|
}
|
|
@@ -4366,9 +4392,11 @@ function ac_save_topic(gobj, event, kw, src)
|
|
|
4366
4392
|
["span", {i18n: "invalid topic name"}, t("invalid topic name")]));
|
|
4367
4393
|
}
|
|
4368
4394
|
if(find_topic(priv.model, priv.treedb_id, name)) {
|
|
4395
|
+
mark_name_field(gobj, true);
|
|
4369
4396
|
yui_shell_show_error(yui_shell_of(gobj), "that topic is already there", {t: t});
|
|
4370
4397
|
return -1;
|
|
4371
4398
|
}
|
|
4399
|
+
mark_name_field(gobj, false);
|
|
4372
4400
|
}
|
|
4373
4401
|
|
|
4374
4402
|
close_dialog(gobj);
|
|
@@ -1748,3 +1748,30 @@ describe("names the treedb refuses", () => {
|
|
|
1748
1748
|
}
|
|
1749
1749
|
});
|
|
1750
1750
|
});
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
/*
|
|
1754
|
+
* The name field says whether the LAST save refused its name (REV-CLOUD
|
|
1755
|
+
* V15.4): set by every refusal of the name, cleared by a save that gets
|
|
1756
|
+
* past them. It was set by the rule only, and never cleared.
|
|
1757
|
+
*/
|
|
1758
|
+
describe("aria-invalid of the name field", () => {
|
|
1759
|
+
test("set by a bad name and by a duplicate, cleared by a good one", () => {
|
|
1760
|
+
const {editor, host} = build("aria_topic", "db");
|
|
1761
|
+
gobj_send_event(editor, "EV_ADD_TOPIC", {}, host);
|
|
1762
|
+
const form = modals[modals.length - 1];
|
|
1763
|
+
const $value = form.$content.querySelector('[data-name="value"]');
|
|
1764
|
+
const save = (name) => {
|
|
1765
|
+
gobj_send_event(editor, "EV_SAVE_TOPIC", {
|
|
1766
|
+
creating: true, topic: "", values: {value: name}
|
|
1767
|
+
}, host);
|
|
1768
|
+
};
|
|
1769
|
+
save("a/b");
|
|
1770
|
+
expect($value.getAttribute("aria-invalid")).toBe("true");
|
|
1771
|
+
save("users"); // a duplicate: the topic is there
|
|
1772
|
+
expect($value.getAttribute("aria-invalid")).toBe("true");
|
|
1773
|
+
save("ok_name");
|
|
1774
|
+
expect($value.getAttribute("aria-invalid")).toBe(null);
|
|
1775
|
+
take("update-node");
|
|
1776
|
+
});
|
|
1777
|
+
});
|
package/src/schema_import.js
CHANGED
|
@@ -211,15 +211,53 @@ function plan_import(treedb, incoming, options)
|
|
|
211
211
|
* stop the plan BEFORE any write: run through, the backend refused
|
|
212
212
|
* them half way, with the writes before them already done.
|
|
213
213
|
*/
|
|
214
|
+
if(topic_name_breaks_the_rule(treedb_id)) {
|
|
215
|
+
/* The treedb is a directory too (treedb_open_db() refuses it) */
|
|
216
|
+
conflicts.push({code: "treedb name breaks the name rule", topic: "", col: ""});
|
|
217
|
+
}
|
|
214
218
|
for(let in_topic of incoming.topics) {
|
|
215
219
|
let name = (in_topic || {}).id;
|
|
216
220
|
if(name && topic_name_breaks_the_rule(name)) {
|
|
217
221
|
conflicts.push({code: "topic name breaks the name rule", topic: name, col: ""});
|
|
218
222
|
}
|
|
223
|
+
let cols = (in_topic || {}).cols;
|
|
224
|
+
if(Array.isArray(cols)) {
|
|
225
|
+
/* incoming_cols() drops a column with no id: said, not dropped */
|
|
226
|
+
for(let c of cols) {
|
|
227
|
+
if(!c || !c.id) {
|
|
228
|
+
conflicts.push({code: "column has no name", topic: name || "", col: ""});
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
219
232
|
for(let in_col of incoming_cols(in_topic || {})) {
|
|
220
233
|
if(col_name_breaks_the_rule(in_col.name)) {
|
|
221
234
|
conflicts.push({code: "column name breaks the name rule", topic: name || "", col: in_col.name});
|
|
222
235
|
}
|
|
236
|
+
/* A dict column carries its id twice: the key (what is
|
|
237
|
+
* imported) and its own `id`. One that breaks the rule is said
|
|
238
|
+
* too, not imported under the key in silence. */
|
|
239
|
+
let inner_id = (in_col.col || {}).id;
|
|
240
|
+
if(typeof inner_id === "string" && inner_id !== in_col.name &&
|
|
241
|
+
col_name_breaks_the_rule(inner_id)) {
|
|
242
|
+
conflicts.push({code: "column name breaks the name rule", topic: name || "", col: inner_id});
|
|
243
|
+
}
|
|
244
|
+
/* The hook map names a child topic and its fkey column: names
|
|
245
|
+
* the treedb refuses too, even when no column declares them. */
|
|
246
|
+
let hook = (in_col.col || {}).hook;
|
|
247
|
+
if(hook && typeof hook === "object" && !Array.isArray(hook)) {
|
|
248
|
+
for(let [child_topic, fkey_col] of Object.entries(hook)) {
|
|
249
|
+
/* The conflict names the BAD name (the child topic, its
|
|
250
|
+
* fkey), not the hook column that maps to it. */
|
|
251
|
+
if(topic_name_breaks_the_rule(child_topic)) {
|
|
252
|
+
conflicts.push({code: "topic name breaks the name rule",
|
|
253
|
+
topic: child_topic, col: ""});
|
|
254
|
+
}
|
|
255
|
+
if(col_name_breaks_the_rule(fkey_col)) {
|
|
256
|
+
conflicts.push({code: "column name breaks the name rule",
|
|
257
|
+
topic: child_topic, col: String(fkey_col)});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
223
261
|
}
|
|
224
262
|
}
|
|
225
263
|
if(conflicts.length > 0) {
|
|
@@ -282,3 +282,50 @@ describe("names the treedb refuses stop the plan before any write (REV-CLOUD V14
|
|
|
282
282
|
expect(plan.writes.length).toBeGreaterThan(0);
|
|
283
283
|
});
|
|
284
284
|
});
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
describe("the import reads the hook map and the treedb id (REV-CLOUD V15.4)", () => {
|
|
288
|
+
test("a hook to an fkey x`y or to a topic a/b that no column declares: conflicts", () => {
|
|
289
|
+
let plan = plan_import(null, {
|
|
290
|
+
id: "db", schema_version: "1",
|
|
291
|
+
topics: [{id: "p", pkey: "id", cols: {
|
|
292
|
+
"id": {id: "id", type: "string"},
|
|
293
|
+
"kids": {id: "kids", type: "dict", flag: ["hook"], hook: {"k": "x`y", "a/b": "pid"}},
|
|
294
|
+
}}]
|
|
295
|
+
});
|
|
296
|
+
expect(plan.writes).toEqual([]);
|
|
297
|
+
let codes = plan.conflicts.map((c) => c.code);
|
|
298
|
+
expect(codes).toContain("column name breaks the name rule");
|
|
299
|
+
expect(codes).toContain("topic name breaks the name rule");
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("a treedb a/b, and a column with no id in an array: conflicts, not dropped", () => {
|
|
303
|
+
let plan = plan_import(null, {
|
|
304
|
+
id: "a/b", schema_version: "1",
|
|
305
|
+
topics: [{id: "t", pkey: "id", cols: [{id: "id", type: "string"}, {id: "", type: "string"}]}]
|
|
306
|
+
});
|
|
307
|
+
expect(plan.writes).toEqual([]);
|
|
308
|
+
let codes = plan.conflicts.map((c) => c.code);
|
|
309
|
+
expect(codes).toContain("treedb name breaks the name rule");
|
|
310
|
+
expect(codes).toContain("column has no name");
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
describe("a conflict names the bad name (REV-CLOUD V16.5)", () => {
|
|
316
|
+
test("the child topic and the fkey of a hook map, and a dict column's inner id", () => {
|
|
317
|
+
let plan = plan_import(null, {
|
|
318
|
+
id: "db", schema_version: "1",
|
|
319
|
+
topics: [{id: "p", pkey: "id", cols: {
|
|
320
|
+
"id": {id: "id", type: "string"},
|
|
321
|
+
"kids": {id: "kids", type: "dict", flag: ["hook"], hook: {"a/b": "x`y"}},
|
|
322
|
+
"good": {id: "go`od", type: "string"},
|
|
323
|
+
}}]
|
|
324
|
+
});
|
|
325
|
+
expect(plan.writes).toEqual([]);
|
|
326
|
+
let named = plan.conflicts.map((c) => `${c.code}|${c.topic}|${c.col}`);
|
|
327
|
+
expect(named).toContain("topic name breaks the name rule|a/b|");
|
|
328
|
+
expect(named).toContain("column name breaks the name rule|a/b|x`y");
|
|
329
|
+
expect(named).toContain("column name breaks the name rule|p|go`od");
|
|
330
|
+
});
|
|
331
|
+
});
|