1
0
Fork 0
mirror of synced 2024-07-29 10:05:55 +12:00

Update grid websocket to send actual changes down to reduce API load

This commit is contained in:
Andrew Kingston 2023-05-12 16:13:32 +01:00
parent d752448403
commit f8f970bf7e
6 changed files with 59 additions and 33 deletions

View file

@ -1,5 +1,5 @@
<script> <script>
import { setContext } from "svelte" import { setContext, onMount } from "svelte"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { fade } from "svelte/transition" import { fade } from "svelte/transition"
import { clickOutside, ProgressCircle } from "@budibase/bbui" import { clickOutside, ProgressCircle } from "@budibase/bbui"
@ -24,6 +24,7 @@
import RowHeightButton from "../controls/RowHeightButton.svelte" import RowHeightButton from "../controls/RowHeightButton.svelte"
import ColumnWidthButton from "../controls/ColumnWidthButton.svelte" import ColumnWidthButton from "../controls/ColumnWidthButton.svelte"
import NewRow from "./NewRow.svelte" import NewRow from "./NewRow.svelte"
import { createWebsocket } from "../lib/websocket"
import { import {
MaxCellRenderHeight, MaxCellRenderHeight,
MaxCellRenderWidthOverflow, MaxCellRenderWidthOverflow,
@ -97,7 +98,7 @@
export const getContext = () => context export const getContext = () => context
// Initialise websocket for multi-user // Initialise websocket for multi-user
// onMount(() => createWebsocket(context)) onMount(() => createWebsocket(context))
</script> </script>
<div <div

View file

@ -38,7 +38,7 @@ export const createWebsocket = context => {
}) })
socket.on("row-update", data => { socket.on("row-update", data => {
if (data.id) { if (data.id) {
rows.actions.refreshRow(data.id) rows.actions.replaceRow(data.id, data.row)
} }
}) })
socket.on("user-update", user => { socket.on("user-update", user => {

View file

@ -268,27 +268,25 @@ export const deriveStores = context => {
return res?.rows?.[0] return res?.rows?.[0]
} }
// Refreshes a specific row, handling updates, addition or deletion // Replaces a row in state with the newly defined row, handling updates,
const refreshRow = async id => { // addition and deletion
// Fetch row from the server again const replaceRow = (id, row) => {
const newRow = await fetchRow(id)
// Get index of row to check if it exists // Get index of row to check if it exists
const $rows = get(rows) const $rows = get(rows)
const $rowLookupMap = get(rowLookupMap) const $rowLookupMap = get(rowLookupMap)
const index = $rowLookupMap[id] const index = $rowLookupMap[id]
// Process as either an update, addition or deletion // Process as either an update, addition or deletion
if (newRow) { if (row) {
if (index != null) { if (index != null) {
// An existing row was updated // An existing row was updated
rows.update(state => { rows.update(state => {
state[index] = { ...newRow } state[index] = { ...row }
return state return state
}) })
} else { } else {
// A new row was created // A new row was created
handleNewRows([newRow]) handleNewRows([row])
} }
} else if (index != null) { } else if (index != null) {
// A row was removed // A row was removed
@ -296,6 +294,15 @@ export const deriveStores = context => {
} }
} }
// Refreshes a specific row
const refreshRow = async id => {
// Fetch row from the server again
const row = await fetchRow(id)
// Update local state
replaceRow(id, row)
}
// Refreshes all data // Refreshes all data
const refreshData = () => { const refreshData = () => {
get(fetch)?.getInitialData() get(fetch)?.getInitialData()
@ -455,6 +462,7 @@ export const deriveStores = context => {
hasRow, hasRow,
loadNextPage, loadNextPage,
refreshRow, refreshRow,
replaceRow,
refreshData, refreshData,
refreshTableDefinition, refreshTableDefinition,
}, },

View file

@ -4,6 +4,7 @@ import * as external from "./external"
import { isExternalTable } from "../../../integrations/utils" import { isExternalTable } from "../../../integrations/utils"
import { Ctx } from "@budibase/types" import { Ctx } from "@budibase/types"
import * as utils from "./utils" import * as utils from "./utils"
import { gridSocket } from "../../../websockets"
function pickApi(tableId: any) { function pickApi(tableId: any) {
if (isExternalTable(tableId)) { if (isExternalTable(tableId)) {
@ -12,21 +13,9 @@ function pickApi(tableId: any) {
return internal return internal
} }
function getTableId(ctx: any) {
if (ctx.request.body && ctx.request.body.tableId) {
return ctx.request.body.tableId
}
if (ctx.params && ctx.params.tableId) {
return ctx.params.tableId
}
if (ctx.params && ctx.params.viewName) {
return ctx.params.viewName
}
}
export async function patch(ctx: any): Promise<any> { export async function patch(ctx: any): Promise<any> {
const appId = ctx.appId const appId = ctx.appId
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
const body = ctx.request.body const body = ctx.request.body
// if it doesn't have an _id then its save // if it doesn't have an _id then its save
if (body && !body._id) { if (body && !body._id) {
@ -47,6 +36,7 @@ export async function patch(ctx: any): Promise<any> {
ctx.eventEmitter.emitRow(`row:update`, appId, row, table) ctx.eventEmitter.emitRow(`row:update`, appId, row, table)
ctx.message = `${table.name} updated successfully.` ctx.message = `${table.name} updated successfully.`
ctx.body = row ctx.body = row
gridSocket?.emitRowUpdate(ctx, row)
} catch (err) { } catch (err) {
ctx.throw(400, err) ctx.throw(400, err)
} }
@ -54,7 +44,7 @@ export async function patch(ctx: any): Promise<any> {
export const save = async (ctx: any) => { export const save = async (ctx: any) => {
const appId = ctx.appId const appId = ctx.appId
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
const body = ctx.request.body const body = ctx.request.body
// if it has an ID already then its a patch // if it has an ID already then its a patch
if (body && body._id) { if (body && body._id) {
@ -69,23 +59,24 @@ export const save = async (ctx: any) => {
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 ctx.body = row
gridSocket?.emitRowUpdate(ctx, row)
} }
export async function fetchView(ctx: any) { export async function fetchView(ctx: any) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetchView(ctx), { ctx.body = await quotas.addQuery(() => pickApi(tableId).fetchView(ctx), {
datasourceId: tableId, datasourceId: tableId,
}) })
} }
export async function fetch(ctx: any) { export async function fetch(ctx: any) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).fetch(ctx), { ctx.body = await quotas.addQuery(() => pickApi(tableId).fetch(ctx), {
datasourceId: tableId, datasourceId: tableId,
}) })
} }
export async function find(ctx: any) { export async function find(ctx: any) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).find(ctx), { ctx.body = await quotas.addQuery(() => pickApi(tableId).find(ctx), {
datasourceId: tableId, datasourceId: tableId,
}) })
@ -94,7 +85,7 @@ export async function find(ctx: any) {
export async function destroy(ctx: any) { export async function destroy(ctx: any) {
const appId = ctx.appId const appId = ctx.appId
const inputs = ctx.request.body const inputs = ctx.request.body
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
let response, row let response, row
if (inputs.rows) { if (inputs.rows) {
let { rows } = await quotas.addQuery( let { rows } = await quotas.addQuery(
@ -107,6 +98,7 @@ export async function destroy(ctx: any) {
response = rows response = rows
for (let row of rows) { for (let row of rows) {
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
gridSocket?.emitRowDeletion(ctx, row._id)
} }
} else { } else {
let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), { let resp = await quotas.addQuery(() => pickApi(tableId).destroy(ctx), {
@ -116,6 +108,7 @@ export async function destroy(ctx: any) {
response = resp.response response = resp.response
row = resp.row row = resp.row
ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row) ctx.eventEmitter && ctx.eventEmitter.emitRow(`row:delete`, appId, row)
gridSocket?.emitRowDeletion(ctx, row._id)
} }
ctx.status = 200 ctx.status = 200
// for automations include the row that was deleted // for automations include the row that was deleted
@ -124,7 +117,7 @@ export async function destroy(ctx: any) {
} }
export async function search(ctx: any) { export async function search(ctx: any) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.status = 200 ctx.status = 200
ctx.body = await quotas.addQuery(() => pickApi(tableId).search(ctx), { ctx.body = await quotas.addQuery(() => pickApi(tableId).search(ctx), {
datasourceId: tableId, datasourceId: tableId,
@ -132,7 +125,7 @@ export async function search(ctx: any) {
} }
export async function validate(ctx: Ctx) { export async function validate(ctx: Ctx) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
// external tables are hard to validate currently // external tables are hard to validate currently
if (isExternalTable(tableId)) { if (isExternalTable(tableId)) {
ctx.body = { valid: true } ctx.body = { valid: true }
@ -145,7 +138,7 @@ export async function validate(ctx: Ctx) {
} }
export async function fetchEnrichedRow(ctx: any) { export async function fetchEnrichedRow(ctx: any) {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery( ctx.body = await quotas.addQuery(
() => pickApi(tableId).fetchEnrichedRow(ctx), () => pickApi(tableId).fetchEnrichedRow(ctx),
{ {
@ -155,7 +148,7 @@ export async function fetchEnrichedRow(ctx: any) {
} }
export const exportRows = async (ctx: any) => { export const exportRows = async (ctx: any) => {
const tableId = getTableId(ctx) const tableId = utils.getTableId(ctx)
ctx.body = await quotas.addQuery(() => pickApi(tableId).exportRows(ctx), { ctx.body = await quotas.addQuery(() => pickApi(tableId).exportRows(ctx), {
datasourceId: tableId, datasourceId: tableId,
}) })

View file

@ -154,3 +154,15 @@ export function cleanExportRows(
return cleanRows return cleanRows
} }
export function getTableId(ctx: any) {
if (ctx.request.body && ctx.request.body.tableId) {
return ctx.request.body.tableId
}
if (ctx.params && ctx.params.tableId) {
return ctx.params.tableId
}
if (ctx.params && ctx.params.viewName) {
return ctx.params.viewName
}
}

View file

@ -3,6 +3,8 @@ import Socket from "./websocket"
import { permissions } from "@budibase/backend-core" import { permissions } from "@budibase/backend-core"
import http from "http" import http from "http"
import Koa from "koa" import Koa from "koa"
import { getTableId } from "../api/controllers/row/utils"
import { Row } from "@budibase/types"
export default class GridSocket extends Socket { export default class GridSocket extends Socket {
constructor(app: Koa, server: http.Server) { constructor(app: Koa, server: http.Server) {
@ -52,4 +54,14 @@ export default class GridSocket extends Socket {
}) })
}) })
} }
emitRowUpdate(ctx: any, row: Row) {
const tableId = getTableId(ctx)
this.io.in(tableId).emit("row-update", { id: row._id, row })
}
emitRowDeletion(ctx: any, id: string) {
const tableId = getTableId(ctx)
this.io.in(tableId).emit("row-update", { id, row: null })
}
} }