Merge pull request #10775 from Budibase/collaboration-fixes
Collaboration fixes
This commit is contained in:
commit
680b70643c
|
@ -16,6 +16,7 @@ export enum Header {
|
||||||
LICENSE_KEY = "x-budibase-license-key",
|
LICENSE_KEY = "x-budibase-license-key",
|
||||||
API_VER = "x-budibase-api-version",
|
API_VER = "x-budibase-api-version",
|
||||||
APP_ID = "x-budibase-app-id",
|
APP_ID = "x-budibase-app-id",
|
||||||
|
SESSION_ID = "x-budibase-session-id",
|
||||||
TYPE = "x-budibase-type",
|
TYPE = "x-budibase-type",
|
||||||
PREVIEW_ROLE = "x-budibase-role",
|
PREVIEW_ROLE = "x-budibase-role",
|
||||||
TENANT_ID = "x-budibase-tenant-id",
|
TENANT_ID = "x-budibase-tenant-id",
|
||||||
|
|
|
@ -15,25 +15,23 @@ export const createBuilderWebsocket = appId => {
|
||||||
socket.on("connect_error", err => {
|
socket.on("connect_error", err => {
|
||||||
console.log("Failed to connect to builder websocket:", err.message)
|
console.log("Failed to connect to builder websocket:", err.message)
|
||||||
})
|
})
|
||||||
|
|
||||||
// User events
|
|
||||||
socket.on(SocketEvent.UserUpdate, userStore.actions.updateUser)
|
|
||||||
socket.on(SocketEvent.UserDisconnect, userStore.actions.removeUser)
|
|
||||||
|
|
||||||
// Table events
|
|
||||||
socket.on(BuilderSocketEvent.TableChange, ({ id, table }) => {
|
|
||||||
tables.replaceTable(id, table)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Datasource events
|
|
||||||
socket.on(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => {
|
|
||||||
datasources.replaceDatasource(id, datasource)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Clean up user store on disconnect
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
userStore.actions.reset()
|
userStore.actions.reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// User events
|
||||||
|
socket.onOther(SocketEvent.UserUpdate, userStore.actions.updateUser)
|
||||||
|
socket.onOther(SocketEvent.UserDisconnect, userStore.actions.removeUser)
|
||||||
|
|
||||||
|
// Table events
|
||||||
|
socket.onOther(BuilderSocketEvent.TableChange, ({ id, table }) => {
|
||||||
|
tables.replaceTable(id, table)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Datasource events
|
||||||
|
socket.onOther(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => {
|
||||||
|
datasources.replaceDatasource(id, datasource)
|
||||||
|
})
|
||||||
|
|
||||||
return socket
|
return socket
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
allowDeleteRows={!isUsersTable}
|
allowDeleteRows={!isUsersTable}
|
||||||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
|
on:updatetable={e => tables.replaceTable(id, e.detail)}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="controls">
|
<svelte:fragment slot="controls">
|
||||||
{#if isInternal}
|
{#if isInternal}
|
||||||
|
|
|
@ -81,6 +81,7 @@
|
||||||
<Label>{label}</Label>
|
<Label>{label}</Label>
|
||||||
<Editor
|
<Editor
|
||||||
editorHeight="250"
|
editorHeight="250"
|
||||||
|
editorWidth="320"
|
||||||
mode="json"
|
mode="json"
|
||||||
on:change={({ detail }) => (value = detail.value)}
|
on:change={({ detail }) => (value = detail.value)}
|
||||||
value={stringVal}
|
value={stringVal}
|
||||||
|
|
|
@ -62,7 +62,7 @@ export function createTablesStore() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const savedTable = await API.saveTable(updatedTable)
|
const savedTable = await API.saveTable(updatedTable)
|
||||||
replaceTable(table._id, savedTable)
|
replaceTable(savedTable._id, savedTable)
|
||||||
await datasources.fetch()
|
await datasources.fetch()
|
||||||
select(savedTable._id)
|
select(savedTable._id)
|
||||||
return savedTable
|
return savedTable
|
||||||
|
|
|
@ -18,7 +18,9 @@ export const initWebsocket = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialise connection
|
// Initialise connection
|
||||||
socket = createWebsocket("/socket/client", false)
|
socket = createWebsocket("/socket/client", {
|
||||||
|
heartbeat: false,
|
||||||
|
})
|
||||||
|
|
||||||
// Event handlers
|
// Event handlers
|
||||||
socket.on("plugin-update", data => {
|
socket.on("plugin-update", data => {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { Helpers } from "@budibase/bbui"
|
||||||
import { ApiVersion } from "../constants"
|
import { ApiVersion } from "../constants"
|
||||||
import { buildAnalyticsEndpoints } from "./analytics"
|
import { buildAnalyticsEndpoints } from "./analytics"
|
||||||
import { buildAppEndpoints } from "./app"
|
import { buildAppEndpoints } from "./app"
|
||||||
|
@ -30,6 +31,14 @@ import { buildEnvironmentVariableEndpoints } from "./environmentVariables"
|
||||||
import { buildEventEndpoints } from "./events"
|
import { buildEventEndpoints } from "./events"
|
||||||
import { buildAuditLogsEndpoints } from "./auditLogs"
|
import { buildAuditLogsEndpoints } from "./auditLogs"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Random identifier to uniquely identify a session in a tab. This is
|
||||||
|
* used to determine the originator of calls to the API, which is in
|
||||||
|
* turn used to determine who caused a websocket message to be sent, so
|
||||||
|
* that we can ignore events caused by ourselves.
|
||||||
|
*/
|
||||||
|
export const APISessionID = Helpers.uuid()
|
||||||
|
|
||||||
const defaultAPIClientConfig = {
|
const defaultAPIClientConfig = {
|
||||||
/**
|
/**
|
||||||
* Certain definitions can't change at runtime for client apps, such as the
|
* Certain definitions can't change at runtime for client apps, such as the
|
||||||
|
@ -116,6 +125,7 @@ export const createAPIClient = config => {
|
||||||
|
|
||||||
// Build headers
|
// Build headers
|
||||||
let headers = { Accept: "application/json" }
|
let headers = { Accept: "application/json" }
|
||||||
|
headers["x-budibase-session-id"] = APISessionID
|
||||||
if (!external) {
|
if (!external) {
|
||||||
headers["x-budibase-api-version"] = ApiVersion
|
headers["x-budibase-api-version"] = ApiVersion
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,15 +26,15 @@ export const createGridWebsocket = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// User events
|
// User events
|
||||||
socket.on(SocketEvent.UserUpdate, user => {
|
socket.onOther(SocketEvent.UserUpdate, user => {
|
||||||
users.actions.updateUser(user)
|
users.actions.updateUser(user)
|
||||||
})
|
})
|
||||||
socket.on(SocketEvent.UserDisconnect, user => {
|
socket.onOther(SocketEvent.UserDisconnect, user => {
|
||||||
users.actions.removeUser(user)
|
users.actions.removeUser(user)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Row events
|
// Row events
|
||||||
socket.on(GridSocketEvent.RowChange, async data => {
|
socket.onOther(GridSocketEvent.RowChange, async data => {
|
||||||
if (data.id) {
|
if (data.id) {
|
||||||
rows.actions.replaceRow(data.id, data.row)
|
rows.actions.replaceRow(data.id, data.row)
|
||||||
} else if (data.row.id) {
|
} else if (data.row.id) {
|
||||||
|
@ -44,7 +44,7 @@ export const createGridWebsocket = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Table events
|
// Table events
|
||||||
socket.on(GridSocketEvent.TableChange, data => {
|
socket.onOther(GridSocketEvent.TableChange, data => {
|
||||||
// Only update table if one exists. If the table was deleted then we don't
|
// Only update table if one exists. If the table was deleted then we don't
|
||||||
// want to know - let the builder navigate away
|
// want to know - let the builder navigate away
|
||||||
if (data.table) {
|
if (data.table) {
|
||||||
|
|
|
@ -46,7 +46,7 @@ export const createStores = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveStores = context => {
|
export const deriveStores = context => {
|
||||||
const { table, columns, stickyColumn, API } = context
|
const { table, columns, stickyColumn, API, dispatch } = context
|
||||||
|
|
||||||
// Updates the tables primary display column
|
// Updates the tables primary display column
|
||||||
const changePrimaryDisplay = async column => {
|
const changePrimaryDisplay = async column => {
|
||||||
|
@ -90,6 +90,10 @@ export const deriveStores = context => {
|
||||||
// Update local state
|
// Update local state
|
||||||
table.set(newTable)
|
table.set(newTable)
|
||||||
|
|
||||||
|
// Broadcast change to external state can be updated, as this change
|
||||||
|
// will not be received by the builder websocket because we caused it ourselves
|
||||||
|
dispatch("updatetable", newTable)
|
||||||
|
|
||||||
// Update server
|
// Update server
|
||||||
await API.saveTable(newTable)
|
await API.saveTable(newTable)
|
||||||
}
|
}
|
||||||
|
|
|
@ -214,8 +214,7 @@ export const deriveStores = context => {
|
||||||
const addRow = async (row, idx, bubble = false) => {
|
const addRow = async (row, idx, bubble = false) => {
|
||||||
try {
|
try {
|
||||||
// Create row
|
// Create row
|
||||||
let newRow = await API.saveRow({ ...row, tableId: get(tableId) })
|
const newRow = await API.saveRow({ ...row, tableId: get(tableId) })
|
||||||
newRow = await fetchRow(newRow._id)
|
|
||||||
|
|
||||||
// Update state
|
// Update state
|
||||||
if (idx != null) {
|
if (idx != null) {
|
||||||
|
@ -437,6 +436,9 @@ export const deriveStores = context => {
|
||||||
|
|
||||||
// Checks if we have a row with a certain ID
|
// Checks if we have a row with a certain ID
|
||||||
const hasRow = id => {
|
const hasRow = id => {
|
||||||
|
if (id === NewRowID) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
return get(rowLookupMap)[id] != null
|
return get(rowLookupMap)[id] != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,17 +1,26 @@
|
||||||
import { io } from "socket.io-client"
|
import { io } from "socket.io-client"
|
||||||
import { SocketEvent, SocketSessionTTL } from "@budibase/shared-core"
|
import { SocketEvent, SocketSessionTTL } from "@budibase/shared-core"
|
||||||
|
import { APISessionID } from "../api"
|
||||||
|
|
||||||
export const createWebsocket = (path, heartbeat = true) => {
|
const DefaultOptions = {
|
||||||
|
heartbeat: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createWebsocket = (path, options = DefaultOptions) => {
|
||||||
if (!path) {
|
if (!path) {
|
||||||
throw "A websocket path must be provided"
|
throw "A websocket path must be provided"
|
||||||
}
|
}
|
||||||
|
const { heartbeat } = {
|
||||||
|
...DefaultOptions,
|
||||||
|
...options,
|
||||||
|
}
|
||||||
|
|
||||||
// Determine connection info
|
// Determine connection info
|
||||||
const tls = location.protocol === "https:"
|
const tls = location.protocol === "https:"
|
||||||
const proto = tls ? "wss:" : "ws:"
|
const proto = tls ? "wss:" : "ws:"
|
||||||
const host = location.hostname
|
const host = location.hostname
|
||||||
const port = location.port || (tls ? 443 : 80)
|
const port = location.port || (tls ? 443 : 80)
|
||||||
const socket = io(`${proto}//${host}:${port}`, {
|
let socket = io(`${proto}//${host}:${port}`, {
|
||||||
path,
|
path,
|
||||||
// Cap reconnection attempts to 3 (total of 15 seconds before giving up)
|
// Cap reconnection attempts to 3 (total of 15 seconds before giving up)
|
||||||
reconnectionAttempts: 3,
|
reconnectionAttempts: 3,
|
||||||
|
@ -37,5 +46,15 @@ export const createWebsocket = (path, heartbeat = true) => {
|
||||||
clearInterval(interval)
|
clearInterval(interval)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Helper utility to ignore events that were triggered due to API
|
||||||
|
// changes made by us
|
||||||
|
socket.onOther = (event, callback) => {
|
||||||
|
socket.on(event, data => {
|
||||||
|
if (data?.apiSessionId !== APISessionID) {
|
||||||
|
callback(data)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return socket
|
return socket
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import {
|
import {
|
||||||
SortDirection,
|
|
||||||
FieldTypes,
|
FieldTypes,
|
||||||
NoEmptyFilterStrings,
|
NoEmptyFilterStrings,
|
||||||
|
SortDirection,
|
||||||
} from "../../../constants"
|
} from "../../../constants"
|
||||||
import {
|
import {
|
||||||
breakExternalTableId,
|
breakExternalTableId,
|
||||||
|
@ -11,20 +11,34 @@ import { ExternalRequest, RunConfig } from "./ExternalRequest"
|
||||||
import * as exporters from "../view/exporters"
|
import * as exporters from "../view/exporters"
|
||||||
import { apiFileReturn } from "../../../utilities/fileSystem"
|
import { apiFileReturn } from "../../../utilities/fileSystem"
|
||||||
import {
|
import {
|
||||||
Operation,
|
|
||||||
UserCtx,
|
|
||||||
Row,
|
|
||||||
PaginationJson,
|
|
||||||
Table,
|
|
||||||
Datasource,
|
Datasource,
|
||||||
IncludeRelationship,
|
IncludeRelationship,
|
||||||
|
Operation,
|
||||||
|
PaginationJson,
|
||||||
|
Row,
|
||||||
SortJson,
|
SortJson,
|
||||||
|
Table,
|
||||||
|
UserCtx,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import * as utils from "./utils"
|
import * as utils from "./utils"
|
||||||
|
|
||||||
const { cleanExportRows } = require("./utils")
|
const { cleanExportRows } = require("./utils")
|
||||||
|
|
||||||
|
async function getRow(
|
||||||
|
tableId: string,
|
||||||
|
rowId: string,
|
||||||
|
opts?: { relationships?: boolean }
|
||||||
|
) {
|
||||||
|
const response = (await handleRequest(Operation.READ, tableId, {
|
||||||
|
id: breakRowIdField(rowId),
|
||||||
|
includeSqlRelationships: opts?.relationships
|
||||||
|
? IncludeRelationship.INCLUDE
|
||||||
|
: IncludeRelationship.EXCLUDE,
|
||||||
|
})) as Row[]
|
||||||
|
return response ? response[0] : response
|
||||||
|
}
|
||||||
|
|
||||||
export async function handleRequest(
|
export async function handleRequest(
|
||||||
operation: Operation,
|
operation: Operation,
|
||||||
tableId: string,
|
tableId: string,
|
||||||
|
@ -63,11 +77,15 @@ export async function patch(ctx: UserCtx) {
|
||||||
if (!validateResult.valid) {
|
if (!validateResult.valid) {
|
||||||
throw { validation: validateResult.errors }
|
throw { validation: validateResult.errors }
|
||||||
}
|
}
|
||||||
return handleRequest(Operation.UPDATE, tableId, {
|
const response = await handleRequest(Operation.UPDATE, tableId, {
|
||||||
id: breakRowIdField(id),
|
id: breakRowIdField(id),
|
||||||
row: inputs,
|
row: inputs,
|
||||||
includeSqlRelationships: IncludeRelationship.INCLUDE,
|
|
||||||
})
|
})
|
||||||
|
const row = await getRow(tableId, id, { relationships: true })
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
row,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function save(ctx: UserCtx) {
|
export async function save(ctx: UserCtx) {
|
||||||
|
@ -80,10 +98,20 @@ export async function save(ctx: UserCtx) {
|
||||||
if (!validateResult.valid) {
|
if (!validateResult.valid) {
|
||||||
throw { validation: validateResult.errors }
|
throw { validation: validateResult.errors }
|
||||||
}
|
}
|
||||||
return handleRequest(Operation.CREATE, tableId, {
|
const response = await handleRequest(Operation.CREATE, tableId, {
|
||||||
row: inputs,
|
row: inputs,
|
||||||
includeSqlRelationships: IncludeRelationship.EXCLUDE,
|
|
||||||
})
|
})
|
||||||
|
const responseRow = response as { row: Row }
|
||||||
|
const rowId = responseRow.row._id
|
||||||
|
if (rowId) {
|
||||||
|
const row = await getRow(tableId, rowId, { relationships: true })
|
||||||
|
return {
|
||||||
|
...response,
|
||||||
|
row,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return response
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchView(ctx: UserCtx) {
|
export async function fetchView(ctx: UserCtx) {
|
||||||
|
@ -104,11 +132,7 @@ export async function fetch(ctx: UserCtx) {
|
||||||
export async function find(ctx: UserCtx) {
|
export async function find(ctx: UserCtx) {
|
||||||
const id = ctx.params.rowId
|
const id = ctx.params.rowId
|
||||||
const tableId = ctx.params.tableId
|
const tableId = ctx.params.tableId
|
||||||
const response = (await handleRequest(Operation.READ, tableId, {
|
return getRow(tableId, id)
|
||||||
id: breakRowIdField(id),
|
|
||||||
includeSqlRelationships: IncludeRelationship.EXCLUDE,
|
|
||||||
})) as Row[]
|
|
||||||
return response ? response[0] : response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: UserCtx) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
|
|
|
@ -50,7 +50,7 @@ export const save = async (ctx: any) => {
|
||||||
if (body && body._id) {
|
if (body && body._id) {
|
||||||
return patch(ctx)
|
return patch(ctx)
|
||||||
}
|
}
|
||||||
const { row, table } = await quotas.addRow(() =>
|
const { row, table, squashed } = await quotas.addRow(() =>
|
||||||
quotas.addQuery(() => pickApi(tableId).save(ctx), {
|
quotas.addQuery(() => pickApi(tableId).save(ctx), {
|
||||||
datasourceId: tableId,
|
datasourceId: tableId,
|
||||||
})
|
})
|
||||||
|
@ -58,8 +58,9 @@ export const save = async (ctx: any) => {
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:save`, appId, row, table)
|
||||||
ctx.message = `${table.name} saved successfully`
|
ctx.message = `${table.name} saved successfully`
|
||||||
ctx.body = row
|
// prefer squashed for response
|
||||||
gridSocket?.emitRowUpdate(ctx, row)
|
ctx.body = row || squashed
|
||||||
|
gridSocket?.emitRowUpdate(ctx, row || squashed)
|
||||||
}
|
}
|
||||||
export async function fetchView(ctx: any) {
|
export async function fetchView(ctx: any) {
|
||||||
const tableId = utils.getTableId(ctx)
|
const tableId = utils.getTableId(ctx)
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
import { FieldTypes, FormulaTypes } from "../../../constants"
|
import { FieldTypes, FormulaTypes } from "../../../constants"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { Table, Row } from "@budibase/types"
|
import { Table, Row } from "@budibase/types"
|
||||||
|
import * as linkRows from "../../../db/linkedRows"
|
||||||
const { isEqual } = require("lodash")
|
const { isEqual } = require("lodash")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
|
@ -166,5 +167,9 @@ export async function finaliseRow(
|
||||||
if (updateFormula) {
|
if (updateFormula) {
|
||||||
await updateRelatedFormula(table, enrichedRow)
|
await updateRelatedFormula(table, enrichedRow)
|
||||||
}
|
}
|
||||||
return { row: enrichedRow, table }
|
const squashed = await linkRows.squashLinksToPrimaryDisplay(
|
||||||
|
table,
|
||||||
|
enrichedRow
|
||||||
|
)
|
||||||
|
return { row: enrichedRow, squashed, table }
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,11 +189,13 @@ export async function attachFullLinkedDocs(table: Table, rows: Row[]) {
|
||||||
*/
|
*/
|
||||||
export async function squashLinksToPrimaryDisplay(
|
export async function squashLinksToPrimaryDisplay(
|
||||||
table: Table,
|
table: Table,
|
||||||
enriched: Row[]
|
enriched: Row[] | Row
|
||||||
) {
|
) {
|
||||||
// will populate this as we find them
|
// will populate this as we find them
|
||||||
const linkedTables = [table]
|
const linkedTables = [table]
|
||||||
for (let row of enriched) {
|
const isArray = Array.isArray(enriched)
|
||||||
|
let enrichedArray = !isArray ? [enriched] : enriched
|
||||||
|
for (let row of enrichedArray) {
|
||||||
// this only fetches the table if its not already in array
|
// this only fetches the table if its not already in array
|
||||||
const rowTable = await getLinkedTable(row.tableId!, linkedTables)
|
const rowTable = await getLinkedTable(row.tableId!, linkedTables)
|
||||||
for (let [column, schema] of Object.entries(rowTable?.schema || {})) {
|
for (let [column, schema] of Object.entries(rowTable?.schema || {})) {
|
||||||
|
@ -213,5 +215,5 @@ export async function squashLinksToPrimaryDisplay(
|
||||||
row[column] = newLinks
|
row[column] = newLinks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return enriched
|
return isArray ? enrichedArray : enrichedArray[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -216,7 +216,10 @@ export async function outputProcessing(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (opts.squash) {
|
if (opts.squash) {
|
||||||
enriched = await linkRows.squashLinksToPrimaryDisplay(table, enriched)
|
enriched = (await linkRows.squashLinksToPrimaryDisplay(
|
||||||
|
table,
|
||||||
|
enriched
|
||||||
|
)) as Row[]
|
||||||
}
|
}
|
||||||
return wasArray ? enriched : enriched[0]
|
return wasArray ? enriched : enriched[0]
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,29 +46,32 @@ export default class BuilderSocket extends BaseSocket {
|
||||||
}
|
}
|
||||||
|
|
||||||
emitTableUpdate(ctx: any, table: Table) {
|
emitTableUpdate(ctx: any, table: Table) {
|
||||||
this.io
|
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.TableChange, {
|
||||||
.in(ctx.appId)
|
id: table._id,
|
||||||
.emit(BuilderSocketEvent.TableChange, { id: table._id, table })
|
table,
|
||||||
gridSocket?.emitTableUpdate(table)
|
})
|
||||||
|
gridSocket?.emitTableUpdate(ctx, table)
|
||||||
}
|
}
|
||||||
|
|
||||||
emitTableDeletion(ctx: any, id: string) {
|
emitTableDeletion(ctx: any, id: string) {
|
||||||
this.io
|
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.TableChange, {
|
||||||
.in(ctx.appId)
|
id,
|
||||||
.emit(BuilderSocketEvent.TableChange, { id, table: null })
|
table: null,
|
||||||
gridSocket?.emitTableDeletion(id)
|
})
|
||||||
|
gridSocket?.emitTableDeletion(ctx, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
emitDatasourceUpdate(ctx: any, datasource: Datasource) {
|
emitDatasourceUpdate(ctx: any, datasource: Datasource) {
|
||||||
this.io.in(ctx.appId).emit(BuilderSocketEvent.DatasourceChange, {
|
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.DatasourceChange, {
|
||||||
id: datasource._id,
|
id: datasource._id,
|
||||||
datasource,
|
datasource,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emitDatasourceDeletion(ctx: any, id: string) {
|
emitDatasourceDeletion(ctx: any, id: string) {
|
||||||
this.io
|
this.emitToRoom(ctx, ctx.appId, BuilderSocketEvent.DatasourceChange, {
|
||||||
.in(ctx.appId)
|
id,
|
||||||
.emit(BuilderSocketEvent.DatasourceChange, { id, datasource: null })
|
datasource: null,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,21 +31,25 @@ export default class GridSocket extends BaseSocket {
|
||||||
|
|
||||||
emitRowUpdate(ctx: any, row: Row) {
|
emitRowUpdate(ctx: any, row: Row) {
|
||||||
const tableId = getTableId(ctx)
|
const tableId = getTableId(ctx)
|
||||||
this.io.in(tableId).emit(GridSocketEvent.RowChange, { id: row._id, row })
|
this.emitToRoom(ctx, tableId, GridSocketEvent.RowChange, {
|
||||||
|
id: row._id,
|
||||||
|
row,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emitRowDeletion(ctx: any, id: string) {
|
emitRowDeletion(ctx: any, id: string) {
|
||||||
const tableId = getTableId(ctx)
|
const tableId = getTableId(ctx)
|
||||||
this.io.in(tableId).emit(GridSocketEvent.RowChange, { id, row: null })
|
this.emitToRoom(ctx, tableId, GridSocketEvent.RowChange, { id, row: null })
|
||||||
}
|
}
|
||||||
|
|
||||||
emitTableUpdate(table: Table) {
|
emitTableUpdate(ctx: any, table: Table) {
|
||||||
this.io
|
this.emitToRoom(ctx, table._id!, GridSocketEvent.TableChange, {
|
||||||
.in(table._id!)
|
id: table._id,
|
||||||
.emit(GridSocketEvent.TableChange, { id: table._id, table })
|
table,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
emitTableDeletion(id: string) {
|
emitTableDeletion(ctx: any, id: string) {
|
||||||
this.io.in(id).emit(GridSocketEvent.TableChange, { id, table: null })
|
this.emitToRoom(ctx, id, GridSocketEvent.TableChange, { id, table: null })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import http from "http"
|
||||||
import Koa from "koa"
|
import Koa from "koa"
|
||||||
import Cookies from "cookies"
|
import Cookies from "cookies"
|
||||||
import { userAgent } from "koa-useragent"
|
import { userAgent } from "koa-useragent"
|
||||||
import { auth, redis } from "@budibase/backend-core"
|
import { auth, Header, redis } from "@budibase/backend-core"
|
||||||
import currentApp from "../middleware/currentapp"
|
import currentApp from "../middleware/currentapp"
|
||||||
import { createAdapter } from "@socket.io/redis-adapter"
|
import { createAdapter } from "@socket.io/redis-adapter"
|
||||||
import { Socket } from "socket.io"
|
import { Socket } from "socket.io"
|
||||||
|
@ -271,4 +271,13 @@ export class BaseSocket {
|
||||||
emit(event: string, payload: any) {
|
emit(event: string, payload: any) {
|
||||||
this.io.sockets.emit(event, payload)
|
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) {
|
||||||
|
this.io.in(room).emit(event, {
|
||||||
|
...payload,
|
||||||
|
apiSessionId: ctx.headers?.[Header.SESSION_ID],
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,6 @@ import {
|
||||||
tenancy,
|
tenancy,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import { checkAnyUserExists } from "../../../utilities/users"
|
import { checkAnyUserExists } from "../../../utilities/users"
|
||||||
import { getLicensedConfig } from "../../../utilities/configs"
|
|
||||||
import {
|
import {
|
||||||
Config,
|
Config,
|
||||||
ConfigType,
|
ConfigType,
|
||||||
|
|
Loading…
Reference in New Issue