From b5546f8d9bf89e433a5bc47619c317c3293a5dce Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 17 Aug 2023 16:04:56 +0100 Subject: [PATCH] Update server and builder to properly sync changes with tables and views across all users --- .../src/components/grid/lib/websocket.js | 17 ++++--- .../server/src/api/controllers/row/index.ts | 4 +- .../server/src/api/controllers/table/index.ts | 2 +- .../src/api/controllers/view/viewsV2.ts | 7 ++- packages/server/src/websockets/builder.ts | 6 +-- packages/server/src/websockets/grid.ts | 46 +++++++++++++++---- packages/server/src/websockets/websocket.ts | 2 +- packages/shared-core/src/constants.ts | 2 +- 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/packages/frontend-core/src/components/grid/lib/websocket.js b/packages/frontend-core/src/components/grid/lib/websocket.js index 1c490f74ca..af34acd530 100644 --- a/packages/frontend-core/src/components/grid/lib/websocket.js +++ b/packages/frontend-core/src/components/grid/lib/websocket.js @@ -3,7 +3,7 @@ import { createWebsocket } from "../../../utils" import { SocketEvent, GridSocketEvent } from "@budibase/shared-core" export const createGridWebsocket = context => { - const { rows, datasource, users, focusedCellId, table, API } = context + const { rows, datasource, users, focusedCellId, definition, API } = context const socket = createWebsocket("/socket/grid") const connectToDatasource = datasource => { @@ -51,13 +51,16 @@ export const createGridWebsocket = context => { }) // Table events - socket.onOther(GridSocketEvent.TableChange, ({ table: newTable }) => { - // Only update table if one exists. If the table was deleted then we don't - // want to know - let the builder navigate away - if (newTable) { - table.set(newTable) + socket.onOther( + GridSocketEvent.DatasourceChange, + ({ datasource: newDatasource }) => { + // Only update definition if one exists. If the datasource was deleted + // then we don't want to know - let the builder navigate away + if (newDatasource) { + definition.set(newDatasource) + } } - }) + ) // Change websocket connection when table changes datasource.subscribe(connectToDatasource) diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index 695e626630..ebe0c32e63 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -159,7 +159,7 @@ async function deleteRows(ctx: UserCtx) { for (let row of rows) { ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) - gridSocket?.emitRowDeletion(ctx, row._id!) + gridSocket?.emitRowDeletion(ctx, row) } return rows @@ -175,7 +175,7 @@ async function deleteRow(ctx: UserCtx) { await quotas.removeRow() ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, resp.row) - gridSocket?.emitRowDeletion(ctx, resp.row._id!) + gridSocket?.emitRowDeletion(ctx, resp.row) return resp } diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 5e09a5237a..759974d6a7 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -93,7 +93,7 @@ export async function destroy(ctx: UserCtx) { ctx.status = 200 ctx.table = deletedTable ctx.body = { message: `Table ${tableId} deleted.` } - builderSocket?.emitTableDeletion(ctx, tableId) + builderSocket?.emitTableDeletion(ctx, deletedTable) } export async function bulkImport(ctx: UserCtx) { diff --git a/packages/server/src/api/controllers/view/viewsV2.ts b/packages/server/src/api/controllers/view/viewsV2.ts index e7e5aefefb..f4f969622a 100644 --- a/packages/server/src/api/controllers/view/viewsV2.ts +++ b/packages/server/src/api/controllers/view/viewsV2.ts @@ -8,7 +8,7 @@ import { ViewResponse, ViewV2, } from "@budibase/types" -import { builderSocket } from "../../../websockets" +import { builderSocket, gridSocket } from "../../../websockets" async function parseSchema(view: CreateViewRequest) { if (!view.schema) { @@ -41,7 +41,7 @@ async function parseSchema(view: CreateViewRequest) { export async function get(ctx: Ctx) { ctx.body = { - data: await sdk.views.get(ctx.params.viewId, { enriched: true }) + data: await sdk.views.get(ctx.params.viewId, { enriched: true }), } } @@ -67,6 +67,7 @@ export async function create(ctx: Ctx) { const table = await sdk.tables.getTable(tableId) builderSocket?.emitTableUpdate(ctx, table) + gridSocket?.emitViewUpdate(ctx, result) } export async function update(ctx: Ctx) { @@ -101,6 +102,7 @@ export async function update(ctx: Ctx) { const table = await sdk.tables.getTable(tableId) builderSocket?.emitTableUpdate(ctx, table) + gridSocket?.emitViewUpdate(ctx, result) } export async function remove(ctx: Ctx) { @@ -111,4 +113,5 @@ export async function remove(ctx: Ctx) { const table = await sdk.tables.getTable(view.tableId) builderSocket?.emitTableUpdate(ctx, table) + gridSocket?.emitViewDeletion(ctx, view) } diff --git a/packages/server/src/websockets/builder.ts b/packages/server/src/websockets/builder.ts index 7f4c9fbd33..d2fdbca20c 100644 --- a/packages/server/src/websockets/builder.ts +++ b/packages/server/src/websockets/builder.ts @@ -108,12 +108,12 @@ export default class BuilderSocket extends BaseSocket { gridSocket?.emitTableUpdate(ctx, table) } - emitTableDeletion(ctx: any, id: string) { + emitTableDeletion(ctx: any, table: Table) { this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.TableChange, { - id, + id: table._id, table: null, }) - gridSocket?.emitTableDeletion(ctx, id) + gridSocket?.emitTableDeletion(ctx, table) } emitDatasourceUpdate(ctx: any, datasource: Datasource) { diff --git a/packages/server/src/websockets/grid.ts b/packages/server/src/websockets/grid.ts index abc1872b56..979a0bf125 100644 --- a/packages/server/src/websockets/grid.ts +++ b/packages/server/src/websockets/grid.ts @@ -5,7 +5,7 @@ import { auth, permissions } from "@budibase/backend-core" import http from "http" import Koa from "koa" import { getTableId } from "../api/controllers/row/utils" -import { Row, Table } from "@budibase/types" +import { Row, Table, View, ViewV2 } from "@budibase/types" import { Socket } from "socket.io" import { GridSocketEvent } from "@budibase/shared-core" import { userAgent } from "koa-useragent" @@ -88,22 +88,52 @@ export default class GridSocket extends BaseSocket { }) } - emitRowDeletion(ctx: any, id: string) { + emitRowDeletion(ctx: any, row: Row) { const resourceId = ctx.params?.viewId || getTableId(ctx) const room = `${ctx.appId}-${resourceId}` - this.emitToRoom(ctx, room, GridSocketEvent.RowChange, { id, row: null }) + this.emitToRoom(ctx, room, GridSocketEvent.RowChange, { + id: row._id, + row: null, + }) } emitTableUpdate(ctx: any, table: Table) { const room = `${ctx.appId}-${table._id}` - this.emitToRoom(ctx, room, GridSocketEvent.TableChange, { + this.emitToRoom(ctx, room, GridSocketEvent.DatasourceChange, { id: table._id, - table, + datasource: table, }) } - emitTableDeletion(ctx: any, id: string) { - const room = `${ctx.appId}-${id}` - this.emitToRoom(ctx, room, GridSocketEvent.TableChange, { id, table: null }) + emitTableDeletion(ctx: any, table: Table) { + const room = `${ctx.appId}-${table._id}` + this.emitToRoom(ctx, room, GridSocketEvent.DatasourceChange, { + id: table._id, + datasource: null, + }) + + // When the table is deleted we need to notify all views that they have + // also been deleted + Object.values(table.views || {}) + .filter((view: View | ViewV2) => (view as ViewV2).version === 2) + .forEach((view: View | ViewV2) => { + this.emitViewDeletion(ctx, view as ViewV2) + }) + } + + emitViewUpdate(ctx: any, view: ViewV2) { + const room = `${ctx.appId}-${view.id}` + this.emitToRoom(ctx, room, GridSocketEvent.DatasourceChange, { + id: view.id, + datasource: view, + }) + } + + emitViewDeletion(ctx: any, view: ViewV2) { + const room = `${ctx.appId}-${view.id}` + this.emitToRoom(ctx, room, GridSocketEvent.DatasourceChange, { + id: view.id, + datasource: null, + }) } } diff --git a/packages/server/src/websockets/websocket.ts b/packages/server/src/websockets/websocket.ts index 9dea67ef5f..c92f0b6897 100644 --- a/packages/server/src/websockets/websocket.ts +++ b/packages/server/src/websockets/websocket.ts @@ -270,7 +270,7 @@ export class BaseSocket { // Emit an event to everyone in a room, including metadata of whom // the originator of the request was - emitToRoom(ctx: any, room: string, event: string, payload: any) { + emitToRoom(ctx: any, room: string | string[], event: string, payload: any) { this.io.in(room).emit(event, { ...payload, apiSessionId: ctx.headers?.[Header.SESSION_ID], diff --git a/packages/shared-core/src/constants.ts b/packages/shared-core/src/constants.ts index 2cb936d9a0..725c246e2f 100644 --- a/packages/shared-core/src/constants.ts +++ b/packages/shared-core/src/constants.ts @@ -76,7 +76,7 @@ export enum SocketEvent { export enum GridSocketEvent { RowChange = "RowChange", - TableChange = "TableChange", + DatasourceChange = "DatasourceChange", SelectDatasource = "SelectDatasource", SelectCell = "SelectCell", }