diff --git a/packages/builder/src/stores/backend/tables.js b/packages/builder/src/stores/backend/tables.js index f8796712a8..d79ed6f072 100644 --- a/packages/builder/src/stores/backend/tables.js +++ b/packages/builder/src/stores/backend/tables.js @@ -1,5 +1,4 @@ import { get, writable, derived } from "svelte/store" -import { datasources } from "./" import { cloneDeep } from "lodash/fp" import { API } from "api" import { SWITCHABLE_TYPES } from "constants/backend" @@ -63,7 +62,6 @@ export function createTablesStore() { const savedTable = await API.saveTable(updatedTable) replaceTable(savedTable._id, savedTable) - await datasources.fetch() select(savedTable._id) return savedTable } diff --git a/packages/server/src/api/controllers/table/external.ts b/packages/server/src/api/controllers/table/external.ts index ee789ddd3a..f35691cb62 100644 --- a/packages/server/src/api/controllers/table/external.ts +++ b/packages/server/src/api/controllers/table/external.ts @@ -26,6 +26,7 @@ import { RelationshipTypes, } from "@budibase/types" import sdk from "../../../sdk" +import { builderSocket } from "../../../websockets" const { cloneDeep } = require("lodash/fp") async function makeTableRequest( @@ -318,6 +319,13 @@ export async function save(ctx: UserCtx) { datasource.entities[tableToSave.name] = tableToSave await db.put(datasource) + // Since tables are stored inside datasources, we need to notify clients + // that the datasource definition changed + const updatedDatasource = await db.get(datasource._id) + builderSocket?.emitDatasourceUpdate(ctx, updatedDatasource, { + includeOriginator: true, + }) + return tableToSave } diff --git a/packages/server/src/websockets/builder.ts b/packages/server/src/websockets/builder.ts index 0f2c43e5ab..53b0a10905 100644 --- a/packages/server/src/websockets/builder.ts +++ b/packages/server/src/websockets/builder.ts @@ -3,7 +3,13 @@ import { BaseSocket } from "./websocket" import { permissions, events } from "@budibase/backend-core" import http from "http" import Koa from "koa" -import { Datasource, Table, SocketSession, ContextUser } from "@budibase/types" +import { + Datasource, + Table, + SocketSession, + ContextUser, + SocketMessageOptions, +} from "@budibase/types" import { gridSocket } from "./index" import { clearLock, updateLock } from "../utilities/redis" import { Socket } from "socket.io" @@ -66,33 +72,61 @@ export default class BuilderSocket extends BaseSocket { } } - emitTableUpdate(ctx: any, table: Table) { - this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.TableChange, { - id: table._id, - table, - }) - gridSocket?.emitTableUpdate(ctx, table) + emitTableUpdate(ctx: any, table: Table, options?: SocketMessageOptions) { + this.emitToRoom( + ctx, + ctx.appId, + BuilderSocketEvent.TableChange, + { + id: table._id, + table, + }, + options + ) + gridSocket?.emitTableUpdate(ctx, table, options) } - emitTableDeletion(ctx: any, id: string) { - this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.TableChange, { - id, - table: null, - }) - gridSocket?.emitTableDeletion(ctx, id) + emitTableDeletion(ctx: any, id: string, options?: SocketMessageOptions) { + this.emitToRoom( + ctx, + ctx.appId, + BuilderSocketEvent.TableChange, + { + id, + table: null, + }, + options + ) + gridSocket?.emitTableDeletion(ctx, id, options) } - emitDatasourceUpdate(ctx: any, datasource: Datasource) { - this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.DatasourceChange, { - id: datasource._id, - datasource, - }) + emitDatasourceUpdate( + ctx: any, + datasource: Datasource, + options?: SocketMessageOptions + ) { + this.emitToRoom( + ctx, + ctx.appId, + BuilderSocketEvent.DatasourceChange, + { + id: datasource._id, + datasource, + }, + options + ) } - emitDatasourceDeletion(ctx: any, id: string) { - this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.DatasourceChange, { - id, - datasource: null, - }) + emitDatasourceDeletion(ctx: any, id: string, options?: SocketMessageOptions) { + this.emitToRoom( + ctx, + ctx.appId, + BuilderSocketEvent.DatasourceChange, + { + id, + datasource: null, + }, + options + ) } } diff --git a/packages/server/src/websockets/grid.ts b/packages/server/src/websockets/grid.ts index 6731c2d899..38ec091816 100644 --- a/packages/server/src/websockets/grid.ts +++ b/packages/server/src/websockets/grid.ts @@ -4,7 +4,7 @@ import { 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, SocketMessageOptions, Table } from "@budibase/types" import { Socket } from "socket.io" import { GridSocketEvent } from "@budibase/shared-core" @@ -29,27 +29,51 @@ export default class GridSocket extends BaseSocket { }) } - emitRowUpdate(ctx: any, row: Row) { + emitRowUpdate(ctx: any, row: Row, options?: SocketMessageOptions) { const tableId = getTableId(ctx) - this.emitToRoom(ctx, tableId, GridSocketEvent.RowChange, { - id: row._id, - row, - }) + this.emitToRoom( + ctx, + tableId, + GridSocketEvent.RowChange, + { + id: row._id, + row, + }, + options + ) } - emitRowDeletion(ctx: any, id: string) { + emitRowDeletion(ctx: any, id: string, options?: SocketMessageOptions) { const tableId = getTableId(ctx) - this.emitToRoom(ctx, tableId, GridSocketEvent.RowChange, { id, row: null }) + this.emitToRoom( + ctx, + tableId, + GridSocketEvent.RowChange, + { id, row: null }, + options + ) } - emitTableUpdate(ctx: any, table: Table) { - this.emitToRoom(ctx, table._id!, GridSocketEvent.TableChange, { - id: table._id, - table, - }) + emitTableUpdate(ctx: any, table: Table, options?: SocketMessageOptions) { + this.emitToRoom( + ctx, + table._id!, + GridSocketEvent.TableChange, + { + id: table._id, + table, + }, + options + ) } - emitTableDeletion(ctx: any, id: string) { - this.emitToRoom(ctx, id, GridSocketEvent.TableChange, { id, table: null }) + emitTableDeletion(ctx: any, id: string, options?: SocketMessageOptions) { + this.emitToRoom( + ctx, + id, + GridSocketEvent.TableChange, + { id, table: null }, + options + ) } } diff --git a/packages/server/src/websockets/websocket.ts b/packages/server/src/websockets/websocket.ts index d8cc10bda4..59a9d698d7 100644 --- a/packages/server/src/websockets/websocket.ts +++ b/packages/server/src/websockets/websocket.ts @@ -9,7 +9,7 @@ import { createAdapter } from "@socket.io/redis-adapter" import { Socket } from "socket.io" import { getSocketPubSubClients } from "../utilities/redis" import { SocketEvent, SocketSessionTTL } from "@budibase/shared-core" -import { SocketSession } from "@budibase/types" +import { SocketSession, SocketMessageOptions } from "@budibase/types" export class BaseSocket { io: Server @@ -276,12 +276,24 @@ export class BaseSocket { this.io.sockets.emit(event, payload) } - // 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) { + // Emit an event to everyone in a room + emitToRoom( + ctx: any, + room: string, + event: string, + payload: any, + options?: SocketMessageOptions + ) { + // By default, we include the session API of the originator so that they can ignore + // this event. If we want to include the originator then we leave it unset to that all + // clients will react to it. + let apiSessionId = null + if (!options?.includeOriginator) { + apiSessionId = ctx.headers?.[Header.SESSION_ID] + } this.io.in(room).emit(event, { ...payload, - apiSessionId: ctx.headers?.[Header.SESSION_ID], + apiSessionId, }) } } diff --git a/packages/types/src/sdk/websocket.ts b/packages/types/src/sdk/websocket.ts index 40e2654e82..770f44d9b2 100644 --- a/packages/types/src/sdk/websocket.ts +++ b/packages/types/src/sdk/websocket.ts @@ -7,3 +7,7 @@ export interface SocketSession { room?: string connectedAt: number } + +export interface SocketMessageOptions { + includeOriginator?: boolean +}