@vuu-ui/vuu-data-test 4.1.0 → 5.0.0-alpha.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/README.md +46 -0
- package/package.json +12 -10
- package/src/Table.js +8 -5
- package/src/basket/BasketModule.js +105 -40
- package/src/basket/basket-schemas.js +16 -0
- package/src/basket/reference-data/constituent-data.js +2 -0
- package/src/basket/reference-data/constituents.js +6 -74
- package/src/basket/reference-data/prices.js +9 -3
- package/src/core/filter/PermissionFilter.js +60 -0
- package/src/core/filter/PermissionFilteredTable.js +59 -0
- package/src/core/module/ModuleContainer.js +8 -0
- package/src/core/module/VuuModule.js +54 -6
- package/src/core/table/TableContainer.js +52 -9
- package/src/core/user/CurrentUser.js +13 -0
- package/src/index.js +7 -0
- package/src/local-datasource-provider/LocalDatasourceProvider.js +67 -43
- package/src/module-admin/ModuleAdminModule.js +124 -0
- package/src/module-admin/index.js +3 -0
- package/src/module-admin/module-admin-schemas.js +95 -0
- package/src/module-admin/snapshot-projection.js +29 -0
- package/src/notifications/NotificationModule.js +131 -0
- package/src/notifications/NotificationsSchema.js +52 -0
- package/src/notifications/index.js +6 -0
- package/src/notifications/simulated/DismissNotificationRpcHandler.js +36 -0
- package/src/notifications/simulated/SimulatedNotificationsModule.js +18 -0
- package/src/notifications/simulated/SimulatedNotificationsProvider.js +151 -0
- package/src/notifications/simulated/notificationsModule.js +3 -0
- package/src/schemas.js +8 -2
- package/src/user-admin/UserAdminModule.js +567 -0
- package/src/user-admin/index.js +3 -0
- package/src/user-admin/initialSnapshot.js +461 -0
- package/src/user-admin/snapshot-projection.js +151 -0
- package/types/basket/BasketModule.d.ts +2 -0
- package/types/basket/reference-data/constituents.d.ts +3 -3
- package/types/basket/reference-data/prices.d.ts +2 -1
- package/types/core/module/ModuleContainer.d.ts +6 -3
- package/types/core/module/VuuModule.d.ts +22 -0
- package/types/core/table/TableContainer.d.ts +6 -6
- package/types/index.d.ts +7 -0
- package/types/local-datasource-provider/LocalDatasourceProvider.d.ts +18 -2
- package/types/schemas.d.ts +5 -3
- package/types/vuu-row-generator.d.ts +1 -1
- package/src/basket/reference-data/ftse100.js +0 -686
- package/src/basket/reference-data/hsi.js +0 -1047
- package/src/basket/reference-data/nasdaq100.js +0 -812
- package/src/basket/reference-data/sp500.js +0 -315
- package/types/basket/reference-data/ftse100.d.ts +0 -2
- package/types/basket/reference-data/hsi.d.ts +0 -2
- package/types/basket/reference-data/nasdaq100.d.ts +0 -2
- package/types/basket/reference-data/sp500.d.ts +0 -2
- package/types/src/ArrayProxy.d.ts +0 -8
- package/types/src/RuntimeVisualLink.d.ts +0 -9
- package/types/src/Table.d.ts +0 -34
- package/types/src/TickingArrayDataSource.d.ts +0 -44
- package/types/src/UpdateGenerator.d.ts +0 -18
- package/types/src/basket/BasketModule.d.ts +0 -55
- package/types/src/basket/basket-schemas.d.ts +0 -9
- package/types/src/basket/index.d.ts +0 -2
- package/types/src/basket/reference-data/constituents.d.ts +0 -3
- package/types/src/basket/reference-data/ftse100.d.ts +0 -2
- package/types/src/basket/reference-data/hsi.d.ts +0 -2
- package/types/src/basket/reference-data/nasdaq100.d.ts +0 -2
- package/types/src/basket/reference-data/prices.d.ts +0 -24
- package/types/src/basket/reference-data/sp500.d.ts +0 -2
- package/types/src/core/module/ModuleContainer.d.ts +0 -11
- package/types/src/core/module/VuuModule.d.ts +0 -81
- package/types/src/core/table/TableContainer.d.ts +0 -16
- package/types/src/data-utils.d.ts +0 -7
- package/types/src/default-column-definitions.d.ts +0 -2
- package/types/src/index.d.ts +0 -12
- package/types/src/local-datasource-provider/LocalDatasourceProvider.d.ts +0 -4
- package/types/src/makeSuggestions.d.ts +0 -3
- package/types/src/rowUpdates.d.ts +0 -15
- package/types/src/schemas.d.ts +0 -8
- package/types/src/session-table-utils.d.ts +0 -6
- package/types/src/simul/SimulModule.d.ts +0 -24
- package/types/src/simul/index.d.ts +0 -2
- package/types/src/simul/reference-data/accounts.d.ts +0 -1
- package/types/src/simul/reference-data/algos.d.ts +0 -1
- package/types/src/simul/reference-data/currencies.d.ts +0 -1
- package/types/src/simul/reference-data/index.d.ts +0 -5
- package/types/src/simul/reference-data/instrument-prices.d.ts +0 -60
- package/types/src/simul/reference-data/instruments-extended.d.ts +0 -1
- package/types/src/simul/reference-data/instruments.d.ts +0 -35
- package/types/src/simul/reference-data/isin-generator/convert-string-to-base-ten-number.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/fixtures/alphabet.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/fixtures/alphanumeric-values.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/fixtures/base-ten-numbers.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-cusip-check-code.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-cusip-without-check-code.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-cusip.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-random-alphanumeric.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-random-base-ten-number.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-random-string-of-alphanumeric-chars.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/generate-random-string-of-base-ten-chars.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/index.d.ts +0 -1
- package/types/src/simul/reference-data/isin-generator/is-odd.d.ts +0 -1
- package/types/src/simul/reference-data/locations.d.ts +0 -4
- package/types/src/simul/reference-data/lotsizes.d.ts +0 -1
- package/types/src/simul/reference-data/orderStatus.d.ts +0 -1
- package/types/src/simul/reference-data/orders.d.ts +0 -36
- package/types/src/simul/reference-data/parent-child-orders.d.ts +0 -4
- package/types/src/simul/reference-data/priceStrategies.d.ts +0 -1
- package/types/src/simul/reference-data/prices.d.ts +0 -34
- package/types/src/simul/reference-data/sides.d.ts +0 -1
- package/types/src/simul/simul-schemas.d.ts +0 -9
- package/types/src/test/TestModule.d.ts +0 -16
- package/types/src/test/index.d.ts +0 -1
- package/types/src/vuu-row-generator.d.ts +0 -17
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { DEFAULT_MODULE_DEFINITIONS, MODULE_ADMIN_RPC, MODULE_NAV_ICONS, executeModuleAdminRpc, toManagedModules } from "@heswell/module-admin/contracts";
|
|
2
|
+
import { VuuModule } from "../core/module/VuuModule.js";
|
|
3
|
+
import TableContainer from "../core/table/TableContainer.js";
|
|
4
|
+
import { buildDataColumnMapFromSchema } from "../Table.js";
|
|
5
|
+
import { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin-schemas.js";
|
|
6
|
+
import { reconcileModuleAdminTables } from "./snapshot-projection.js";
|
|
7
|
+
const createTable = (tableName)=>{
|
|
8
|
+
const schema = MODULE_ADMIN_TABLE_SCHEMAS[tableName];
|
|
9
|
+
return TableContainer.createTable(schema, [], buildDataColumnMapFromSchema(schema));
|
|
10
|
+
};
|
|
11
|
+
const createTables = ()=>({
|
|
12
|
+
modulePermissions: createTable("modulePermissions"),
|
|
13
|
+
modules: createTable("modules")
|
|
14
|
+
});
|
|
15
|
+
const INITIAL_TIMESTAMP = 1710000000000;
|
|
16
|
+
const INITIAL_ACCESS_ROLES = [
|
|
17
|
+
{
|
|
18
|
+
moduleName: "moduleAdmin",
|
|
19
|
+
role: "module-admin-access"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
moduleName: "userAdmin",
|
|
23
|
+
role: "user-admin-access"
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
moduleName: "basket-trading",
|
|
27
|
+
role: "basket-trading-access"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
moduleName: "vuu-table-browser",
|
|
31
|
+
role: "vuu-table-browser-access"
|
|
32
|
+
}
|
|
33
|
+
];
|
|
34
|
+
const EXAMPLE_MODULES = [
|
|
35
|
+
{
|
|
36
|
+
id: 6,
|
|
37
|
+
parentModuleId: 0,
|
|
38
|
+
name: "order-blotter",
|
|
39
|
+
title: "Order blotter",
|
|
40
|
+
description: "Monitor parent and child orders across all desks.",
|
|
41
|
+
version: 3,
|
|
42
|
+
enabled: false,
|
|
43
|
+
location: "/Trading/Orders",
|
|
44
|
+
path: "/trading/orders",
|
|
45
|
+
mfComponent: "OrderBlotter",
|
|
46
|
+
mfScope: "orderBlotter",
|
|
47
|
+
mfUrl: "http://localhost:5009",
|
|
48
|
+
navIconUrl: MODULE_NAV_ICONS.tables,
|
|
49
|
+
vuuConnectionId: "",
|
|
50
|
+
vuuWebsocketUrl: "",
|
|
51
|
+
vuuRestUrl: "",
|
|
52
|
+
accessRole: "",
|
|
53
|
+
created: INITIAL_TIMESTAMP,
|
|
54
|
+
updated: INITIAL_TIMESTAMP
|
|
55
|
+
}
|
|
56
|
+
];
|
|
57
|
+
const MODULE_ADMIN_INITIAL_SNAPSHOT = {
|
|
58
|
+
modules: [
|
|
59
|
+
...toManagedModules(DEFAULT_MODULE_DEFINITIONS, INITIAL_ACCESS_ROLES, INITIAL_TIMESTAMP),
|
|
60
|
+
...EXAMPLE_MODULES
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
class ModuleAdminModule extends VuuModule {
|
|
64
|
+
now;
|
|
65
|
+
#modules;
|
|
66
|
+
#tables;
|
|
67
|
+
constructor(snapshot = MODULE_ADMIN_INITIAL_SNAPSHOT, now = Date.now){
|
|
68
|
+
super(MODULE_ADMIN_MODULE_NAME), this.now = now;
|
|
69
|
+
this.#modules = snapshot.modules;
|
|
70
|
+
this.#tables = createTables();
|
|
71
|
+
reconcileModuleAdminTables(snapshot, this.#tables);
|
|
72
|
+
}
|
|
73
|
+
get modules() {
|
|
74
|
+
return this.#modules;
|
|
75
|
+
}
|
|
76
|
+
get menus() {
|
|
77
|
+
return {
|
|
78
|
+
modulePermissions: void 0,
|
|
79
|
+
modules: void 0
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
get includeDefaultServices() {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
get menuServices() {}
|
|
86
|
+
get schemas() {
|
|
87
|
+
return MODULE_ADMIN_TABLE_SCHEMAS;
|
|
88
|
+
}
|
|
89
|
+
service = (rpcName)=>async (request)=>{
|
|
90
|
+
try {
|
|
91
|
+
if ("RPC_REQUEST" !== request.type || request.rpcName !== rpcName) throw new Error(`Expected ${rpcName} RPC request`);
|
|
92
|
+
const { modules, result } = executeModuleAdminRpc(this.#modules, rpcName, request.params, this.now);
|
|
93
|
+
this.#modules = modules;
|
|
94
|
+
reconcileModuleAdminTables({
|
|
95
|
+
modules
|
|
96
|
+
}, this.#tables);
|
|
97
|
+
return {
|
|
98
|
+
data: result,
|
|
99
|
+
type: "SUCCESS_RESULT"
|
|
100
|
+
};
|
|
101
|
+
} catch (error) {
|
|
102
|
+
return {
|
|
103
|
+
errorMessage: error instanceof Error ? error.message : "Module admin request failed",
|
|
104
|
+
type: "ERROR_RESULT"
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
get services() {
|
|
109
|
+
const services = Object.keys(MODULE_ADMIN_RPC).map((rpcName)=>({
|
|
110
|
+
rpcName,
|
|
111
|
+
service: this.service(rpcName)
|
|
112
|
+
}));
|
|
113
|
+
return {
|
|
114
|
+
modulePermissions: services,
|
|
115
|
+
modules: services
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
get tables() {
|
|
119
|
+
return this.#tables;
|
|
120
|
+
}
|
|
121
|
+
get visualLinks() {}
|
|
122
|
+
}
|
|
123
|
+
const moduleAdminModule = new ModuleAdminModule();
|
|
124
|
+
export { MODULE_ADMIN_INITIAL_SNAPSHOT, ModuleAdminModule, moduleAdminModule };
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { MODULE_ADMIN_INITIAL_SNAPSHOT, ModuleAdminModule, moduleAdminModule } from "./ModuleAdminModule.js";
|
|
2
|
+
export { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin-schemas.js";
|
|
3
|
+
export { projectModuleAdminSnapshot, reconcileModuleAdminTables } from "./snapshot-projection.js";
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { VUU_DEFAULT_COLUMNS } from "../default-column-definitions.js";
|
|
2
|
+
const DEFAULT_RANGE_LIMITS = {
|
|
3
|
+
maxRangeEnd: 1000000,
|
|
4
|
+
maxRangeWidth: 1000
|
|
5
|
+
};
|
|
6
|
+
const MODULE_ADMIN_MODULE_NAME = "MODULE_DISCOVERY";
|
|
7
|
+
const MODULE_ADMIN_TABLE_SCHEMAS = {
|
|
8
|
+
modules: {
|
|
9
|
+
columns: [
|
|
10
|
+
{
|
|
11
|
+
name: "id",
|
|
12
|
+
serverDataType: "int"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "parentModuleId",
|
|
16
|
+
serverDataType: "int"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: "name",
|
|
20
|
+
serverDataType: "string"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
name: "title",
|
|
24
|
+
serverDataType: "string"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "description",
|
|
28
|
+
serverDataType: "string"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "version",
|
|
32
|
+
serverDataType: "int"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: "enabled",
|
|
36
|
+
serverDataType: "boolean"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: "location",
|
|
40
|
+
serverDataType: "string"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "path",
|
|
44
|
+
serverDataType: "string"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "mfComponent",
|
|
48
|
+
serverDataType: "string"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "mfScope",
|
|
52
|
+
serverDataType: "string"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: "mfUrl",
|
|
56
|
+
serverDataType: "string"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "navIconUrl",
|
|
60
|
+
serverDataType: "string"
|
|
61
|
+
},
|
|
62
|
+
...VUU_DEFAULT_COLUMNS
|
|
63
|
+
],
|
|
64
|
+
key: "id",
|
|
65
|
+
rangeLimits: DEFAULT_RANGE_LIMITS,
|
|
66
|
+
table: {
|
|
67
|
+
module: MODULE_ADMIN_MODULE_NAME,
|
|
68
|
+
table: "modules"
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
modulePermissions: {
|
|
72
|
+
columns: [
|
|
73
|
+
{
|
|
74
|
+
name: "id",
|
|
75
|
+
serverDataType: "int"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: "module_id",
|
|
79
|
+
serverDataType: "int"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: "role",
|
|
83
|
+
serverDataType: "string"
|
|
84
|
+
},
|
|
85
|
+
...VUU_DEFAULT_COLUMNS
|
|
86
|
+
],
|
|
87
|
+
key: "id",
|
|
88
|
+
rangeLimits: DEFAULT_RANGE_LIMITS,
|
|
89
|
+
table: {
|
|
90
|
+
module: MODULE_ADMIN_MODULE_NAME,
|
|
91
|
+
table: "modulePermissions"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
export { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { managedModuleColumnValues, managedModulePermissionValues } from "@heswell/module-admin/contracts";
|
|
2
|
+
const rowsEqual = (left, right)=>left.length === right.length && left.every((value, index)=>value === right[index]);
|
|
3
|
+
const toRow = (table, values)=>table.schema.columns.map(({ name })=>values[name] ?? "");
|
|
4
|
+
const projectModuleAdminSnapshot = (snapshot, tables)=>({
|
|
5
|
+
modulePermissions: managedModulePermissionValues(snapshot.modules).map((values)=>toRow(tables.modulePermissions, values)),
|
|
6
|
+
modules: snapshot.modules.map((module)=>toRow(tables.modules, managedModuleColumnValues(module)))
|
|
7
|
+
});
|
|
8
|
+
const reconcileModuleAdminTables = (snapshot, tables)=>{
|
|
9
|
+
const projected = projectModuleAdminSnapshot(snapshot, tables);
|
|
10
|
+
for (const tableName of Object.keys(tables)){
|
|
11
|
+
const table = tables[tableName];
|
|
12
|
+
const desiredRows = projected[tableName];
|
|
13
|
+
const keyIndex = table.map[table.schema.key];
|
|
14
|
+
const desiredKeys = new Set(desiredRows.map((row)=>String(row[keyIndex])));
|
|
15
|
+
for (const existingRow of [
|
|
16
|
+
...table.data
|
|
17
|
+
]){
|
|
18
|
+
const key = String(existingRow[keyIndex]);
|
|
19
|
+
if (!desiredKeys.has(key)) table.delete(key);
|
|
20
|
+
}
|
|
21
|
+
for (const row of desiredRows){
|
|
22
|
+
const key = String(row[keyIndex]);
|
|
23
|
+
const existingRow = table.findByKey(key);
|
|
24
|
+
if (void 0 === existingRow) table.insert(row);
|
|
25
|
+
else if (!rowsEqual(existingRow, row)) table.updateRow(row);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
export { projectModuleAdminSnapshot, reconcileModuleAdminTables };
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { VuuModule } from "../core/module/VuuModule.js";
|
|
2
|
+
import TableContainer from "../core/table/TableContainer.js";
|
|
3
|
+
import { buildDataColumnMapFromSchema } from "../Table.js";
|
|
4
|
+
import { NotificationsSchema } from "./NotificationsSchema.js";
|
|
5
|
+
const NAME = "NOTIFICATIONS";
|
|
6
|
+
const TABLE_NAME = "notifications";
|
|
7
|
+
const DEFAULT_RANGE_LIMITS = {
|
|
8
|
+
maxRangeEnd: 1000000,
|
|
9
|
+
maxRangeWidth: 1000
|
|
10
|
+
};
|
|
11
|
+
const VUU_DEFAULT_COLUMNS = [
|
|
12
|
+
{
|
|
13
|
+
name: "vuuCreatedTimestamp",
|
|
14
|
+
serverDataType: "epochtimestamp"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
name: "vuuUpdatedTimestamp",
|
|
18
|
+
serverDataType: "epochtimestamp"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
name: "vuuMsg",
|
|
22
|
+
serverDataType: "string"
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
class NotificationModule extends VuuModule {
|
|
26
|
+
static NAME = NAME;
|
|
27
|
+
static TABLE_NAME = TABLE_NAME;
|
|
28
|
+
#autoStarted = false;
|
|
29
|
+
#expiryTimers = new Map();
|
|
30
|
+
#provider;
|
|
31
|
+
#running = false;
|
|
32
|
+
#schemas;
|
|
33
|
+
#services;
|
|
34
|
+
#tables;
|
|
35
|
+
constructor(providerFactory, permissionFunction, serviceFactory, ...additionalColumns){
|
|
36
|
+
super(NAME);
|
|
37
|
+
const schema = {
|
|
38
|
+
columns: [
|
|
39
|
+
...NotificationsSchema.allFrom(...additionalColumns),
|
|
40
|
+
...VUU_DEFAULT_COLUMNS
|
|
41
|
+
],
|
|
42
|
+
key: "id",
|
|
43
|
+
rangeLimits: DEFAULT_RANGE_LIMITS,
|
|
44
|
+
table: {
|
|
45
|
+
module: NAME,
|
|
46
|
+
table: TABLE_NAME
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
const table = TableContainer.createTable(schema, [], buildDataColumnMapFromSchema(schema));
|
|
50
|
+
this.#schemas = {
|
|
51
|
+
[TABLE_NAME]: schema
|
|
52
|
+
};
|
|
53
|
+
this.#tables = {
|
|
54
|
+
[TABLE_NAME]: table
|
|
55
|
+
};
|
|
56
|
+
this.permissionFunctions = {
|
|
57
|
+
[TABLE_NAME]: permissionFunction
|
|
58
|
+
};
|
|
59
|
+
this.#provider = providerFactory(table);
|
|
60
|
+
this.#services = {
|
|
61
|
+
[TABLE_NAME]: serviceFactory(table, this)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
beforeCreateDataSource() {
|
|
65
|
+
if (!this.#autoStarted) {
|
|
66
|
+
this.#autoStarted = true;
|
|
67
|
+
this.start();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
publish(notification = {}) {
|
|
71
|
+
const table = this.#tables[TABLE_NAME];
|
|
72
|
+
const { map, schema } = table;
|
|
73
|
+
const id = notification.id ?? crypto.randomUUID();
|
|
74
|
+
const values = {
|
|
75
|
+
type: "toast",
|
|
76
|
+
title: "Notification",
|
|
77
|
+
message: "",
|
|
78
|
+
level: "INFO",
|
|
79
|
+
audience: "all",
|
|
80
|
+
...notification,
|
|
81
|
+
id
|
|
82
|
+
};
|
|
83
|
+
const row = schema.columns.map(({ serverDataType })=>"string" === serverDataType ? "" : "boolean" === serverDataType ? false : 0);
|
|
84
|
+
for (const [name, value] of Object.entries(values)){
|
|
85
|
+
const idx = map[name];
|
|
86
|
+
if (void 0 !== idx && void 0 !== value) row[idx] = value;
|
|
87
|
+
}
|
|
88
|
+
const existingRow = table.findByKey(id);
|
|
89
|
+
if (existingRow) {
|
|
90
|
+
row[map.vuuCreatedTimestamp] = existingRow[map.vuuCreatedTimestamp];
|
|
91
|
+
table.updateRow(row);
|
|
92
|
+
} else table.insert(row);
|
|
93
|
+
clearTimeout(this.#expiryTimers.get(id));
|
|
94
|
+
this.#expiryTimers.delete(id);
|
|
95
|
+
const { expiryTime } = values;
|
|
96
|
+
if ("number" == typeof expiryTime && expiryTime > 0) this.#expiryTimers.set(id, setTimeout(()=>{
|
|
97
|
+
this.#expiryTimers.delete(id);
|
|
98
|
+
if (table.findByKey(id)) table.delete(id);
|
|
99
|
+
}, Math.max(0, expiryTime - Date.now())));
|
|
100
|
+
return id;
|
|
101
|
+
}
|
|
102
|
+
get isRunning() {
|
|
103
|
+
return this.#running;
|
|
104
|
+
}
|
|
105
|
+
start() {
|
|
106
|
+
if (!this.#running) {
|
|
107
|
+
this.#running = true;
|
|
108
|
+
this.#provider.start();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
stop() {
|
|
112
|
+
this.#autoStarted = true;
|
|
113
|
+
if (this.#running) {
|
|
114
|
+
this.#running = false;
|
|
115
|
+
this.#provider.stop();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
get menus() {}
|
|
119
|
+
get menuServices() {}
|
|
120
|
+
get schemas() {
|
|
121
|
+
return this.#schemas;
|
|
122
|
+
}
|
|
123
|
+
get services() {
|
|
124
|
+
return this.#services;
|
|
125
|
+
}
|
|
126
|
+
get tables() {
|
|
127
|
+
return this.#tables;
|
|
128
|
+
}
|
|
129
|
+
get visualLinks() {}
|
|
130
|
+
}
|
|
131
|
+
export { NotificationModule };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const VuuColumnDataTypes = {
|
|
2
|
+
boolean: "boolean",
|
|
3
|
+
char: "char",
|
|
4
|
+
double: "double",
|
|
5
|
+
epochtimestamp: "epochtimestamp",
|
|
6
|
+
epochtimestampnano: "epochtimestampnano",
|
|
7
|
+
int: "int",
|
|
8
|
+
long: "long",
|
|
9
|
+
string: "string"
|
|
10
|
+
};
|
|
11
|
+
const toColumn = (columnDefinition)=>{
|
|
12
|
+
if ("string" == typeof columnDefinition) {
|
|
13
|
+
const [name, dataType = ""] = columnDefinition.split(":");
|
|
14
|
+
const serverDataType = VuuColumnDataTypes[dataType.toLowerCase()];
|
|
15
|
+
if (name && serverDataType) return {
|
|
16
|
+
name,
|
|
17
|
+
serverDataType
|
|
18
|
+
};
|
|
19
|
+
throw Error(`[NotificationsSchema] invalid column definition '${columnDefinition}'`);
|
|
20
|
+
}
|
|
21
|
+
return columnDefinition;
|
|
22
|
+
};
|
|
23
|
+
const Id = "id:String";
|
|
24
|
+
const Type = "type:String";
|
|
25
|
+
const ExpiryTime = "expiryTime:EpochTimestamp";
|
|
26
|
+
const Title = "title:String";
|
|
27
|
+
const Message = "message:String";
|
|
28
|
+
const Level = "level:String";
|
|
29
|
+
const Audience = "audience:String";
|
|
30
|
+
const genericColumns = [
|
|
31
|
+
Id,
|
|
32
|
+
Type,
|
|
33
|
+
ExpiryTime,
|
|
34
|
+
Title,
|
|
35
|
+
Message,
|
|
36
|
+
Level,
|
|
37
|
+
Audience
|
|
38
|
+
];
|
|
39
|
+
const NotificationsSchema = {
|
|
40
|
+
Id: Id,
|
|
41
|
+
Type: Type,
|
|
42
|
+
ExpiryTime: ExpiryTime,
|
|
43
|
+
Title: Title,
|
|
44
|
+
Message: Message,
|
|
45
|
+
Level: Level,
|
|
46
|
+
Audience: Audience,
|
|
47
|
+
allFrom: (...additionalColumns)=>[
|
|
48
|
+
...genericColumns,
|
|
49
|
+
...additionalColumns
|
|
50
|
+
].map(toColumn)
|
|
51
|
+
};
|
|
52
|
+
export { NotificationsSchema, toColumn };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from "./NotificationModule.js";
|
|
2
|
+
export * from "./NotificationsSchema.js";
|
|
3
|
+
export * from "./simulated/DismissNotificationRpcHandler.js";
|
|
4
|
+
export * from "./simulated/SimulatedNotificationsModule.js";
|
|
5
|
+
export * from "./simulated/SimulatedNotificationsProvider.js";
|
|
6
|
+
export * from "./simulated/notificationsModule.js";
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const errorResult = (errorMessage)=>({
|
|
2
|
+
type: "ERROR_RESULT",
|
|
3
|
+
errorMessage
|
|
4
|
+
});
|
|
5
|
+
const DismissNotificationRpcHandler = (table, module)=>{
|
|
6
|
+
const dismissNotification = async (rpcRequest)=>{
|
|
7
|
+
if ("VIEWPORT_CONTEXT" !== rpcRequest.context.type) return errorResult("dismissNotification requires a viewport context");
|
|
8
|
+
const { dataSource, table: viewportTable, user } = module.getSubscriptionByViewport(rpcRequest.context.viewPortId);
|
|
9
|
+
const selectedRowIds = dataSource.getSelectedRowIds();
|
|
10
|
+
const selection = selectedRowIds.includes("*") ? viewportTable.data.map((row)=>row[viewportTable.map[viewportTable.schema.key]]) : selectedRowIds;
|
|
11
|
+
const { map } = table;
|
|
12
|
+
for (const rowKey of selection){
|
|
13
|
+
const row = table.findByKey(rowKey);
|
|
14
|
+
if (row) {
|
|
15
|
+
const updatedRow = row.slice();
|
|
16
|
+
updatedRow[map.status] = "dismissed";
|
|
17
|
+
updatedRow[map.dismissedBy] = user.name;
|
|
18
|
+
table.updateRow(updatedRow);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return {
|
|
22
|
+
type: "SUCCESS_RESULT",
|
|
23
|
+
data: {
|
|
24
|
+
success: true,
|
|
25
|
+
dismissedCount: selection.length
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
rpcName: "dismissNotification",
|
|
32
|
+
service: dismissNotification
|
|
33
|
+
}
|
|
34
|
+
];
|
|
35
|
+
};
|
|
36
|
+
export { DismissNotificationRpcHandler };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { PermissionFilter } from "../../core/filter/PermissionFilter.js";
|
|
2
|
+
import { NotificationModule } from "../NotificationModule.js";
|
|
3
|
+
import { DismissNotificationRpcHandler } from "./DismissNotificationRpcHandler.js";
|
|
4
|
+
import { SimulatedNotificationsProvider } from "./SimulatedNotificationsProvider.js";
|
|
5
|
+
const noSimulation = {
|
|
6
|
+
start: ()=>void 0,
|
|
7
|
+
stop: ()=>void 0
|
|
8
|
+
};
|
|
9
|
+
const SimulatedNotificationsModule = ({ simulate = true } = {})=>new NotificationModule((table)=>simulate ? new SimulatedNotificationsProvider(table) : noSimulation, (viewport)=>{
|
|
10
|
+
const currentUser = viewport.user.name;
|
|
11
|
+
return PermissionFilter("audience", new Set([
|
|
12
|
+
"all",
|
|
13
|
+
currentUser,
|
|
14
|
+
"admin",
|
|
15
|
+
"trader"
|
|
16
|
+
]));
|
|
17
|
+
}, DismissNotificationRpcHandler, "source:String", "priority:Int", "status:String", "dismissedBy:String");
|
|
18
|
+
export { SimulatedNotificationsModule };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const sampleTemplates = [
|
|
2
|
+
[
|
|
3
|
+
"toast",
|
|
4
|
+
"Order Execution",
|
|
5
|
+
"Order #%04d filled %d shares of AAPL at $%d.%02d",
|
|
6
|
+
"INFO",
|
|
7
|
+
12000,
|
|
8
|
+
"OMS",
|
|
9
|
+
2,
|
|
10
|
+
"trader"
|
|
11
|
+
],
|
|
12
|
+
[
|
|
13
|
+
"toast",
|
|
14
|
+
"Risk Limit Alert",
|
|
15
|
+
"Account ACC-%04d exceeded intraday VaR limit by %d%%",
|
|
16
|
+
"WARNING",
|
|
17
|
+
15000,
|
|
18
|
+
"RISK",
|
|
19
|
+
5,
|
|
20
|
+
"risk_manager"
|
|
21
|
+
],
|
|
22
|
+
[
|
|
23
|
+
"toast",
|
|
24
|
+
"Feed Synchronization",
|
|
25
|
+
"Connected to pricing feed NYC-%02d with latency %dms",
|
|
26
|
+
"INFO",
|
|
27
|
+
10000,
|
|
28
|
+
"PRICING",
|
|
29
|
+
1,
|
|
30
|
+
"admin"
|
|
31
|
+
],
|
|
32
|
+
[
|
|
33
|
+
"toast",
|
|
34
|
+
"Connection Warning",
|
|
35
|
+
"Intermittent packet loss detected on gateway LDN-%02d",
|
|
36
|
+
"WARNING",
|
|
37
|
+
14000,
|
|
38
|
+
"GATEWAY",
|
|
39
|
+
3,
|
|
40
|
+
"admin"
|
|
41
|
+
],
|
|
42
|
+
[
|
|
43
|
+
"toast",
|
|
44
|
+
"Order Rejection",
|
|
45
|
+
"Order #%04d rejected by exchange: Insufficient margin",
|
|
46
|
+
"ERROR",
|
|
47
|
+
18000,
|
|
48
|
+
"OMS",
|
|
49
|
+
10,
|
|
50
|
+
"all"
|
|
51
|
+
],
|
|
52
|
+
[
|
|
53
|
+
"banner",
|
|
54
|
+
"System Maintenance",
|
|
55
|
+
"Scheduled system maintenance will begin in %d minutes",
|
|
56
|
+
"WARNING",
|
|
57
|
+
45000,
|
|
58
|
+
"SYSTEM",
|
|
59
|
+
5,
|
|
60
|
+
"all"
|
|
61
|
+
],
|
|
62
|
+
[
|
|
63
|
+
"banner",
|
|
64
|
+
"Market Status",
|
|
65
|
+
"US Equity markets are now OPEN. Trading session #%d active",
|
|
66
|
+
"INFO",
|
|
67
|
+
60000,
|
|
68
|
+
"SYSTEM",
|
|
69
|
+
1,
|
|
70
|
+
"all"
|
|
71
|
+
]
|
|
72
|
+
];
|
|
73
|
+
const MAX_ACTIVE_NOTIFICATIONS = 8;
|
|
74
|
+
const INITIAL_NOTIFICATIONS = 3;
|
|
75
|
+
const CYCLE_TIME_MS = 4000;
|
|
76
|
+
const randomInt = (bound)=>Math.floor(Math.random() * bound);
|
|
77
|
+
const formatMessage = (format, ...args)=>{
|
|
78
|
+
let argIndex = 0;
|
|
79
|
+
return format.replace(/%(0(\d+))?d|%%/g, (match, _, width)=>{
|
|
80
|
+
if ("%%" === match) return "%";
|
|
81
|
+
const value = String(args[argIndex++]);
|
|
82
|
+
return width ? value.padStart(Number(width), "0") : value;
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
class SimulatedNotificationsProvider {
|
|
86
|
+
#activeNotifications = new Map();
|
|
87
|
+
#table;
|
|
88
|
+
#timer;
|
|
89
|
+
constructor(table){
|
|
90
|
+
this.#table = table;
|
|
91
|
+
}
|
|
92
|
+
start() {
|
|
93
|
+
if (void 0 === this.#timer) {
|
|
94
|
+
for(let i = 0; i < INITIAL_NOTIFICATIONS; i++)this.generateRandomNotification();
|
|
95
|
+
this.#timer = setInterval(this.runOnce, CYCLE_TIME_MS);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
stop() {
|
|
99
|
+
if (void 0 !== this.#timer) {
|
|
100
|
+
clearInterval(this.#timer);
|
|
101
|
+
this.#timer = void 0;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
runOnce = ()=>{
|
|
105
|
+
try {
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
for (const [id, expiryTime] of Array.from(this.#activeNotifications))if (expiryTime < now) {
|
|
108
|
+
this.#table.delete(id);
|
|
109
|
+
this.#activeNotifications.delete(id);
|
|
110
|
+
}
|
|
111
|
+
if (this.#activeNotifications.size < MAX_ACTIVE_NOTIFICATIONS) this.generateRandomNotification();
|
|
112
|
+
} catch (e) {
|
|
113
|
+
console.error("Error occurred in SimulatedNotificationsProvider runOnce", e);
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
generateRandomNotification() {
|
|
117
|
+
const id = crypto.randomUUID();
|
|
118
|
+
const now = Date.now();
|
|
119
|
+
const [type, title, messageFormat, level, duration, source, priority, audience] = sampleTemplates[randomInt(sampleTemplates.length)];
|
|
120
|
+
const expiryTime = now + duration;
|
|
121
|
+
const message = messageFormat.includes("%") ? formatMessage(messageFormat, randomInt(9000) + 1000, 10 * randomInt(90) + 100, randomInt(200) + 50, randomInt(99)) : messageFormat;
|
|
122
|
+
const rowData = {
|
|
123
|
+
id,
|
|
124
|
+
type,
|
|
125
|
+
expiryTime,
|
|
126
|
+
title,
|
|
127
|
+
message,
|
|
128
|
+
level,
|
|
129
|
+
audience,
|
|
130
|
+
source,
|
|
131
|
+
priority,
|
|
132
|
+
status: "",
|
|
133
|
+
dismissedBy: "",
|
|
134
|
+
vuuCreatedTimestamp: now,
|
|
135
|
+
vuuUpdatedTimestamp: now,
|
|
136
|
+
vuuMsg: ""
|
|
137
|
+
};
|
|
138
|
+
this.#table.insert(this.toDataRow(rowData));
|
|
139
|
+
this.#activeNotifications.set(id, expiryTime);
|
|
140
|
+
}
|
|
141
|
+
toDataRow(rowData) {
|
|
142
|
+
const { map, schema } = this.#table;
|
|
143
|
+
const row = Array(schema.columns.length);
|
|
144
|
+
for (const [name, value] of Object.entries(rowData)){
|
|
145
|
+
const idx = map[name];
|
|
146
|
+
if (void 0 !== idx) row[idx] = value;
|
|
147
|
+
}
|
|
148
|
+
return row;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export { SimulatedNotificationsProvider, formatMessage };
|
package/src/schemas.js
CHANGED
|
@@ -1,15 +1,21 @@
|
|
|
1
1
|
import { schemas } from "./basket/basket-schemas.js";
|
|
2
2
|
import { schemas as simul_schemas_js_schemas } from "./simul/simul-schemas.js";
|
|
3
3
|
import { testModule } from "./test/TestModule.js";
|
|
4
|
+
import { USER_ADMIN_TABLE_SCHEMAS } from "@heswell/user-admin/contracts";
|
|
5
|
+
import { MODULE_ADMIN_TABLE_SCHEMAS } from "./module-admin/index.js";
|
|
4
6
|
const schemas_schemas = {
|
|
5
7
|
...schemas,
|
|
6
8
|
...simul_schemas_js_schemas,
|
|
7
|
-
...testModule.schemas
|
|
9
|
+
...testModule.schemas,
|
|
10
|
+
...USER_ADMIN_TABLE_SCHEMAS,
|
|
11
|
+
...MODULE_ADMIN_TABLE_SCHEMAS
|
|
8
12
|
};
|
|
9
13
|
const allSchemas = {
|
|
10
14
|
...schemas,
|
|
11
15
|
...simul_schemas_js_schemas,
|
|
12
|
-
...testModule.schemas
|
|
16
|
+
...testModule.schemas,
|
|
17
|
+
...USER_ADMIN_TABLE_SCHEMAS,
|
|
18
|
+
...MODULE_ADMIN_TABLE_SCHEMAS
|
|
13
19
|
};
|
|
14
20
|
const getAllSchemas = ()=>schemas_schemas;
|
|
15
21
|
const getSchema = (tableName)=>{
|