@yuneta/gobj-ui 7.25.12 → 7.25.13
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/README.md +20 -0
- package/dist/gobj-ui.cjs.js +39 -6
- package/dist/gobj-ui.es.js +39 -6
- package/package.json +1 -1
- package/src/c_yui_treedb_topics.js +61 -10
- package/src/form_writes_in_flight.js +11 -3
- package/src/treedb_topics_transport.wiring.test.js +116 -3
package/README.md
CHANGED
|
@@ -819,6 +819,26 @@ A write is known by its topic AND its serial (`<topic>^<form_write>`): each
|
|
|
819
819
|
topic's form counts its serials from 1, and keyed by the serial alone two
|
|
820
820
|
forms saving at once shared one entry.
|
|
821
821
|
|
|
822
|
+
**A write cut by the drop is not a refusal** (7.25.13). A refused write
|
|
823
|
+
reads its topic again at once, so a cell edited in place goes back to what the
|
|
824
|
+
store has. A write that fails while the transport is NOT in session is the
|
|
825
|
+
drop: a routing adapter (gui_agent's `C_AGENT_TREEDB_LINK`) settles what it
|
|
826
|
+
had in flight when its session closes. The topic cannot be read then — the
|
|
827
|
+
transport refuses the read (*"cannot route 'nodes' -- not in session"*) — so
|
|
828
|
+
the view logs a warning and owes the read. Writes in flight on the
|
|
829
|
+
disconnect edge are owed the same way. The first edge that finds the
|
|
830
|
+
transport in session again pays it: each owed topic whose table is open is
|
|
831
|
+
read once. An `EV_CONNECTION_STATE` "up" that comes before the view's own
|
|
832
|
+
transport is in session leaves the read owed. A form is answered once, also
|
|
833
|
+
when the edge answered it before the failure arrived.
|
|
834
|
+
|
|
835
|
+
```js
|
|
836
|
+
// Session up: a cell edit goes out as update-node.
|
|
837
|
+
// Session drops; the adapter answers it {result: -1} -> no read, a warning
|
|
838
|
+
// EV_TRANSPORT_STATE {connected: true}, transport in session
|
|
839
|
+
// -> `nodes` of that topic, once
|
|
840
|
+
```
|
|
841
|
+
|
|
822
842
|
**The pkey2 of an update goes back as the record had it** (7.25.5). It names
|
|
823
843
|
the instance the write is for, and a `time` pkey2 that crossed the form's
|
|
824
844
|
`datetime-local` came back without its seconds — another instance, or none.
|
package/dist/gobj-ui.cjs.js
CHANGED
|
@@ -23160,11 +23160,17 @@ function track_form_write(in_flight, form_write, topic_name) {
|
|
|
23160
23160
|
};
|
|
23161
23161
|
}
|
|
23162
23162
|
/************************************************************
|
|
23163
|
-
* A write was answered, one way or the other
|
|
23163
|
+
* A write was answered, one way or the other. True when it was
|
|
23164
|
+
* still in flight: false when something else answered it already
|
|
23165
|
+
* (the transport closing, abandon_form_writes), so its form must
|
|
23166
|
+
* not be answered twice.
|
|
23164
23167
|
************************************************************/
|
|
23165
23168
|
function settle_form_write(in_flight, form_write, topic_name) {
|
|
23166
|
-
if (!in_flight || !form_write) return;
|
|
23167
|
-
|
|
23169
|
+
if (!in_flight || !form_write) return false;
|
|
23170
|
+
const key = form_write_key(topic_name, form_write);
|
|
23171
|
+
if (!Object.prototype.hasOwnProperty.call(in_flight, key)) return false;
|
|
23172
|
+
delete in_flight[key];
|
|
23173
|
+
return true;
|
|
23168
23174
|
}
|
|
23169
23175
|
/************************************************************
|
|
23170
23176
|
* Nothing in flight can be answered any more: take them all,
|
|
@@ -23751,6 +23757,7 @@ var __gclass__$6 = null;
|
|
|
23751
23757
|
***************************************************************/
|
|
23752
23758
|
function mt_create$6(gobj) {
|
|
23753
23759
|
gobj.priv.form_writes = {};
|
|
23760
|
+
gobj.priv.reload_on_session = {};
|
|
23754
23761
|
gobj.priv.conn_shell = null;
|
|
23755
23762
|
build_ui$6(gobj);
|
|
23756
23763
|
let subscriber = (0, _yuneta_gobj_js.gobj_read_pointer_attr)(gobj, "subscriber");
|
|
@@ -25181,9 +25188,15 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25181
25188
|
yui_shell_show_error(yui_shell_of(gobj), comment, { t: i18next.t });
|
|
25182
25189
|
if (command === "update-node" || command === "create-node") {
|
|
25183
25190
|
let failed_topic = (0, _yuneta_gobj_js.kw_get_str)(gobj, kw_command, "topic_name", "", 0);
|
|
25184
|
-
|
|
25185
|
-
|
|
25186
|
-
|
|
25191
|
+
let form_write = (0, _yuneta_gobj_js.kw_get_int)(gobj, kw_command, "form_write", 0, 0);
|
|
25192
|
+
if (failed_topic) {
|
|
25193
|
+
if (is_connected$1(gobj)) (0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
25194
|
+
else {
|
|
25195
|
+
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: the write of '${failed_topic}' was cut by the drop: the topic is read again when the session is back`);
|
|
25196
|
+
gobj.priv.reload_on_session[failed_topic] = true;
|
|
25197
|
+
}
|
|
25198
|
+
}
|
|
25199
|
+
if (settle_form_write(gobj.priv.form_writes, form_write, failed_topic)) answer_form_write(gobj, get_gobj_formtable(gobj, failed_topic), form_write, false);
|
|
25187
25200
|
}
|
|
25188
25201
|
}
|
|
25189
25202
|
return 0;
|
|
@@ -25383,11 +25396,31 @@ function ac_transport_state$2(gobj, event, kw, src) {
|
|
|
25383
25396
|
refresh_toolbar_buttons(gobj, connected);
|
|
25384
25397
|
if (!connected) abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
25385
25398
|
(0, _yuneta_gobj_js.log_warning)(`${(0, _yuneta_gobj_js.gobj_short_name)(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
25399
|
+
if (w.topic_name) gobj.priv.reload_on_session[w.topic_name] = true;
|
|
25386
25400
|
answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
|
|
25387
25401
|
});
|
|
25402
|
+
else reload_owed_topics(gobj);
|
|
25388
25403
|
return 0;
|
|
25389
25404
|
}
|
|
25390
25405
|
/************************************************************
|
|
25406
|
+
* The session is back: read again the topics a drop left
|
|
25407
|
+
* unknown. Only when the TRANSPORT says so too -- the app's
|
|
25408
|
+
* connection (EV_CONNECTION_STATE) can report "up" before the
|
|
25409
|
+
* transport of this view is in session, and a read asked then is
|
|
25410
|
+
* refused. What is owed stays owed until an edge finds the
|
|
25411
|
+
* transport in session; each topic is read once.
|
|
25412
|
+
************************************************************/
|
|
25413
|
+
function reload_owed_topics(gobj) {
|
|
25414
|
+
let priv = gobj.priv;
|
|
25415
|
+
if (!is_connected$1(gobj)) return;
|
|
25416
|
+
let topics = Object.keys(priv.reload_on_session);
|
|
25417
|
+
priv.reload_on_session = {};
|
|
25418
|
+
topics.forEach((topic_name) => {
|
|
25419
|
+
if (!get_gobj_formtable(gobj, topic_name)) return;
|
|
25420
|
+
(0, _yuneta_gobj_js.gobj_send_event)(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
|
|
25421
|
+
});
|
|
25422
|
+
}
|
|
25423
|
+
/************************************************************
|
|
25391
25424
|
* Parent (routing) inform us that we go hidden
|
|
25392
25425
|
************************************************************/
|
|
25393
25426
|
function ac_hide$4(gobj, event, kw, src) {
|
package/dist/gobj-ui.es.js
CHANGED
|
@@ -23155,11 +23155,17 @@ function track_form_write(in_flight, form_write, topic_name) {
|
|
|
23155
23155
|
};
|
|
23156
23156
|
}
|
|
23157
23157
|
/************************************************************
|
|
23158
|
-
* A write was answered, one way or the other
|
|
23158
|
+
* A write was answered, one way or the other. True when it was
|
|
23159
|
+
* still in flight: false when something else answered it already
|
|
23160
|
+
* (the transport closing, abandon_form_writes), so its form must
|
|
23161
|
+
* not be answered twice.
|
|
23159
23162
|
************************************************************/
|
|
23160
23163
|
function settle_form_write(in_flight, form_write, topic_name) {
|
|
23161
|
-
if (!in_flight || !form_write) return;
|
|
23162
|
-
|
|
23164
|
+
if (!in_flight || !form_write) return false;
|
|
23165
|
+
const key = form_write_key(topic_name, form_write);
|
|
23166
|
+
if (!Object.prototype.hasOwnProperty.call(in_flight, key)) return false;
|
|
23167
|
+
delete in_flight[key];
|
|
23168
|
+
return true;
|
|
23163
23169
|
}
|
|
23164
23170
|
/************************************************************
|
|
23165
23171
|
* Nothing in flight can be answered any more: take them all,
|
|
@@ -23746,6 +23752,7 @@ var __gclass__$6 = null;
|
|
|
23746
23752
|
***************************************************************/
|
|
23747
23753
|
function mt_create$6(gobj) {
|
|
23748
23754
|
gobj.priv.form_writes = {};
|
|
23755
|
+
gobj.priv.reload_on_session = {};
|
|
23749
23756
|
gobj.priv.conn_shell = null;
|
|
23750
23757
|
build_ui$6(gobj);
|
|
23751
23758
|
let subscriber = gobj_read_pointer_attr(gobj, "subscriber");
|
|
@@ -25176,9 +25183,15 @@ function ac_mt_command_answer$2(gobj, event, kw, src) {
|
|
|
25176
25183
|
yui_shell_show_error(yui_shell_of(gobj), comment, { t });
|
|
25177
25184
|
if (command === "update-node" || command === "create-node") {
|
|
25178
25185
|
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
25179
|
-
|
|
25180
|
-
|
|
25181
|
-
|
|
25186
|
+
let form_write = kw_get_int(gobj, kw_command, "form_write", 0, 0);
|
|
25187
|
+
if (failed_topic) {
|
|
25188
|
+
if (is_connected$1(gobj)) gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name: failed_topic }, gobj);
|
|
25189
|
+
else {
|
|
25190
|
+
log_warning(`${gobj_short_name(gobj)}: the write of '${failed_topic}' was cut by the drop: the topic is read again when the session is back`);
|
|
25191
|
+
gobj.priv.reload_on_session[failed_topic] = true;
|
|
25192
|
+
}
|
|
25193
|
+
}
|
|
25194
|
+
if (settle_form_write(gobj.priv.form_writes, form_write, failed_topic)) answer_form_write(gobj, get_gobj_formtable(gobj, failed_topic), form_write, false);
|
|
25182
25195
|
}
|
|
25183
25196
|
}
|
|
25184
25197
|
return 0;
|
|
@@ -25378,11 +25391,31 @@ function ac_transport_state$2(gobj, event, kw, src) {
|
|
|
25378
25391
|
refresh_toolbar_buttons(gobj, connected);
|
|
25379
25392
|
if (!connected) abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
25380
25393
|
log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
25394
|
+
if (w.topic_name) gobj.priv.reload_on_session[w.topic_name] = true;
|
|
25381
25395
|
answer_form_write(gobj, get_gobj_formtable(gobj, w.topic_name), w.form_write, false);
|
|
25382
25396
|
});
|
|
25397
|
+
else reload_owed_topics(gobj);
|
|
25383
25398
|
return 0;
|
|
25384
25399
|
}
|
|
25385
25400
|
/************************************************************
|
|
25401
|
+
* The session is back: read again the topics a drop left
|
|
25402
|
+
* unknown. Only when the TRANSPORT says so too -- the app's
|
|
25403
|
+
* connection (EV_CONNECTION_STATE) can report "up" before the
|
|
25404
|
+
* transport of this view is in session, and a read asked then is
|
|
25405
|
+
* refused. What is owed stays owed until an edge finds the
|
|
25406
|
+
* transport in session; each topic is read once.
|
|
25407
|
+
************************************************************/
|
|
25408
|
+
function reload_owed_topics(gobj) {
|
|
25409
|
+
let priv = gobj.priv;
|
|
25410
|
+
if (!is_connected$1(gobj)) return;
|
|
25411
|
+
let topics = Object.keys(priv.reload_on_session);
|
|
25412
|
+
priv.reload_on_session = {};
|
|
25413
|
+
topics.forEach((topic_name) => {
|
|
25414
|
+
if (!get_gobj_formtable(gobj, topic_name)) return;
|
|
25415
|
+
gobj_send_event(gobj, "EV_REFRESH_TOPIC", { topic_name }, gobj);
|
|
25416
|
+
});
|
|
25417
|
+
}
|
|
25418
|
+
/************************************************************
|
|
25386
25419
|
* Parent (routing) inform us that we go hidden
|
|
25387
25420
|
************************************************************/
|
|
25388
25421
|
function ac_hide$4(gobj, event, kw, src) {
|
package/package.json
CHANGED
|
@@ -160,6 +160,10 @@ function mt_create(gobj)
|
|
|
160
160
|
* and serial (form_writes_in_flight): answered refused when the
|
|
161
161
|
* transport closes under them. */
|
|
162
162
|
gobj.priv.form_writes = {};
|
|
163
|
+
/* The topics a write cut by a DROP left unknown: the table may show
|
|
164
|
+
* what was typed and the store what it had. Read again when the
|
|
165
|
+
* session is back (reload_owed_topics), never out of session. */
|
|
166
|
+
gobj.priv.reload_on_session = {};
|
|
163
167
|
gobj.priv.conn_shell = null; /* the shell whose EV_CONNECTION_STATE we hear */
|
|
164
168
|
|
|
165
169
|
build_ui(gobj);
|
|
@@ -2092,18 +2096,34 @@ function ac_mt_command_answer(gobj, event, kw, src)
|
|
|
2092
2096
|
* topic back to what the treedb actually has. */
|
|
2093
2097
|
if(command === "update-node" || command === "create-node") {
|
|
2094
2098
|
let failed_topic = kw_get_str(gobj, kw_command, "topic_name", "", 0);
|
|
2099
|
+
let form_write = kw_get_int(gobj, kw_command, "form_write", 0, 0);
|
|
2095
2100
|
if(failed_topic) {
|
|
2096
|
-
|
|
2097
|
-
|
|
2101
|
+
if(is_connected(gobj)) {
|
|
2102
|
+
gobj_send_event(gobj, "EV_REFRESH_TOPIC",
|
|
2103
|
+
{topic_name: failed_topic}, gobj);
|
|
2104
|
+
} else {
|
|
2105
|
+
/* A failure out of session is the DROP, not a
|
|
2106
|
+
* refusal: a routing adapter settles what it had
|
|
2107
|
+
* in flight when its session closes. The topic
|
|
2108
|
+
* cannot be read now -- asked, the transport
|
|
2109
|
+
* refuses it ("cannot route 'nodes' -- not in
|
|
2110
|
+
* session"). The reconnect reads it. */
|
|
2111
|
+
log_warning(`${gobj_short_name(gobj)}: the write of ` +
|
|
2112
|
+
`'${failed_topic}' was cut by the drop: the topic is ` +
|
|
2113
|
+
`read again when the session is back`);
|
|
2114
|
+
gobj.priv.reload_on_session[failed_topic] = true;
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
/* Answered once: the transport closing may have answered
|
|
2118
|
+
* this form already (ac_transport_state). */
|
|
2119
|
+
if(settle_form_write(gobj.priv.form_writes, form_write, failed_topic)) {
|
|
2120
|
+
answer_form_write(
|
|
2121
|
+
gobj,
|
|
2122
|
+
get_gobj_formtable(gobj, failed_topic),
|
|
2123
|
+
form_write,
|
|
2124
|
+
false
|
|
2125
|
+
);
|
|
2098
2126
|
}
|
|
2099
|
-
settle_form_write(gobj.priv.form_writes,
|
|
2100
|
-
kw_get_int(gobj, kw_command, "form_write", 0, 0), failed_topic);
|
|
2101
|
-
answer_form_write(
|
|
2102
|
-
gobj,
|
|
2103
|
-
get_gobj_formtable(gobj, failed_topic),
|
|
2104
|
-
kw_get_int(gobj, kw_command, "form_write", 0, 0),
|
|
2105
|
-
false
|
|
2106
|
-
);
|
|
2107
2127
|
}
|
|
2108
2128
|
}
|
|
2109
2129
|
return 0;
|
|
@@ -2462,6 +2482,11 @@ function ac_transport_state(gobj, event, kw, src)
|
|
|
2462
2482
|
if(!connected) {
|
|
2463
2483
|
abandon_form_writes(gobj.priv.form_writes).forEach((w) => {
|
|
2464
2484
|
log_warning(`${gobj_short_name(gobj)}: transport closed, the write ${w.form_write} of '${w.topic_name}' is lost`);
|
|
2485
|
+
/* Whether it reached the store is unknown: read it again
|
|
2486
|
+
* when the session is back. */
|
|
2487
|
+
if(w.topic_name) {
|
|
2488
|
+
gobj.priv.reload_on_session[w.topic_name] = true;
|
|
2489
|
+
}
|
|
2465
2490
|
answer_form_write(
|
|
2466
2491
|
gobj,
|
|
2467
2492
|
get_gobj_formtable(gobj, w.topic_name),
|
|
@@ -2469,10 +2494,36 @@ function ac_transport_state(gobj, event, kw, src)
|
|
|
2469
2494
|
false
|
|
2470
2495
|
);
|
|
2471
2496
|
});
|
|
2497
|
+
} else {
|
|
2498
|
+
reload_owed_topics(gobj);
|
|
2472
2499
|
}
|
|
2473
2500
|
return 0;
|
|
2474
2501
|
}
|
|
2475
2502
|
|
|
2503
|
+
/************************************************************
|
|
2504
|
+
* The session is back: read again the topics a drop left
|
|
2505
|
+
* unknown. Only when the TRANSPORT says so too -- the app's
|
|
2506
|
+
* connection (EV_CONNECTION_STATE) can report "up" before the
|
|
2507
|
+
* transport of this view is in session, and a read asked then is
|
|
2508
|
+
* refused. What is owed stays owed until an edge finds the
|
|
2509
|
+
* transport in session; each topic is read once.
|
|
2510
|
+
************************************************************/
|
|
2511
|
+
function reload_owed_topics(gobj)
|
|
2512
|
+
{
|
|
2513
|
+
let priv = gobj.priv;
|
|
2514
|
+
if(!is_connected(gobj)) {
|
|
2515
|
+
return;
|
|
2516
|
+
}
|
|
2517
|
+
let topics = Object.keys(priv.reload_on_session);
|
|
2518
|
+
priv.reload_on_session = {};
|
|
2519
|
+
topics.forEach((topic_name) => {
|
|
2520
|
+
if(!get_gobj_formtable(gobj, topic_name)) {
|
|
2521
|
+
return; /* its table is closed: opening it reads it */
|
|
2522
|
+
}
|
|
2523
|
+
gobj_send_event(gobj, "EV_REFRESH_TOPIC", {topic_name: topic_name}, gobj);
|
|
2524
|
+
});
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2476
2527
|
/************************************************************
|
|
2477
2528
|
* Parent (routing) inform us that we go hidden
|
|
2478
2529
|
************************************************************/
|
|
@@ -44,14 +44,22 @@ function track_form_write(in_flight, form_write, topic_name)
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
/************************************************************
|
|
47
|
-
* A write was answered, one way or the other
|
|
47
|
+
* A write was answered, one way or the other. True when it was
|
|
48
|
+
* still in flight: false when something else answered it already
|
|
49
|
+
* (the transport closing, abandon_form_writes), so its form must
|
|
50
|
+
* not be answered twice.
|
|
48
51
|
************************************************************/
|
|
49
52
|
function settle_form_write(in_flight, form_write, topic_name)
|
|
50
53
|
{
|
|
51
54
|
if(!in_flight || !form_write) {
|
|
52
|
-
return;
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const key = form_write_key(topic_name, form_write);
|
|
58
|
+
if(!Object.prototype.hasOwnProperty.call(in_flight, key)) {
|
|
59
|
+
return false;
|
|
53
60
|
}
|
|
54
|
-
delete in_flight[
|
|
61
|
+
delete in_flight[key];
|
|
62
|
+
return true;
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
/************************************************************
|
|
@@ -22,7 +22,7 @@ const {
|
|
|
22
22
|
gclass_create,
|
|
23
23
|
gobj_start_up, gobj_create_yuno, gobj_create, gobj_create_service,
|
|
24
24
|
gobj_start, gobj_send_event,
|
|
25
|
-
gobj_change_state,
|
|
25
|
+
gobj_change_state, gobj_current_state,
|
|
26
26
|
set_log_callback,
|
|
27
27
|
} = gobj_js;
|
|
28
28
|
const {register_c_yui_shell, yui_shell_set_connection_state} = await import("./c_yui_shell.js");
|
|
@@ -34,11 +34,16 @@ const commands = []; /* what the fake transport was asked */
|
|
|
34
34
|
|
|
35
35
|
/*
|
|
36
36
|
* The transport: answers nothing, like a C_IEVENT_CLI whose socket is
|
|
37
|
-
* about to drop. Its state is what `is_connected()` reads.
|
|
37
|
+
* about to drop. Its state is what `is_connected()` reads. Out of
|
|
38
|
+
* session it refuses what it is asked, as gui_agent's routing adapter
|
|
39
|
+
* (C_AGENT_TREEDB_LINK) does.
|
|
38
40
|
*/
|
|
39
41
|
function remote_command_parser(gobj, command, kw, src)
|
|
40
42
|
{
|
|
41
43
|
commands.push({command, kw});
|
|
44
|
+
if(gobj_current_state(gobj) !== "ST_SESSION") {
|
|
45
|
+
return `cannot route '${command}' -- not in session`;
|
|
46
|
+
}
|
|
42
47
|
return null;
|
|
43
48
|
}
|
|
44
49
|
|
|
@@ -78,7 +83,11 @@ beforeAll(() => {
|
|
|
78
83
|
]]],
|
|
79
84
|
{},
|
|
80
85
|
0,
|
|
81
|
-
[
|
|
86
|
+
[
|
|
87
|
+
SDATA(data_type_t.DTP_STRING, "topic_name", 0, "", "topic"),
|
|
88
|
+
SDATA(data_type_t.DTP_BOOLEAN, "with_remote_paging", 0, false, "pulls its pages"),
|
|
89
|
+
SDATA_END()
|
|
90
|
+
],
|
|
82
91
|
{}, 0, 0, 0, 0
|
|
83
92
|
);
|
|
84
93
|
/* The host of the view: it hears what a CHILD view publishes. */
|
|
@@ -226,3 +235,107 @@ describe("a Save in flight when the backend drops (M8)", () => {
|
|
|
226
235
|
]);
|
|
227
236
|
});
|
|
228
237
|
});
|
|
238
|
+
|
|
239
|
+
/*
|
|
240
|
+
* The answer the routing adapter gives a write it had in flight when
|
|
241
|
+
* its session closed: a failure, settled by the adapter itself.
|
|
242
|
+
*/
|
|
243
|
+
function failed_write(topic_name, form_write)
|
|
244
|
+
{
|
|
245
|
+
const md = {topic_name: topic_name, treedb_name: "treedb_test", record: {id: "x"}};
|
|
246
|
+
if(form_write) {
|
|
247
|
+
md.form_write = form_write;
|
|
248
|
+
}
|
|
249
|
+
return {result: -1, comment: "the session closed", data: null, __md_iev__: {
|
|
250
|
+
command_stack: [{command: "update-node", kw: md}]
|
|
251
|
+
}};
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function nodes_asked()
|
|
255
|
+
{
|
|
256
|
+
return commands.filter((c) => c.command === "nodes").map((c) => c.kw.topic_name);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
describe("a write cut by the drop does not reload out of session", () => {
|
|
260
|
+
|
|
261
|
+
test("the failure lands before the edge: no reload now, one on the reconnect", () => {
|
|
262
|
+
const {shell, topics} = build("t6");
|
|
263
|
+
yui_shell_set_connection_state(shell, true);
|
|
264
|
+
|
|
265
|
+
/* A cell edited in place: it looks saved until the topic is read again. */
|
|
266
|
+
gobj_send_event(topics, "EV_UPDATE_FIELD",
|
|
267
|
+
{topic_name: "users", id: "x", field: "name", value: "typed"}, topics);
|
|
268
|
+
expect(commands.filter((c) => c.command === "update-node").length).toBe(1);
|
|
269
|
+
|
|
270
|
+
/* The adapter leaves ST_SESSION and settles what it had in flight. */
|
|
271
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
272
|
+
gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", failed_write("users", 0), remote);
|
|
273
|
+
|
|
274
|
+
expect(nodes_asked()).toEqual([]);
|
|
275
|
+
expect(errors().filter((m) => m.includes("not in session"))).toEqual([]);
|
|
276
|
+
|
|
277
|
+
gobj_send_event(topics, "EV_TRANSPORT_STATE", {connected: false}, topics);
|
|
278
|
+
expect(nodes_asked()).toEqual([]);
|
|
279
|
+
|
|
280
|
+
/* The reconnect reloads what the drop left unknown -- once, though
|
|
281
|
+
* two edges report it. */
|
|
282
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
283
|
+
gobj_send_event(topics, "EV_TRANSPORT_STATE", {connected: true}, topics);
|
|
284
|
+
yui_shell_set_connection_state(shell, false);
|
|
285
|
+
yui_shell_set_connection_state(shell, true);
|
|
286
|
+
expect(nodes_asked()).toEqual(["users"]);
|
|
287
|
+
expect(errors()).toEqual([]);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
test("the edge lands first: the form is answered once, the reload waits", () => {
|
|
291
|
+
const {shell, topics, forms} = build("t7");
|
|
292
|
+
yui_shell_set_connection_state(shell, true);
|
|
293
|
+
save(topics, forms.users, 1);
|
|
294
|
+
|
|
295
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
296
|
+
yui_shell_set_connection_state(shell, false);
|
|
297
|
+
gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", failed_write("users", 1), remote);
|
|
298
|
+
|
|
299
|
+
expect(answers).toEqual([
|
|
300
|
+
{topic: "users", event: "EV_WRITE_REFUSED", form_write: 1},
|
|
301
|
+
]);
|
|
302
|
+
expect(nodes_asked()).toEqual([]);
|
|
303
|
+
|
|
304
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
305
|
+
yui_shell_set_connection_state(shell, true);
|
|
306
|
+
expect(nodes_asked()).toEqual(["users"]);
|
|
307
|
+
expect(errors()).toEqual([]);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("an edge that says 'up' before the transport is: the reload still waits", () => {
|
|
311
|
+
const {shell, topics} = build("t8");
|
|
312
|
+
yui_shell_set_connection_state(shell, true);
|
|
313
|
+
gobj_send_event(topics, "EV_UPDATE_FIELD",
|
|
314
|
+
{topic_name: "roles", id: "x", field: "name", value: "typed"}, topics);
|
|
315
|
+
gobj_change_state(remote, "ST_DISCONNECTED");
|
|
316
|
+
gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", failed_write("roles", 0), remote);
|
|
317
|
+
yui_shell_set_connection_state(shell, false);
|
|
318
|
+
|
|
319
|
+
/* The app's connection is back, the view's transport is not yet. */
|
|
320
|
+
yui_shell_set_connection_state(shell, true);
|
|
321
|
+
expect(nodes_asked()).toEqual([]);
|
|
322
|
+
|
|
323
|
+
gobj_change_state(remote, "ST_SESSION");
|
|
324
|
+
gobj_send_event(topics, "EV_TRANSPORT_STATE", {connected: true}, topics);
|
|
325
|
+
expect(nodes_asked()).toEqual(["roles"]);
|
|
326
|
+
expect(errors().filter((m) => m.includes("not in session"))).toEqual([]);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
test("a write refused IN session reloads its topic at once, and only then", () => {
|
|
330
|
+
const {shell, topics} = build("t9");
|
|
331
|
+
yui_shell_set_connection_state(shell, true);
|
|
332
|
+
gobj_send_event(topics, "EV_UPDATE_FIELD",
|
|
333
|
+
{topic_name: "users", id: "x", field: "name", value: "typed"}, topics);
|
|
334
|
+
gobj_send_event(topics, "EV_MT_COMMAND_ANSWER", failed_write("users", 0), remote);
|
|
335
|
+
expect(nodes_asked()).toEqual(["users"]);
|
|
336
|
+
|
|
337
|
+
gobj_send_event(topics, "EV_TRANSPORT_STATE", {connected: false}, topics);
|
|
338
|
+
gobj_send_event(topics, "EV_TRANSPORT_STATE", {connected: true}, topics);
|
|
339
|
+
expect(nodes_asked()).toEqual(["users"]);
|
|
340
|
+
});
|
|
341
|
+
});
|