@vuu-ui/vuu-data-test 2.1.19-beta.1 → 2.1.19-beta.2
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 +8 -9
- package/src/ArrayProxy.js +38 -0
- package/src/RuntimeVisualLink.js +45 -0
- package/src/SessionTable.js +49 -0
- package/src/Table.js +96 -0
- package/src/TickingArrayDataSource.js +208 -0
- package/src/UpdateGenerator.js +72 -0
- package/src/basket/BasketModule.js +272 -0
- package/src/basket/basket-schemas.js +355 -0
- package/src/basket/index.js +2 -0
- package/src/basket/reference-data/constituents.js +76 -0
- package/src/basket/reference-data/ftse100.js +686 -0
- package/src/basket/reference-data/hsi.js +1047 -0
- package/src/basket/reference-data/nasdaq100.js +812 -0
- package/src/basket/reference-data/prices.js +43 -0
- package/src/basket/reference-data/sp500.js +315 -0
- package/src/core/module/ModuleContainer.js +21 -0
- package/src/core/module/VuuModule.js +476 -0
- package/src/core/table/TableContainer.js +97 -0
- package/src/data-utils.js +53 -0
- package/src/index.js +13 -0
- package/src/local-datasource-provider/LocalDatasourceProvider.js +44 -0
- package/src/makeSuggestions.js +20 -0
- package/src/rowUpdates.js +0 -0
- package/src/schemas.js +19 -0
- package/src/session-table-utils.js +16 -0
- package/src/simul/SimulModule.js +175 -0
- package/src/simul/index.js +2 -0
- package/src/simul/reference-data/accounts.js +23 -0
- package/src/simul/reference-data/algos.js +9 -0
- package/src/simul/reference-data/currencies.js +8 -0
- package/src/simul/reference-data/index.js +5 -0
- package/src/simul/reference-data/instrument-prices.js +48 -0
- package/src/simul/reference-data/instruments-extended.js +12 -0
- package/src/simul/reference-data/instruments.js +67 -0
- package/src/simul/reference-data/isin-generator/convert-string-to-base-ten-number.js +2 -0
- package/src/simul/reference-data/isin-generator/fixtures/alphabet.js +27 -0
- package/src/simul/reference-data/isin-generator/fixtures/alphanumeric-values.js +7 -0
- package/src/simul/reference-data/isin-generator/fixtures/base-ten-numbers.js +13 -0
- package/src/simul/reference-data/isin-generator/generate-cusip-check-code.js +17 -0
- package/src/simul/reference-data/isin-generator/generate-cusip-without-check-code.js +8 -0
- package/src/simul/reference-data/isin-generator/generate-cusip.js +8 -0
- package/src/simul/reference-data/isin-generator/generate-random-alphanumeric.js +3 -0
- package/src/simul/reference-data/isin-generator/generate-random-base-ten-number.js +2 -0
- package/src/simul/reference-data/isin-generator/generate-random-string-of-alphanumeric-chars.js +7 -0
- package/src/simul/reference-data/isin-generator/generate-random-string-of-base-ten-chars.js +7 -0
- package/src/simul/reference-data/isin-generator/index.js +280 -0
- package/src/simul/reference-data/isin-generator/is-odd.js +2 -0
- package/src/simul/reference-data/locations.js +45 -0
- package/src/simul/reference-data/lotsizes.js +24 -0
- package/src/simul/reference-data/orderStatus.js +6 -0
- package/src/simul/reference-data/orders.js +64 -0
- package/src/simul/reference-data/parent-child-orders.js +117 -0
- package/src/simul/reference-data/priceStrategies.js +8 -0
- package/src/simul/reference-data/prices.js +78 -0
- package/src/simul/reference-data/sides.js +5 -0
- package/src/simul/simul-schemas.js +428 -0
- package/src/test/TestModule.js +95 -0
- package/src/test/index.js +1 -0
- package/src/test/test-schemas.js +74 -0
- package/src/vuu-row-generator.js +71 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { isAddRowRpcRequest, isBeginEditSessionRpcRequest, isEditCellRpcRequest, isEndEditSessionRpcRequest, isRpcSuccess, uuid } from "@vuu-ui/vuu-utils";
|
|
2
|
+
import { Table, buildDataColumnMapFromSchema } from "../../Table.js";
|
|
3
|
+
import { SessionTable, isProxySessionTable } from "../../SessionTable.js";
|
|
4
|
+
import { TickingArrayDataSource } from "../../TickingArrayDataSource.js";
|
|
5
|
+
import { RuntimeVisualLink } from "../../RuntimeVisualLink.js";
|
|
6
|
+
import ModuleContainer from "./ModuleContainer.js";
|
|
7
|
+
import { sessionTableSchema } from "../../session-table-utils.js";
|
|
8
|
+
const assertUpdateIsValid = (schema, column, data)=>{
|
|
9
|
+
const col = schema.columns.find((col)=>col.name === column);
|
|
10
|
+
if (col) console.log(`data type ${col.serverDataType} data ${data}`);
|
|
11
|
+
else throw Error(`schema for table ${schema.table.table} does not include column ${column}`);
|
|
12
|
+
};
|
|
13
|
+
class VuuModule {
|
|
14
|
+
#name;
|
|
15
|
+
#runtimeVisualLinks = new Map();
|
|
16
|
+
#sessionTableMap = {};
|
|
17
|
+
#tableServices;
|
|
18
|
+
#subscriptionMap = new Map();
|
|
19
|
+
constructor(name){
|
|
20
|
+
this.#name = name;
|
|
21
|
+
ModuleContainer.register(this);
|
|
22
|
+
}
|
|
23
|
+
getTableSchema(tableName) {
|
|
24
|
+
return this.schemas[tableName] ?? this.#sessionTableMap[tableName]?.schema;
|
|
25
|
+
}
|
|
26
|
+
getTableList() {
|
|
27
|
+
return Object.keys(this.tables);
|
|
28
|
+
}
|
|
29
|
+
handlePostSubscribeActivities = ({ clientViewportId, tableSchema })=>{
|
|
30
|
+
const visualLinks = this.getVisualLinks(tableSchema.table.table);
|
|
31
|
+
const { dataSource } = this.getSubscriptionByViewport(clientViewportId);
|
|
32
|
+
requestAnimationFrame(()=>{
|
|
33
|
+
dataSource.links = visualLinks;
|
|
34
|
+
});
|
|
35
|
+
if (this.visualLinks && dataSource.tableSchema) {
|
|
36
|
+
const { table: { table: sourceTable } } = dataSource.tableSchema;
|
|
37
|
+
for (const [table, links] of Object.entries(this.visualLinks))if (links) {
|
|
38
|
+
const potentialTargets = links.filter((l)=>l.toTable === sourceTable);
|
|
39
|
+
if (potentialTargets.length) {
|
|
40
|
+
for (const subscriptions of this.#subscriptionMap.values())for (const { dataSource } of subscriptions)if (dataSource.tableSchema?.table.table === table) dataSource.links = this.getVisualLinks(table);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
unregisterViewport = (viewportId)=>{
|
|
46
|
+
const { dataSource } = this.getSubscriptionByViewport(viewportId);
|
|
47
|
+
const tableName = dataSource.tableSchema?.table.table;
|
|
48
|
+
if (tableName) {
|
|
49
|
+
const subscriptionsForTable = this.#subscriptionMap.get(tableName);
|
|
50
|
+
if (subscriptionsForTable) {
|
|
51
|
+
for (const { viewportId: vp } of subscriptionsForTable)if (vp === viewportId) if (1 === subscriptionsForTable.length) this.#subscriptionMap.delete(tableName);
|
|
52
|
+
else this.#subscriptionMap.set(tableName, subscriptionsForTable.filter((s)=>s.viewportId !== viewportId));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
for (const [tableName, subscriptions] of this.#subscriptionMap)if (subscriptions[0].viewportId.toString() === viewportId) this.#subscriptionMap.delete(tableName);
|
|
56
|
+
if (this.visualLinks && dataSource.tableSchema) {
|
|
57
|
+
const { table: { table: sourceTable } } = dataSource.tableSchema;
|
|
58
|
+
for (const [table, links] of Object.entries(this.visualLinks))if (links) {
|
|
59
|
+
const potentialTargets = links.filter((l)=>l.toTable === sourceTable);
|
|
60
|
+
if (potentialTargets.length) {
|
|
61
|
+
for (const subscriptions of this.#subscriptionMap.values())for (const { dataSource } of subscriptions)if (dataSource.tableSchema?.table.table === table) dataSource.links = this.getVisualLinks(table);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
get name() {
|
|
67
|
+
return this.#name;
|
|
68
|
+
}
|
|
69
|
+
getSubscriptionByViewport(viewportId) {
|
|
70
|
+
for (const subscriptions of this.#subscriptionMap.values())for (const subscription of subscriptions)if (subscription.viewportId === viewportId) return subscription;
|
|
71
|
+
throw Error(`[VuuModule] getSubscriptionByViewport, no subscription found for ${viewportId}`);
|
|
72
|
+
}
|
|
73
|
+
getSubscribedDataSource(vpId) {
|
|
74
|
+
for (const subscriptions of this.#subscriptionMap.values())for (const { viewportId, dataSource } of subscriptions)if (viewportId === vpId) return dataSource;
|
|
75
|
+
throw Error(`getSubscribedDataSource #${vpId} not in subscriptionMap`);
|
|
76
|
+
}
|
|
77
|
+
getLinks = (subscriptionMap, vuuLinks)=>{
|
|
78
|
+
const visualLinks = [];
|
|
79
|
+
for(let i = 0; i < vuuLinks.length; i++){
|
|
80
|
+
const subscriptions = subscriptionMap.get(vuuLinks[i].toTable);
|
|
81
|
+
if (subscriptions) subscriptions.forEach(({ viewportId, dataSource: { status } })=>{
|
|
82
|
+
if ("suspended" !== status) {
|
|
83
|
+
const newLink = {
|
|
84
|
+
parentClientVpId: viewportId,
|
|
85
|
+
parentVpId: viewportId,
|
|
86
|
+
link: vuuLinks[i]
|
|
87
|
+
};
|
|
88
|
+
visualLinks.push(newLink);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return visualLinks;
|
|
93
|
+
};
|
|
94
|
+
visualLinkService = (message)=>new Promise((resolve)=>{
|
|
95
|
+
if ("CREATE_VISUAL_LINK" === message.type) {
|
|
96
|
+
const { childColumnName, childVpId, parentColumnName, parentVpId } = message;
|
|
97
|
+
const childDataSource = this.getSubscribedDataSource(childVpId);
|
|
98
|
+
const parentDataSource = this.getSubscribedDataSource(parentVpId);
|
|
99
|
+
const runtimeVisualLink = new RuntimeVisualLink(childDataSource, parentDataSource, childColumnName, parentColumnName);
|
|
100
|
+
this.#runtimeVisualLinks.set(childVpId, runtimeVisualLink);
|
|
101
|
+
resolve({
|
|
102
|
+
clientViewportId: childVpId,
|
|
103
|
+
colName: childColumnName,
|
|
104
|
+
parentColName: parentColumnName,
|
|
105
|
+
parentViewportId: parentVpId,
|
|
106
|
+
type: "vuu-link-created"
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
const runtimeVisualLink = this.#runtimeVisualLinks.get(message.childVpId);
|
|
110
|
+
if (runtimeVisualLink) {
|
|
111
|
+
runtimeVisualLink.remove();
|
|
112
|
+
this.#runtimeVisualLinks.delete(message.childVpId);
|
|
113
|
+
} else throw Error(`visualLinkService no visual link found for viewport #${message.childVpId}`);
|
|
114
|
+
resolve();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
getVisualLinks = (tableName)=>{
|
|
118
|
+
const linksForTable = this.visualLinks?.[tableName];
|
|
119
|
+
return void 0 === linksForTable ? void 0 : this.getLinks(this.#subscriptionMap, linksForTable);
|
|
120
|
+
};
|
|
121
|
+
createDataSource = (tableName, viewport, config)=>{
|
|
122
|
+
const columnDescriptors = this.getColumnDescriptors(tableName);
|
|
123
|
+
const table = this.tables[tableName];
|
|
124
|
+
const sessionTable = this.#sessionTableMap[tableName];
|
|
125
|
+
const vuuModule = this;
|
|
126
|
+
const dataSource = new TickingArrayDataSource({
|
|
127
|
+
...config,
|
|
128
|
+
columnDescriptors,
|
|
129
|
+
getVisualLinks: this.getVisualLinks,
|
|
130
|
+
keyColumn: void 0 === this.schemas[tableName] ? this.#sessionTableMap[tableName].schema.key : this.schemas[tableName].key,
|
|
131
|
+
table: table || sessionTable,
|
|
132
|
+
menu: this.menus?.[tableName],
|
|
133
|
+
rpcServices: this.getServices(tableName),
|
|
134
|
+
rpcMenuServices: this.getMenuServices(tableName),
|
|
135
|
+
sessionTables: this.#sessionTableMap,
|
|
136
|
+
viewport,
|
|
137
|
+
visualLinkService: this.visualLinkService,
|
|
138
|
+
vuuModule
|
|
139
|
+
});
|
|
140
|
+
dataSource.on("subscribed", this.handlePostSubscribeActivities);
|
|
141
|
+
dataSource.on("unsubscribed", this.unregisterViewport);
|
|
142
|
+
const existingSubscriptions = this.#subscriptionMap.get(tableName);
|
|
143
|
+
const subscription = {
|
|
144
|
+
viewportId: dataSource.viewport,
|
|
145
|
+
dataSource
|
|
146
|
+
};
|
|
147
|
+
if (existingSubscriptions) existingSubscriptions.push(subscription);
|
|
148
|
+
else this.#subscriptionMap.set(tableName, [
|
|
149
|
+
subscription
|
|
150
|
+
]);
|
|
151
|
+
return dataSource;
|
|
152
|
+
};
|
|
153
|
+
getServices(tableName) {
|
|
154
|
+
const tableServices = this.services?.[tableName];
|
|
155
|
+
if (Array.isArray(tableServices)) return this.#moduleServices.concat(tableServices);
|
|
156
|
+
return this.#moduleServices;
|
|
157
|
+
}
|
|
158
|
+
getMenuServices(tableName) {
|
|
159
|
+
const tableServices = this.menuServices?.[tableName];
|
|
160
|
+
if (Array.isArray(tableServices)) return this.#moduleMenuServices.concat(tableServices);
|
|
161
|
+
return this.#moduleMenuServices;
|
|
162
|
+
}
|
|
163
|
+
get sessionTableMap() {
|
|
164
|
+
return this.#sessionTableMap;
|
|
165
|
+
}
|
|
166
|
+
getSessionTable(sessionTableName, throwIfNotFound = true) {
|
|
167
|
+
const sessionTable = this.#sessionTableMap[sessionTableName];
|
|
168
|
+
if (sessionTable) return sessionTable;
|
|
169
|
+
if (throwIfNotFound) throw Error(`getSessionTable: no session table with name ${sessionTableName}`);
|
|
170
|
+
}
|
|
171
|
+
deleteRow = async (rpcRequest)=>{
|
|
172
|
+
if ("VIEWPORT_CONTEXT" === rpcRequest.context.type) {
|
|
173
|
+
const { viewPortId } = rpcRequest.context;
|
|
174
|
+
const { key } = rpcRequest.params;
|
|
175
|
+
const sessionTable = this.getSessionTable(viewPortId, false);
|
|
176
|
+
if (sessionTable) {
|
|
177
|
+
sessionTable.delete(key);
|
|
178
|
+
return {
|
|
179
|
+
type: "SUCCESS_RESULT",
|
|
180
|
+
data: void 0
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
{
|
|
184
|
+
const { dataSource } = this.getSubscriptionByViewport(viewPortId);
|
|
185
|
+
if (dataSource.table) {
|
|
186
|
+
const table = this.tables[dataSource.table.table];
|
|
187
|
+
if (table) {
|
|
188
|
+
table.delete(key);
|
|
189
|
+
return {
|
|
190
|
+
type: "SUCCESS_RESULT",
|
|
191
|
+
data: void 0
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
type: "ERROR_RESULT",
|
|
199
|
+
errorMessage: "something went wrong"
|
|
200
|
+
};
|
|
201
|
+
};
|
|
202
|
+
getColumnDescriptors(tableName) {
|
|
203
|
+
const schema = this.schemas[tableName] || this.getSessionTable(tableName)?.schema;
|
|
204
|
+
if (schema) return schema.columns;
|
|
205
|
+
throw Error(` no schema found for module ${this.#name} table ${tableName}`);
|
|
206
|
+
}
|
|
207
|
+
editCell = async (rpcRequest)=>{
|
|
208
|
+
if (isEditCellRpcRequest(rpcRequest)) {
|
|
209
|
+
const { viewPortId } = rpcRequest.context;
|
|
210
|
+
const { column, data, key } = rpcRequest.params;
|
|
211
|
+
let targetTable = this.#sessionTableMap[viewPortId];
|
|
212
|
+
if (!targetTable) {
|
|
213
|
+
const { dataSource } = this.getSubscriptionByViewport(viewPortId);
|
|
214
|
+
if (dataSource.table) targetTable = this.tables[dataSource.table.table];
|
|
215
|
+
}
|
|
216
|
+
if (targetTable) try {
|
|
217
|
+
assertUpdateIsValid(targetTable.schema, column, data);
|
|
218
|
+
targetTable.update(key, column, data);
|
|
219
|
+
return {
|
|
220
|
+
type: "SUCCESS_RESULT",
|
|
221
|
+
data: void 0
|
|
222
|
+
};
|
|
223
|
+
} catch (e) {
|
|
224
|
+
const { message } = e;
|
|
225
|
+
return {
|
|
226
|
+
type: "ERROR_RESULT",
|
|
227
|
+
errorMessage: message
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
throw Error("[VuuModule] editCell unable to find table for dataSource");
|
|
231
|
+
}
|
|
232
|
+
throw Error("[VuuModule] editCell invalid rpc type");
|
|
233
|
+
};
|
|
234
|
+
beginEditSessionMenuHandler = async ({ rpcName, type, vpId })=>{
|
|
235
|
+
if ("VIEW_PORT_MENUS_SELECT_RPC" !== type) return {
|
|
236
|
+
error: "no can do",
|
|
237
|
+
rpcName,
|
|
238
|
+
type: "VIEW_PORT_MENU_REJ",
|
|
239
|
+
vpId
|
|
240
|
+
};
|
|
241
|
+
{
|
|
242
|
+
console.log(`rpcName ${rpcName}`);
|
|
243
|
+
const result = await this.beginEditSession({
|
|
244
|
+
context: {
|
|
245
|
+
type: "VIEWPORT_CONTEXT",
|
|
246
|
+
viewPortId: vpId
|
|
247
|
+
},
|
|
248
|
+
params: {
|
|
249
|
+
editSessionMode: "selected-rows"
|
|
250
|
+
},
|
|
251
|
+
rpcName,
|
|
252
|
+
type: "RPC_REQUEST"
|
|
253
|
+
});
|
|
254
|
+
if (!isRpcSuccess(result)) return {
|
|
255
|
+
error: result.errorMessage,
|
|
256
|
+
rpcName,
|
|
257
|
+
type: "VIEW_PORT_MENU_REJ",
|
|
258
|
+
vpId
|
|
259
|
+
};
|
|
260
|
+
{
|
|
261
|
+
const { table } = result.data;
|
|
262
|
+
return {
|
|
263
|
+
type: "VIEW_PORT_MENU_RESP",
|
|
264
|
+
rpcName,
|
|
265
|
+
action: {
|
|
266
|
+
renderComponent: "grid",
|
|
267
|
+
type: "OPEN_DIALOG_ACTION",
|
|
268
|
+
table
|
|
269
|
+
},
|
|
270
|
+
vpId
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
beginEditSession = async (rpcRequest)=>{
|
|
276
|
+
if (isBeginEditSessionRpcRequest(rpcRequest)) {
|
|
277
|
+
const { viewPortId } = rpcRequest.context;
|
|
278
|
+
const { editSessionMode = "inline-all-rows" } = rpcRequest.params;
|
|
279
|
+
const subscription = this.getSubscriptionByViewport(viewPortId);
|
|
280
|
+
const { dataSource } = subscription;
|
|
281
|
+
const { table: vuuTable } = dataSource;
|
|
282
|
+
if (vuuTable) {
|
|
283
|
+
const sourceTable = this.tables[vuuTable.table];
|
|
284
|
+
if (sourceTable) {
|
|
285
|
+
const sessionTableName = `session-${uuid()}`;
|
|
286
|
+
try {
|
|
287
|
+
const sessionTable = this.createSessionTable(sourceTable, sessionTableName, editSessionMode, dataSource);
|
|
288
|
+
this.#sessionTableMap[sessionTableName] = sessionTable;
|
|
289
|
+
subscription.sessionTableName = sessionTableName;
|
|
290
|
+
return {
|
|
291
|
+
data: {
|
|
292
|
+
renderComponent: "inline-form",
|
|
293
|
+
table: {
|
|
294
|
+
module: vuuTable.module,
|
|
295
|
+
table: sessionTableName
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
type: "SUCCESS_RESULT"
|
|
299
|
+
};
|
|
300
|
+
} catch (e) {
|
|
301
|
+
return {
|
|
302
|
+
type: "ERROR_RESULT",
|
|
303
|
+
errorMessage: e.message
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
} else throw Error("[VuuModule] endEditSession invalid rpc type");
|
|
309
|
+
return {
|
|
310
|
+
type: "ERROR_RESULT",
|
|
311
|
+
errorMessage: "not implemented yet"
|
|
312
|
+
};
|
|
313
|
+
};
|
|
314
|
+
addRow = async (rpcRequest)=>{
|
|
315
|
+
if (isAddRowRpcRequest(rpcRequest)) {
|
|
316
|
+
const { viewPortId } = rpcRequest.context;
|
|
317
|
+
const subscription = this.getSubscriptionByViewport(viewPortId);
|
|
318
|
+
const sessionTable = subscription.sessionTableName ? this.#sessionTableMap[subscription.sessionTableName] : void 0;
|
|
319
|
+
if (!sessionTable) return {
|
|
320
|
+
type: "ERROR_RESULT",
|
|
321
|
+
errorMessage: `addRow: no active session table for viewport ${viewPortId}`
|
|
322
|
+
};
|
|
323
|
+
const { data } = rpcRequest.params;
|
|
324
|
+
const columnMap = sessionTable.map;
|
|
325
|
+
const columnCount = Object.keys(columnMap).length;
|
|
326
|
+
const row = new Array(columnCount).fill("");
|
|
327
|
+
for (const [col, idx] of Object.entries(columnMap))if (void 0 !== data[col]) row[idx] = data[col];
|
|
328
|
+
sessionTable.insert(row);
|
|
329
|
+
return {
|
|
330
|
+
type: "SUCCESS_RESULT",
|
|
331
|
+
data: void 0
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
type: "ERROR_RESULT",
|
|
336
|
+
errorMessage: "addRow: invalid rpc context"
|
|
337
|
+
};
|
|
338
|
+
};
|
|
339
|
+
endEditSession = async (rpcRequest)=>{
|
|
340
|
+
if (isEndEditSessionRpcRequest(rpcRequest)) {
|
|
341
|
+
const { viewPortId } = rpcRequest.context;
|
|
342
|
+
const subscription = this.getSubscriptionByViewport(viewPortId);
|
|
343
|
+
const sessionTableName = subscription.sessionTableName ?? viewPortId;
|
|
344
|
+
const sessionTable = this.#sessionTableMap[sessionTableName];
|
|
345
|
+
const { dataSource } = subscription;
|
|
346
|
+
if (dataSource.table) {
|
|
347
|
+
const sourceTable = this.tables[dataSource.table.table];
|
|
348
|
+
if (true !== rpcRequest.params.save) return {
|
|
349
|
+
type: "SUCCESS_RESULT",
|
|
350
|
+
data: void 0
|
|
351
|
+
};
|
|
352
|
+
{
|
|
353
|
+
let rejectedCount = 0;
|
|
354
|
+
if (isProxySessionTable(sessionTable)) {
|
|
355
|
+
const updates = sessionTable.getSessionUpdates();
|
|
356
|
+
updates.forEach((rowUpdates, key)=>{
|
|
357
|
+
const { cellUpdates, lastUpdateTimestamp } = rowUpdates;
|
|
358
|
+
const currentRow = sourceTable.findByKey(key);
|
|
359
|
+
const lastUpdateTimestampOnTable = currentRow[sourceTable.map.vuuUpdatedTimestamp];
|
|
360
|
+
if (lastUpdateTimestamp !== lastUpdateTimestampOnTable) {
|
|
361
|
+
rejectedCount += 1;
|
|
362
|
+
const sessionTableRow = sessionTable.findByKey(key);
|
|
363
|
+
const newRow = sessionTableRow.slice();
|
|
364
|
+
const messages = [];
|
|
365
|
+
Object.entries(cellUpdates).forEach(([column, value])=>{
|
|
366
|
+
messages.push(`${column}:${value}`);
|
|
367
|
+
});
|
|
368
|
+
newRow[sessionTable.map.vuuMsg] = messages.join(",");
|
|
369
|
+
sessionTable.updateRow(newRow);
|
|
370
|
+
} else {
|
|
371
|
+
const newRow = currentRow.slice();
|
|
372
|
+
Object.entries(cellUpdates).forEach(([column, value])=>{
|
|
373
|
+
newRow[sourceTable.map[column]] = value;
|
|
374
|
+
});
|
|
375
|
+
sourceTable.updateRow(newRow);
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
updates.clear();
|
|
379
|
+
} else {
|
|
380
|
+
const vuuMsgIdx = sessionTable.map["vuuMsg"];
|
|
381
|
+
const sourceColumns = sourceTable.schema.columns;
|
|
382
|
+
for(let i = 0; i < sessionTable.data.length; i++){
|
|
383
|
+
const sessionRow = sessionTable.data[i];
|
|
384
|
+
if (sessionRow[vuuMsgIdx]) continue;
|
|
385
|
+
const sourceRow = sourceColumns.map((col)=>sessionRow[sessionTable.map[col.name]]);
|
|
386
|
+
sourceTable.insert(sourceRow);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
if (rejectedCount > 0) return {
|
|
390
|
+
errorMessage: "stale update",
|
|
391
|
+
type: "ERROR_RESULT"
|
|
392
|
+
};
|
|
393
|
+
return {
|
|
394
|
+
type: "SUCCESS_RESULT",
|
|
395
|
+
data: void 0
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
throw Error("[VuuModule], exitEditMode");
|
|
400
|
+
}
|
|
401
|
+
throw Error("[VuuModule] endEditSession invalid rpc type");
|
|
402
|
+
};
|
|
403
|
+
applyBulkEdits = async (rpcRequest)=>{
|
|
404
|
+
if ("VIEWPORT_CONTEXT" === rpcRequest.context.type) {
|
|
405
|
+
const { viewPortId } = rpcRequest.context;
|
|
406
|
+
const sessionTable = this.getSessionTable(viewPortId);
|
|
407
|
+
for(let i = 0; i < sessionTable.data.length; i++){
|
|
408
|
+
const newRow = sessionTable.data[i];
|
|
409
|
+
const { column, value } = rpcRequest.params;
|
|
410
|
+
const keyIndex = sessionTable.map[sessionTable.schema.key];
|
|
411
|
+
sessionTable.update(String(newRow[keyIndex]), column, value);
|
|
412
|
+
}
|
|
413
|
+
return {
|
|
414
|
+
type: "SUCCESS_RESULT",
|
|
415
|
+
data: void 0
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
throw Error("[VuuModule] applyBulkEdits invalid rpc type");
|
|
419
|
+
};
|
|
420
|
+
createSessionTable(sourceTable, sessionTableName, editSessionMode = "inline-all-rows", dataSource) {
|
|
421
|
+
if (editSessionMode.endsWith("all-rows")) return this.createSessionTableWithAllRows(sourceTable, sessionTableName);
|
|
422
|
+
if ("selected-rows" === editSessionMode) return this.createSessionTableFromSelectedRows(sourceTable, sessionTableName, dataSource);
|
|
423
|
+
if ("empty-session-table" === editSessionMode) return this.createEmptySessionTable(sourceTable, sessionTableName);
|
|
424
|
+
throw Error(`[VuuModule] createSessionTable, invalid editSessionMode ${editSessionMode}`);
|
|
425
|
+
}
|
|
426
|
+
createSessionTableWithAllRows(sourceTable, sessionTableName) {
|
|
427
|
+
return SessionTable(sourceTable, sessionTableName);
|
|
428
|
+
}
|
|
429
|
+
createEmptySessionTable({ schema }, sessionTableName) {
|
|
430
|
+
const sessionSchema = sessionTableSchema({
|
|
431
|
+
...schema,
|
|
432
|
+
table: {
|
|
433
|
+
...schema.table,
|
|
434
|
+
table: sessionTableName
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
return new Table(sessionSchema, [], buildDataColumnMapFromSchema(sessionSchema));
|
|
438
|
+
}
|
|
439
|
+
createSessionTableFromSelectedRows({ data, map, schema }, sessionTableName, dataSource) {
|
|
440
|
+
const selectedRowIds = dataSource.getSelectedRowIds();
|
|
441
|
+
const keyIndex = map[schema.key];
|
|
442
|
+
const sessionData = [];
|
|
443
|
+
for(let i = 0; i < selectedRowIds.length; i++)for(let j = 0; j < data.length; j++)if (data[j][keyIndex] === selectedRowIds[i]) sessionData.push(data[j]);
|
|
444
|
+
const sessionSchema = sessionTableSchema(schema);
|
|
445
|
+
return new Table(sessionSchema, sessionData, buildDataColumnMapFromSchema(sessionSchema));
|
|
446
|
+
}
|
|
447
|
+
#moduleServices = [
|
|
448
|
+
{
|
|
449
|
+
rpcName: "addRow",
|
|
450
|
+
service: this.addRow
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
rpcName: "VP_BULK_EDIT_COLUMN_CELLS_RPC",
|
|
454
|
+
service: this.applyBulkEdits
|
|
455
|
+
},
|
|
456
|
+
{
|
|
457
|
+
rpcName: "beginEditSession",
|
|
458
|
+
service: this.beginEditSession
|
|
459
|
+
},
|
|
460
|
+
{
|
|
461
|
+
rpcName: "endEditSession",
|
|
462
|
+
service: this.endEditSession
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
rpcName: "editCell",
|
|
466
|
+
service: this.editCell
|
|
467
|
+
}
|
|
468
|
+
];
|
|
469
|
+
#moduleMenuServices = [
|
|
470
|
+
{
|
|
471
|
+
rpcName: "beginEditSession",
|
|
472
|
+
service: this.beginEditSessionMenuHandler
|
|
473
|
+
}
|
|
474
|
+
];
|
|
475
|
+
}
|
|
476
|
+
export { VuuModule };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Table, buildDataColumnMapFromSchema } from "../../Table.js";
|
|
2
|
+
class TableContainer {
|
|
3
|
+
static #instance;
|
|
4
|
+
static get instance() {
|
|
5
|
+
if (!TableContainer.#instance) TableContainer.#instance = new TableContainer();
|
|
6
|
+
return TableContainer.#instance;
|
|
7
|
+
}
|
|
8
|
+
#tables = new Map();
|
|
9
|
+
createTable = (schema, data, dataMap, updateGenerator)=>{
|
|
10
|
+
const table = new Table(schema, data, dataMap, updateGenerator);
|
|
11
|
+
this.addTable(table);
|
|
12
|
+
return table;
|
|
13
|
+
};
|
|
14
|
+
createJoinTable(joinTable, { table: t1 }, { table: t2 }, joinColumn) {
|
|
15
|
+
const table1 = this.getTable(t1);
|
|
16
|
+
const table2 = this.getTable(t2);
|
|
17
|
+
const { map: m1, schema: schema1 } = table1;
|
|
18
|
+
const { map: m2, schema: schema2 } = table2;
|
|
19
|
+
const k1 = m1[joinColumn];
|
|
20
|
+
const k2 = m2[joinColumn];
|
|
21
|
+
const combinedColumns = new Set([
|
|
22
|
+
...schema1.columns,
|
|
23
|
+
...schema2.columns
|
|
24
|
+
].map((col)=>col.name).sort());
|
|
25
|
+
const combinedSchema = {
|
|
26
|
+
key: joinColumn,
|
|
27
|
+
table: joinTable,
|
|
28
|
+
columns: Array.from(combinedColumns).map((columnName)=>({
|
|
29
|
+
name: columnName,
|
|
30
|
+
serverDataType: getServerDataType(columnName, schema1, schema2)
|
|
31
|
+
}))
|
|
32
|
+
};
|
|
33
|
+
const data = [];
|
|
34
|
+
const combinedColumnMap = buildDataColumnMapFromSchema(combinedSchema);
|
|
35
|
+
for (const row of table1.data){
|
|
36
|
+
const row2 = table2.findByKey(String(row[k1]));
|
|
37
|
+
if (row2) {
|
|
38
|
+
const out = [];
|
|
39
|
+
for (const column of table1.schema.columns){
|
|
40
|
+
const value = row[m1[column.name]];
|
|
41
|
+
out[combinedColumnMap[column.name]] = value;
|
|
42
|
+
}
|
|
43
|
+
for (const column of table2.schema.columns){
|
|
44
|
+
const value = row2[m2[column.name]];
|
|
45
|
+
out[combinedColumnMap[column.name]] = value;
|
|
46
|
+
}
|
|
47
|
+
data.push(out);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const newTable = new Table(combinedSchema, data, combinedColumnMap);
|
|
51
|
+
table1.on("insert", (row)=>{
|
|
52
|
+
const row2 = table2.findByKey(String(row[k1]));
|
|
53
|
+
if (row2) {
|
|
54
|
+
const out = [];
|
|
55
|
+
for (const column of table1.schema.columns){
|
|
56
|
+
const value = row[m1[column.name]];
|
|
57
|
+
out[combinedColumnMap[column.name]] = value;
|
|
58
|
+
}
|
|
59
|
+
for (const column of table2.schema.columns){
|
|
60
|
+
const value = row2[m2[column.name]];
|
|
61
|
+
out[combinedColumnMap[column.name]] = value;
|
|
62
|
+
}
|
|
63
|
+
newTable.insert(out);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
table2.on("update", (row)=>{
|
|
67
|
+
const keyValue = row[k2];
|
|
68
|
+
const targetRow = newTable.findByKey(keyValue);
|
|
69
|
+
if (targetRow) {
|
|
70
|
+
const updatedRow = targetRow.slice();
|
|
71
|
+
for (const { name } of table2.schema.columns)if (row[m2[name]] !== updatedRow[combinedColumnMap[name]]) updatedRow[combinedColumnMap[name]] = row[m2[name]];
|
|
72
|
+
newTable.updateRow(updatedRow);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
this.addTable(newTable);
|
|
76
|
+
return newTable;
|
|
77
|
+
}
|
|
78
|
+
addTable(table) {
|
|
79
|
+
this.#tables.set(table.name, table);
|
|
80
|
+
}
|
|
81
|
+
getTable(tableName) {
|
|
82
|
+
const table = this.#tables.get(tableName);
|
|
83
|
+
if (table) return table;
|
|
84
|
+
throw Error(`[TableContainer] no table ${tableName}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
var table_TableContainer = TableContainer.instance;
|
|
88
|
+
const getServerDataType = (columnName, { columns: cols1, table: t1 }, { columns: cols2, table: t2 })=>{
|
|
89
|
+
const col1 = cols1.find((col)=>col.name === columnName);
|
|
90
|
+
const col2 = cols2.find((col)=>col.name === columnName);
|
|
91
|
+
if (col1 && col2) if (col1.serverDataType === col2.serverDataType) return col1.serverDataType;
|
|
92
|
+
else throw Error(`both tables ${t1.table} and ${t2.table} implement column ${columnName}, but with types differ`);
|
|
93
|
+
if (col1) return col1.serverDataType;
|
|
94
|
+
if (col2) return col2.serverDataType;
|
|
95
|
+
throw Error("how is this possible");
|
|
96
|
+
};
|
|
97
|
+
export default table_TableContainer;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
function random(min, max) {
|
|
2
|
+
min = Math.ceil(min);
|
|
3
|
+
max = Math.floor(max);
|
|
4
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
5
|
+
}
|
|
6
|
+
function randomPercentage(value) {
|
|
7
|
+
const dec = random(2, 99);
|
|
8
|
+
const percentage = dec / 100;
|
|
9
|
+
return value * percentage;
|
|
10
|
+
}
|
|
11
|
+
const data_utils_nextRandomDouble = (min, max)=>min + (max - min) * Math.random();
|
|
12
|
+
const initBidAsk = (priceMaxDelta, nextRandomDouble)=>{
|
|
13
|
+
const mid = nextRandomDouble(0, 1000);
|
|
14
|
+
const tempBid = nextRandomDouble(mid - priceMaxDelta, mid - 1);
|
|
15
|
+
const ask = nextRandomDouble(mid + 1, mid + priceMaxDelta);
|
|
16
|
+
const bid = tempBid < 0 ? mid : tempBid;
|
|
17
|
+
const newBid = Math.round(100 * bid) / 100.0;
|
|
18
|
+
const newAsk = Math.round(100 * ask) / 100.0;
|
|
19
|
+
return [
|
|
20
|
+
newBid,
|
|
21
|
+
newAsk
|
|
22
|
+
];
|
|
23
|
+
};
|
|
24
|
+
const maxAsk = (bid, ask, spreadMultipler, priceMaxDelta)=>{
|
|
25
|
+
const spread = ask - bid;
|
|
26
|
+
return Math.min(ask + spreadMultipler * spread, spread / 2 + bid + priceMaxDelta);
|
|
27
|
+
};
|
|
28
|
+
const minAsk = (bid, ask)=>Math.max(bid + 1, (ask - bid) / 2 + bid);
|
|
29
|
+
const maxBid = (bid, ask)=>{
|
|
30
|
+
const result = Math.min(ask - 1, (ask - bid) / 2 + bid);
|
|
31
|
+
return result < 1 ? bid + 1 : result;
|
|
32
|
+
};
|
|
33
|
+
const minBid = (bid, ask, spreadMultipler, priceMaxDelta)=>{
|
|
34
|
+
const spread = ask - bid;
|
|
35
|
+
const mid = spread / 2 + bid;
|
|
36
|
+
const result = Math.max(bid - Math.min(spreadMultipler * spread, 10), mid - priceMaxDelta);
|
|
37
|
+
return result < 0 ? bid : result;
|
|
38
|
+
};
|
|
39
|
+
const generateNextBidAsk = (bid, ask, spreadMultipler, priceMaxDelta, nextRandomDouble)=>{
|
|
40
|
+
let tempAsk = ask;
|
|
41
|
+
if (Math.abs(bid - ask) <= 1) tempAsk = ask + 1;
|
|
42
|
+
const minBidValue = minBid(bid, tempAsk, spreadMultipler, priceMaxDelta);
|
|
43
|
+
const maxBidValue = maxBid(bid, tempAsk);
|
|
44
|
+
const minAskValue = minAsk(bid, tempAsk);
|
|
45
|
+
const maxAskValue = maxAsk(bid, tempAsk, spreadMultipler, priceMaxDelta);
|
|
46
|
+
const newBid = Math.round(100 * nextRandomDouble(minBidValue, maxBidValue)) / 100.0;
|
|
47
|
+
const newAsk = Math.round(100 * nextRandomDouble(minAskValue, maxAskValue)) / 100.0;
|
|
48
|
+
return [
|
|
49
|
+
newBid,
|
|
50
|
+
newAsk
|
|
51
|
+
];
|
|
52
|
+
};
|
|
53
|
+
export { data_utils_nextRandomDouble as nextRandomDouble, generateNextBidAsk, initBidAsk, random, randomPercentage };
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./ArrayProxy.js";
|
|
2
|
+
export * from "./core/module/VuuModule.js";
|
|
3
|
+
export * from "./local-datasource-provider/LocalDatasourceProvider.js";
|
|
4
|
+
export * from "./makeSuggestions.js";
|
|
5
|
+
export * from "./schemas.js";
|
|
6
|
+
export * from "./simul/index.js";
|
|
7
|
+
export * from "./basket/index.js";
|
|
8
|
+
export * from "./test/index.js";
|
|
9
|
+
export * from "./TickingArrayDataSource.js";
|
|
10
|
+
export * from "./vuu-row-generator.js";
|
|
11
|
+
export { default as tableContainer } from "./core/table/TableContainer.js";
|
|
12
|
+
export { SessionTable } from "./SessionTable.js";
|
|
13
|
+
export { Table, buildDataColumnMap, buildDataColumnMapFromSchema } from "./Table.js";
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { DataProvider } from "@vuu-ui/vuu-utils";
|
|
3
|
+
import ModuleContainer from "../core/module/ModuleContainer.js";
|
|
4
|
+
import TableContainer from "../core/table/TableContainer.js";
|
|
5
|
+
const serverAPI = {
|
|
6
|
+
getTableList: async ()=>{
|
|
7
|
+
const tables = ModuleContainer.moduleNames.reduce((tableList, moduleName)=>{
|
|
8
|
+
const moduleTables = ModuleContainer.get(moduleName).getTableList();
|
|
9
|
+
moduleTables.forEach((tableName)=>{
|
|
10
|
+
const table = TableContainer.getTable(tableName);
|
|
11
|
+
tableList.push(table.schema.table);
|
|
12
|
+
});
|
|
13
|
+
return tableList;
|
|
14
|
+
}, []);
|
|
15
|
+
return {
|
|
16
|
+
tables
|
|
17
|
+
};
|
|
18
|
+
},
|
|
19
|
+
getTableSchema: async ({ module, table })=>ModuleContainer.get(module).getTableSchema(table),
|
|
20
|
+
rpcCall: async ()=>{
|
|
21
|
+
throw Error("RpcCall no longer supported on LocalDataSOurceProvider ServerAPI");
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const getServerAPI = async ()=>serverAPI;
|
|
25
|
+
class VuuDataSource {
|
|
26
|
+
constructor({ aggregations, columns, filterSpec, groupBy, sort, table, viewport, visualLink }){
|
|
27
|
+
const config = {
|
|
28
|
+
aggregations,
|
|
29
|
+
columns,
|
|
30
|
+
filterSpec,
|
|
31
|
+
groupBy,
|
|
32
|
+
sort,
|
|
33
|
+
visualLink
|
|
34
|
+
};
|
|
35
|
+
const module = ModuleContainer.get(table.module);
|
|
36
|
+
return module.createDataSource(table.table, viewport, config);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const LocalDataSourceProvider = ({ children })=>/*#__PURE__*/ jsx(DataProvider, {
|
|
40
|
+
VuuDataSource: VuuDataSource,
|
|
41
|
+
getServerAPI: getServerAPI,
|
|
42
|
+
children: children
|
|
43
|
+
});
|
|
44
|
+
export { LocalDataSourceProvider };
|