@vuu-ui/vuu-data-test 4.0.0-alpha.3 → 4.0.0-alpha.31
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 +38 -0
- package/package.json +10 -9
- package/src/TickingArrayDataSource.js +7 -2
- 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/VuuModule.js +30 -4
- package/src/core/table/TableContainer.js +51 -8
- package/src/core/user/CurrentUser.js +13 -0
- package/src/index.js +4 -0
- package/src/local-datasource-provider/LocalDatasourceProvider.js +64 -44
- package/src/module-admin/ModuleAdminModule.js +84 -20
- package/src/module-admin/module-admin-schemas.js +5 -9
- package/src/module-admin/snapshot-projection.js +3 -31
- 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/user-admin/UserAdminModule.js +163 -8
- package/src/user-admin/initialSnapshot.js +49 -65
- package/types/TickingArrayDataSource.d.ts +1 -1
- 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/filter/PermissionFilter.d.ts +41 -0
- package/types/core/filter/PermissionFilteredTable.d.ts +24 -0
- package/types/core/module/VuuModule.d.ts +19 -0
- package/types/core/table/TableContainer.d.ts +1 -1
- package/types/core/user/CurrentUser.d.ts +14 -0
- package/types/index.d.ts +4 -0
- package/types/local-datasource-provider/LocalDatasourceProvider.d.ts +18 -2
- package/types/module-admin/ModuleAdminModule.d.ts +12 -3
- package/types/module-admin/index.d.ts +1 -1
- package/types/module-admin/snapshot-projection.d.ts +3 -4
- package/types/notifications/NotificationModule.d.ts +60 -0
- package/types/notifications/NotificationsSchema.d.ts +20 -0
- package/types/notifications/index.d.ts +6 -0
- package/types/notifications/simulated/DismissNotificationRpcHandler.d.ts +8 -0
- package/types/notifications/simulated/SimulatedNotificationsModule.d.ts +9 -0
- package/types/notifications/simulated/SimulatedNotificationsProvider.d.ts +21 -0
- package/types/notifications/simulated/notificationsModule.d.ts +5 -0
- package/types/user-admin/UserAdminModule.d.ts +5 -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
|
@@ -65,6 +65,19 @@ const parseAssignments = (value)=>{
|
|
|
65
65
|
if (!Array.isArray(parsed) || parsed.some((assignment)=>!isRecord(assignment) || "string" != typeof assignment.accessRole || 0 === assignment.accessRole.length || "string" != typeof assignment.groupId || 0 === assignment.groupId.length || Object.keys(assignment).some((name)=>"accessRole" !== name && "groupId" !== name))) throw new Error("assignments must be a JSON array of accessRole and groupId strings");
|
|
66
66
|
return parsed;
|
|
67
67
|
};
|
|
68
|
+
const parseSessionRoleAssignments = (value)=>{
|
|
69
|
+
if ("string" != typeof value) throw new Error("role_assignments must be a serialized array");
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(value);
|
|
73
|
+
} catch {
|
|
74
|
+
throw new Error("role_assignments must be a valid JSON array");
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(parsed) || parsed.some((roleId)=>"string" != typeof roleId || !roleId)) throw new Error("role_assignments must be an array of role ID strings");
|
|
77
|
+
return [
|
|
78
|
+
...new Set(parsed)
|
|
79
|
+
];
|
|
80
|
+
};
|
|
68
81
|
const parseSessionPermissions = (value)=>{
|
|
69
82
|
if ("string" != typeof value) throw new Error("permissions must be a serialized array");
|
|
70
83
|
let parsed;
|
|
@@ -206,17 +219,32 @@ class UserAdminModule extends VuuModule {
|
|
|
206
219
|
}
|
|
207
220
|
createSessionTable(sourceTable, sessionTableName, editSessionMode, dataSource, sessionType) {
|
|
208
221
|
const sessionTable = super.createSessionTable(sourceTable, sessionTableName, editSessionMode, dataSource, sessionType);
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
...sessionTable.schema,
|
|
212
|
-
columns: sessionTable.schema.columns.concat({
|
|
222
|
+
const extraColumns = "users" === sourceTable.name ? [
|
|
223
|
+
{
|
|
213
224
|
name: "permissions",
|
|
214
225
|
serverDataType: "string"
|
|
215
|
-
}
|
|
226
|
+
}
|
|
227
|
+
] : "groups" === sourceTable.name ? [
|
|
228
|
+
{
|
|
229
|
+
name: "group_name",
|
|
230
|
+
serverDataType: "string"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
name: "role_assignments",
|
|
234
|
+
serverDataType: "string"
|
|
235
|
+
}
|
|
236
|
+
] : [];
|
|
237
|
+
const columnsToAdd = extraColumns.filter(({ name })=>!sessionTable.schema.columns.some((column)=>column.name === name));
|
|
238
|
+
if (0 === columnsToAdd.length) return sessionTable;
|
|
239
|
+
const sessionSchema = {
|
|
240
|
+
...sessionTable.schema,
|
|
241
|
+
columns: sessionTable.schema.columns.concat(columnsToAdd)
|
|
216
242
|
};
|
|
217
|
-
return new Table(sessionSchema, sessionTable.data.map((row)=>row.concat("")), buildDataColumnMapFromSchema(sessionSchema));
|
|
243
|
+
return new Table(sessionSchema, sessionTable.data.map((row)=>row.concat(columnsToAdd.map(()=>""))), buildDataColumnMapFromSchema(sessionSchema));
|
|
218
244
|
}
|
|
219
245
|
async beforeSessionSave(sourceTable, sessionTable) {
|
|
246
|
+
if ("roles" === sourceTable.name) return void await this.saveClientRolesFromSession(sessionTable);
|
|
247
|
+
if ("groups" === sourceTable.name) return void await this.saveGroupsFromSession(sessionTable);
|
|
220
248
|
if ("users" !== sourceTable.name) return;
|
|
221
249
|
const permissionsIndex = sessionTable.map.permissions;
|
|
222
250
|
const actionIndex = sessionTable.map.vuuAction;
|
|
@@ -230,8 +258,135 @@ class UserAdminModule extends VuuModule {
|
|
|
230
258
|
if ("string" == typeof row[permissionsIndex] && "" !== row[permissionsIndex]) await this.store.setUserModuleAccess(userId, parseSessionPermissions(row[permissionsIndex]));
|
|
231
259
|
}
|
|
232
260
|
}
|
|
233
|
-
async afterSessionSave(sourceTable) {
|
|
234
|
-
|
|
261
|
+
async afterSessionSave(sourceTable, sessionTable) {
|
|
262
|
+
const roleCreationSaved = "roles" === sourceTable.name && sessionTable.data.some((row)=>"addRow" === row[sessionTable.map.vuuAction] && "client role created" === row[sessionTable.map.vuuMsg]);
|
|
263
|
+
const groupChangesSaved = "groups" === sourceTable.name && sessionTable.data.some((row)=>"group created" === row[sessionTable.map.vuuMsg] || "group roles updated" === row[sessionTable.map.vuuMsg]);
|
|
264
|
+
if ("users" === sourceTable.name || roleCreationSaved || groupChangesSaved) await this.reconcile();
|
|
265
|
+
}
|
|
266
|
+
async saveGroupsFromSession(sessionTable) {
|
|
267
|
+
const actionIndex = sessionTable.map.vuuAction;
|
|
268
|
+
const messageIndex = sessionTable.map.vuuMsg;
|
|
269
|
+
const keyIndex = sessionTable.map[sessionTable.schema.key];
|
|
270
|
+
const groupIdIndex = sessionTable.map.group_id;
|
|
271
|
+
const groupNameIndex = sessionTable.map.group_name;
|
|
272
|
+
const roleAssignmentsIndex = sessionTable.map.role_assignments;
|
|
273
|
+
for (const row of sessionTable.data)if ("addRow" !== row[actionIndex] || row[messageIndex]) {
|
|
274
|
+
if ("editCell" === row[actionIndex] && "string" == typeof row[roleAssignmentsIndex] && "" !== row[roleAssignmentsIndex]) {
|
|
275
|
+
const groupId = requiredString({
|
|
276
|
+
group_id: row[groupIdIndex]
|
|
277
|
+
}, "group_id");
|
|
278
|
+
const roleIds = parseSessionRoleAssignments(row[roleAssignmentsIndex]);
|
|
279
|
+
await this.saveGroupRoleAssignments(groupId, roleIds, await this.store.snapshot());
|
|
280
|
+
sessionTable.update(String(row[keyIndex]), "vuuMsg", "group roles updated");
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
const name = requiredString({
|
|
284
|
+
group_name: row[groupNameIndex]
|
|
285
|
+
}, "group_name").trim();
|
|
286
|
+
if (!name) throw new Error("group_name must be a non-empty string");
|
|
287
|
+
const roleIds = parseSessionRoleAssignments(row[roleAssignmentsIndex]);
|
|
288
|
+
const before = await this.store.snapshot();
|
|
289
|
+
this.resolveGroupRoleAssignments(roleIds, before);
|
|
290
|
+
const existingGroupIds = new Set(before.groups.map(({ id })=>id));
|
|
291
|
+
await this.store.addGroup({
|
|
292
|
+
name
|
|
293
|
+
});
|
|
294
|
+
const snapshot = await this.store.snapshot();
|
|
295
|
+
const group = snapshot.groups.find(({ id, name: groupName })=>!existingGroupIds.has(id) && groupName === name);
|
|
296
|
+
if (!group) throw new Error("Created group could not be found");
|
|
297
|
+
await this.saveGroupRoleAssignments(group.id, roleIds, snapshot);
|
|
298
|
+
sessionTable.update(String(row[keyIndex]), "vuuMsg", "group created");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
async saveGroupRoleAssignments(groupId, desiredRoleIds, snapshot) {
|
|
302
|
+
if (!snapshot.groups.some(({ id })=>id === groupId)) throw new Error(`User admin group not found: ${groupId}`);
|
|
303
|
+
const groupRoles = snapshot.groupRoles.filter(({ group })=>group.id === groupId);
|
|
304
|
+
const roleAssignmentsById = new Map(snapshot.clientRoles.map((assignment)=>[
|
|
305
|
+
assignment.role.id,
|
|
306
|
+
assignment
|
|
307
|
+
]));
|
|
308
|
+
const desiredRoleSet = new Set(desiredRoleIds);
|
|
309
|
+
const currentAssignments = groupRoles.map(({ role })=>{
|
|
310
|
+
const assignment = roleAssignmentsById.get(role.id);
|
|
311
|
+
if (!assignment) throw new Error(`Assigned user admin role no longer exists: ${role.id}`);
|
|
312
|
+
return assignment;
|
|
313
|
+
});
|
|
314
|
+
const currentRoleSet = new Set(groupRoles.map(({ role })=>role.id));
|
|
315
|
+
const desiredAssignments = this.resolveGroupRoleAssignments(desiredRoleIds, snapshot);
|
|
316
|
+
for (const assignment of currentAssignments)if (!desiredRoleSet.has(assignment.role.id)) {
|
|
317
|
+
const clientRef = assignment.role.clientRole && assignment.client?.clientId ? {
|
|
318
|
+
clientKey: assignment.client.clientId
|
|
319
|
+
} : void 0;
|
|
320
|
+
await this.store.removeRoleFromGroup({
|
|
321
|
+
groupId
|
|
322
|
+
}, {
|
|
323
|
+
roleId: assignment.role.id
|
|
324
|
+
}, clientRef);
|
|
325
|
+
}
|
|
326
|
+
for (const assignment of desiredAssignments)if (!currentRoleSet.has(assignment.role.id)) {
|
|
327
|
+
const clientRef = assignment.role.clientRole && assignment.client?.clientId ? {
|
|
328
|
+
clientKey: assignment.client.clientId
|
|
329
|
+
} : void 0;
|
|
330
|
+
await this.store.addRoleToGroup({
|
|
331
|
+
groupId
|
|
332
|
+
}, {
|
|
333
|
+
roleId: assignment.role.id
|
|
334
|
+
}, clientRef);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
resolveGroupRoleAssignments(roleIds, snapshot) {
|
|
338
|
+
const roleAssignmentsById = new Map(snapshot.clientRoles.map((assignment)=>[
|
|
339
|
+
assignment.role.id,
|
|
340
|
+
assignment
|
|
341
|
+
]));
|
|
342
|
+
return roleIds.map((roleId)=>{
|
|
343
|
+
const assignment = roleAssignmentsById.get(roleId);
|
|
344
|
+
if (!assignment) throw new Error(`User admin role not found: ${roleId}`);
|
|
345
|
+
if (assignment.role.clientRole && !assignment.client?.clientId) throw new Error(`User admin client role has no owning client: ${roleId}`);
|
|
346
|
+
return assignment;
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
async saveClientRolesFromSession(sessionTable) {
|
|
350
|
+
const actionIndex = sessionTable.map.vuuAction;
|
|
351
|
+
const messageIndex = sessionTable.map.vuuMsg;
|
|
352
|
+
const keyIndex = sessionTable.map[sessionTable.schema.key];
|
|
353
|
+
const snapshot = await this.store.snapshot();
|
|
354
|
+
const clientsById = new Map(snapshot.clients.map((client)=>[
|
|
355
|
+
client.id,
|
|
356
|
+
client
|
|
357
|
+
]));
|
|
358
|
+
const pendingRoles = [
|
|
359
|
+
...sessionTable.data
|
|
360
|
+
].flatMap((row)=>{
|
|
361
|
+
if ("addRow" !== row[actionIndex] || row[messageIndex]) return [];
|
|
362
|
+
const params = Object.fromEntries(sessionTable.schema.columns.map(({ name })=>[
|
|
363
|
+
name,
|
|
364
|
+
row[sessionTable.map[name]]
|
|
365
|
+
]));
|
|
366
|
+
const roleName = requiredString(params, "role_name").trim();
|
|
367
|
+
if (!roleName) throw new Error("role_name must be a non-empty string");
|
|
368
|
+
const clientId = requiredString(params, "client_id");
|
|
369
|
+
const clientIdentifier = requiredString(params, "client_identifier");
|
|
370
|
+
const client = clientsById.get(clientId);
|
|
371
|
+
if (!client || client.clientId !== clientIdentifier) throw new Error("client_id and client_identifier must identify the same client");
|
|
372
|
+
return [
|
|
373
|
+
{
|
|
374
|
+
client,
|
|
375
|
+
description: optionalString(params, "description") ?? "",
|
|
376
|
+
roleName,
|
|
377
|
+
row
|
|
378
|
+
}
|
|
379
|
+
];
|
|
380
|
+
});
|
|
381
|
+
for (const { client, description, roleName, row } of pendingRoles){
|
|
382
|
+
await this.store.addClientRole({
|
|
383
|
+
clientKey: client.clientId
|
|
384
|
+
}, {
|
|
385
|
+
description,
|
|
386
|
+
name: roleName
|
|
387
|
+
});
|
|
388
|
+
sessionTable.update(String(row[keyIndex]), "vuuMsg", "client role created");
|
|
389
|
+
}
|
|
235
390
|
}
|
|
236
391
|
mutation = (rpcName, mutate)=>async (request)=>{
|
|
237
392
|
try {
|
|
@@ -6,6 +6,34 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
6
6
|
enabled: true,
|
|
7
7
|
id: "client-portal",
|
|
8
8
|
name: "VUU Portal"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
clientId: "vuu-user-admin",
|
|
12
|
+
description: "User administration",
|
|
13
|
+
enabled: true,
|
|
14
|
+
id: "client-user-admin",
|
|
15
|
+
name: "User Admin"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
clientId: "vuu-basket-trading",
|
|
19
|
+
description: "Basket trading",
|
|
20
|
+
enabled: true,
|
|
21
|
+
id: "client-basket-trading",
|
|
22
|
+
name: "Basket Trading"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
clientId: "vuu-module-admin",
|
|
26
|
+
description: "Module administration",
|
|
27
|
+
enabled: true,
|
|
28
|
+
id: "client-module-admin",
|
|
29
|
+
name: "Module Admin"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
clientId: "vuu-feature-filter-table",
|
|
33
|
+
description: "Instrument tables",
|
|
34
|
+
enabled: true,
|
|
35
|
+
id: "client-feature-filter-table",
|
|
36
|
+
name: "Instruments"
|
|
9
37
|
}
|
|
10
38
|
],
|
|
11
39
|
clientRoles: [
|
|
@@ -67,13 +95,13 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
67
95
|
},
|
|
68
96
|
{
|
|
69
97
|
client: {
|
|
70
|
-
clientId: "vuu-
|
|
71
|
-
id: "client-
|
|
72
|
-
name: "
|
|
98
|
+
clientId: "vuu-user-admin",
|
|
99
|
+
id: "client-user-admin",
|
|
100
|
+
name: "User Admin"
|
|
73
101
|
},
|
|
74
102
|
role: {
|
|
75
103
|
clientRole: true,
|
|
76
|
-
containerId: "client-
|
|
104
|
+
containerId: "client-user-admin",
|
|
77
105
|
id: "role-user-admin-admin",
|
|
78
106
|
name: "user-admin-admin",
|
|
79
107
|
roleDisplayName: "admin"
|
|
@@ -81,13 +109,13 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
81
109
|
},
|
|
82
110
|
{
|
|
83
111
|
client: {
|
|
84
|
-
clientId: "vuu-
|
|
85
|
-
id: "client-
|
|
86
|
-
name: "
|
|
112
|
+
clientId: "vuu-module-admin",
|
|
113
|
+
id: "client-module-admin",
|
|
114
|
+
name: "Module Admin"
|
|
87
115
|
},
|
|
88
116
|
role: {
|
|
89
117
|
clientRole: true,
|
|
90
|
-
containerId: "client-
|
|
118
|
+
containerId: "client-module-admin",
|
|
91
119
|
id: "role-module-admin-admin",
|
|
92
120
|
name: "module-admin-admin",
|
|
93
121
|
roleDisplayName: "admin"
|
|
@@ -95,29 +123,17 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
95
123
|
},
|
|
96
124
|
{
|
|
97
125
|
client: {
|
|
98
|
-
clientId: "vuu-
|
|
99
|
-
id: "client-
|
|
100
|
-
name: "
|
|
126
|
+
clientId: "vuu-basket-trading",
|
|
127
|
+
id: "client-basket-trading",
|
|
128
|
+
name: "Basket Trading"
|
|
101
129
|
},
|
|
102
130
|
role: {
|
|
103
131
|
clientRole: true,
|
|
104
|
-
containerId: "client-
|
|
132
|
+
containerId: "client-basket-trading",
|
|
105
133
|
id: "role-basket-trading-trade",
|
|
106
134
|
name: "basket-trading-trade",
|
|
107
135
|
roleDisplayName: "trade"
|
|
108
136
|
}
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
client: {
|
|
112
|
-
clientId: "realm",
|
|
113
|
-
id: "realm",
|
|
114
|
-
name: "Realm"
|
|
115
|
-
},
|
|
116
|
-
role: {
|
|
117
|
-
id: "role-admin",
|
|
118
|
-
name: "admin",
|
|
119
|
-
roleDisplayName: "admin"
|
|
120
|
-
}
|
|
121
137
|
}
|
|
122
138
|
],
|
|
123
139
|
groupRoles: [
|
|
@@ -225,9 +241,9 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
225
241
|
},
|
|
226
242
|
{
|
|
227
243
|
client: {
|
|
228
|
-
clientId: "vuu-
|
|
229
|
-
id: "client-
|
|
230
|
-
name: "
|
|
244
|
+
clientId: "vuu-user-admin",
|
|
245
|
+
id: "client-user-admin",
|
|
246
|
+
name: "User Admin"
|
|
231
247
|
},
|
|
232
248
|
group: {
|
|
233
249
|
id: "group-user-admin-admin",
|
|
@@ -261,9 +277,9 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
261
277
|
},
|
|
262
278
|
{
|
|
263
279
|
client: {
|
|
264
|
-
clientId: "vuu-
|
|
265
|
-
id: "client-
|
|
266
|
-
name: "
|
|
280
|
+
clientId: "vuu-module-admin",
|
|
281
|
+
id: "client-module-admin",
|
|
282
|
+
name: "Module Admin"
|
|
267
283
|
},
|
|
268
284
|
group: {
|
|
269
285
|
id: "group-module-admin-admin",
|
|
@@ -297,9 +313,9 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
297
313
|
},
|
|
298
314
|
{
|
|
299
315
|
client: {
|
|
300
|
-
clientId: "vuu-
|
|
301
|
-
id: "client-
|
|
302
|
-
name: "
|
|
316
|
+
clientId: "vuu-basket-trading",
|
|
317
|
+
id: "client-basket-trading",
|
|
318
|
+
name: "Basket Trading"
|
|
303
319
|
},
|
|
304
320
|
group: {
|
|
305
321
|
id: "group-basket-trading-trade",
|
|
@@ -312,19 +328,6 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
312
328
|
name: "basket-trading-trade",
|
|
313
329
|
roleDisplayName: "trade"
|
|
314
330
|
}
|
|
315
|
-
},
|
|
316
|
-
{
|
|
317
|
-
group: {
|
|
318
|
-
id: "group-admins",
|
|
319
|
-
name: "administrators",
|
|
320
|
-
groupDisplayName: "administrators",
|
|
321
|
-
path: "/vuu/administrators"
|
|
322
|
-
},
|
|
323
|
-
role: {
|
|
324
|
-
id: "role-admin",
|
|
325
|
-
name: "admin",
|
|
326
|
-
roleDisplayName: "admin"
|
|
327
|
-
}
|
|
328
331
|
}
|
|
329
332
|
],
|
|
330
333
|
groups: [
|
|
@@ -381,12 +384,6 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
381
384
|
name: "basket-trading-trade",
|
|
382
385
|
groupDisplayName: "trade",
|
|
383
386
|
path: "/vuu/basket-trading-trade"
|
|
384
|
-
},
|
|
385
|
-
{
|
|
386
|
-
id: "group-admins",
|
|
387
|
-
name: "administrators",
|
|
388
|
-
groupDisplayName: "administrators",
|
|
389
|
-
path: "/vuu/administrators"
|
|
390
387
|
}
|
|
391
388
|
],
|
|
392
389
|
timestamp: 1710000000000,
|
|
@@ -438,19 +435,6 @@ const USER_ADMIN_INITIAL_SNAPSHOT = {
|
|
|
438
435
|
id: "user-alice",
|
|
439
436
|
username: "alice"
|
|
440
437
|
}
|
|
441
|
-
},
|
|
442
|
-
{
|
|
443
|
-
group: {
|
|
444
|
-
id: "group-admins",
|
|
445
|
-
name: "administrators",
|
|
446
|
-
groupDisplayName: "administrators",
|
|
447
|
-
path: "/vuu/administrators"
|
|
448
|
-
},
|
|
449
|
-
user: {
|
|
450
|
-
email: "alice@example.com",
|
|
451
|
-
id: "user-alice",
|
|
452
|
-
username: "alice"
|
|
453
|
-
}
|
|
454
438
|
}
|
|
455
439
|
],
|
|
456
440
|
users: [
|
|
@@ -33,7 +33,7 @@ export declare class TickingArrayDataSource extends ArrayDataSource {
|
|
|
33
33
|
undoRowChange: (key: string) => Promise<RpcResultSuccess | RpcResultError>;
|
|
34
34
|
set columns(columns: string[]);
|
|
35
35
|
get columns(): string[];
|
|
36
|
-
endEditSession(saveChanges?: boolean): Promise<void>;
|
|
36
|
+
endEditSession(saveChanges?: boolean, force?: boolean): Promise<void>;
|
|
37
37
|
rpcRequest(rpcRequest: Omit<VuuRpcServiceRequest, "context">): Promise<RpcResultSuccess | RpcResultError>;
|
|
38
38
|
menuRpcCall(rpcRequest: Omit<VuuRpcMenuRequest, "vpId">): Promise<VuuRpcMenuResponse>;
|
|
39
39
|
getTypeaheadSuggestions(column: string, pattern?: string): Promise<string[]>;
|
|
@@ -47,9 +47,11 @@ export declare class BasketModule extends VuuModule<BasketsTableName> {
|
|
|
47
47
|
}[];
|
|
48
48
|
};
|
|
49
49
|
private createTradingBasket;
|
|
50
|
+
private insertTradingConstituent;
|
|
50
51
|
private createNewBasket;
|
|
51
52
|
private sendToMarket;
|
|
52
53
|
private takeOffMarket;
|
|
53
54
|
private addConstituent;
|
|
55
|
+
private updateConstituentStatus;
|
|
54
56
|
}
|
|
55
57
|
export declare const basketModule: BasketModule;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
|
|
2
|
-
declare const
|
|
3
|
-
export default
|
|
1
|
+
import type { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
|
|
2
|
+
declare const basketConstituentData: VuuRowDataItemType[][];
|
|
3
|
+
export default basketConstituentData;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Table } from "../../Table";
|
|
1
2
|
export type ask = number;
|
|
2
3
|
export type askSize = number;
|
|
3
4
|
export type bid = number;
|
|
@@ -20,5 +21,5 @@ export type PricesDataRow = [
|
|
|
20
21
|
ric,
|
|
21
22
|
scenario
|
|
22
23
|
];
|
|
23
|
-
declare const pricesTable:
|
|
24
|
+
declare const pricesTable: Table;
|
|
24
25
|
export default pricesTable;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { VuuRowDataItemType } from "@vuu-ui/vuu-protocol-types";
|
|
2
|
+
import type { VuuModuleUser } from "../user/CurrentUser";
|
|
3
|
+
import type { ColumnMap } from "@vuu-ui/vuu-utils";
|
|
4
|
+
import type tableContainer from "../table/TableContainer";
|
|
5
|
+
export type TableRow = Array<bigint | VuuRowDataItemType>;
|
|
6
|
+
export type RowPredicate = (row: TableRow) => boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Read access to a single row, by column name.
|
|
9
|
+
*/
|
|
10
|
+
export interface RowData {
|
|
11
|
+
get: (columnName: string) => bigint | VuuRowDataItemType | undefined;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Restricts the rows of a table visible through a viewport, typically
|
|
15
|
+
* based on the user that owns the viewport. Permission filters are applied
|
|
16
|
+
* in addition to any client filter.
|
|
17
|
+
*/
|
|
18
|
+
export interface PermissionFilter {
|
|
19
|
+
/**
|
|
20
|
+
* Bind this permission filter to the column layout of a table, returning a
|
|
21
|
+
* predicate that can be evaluated against raw table rows.
|
|
22
|
+
*/
|
|
23
|
+
createPredicate: (columnMap: ColumnMap) => RowPredicate;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The subset of viewport state available to a PermissionFunction.
|
|
27
|
+
*/
|
|
28
|
+
export interface PermissionViewport {
|
|
29
|
+
id: string;
|
|
30
|
+
user: VuuModuleUser;
|
|
31
|
+
}
|
|
32
|
+
export type TableContainer = typeof tableContainer;
|
|
33
|
+
/**
|
|
34
|
+
* Creates the PermissionFilter for a viewport. Configured per table on a VuuModule.
|
|
35
|
+
*/
|
|
36
|
+
export type PermissionFunction = (viewport: PermissionViewport, tableContainer: TableContainer) => PermissionFilter;
|
|
37
|
+
export declare const AllowAllPermissionFilter: PermissionFilter;
|
|
38
|
+
export declare const DenyAllPermissionFilter: PermissionFilter;
|
|
39
|
+
export declare function PermissionFilter(rowPredicate: (row: RowData) => boolean): PermissionFilter;
|
|
40
|
+
export declare function PermissionFilter(columnName: string, allowedValues: Set<string>): PermissionFilter;
|
|
41
|
+
export declare function PermissionFilter(filters: Iterable<PermissionFilter>): PermissionFilter;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Table } from "../../Table";
|
|
2
|
+
import type { RowPredicate } from "./PermissionFilter";
|
|
3
|
+
/**
|
|
4
|
+
* A per-viewport view of a source table, containing only those rows that
|
|
5
|
+
* satisfy a permission predicate. Changes to the source table are mirrored,
|
|
6
|
+
* an update that moves a row into or out of the permitted set is emitted as
|
|
7
|
+
* an insert or delete.
|
|
8
|
+
*/
|
|
9
|
+
export declare class PermissionFilteredTable extends Table {
|
|
10
|
+
#private;
|
|
11
|
+
constructor(source: Table, predicate: RowPredicate);
|
|
12
|
+
get source(): Table;
|
|
13
|
+
/**
|
|
14
|
+
* Start mirroring changes from the source table. Invoked on construction,
|
|
15
|
+
* only needs to be called again following dispose.
|
|
16
|
+
*/
|
|
17
|
+
attach(): void;
|
|
18
|
+
dispose(): void;
|
|
19
|
+
private contains;
|
|
20
|
+
private handleSourceInsert;
|
|
21
|
+
private handleSourceUpdate;
|
|
22
|
+
private handleSourceDelete;
|
|
23
|
+
private replaceRow;
|
|
24
|
+
}
|
|
@@ -2,6 +2,8 @@ import type { DataSourceBase, DataSourceConfig, DataSourceRowWithBigint, DataSou
|
|
|
2
2
|
import type { VuuMenu, VuuLink, LinkDescriptorWithLabel, VuuRpcMenuResponse, VuuCreateVisualLink, VuuRemoveVisualLink, VuuRpcServiceRequest, RpcResultSuccess, RpcResultError, VuuRpcMenuRequest } from "@vuu-ui/vuu-protocol-types";
|
|
3
3
|
import { Table } from "../../Table";
|
|
4
4
|
import { TickingArrayDataSource } from "../../TickingArrayDataSource";
|
|
5
|
+
import { type PermissionFunction } from "../filter/PermissionFilter";
|
|
6
|
+
import { type VuuModuleUser } from "../user/CurrentUser";
|
|
5
7
|
export interface IVuuModule<T extends string = string> {
|
|
6
8
|
createDataSource: (tableName: T, viewport?: string, config?: DataSourceConfig & {
|
|
7
9
|
session?: SessionDataSourceOverrides;
|
|
@@ -28,6 +30,12 @@ type Subscription = {
|
|
|
28
30
|
dataSource: DataSourceBase<DataSourceRowWithBigint>;
|
|
29
31
|
sessionTableName?: string;
|
|
30
32
|
sourceTableName?: string;
|
|
33
|
+
/**
|
|
34
|
+
* The table viewed by this subscription. Where a permission filter applies,
|
|
35
|
+
* this will be a PermissionFilteredTable
|
|
36
|
+
*/
|
|
37
|
+
table: Table;
|
|
38
|
+
user: VuuModuleUser;
|
|
31
39
|
};
|
|
32
40
|
export declare abstract class VuuModule<T extends string = string> implements IVuuModule<T> {
|
|
33
41
|
#private;
|
|
@@ -41,6 +49,16 @@ export declare abstract class VuuModule<T extends string = string> implements IV
|
|
|
41
49
|
protected get includeDefaultServices(): boolean;
|
|
42
50
|
protected beforeSessionSave(_sourceTable: Table, _sessionTable: Table): Promise<void>;
|
|
43
51
|
protected afterSessionSave(_sourceTable: Table, _sessionTable: Table): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Optional, per table. Restricts the rows visible to each viewport, based on
|
|
54
|
+
* the viewport (and the user who owns it).
|
|
55
|
+
*/
|
|
56
|
+
protected permissionFunctions?: Partial<Record<T, PermissionFunction>>;
|
|
57
|
+
/**
|
|
58
|
+
* Invoked before a dataSource is created. Can be used, for example, to
|
|
59
|
+
* lazily start a data provider.
|
|
60
|
+
*/
|
|
61
|
+
protected beforeCreateDataSource?(tableName: T): void;
|
|
44
62
|
getTableSchema(tableName: string): Record<T, Readonly<TableSchema>>[T];
|
|
45
63
|
getTableList(): string[];
|
|
46
64
|
protected handlePostSubscribeActivities: ({ clientViewportId, tableSchema, }: DataSourceSubscribedMessage) => void;
|
|
@@ -54,6 +72,7 @@ export declare abstract class VuuModule<T extends string = string> implements IV
|
|
|
54
72
|
createDataSource: (tableName: T, viewport?: string, config?: DataSourceConfig & {
|
|
55
73
|
session?: SessionDataSourceOverrides;
|
|
56
74
|
}) => DataSourceBase<DataSourceRowWithBigint>;
|
|
75
|
+
private createPermissionFilteredTable;
|
|
57
76
|
getServices(tableName: T): RpcService[];
|
|
58
77
|
getMenuServices(tableName: T): RpcMenuService[];
|
|
59
78
|
protected get sessionTableMap(): SessionTableMap;
|
|
@@ -8,7 +8,7 @@ declare class TableContainer {
|
|
|
8
8
|
private constructor();
|
|
9
9
|
static get instance(): TableContainer;
|
|
10
10
|
createTable: (schema: TableSchema, data?: Array<Array<VuuRowDataItemType | bigint>>, dataMap?: ColumnMap, updateGenerator?: UpdateGenerator) => Table;
|
|
11
|
-
createJoinTable(joinTable: VuuTable,
|
|
11
|
+
createJoinTable(joinTable: VuuTable, source1: VuuTable | Table, source2: VuuTable | Table, joinColumn: string): Table;
|
|
12
12
|
addTable(table: Table): void;
|
|
13
13
|
getTable<T = Table>(tableName: string): NonNullable<T>;
|
|
14
14
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user that owns a viewport, as seen by permission functions and
|
|
3
|
+
* rpc services.
|
|
4
|
+
*/
|
|
5
|
+
export interface VuuModuleUser {
|
|
6
|
+
authorizations: string[];
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const getCurrentUser: () => VuuModuleUser;
|
|
10
|
+
/**
|
|
11
|
+
* Applies to viewports created after the change. Pass undefined to restore
|
|
12
|
+
* the default user.
|
|
13
|
+
*/
|
|
14
|
+
export declare const setCurrentUser: (user?: string | VuuModuleUser) => void;
|
package/types/index.d.ts
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
export * from "./ArrayProxy";
|
|
2
|
+
export * from "./core/filter/PermissionFilter";
|
|
3
|
+
export * from "./core/filter/PermissionFilteredTable";
|
|
2
4
|
export * from "./core/module/VuuModule";
|
|
3
5
|
export { ensureVuuModule } from "./core/module/ModuleContainer";
|
|
6
|
+
export * from "./core/user/CurrentUser";
|
|
4
7
|
export { default as tableContainer } from "./core/table/TableContainer";
|
|
5
8
|
export * from "./local-datasource-provider/LocalDatasourceProvider";
|
|
6
9
|
export * from "./makeSuggestions";
|
|
10
|
+
export * from "./notifications";
|
|
7
11
|
export * from "./schemas";
|
|
8
12
|
export * from "./simul";
|
|
9
13
|
export * from "./basket";
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
+
import { type LocalVuuServer } from "@vuu-ui/core";
|
|
1
2
|
import type { ReactNode } from "react";
|
|
2
|
-
|
|
3
|
+
import type { VuuModule } from "../core/module/VuuModule";
|
|
4
|
+
/** Data context covering every VuuModule registered in the browser. */
|
|
5
|
+
export declare const LocalDataSourceProvider: ({ children }: {
|
|
3
6
|
children: ReactNode;
|
|
4
|
-
}) => import("react").JSX.Element;
|
|
7
|
+
}) => import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export interface LocalVuuServerOptions {
|
|
9
|
+
/** Matches the `vuu.connectionId` of the modules this server serves. */
|
|
10
|
+
connectionId: string;
|
|
11
|
+
modules: VuuModule<any>[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Simulates a Vuu server in the browser, publishing the tables of the given
|
|
15
|
+
* modules. Module instances are resolved from this server, not by name from
|
|
16
|
+
* the global registry, so servers can each have their own instance of a
|
|
17
|
+
* module (e.g. NOTIFICATIONS). Pass the result to a local-mode `AuthenticationProvider` as one of
|
|
18
|
+
* its `localServers`.
|
|
19
|
+
*/
|
|
20
|
+
export declare const createLocalVuuServer: ({ connectionId, modules, }: LocalVuuServerOptions) => LocalVuuServer;
|
|
@@ -1,13 +1,21 @@
|
|
|
1
|
+
import { type ManagedModule } from "@heswell/module-admin/contracts";
|
|
1
2
|
import type { TableSchema } from "@vuu-ui/vuu-data-types";
|
|
2
|
-
import { VuuModule } from "../core/module/VuuModule";
|
|
3
|
+
import { VuuModule, type RpcService } from "../core/module/VuuModule";
|
|
3
4
|
import { type Table } from "../Table";
|
|
4
5
|
import { type ModuleAdminTableName } from "./module-admin-schemas";
|
|
5
6
|
import { type ModuleAdminSnapshot } from "./snapshot-projection";
|
|
6
7
|
type ModuleAdminTables = Record<ModuleAdminTableName, Table>;
|
|
7
8
|
export declare const MODULE_ADMIN_INITIAL_SNAPSHOT: ModuleAdminSnapshot;
|
|
9
|
+
/**
|
|
10
|
+
* In-browser MODULE_DISCOVERY server. Supports the module admin RPCs from
|
|
11
|
+
* `@heswell/module-admin/contracts` on both tables; state is held in memory.
|
|
12
|
+
*/
|
|
8
13
|
export declare class ModuleAdminModule extends VuuModule<ModuleAdminTableName> {
|
|
9
14
|
#private;
|
|
10
|
-
|
|
15
|
+
private readonly now;
|
|
16
|
+
constructor(snapshot?: ModuleAdminSnapshot, now?: () => number);
|
|
17
|
+
/** Current module state. */
|
|
18
|
+
get modules(): readonly ManagedModule[];
|
|
11
19
|
get menus(): {
|
|
12
20
|
modulePermissions: undefined;
|
|
13
21
|
modules: undefined;
|
|
@@ -15,7 +23,8 @@ export declare class ModuleAdminModule extends VuuModule<ModuleAdminTableName> {
|
|
|
15
23
|
protected get includeDefaultServices(): boolean;
|
|
16
24
|
get menuServices(): undefined;
|
|
17
25
|
get schemas(): Record<ModuleAdminTableName, Readonly<TableSchema>>;
|
|
18
|
-
|
|
26
|
+
private service;
|
|
27
|
+
get services(): Record<ModuleAdminTableName, RpcService[]>;
|
|
19
28
|
get tables(): ModuleAdminTables;
|
|
20
29
|
get visualLinks(): undefined;
|
|
21
30
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { ModuleAdminModule, MODULE_ADMIN_INITIAL_SNAPSHOT, moduleAdminModule, } from "./ModuleAdminModule";
|
|
2
2
|
export { MODULE_ADMIN_MODULE_NAME, MODULE_ADMIN_TABLE_SCHEMAS, type ModuleAdminTableName, } from "./module-admin-schemas";
|
|
3
3
|
export { projectModuleAdminSnapshot, reconcileModuleAdminTables, type ModuleAdminSnapshot, } from "./snapshot-projection";
|
|
4
|
-
export type { ModuleAccessRole, ModuleDefinition, ModulePermissionRow, ModuleRow, } from "@heswell/module-admin/contracts";
|
|
4
|
+
export type { ManagedModule, ModuleAccessRole, ModuleConfig, ModuleDefinition, ModulePermissionRow, ModuleRow, } from "@heswell/module-admin/contracts";
|