From 6b566d8b7ad81cb69fe21c009cfcdee27851b5c4 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Jan 2025 16:58:49 +0000 Subject: [PATCH 01/29] TS conversions of components required to create generic deletion component. --- packages/bbui/src/Form/Core/TextField.svelte | 27 +-- packages/bbui/src/Form/Field.svelte | 10 +- packages/bbui/src/Form/Input.svelte | 11 +- packages/bbui/src/Link/Link.svelte | 2 +- .../modals/DeleteDataConfirmationModal.svelte | 164 ++++++++++++++++++ packages/types/src/ui/common.ts | 3 + packages/types/src/ui/index.ts | 1 + 7 files changed, 193 insertions(+), 25 deletions(-) create mode 100644 packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte create mode 100644 packages/types/src/ui/common.ts diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index 917bb2a452..a4f614ce29 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -1,8 +1,9 @@ - diff --git a/packages/bbui/src/Form/Input.svelte b/packages/bbui/src/Form/Input.svelte index 47962720af..39055237f0 100644 --- a/packages/bbui/src/Form/Input.svelte +++ b/packages/bbui/src/Form/Input.svelte @@ -1,12 +1,12 @@ - + + +
+

+ Are you sure you wish to delete the table + + + + {source?.name} + ? + +

+ +

All table data will be deleted{viewsMessage}.

+

This action cannot be undone.

+ + {#if affectedScreens.length > 0} +
+ +
    + {#each affectedScreens as item} +
  • + {item.text} +
  • + {/each} +
+
+
+ {/if} +

+ Please enter the "{source?.name}" below to confirm. +

+ +
+
+ + diff --git a/packages/types/src/ui/common.ts b/packages/types/src/ui/common.ts new file mode 100644 index 0000000000..2f5d864ca0 --- /dev/null +++ b/packages/types/src/ui/common.ts @@ -0,0 +1,3 @@ +export type UIEvent = Event & { + currentTarget: EventTarget & HTMLInputElement +} & { key?: string } & { target?: any } diff --git a/packages/types/src/ui/index.ts b/packages/types/src/ui/index.ts index 6e5f37608c..9c4ba93058 100644 --- a/packages/types/src/ui/index.ts +++ b/packages/types/src/ui/index.ts @@ -3,3 +3,4 @@ export * from "./bindings" export * from "./components" export * from "./dataFetch" export * from "./datasource" +export * from "./common" From 795df854af65be542c70deb1cc05a49698e5ec0c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Jan 2025 17:09:40 +0000 Subject: [PATCH 02/29] More TS fixes. --- packages/bbui/src/Form/Core/TextField.svelte | 8 ++++---- packages/bbui/src/Form/Input.svelte | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/bbui/src/Form/Core/TextField.svelte b/packages/bbui/src/Form/Core/TextField.svelte index a4f614ce29..2de310cd25 100644 --- a/packages/bbui/src/Form/Core/TextField.svelte +++ b/packages/bbui/src/Form/Core/TextField.svelte @@ -4,7 +4,7 @@ import type { UIEvent } from "@budibase/types" export let value: string | null = null - export let placeholder = null + export let placeholder: string | null = null export let type = "text" export let disabled = false export let id = null @@ -12,8 +12,8 @@ export let updateOnChange = true export let quiet = false export let align: string | null = null - export let autofocus = false - export let autocomplete = null + export let autofocus: boolean | null = false + export let autocomplete: string | null = null const dispatch = createEventDispatcher() @@ -71,7 +71,7 @@ onMount(async () => { if (disabled) return - focus = autofocus + focus = autofocus || false if (focus) { await tick() field.focus() diff --git a/packages/bbui/src/Form/Input.svelte b/packages/bbui/src/Form/Input.svelte index 39055237f0..f141e85a51 100644 --- a/packages/bbui/src/Form/Input.svelte +++ b/packages/bbui/src/Form/Input.svelte @@ -13,8 +13,8 @@ export let error = null export let updateOnChange = true export let quiet = false - export let autofocus - export let autocomplete + export let autofocus: boolean | null = null + export let autocomplete: string | null = null export let helpText = null const dispatch = createEventDispatcher() From b8e5512f307a5bb03adcab66ad35bad56da59dc8 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 29 Jan 2025 17:40:45 +0000 Subject: [PATCH 03/29] Remove table word, pass this in instead --- .../modals/DeleteDataConfirmationModal.svelte | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 64c70f0dd0..d9e55a4cf2 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -5,6 +5,7 @@ import type { Table, View, Datasource, Query } from "@budibase/types" export let source: Table | View | Datasource | Query | undefined + export let type: "table" | "view" | "datasource" | "query" export let deleteSourceFn: () => Promise let confirmDeleteDialog: any @@ -54,7 +55,7 @@

- Are you sure you wish to delete the table - + Are you sure you wish to delete the {type} + {source?.name} @@ -71,13 +72,13 @@

-

All table data will be deleted{viewsMessage}.

+

All {type} data will be deleted{viewsMessage}.

This action cannot be undone.

{#if affectedScreens.length > 0}
    {#each affectedScreens as item} @@ -109,7 +110,7 @@ max-width: 100%; } - .tableNameLine { + .sourceNameLine { display: inline-flex; max-width: 100%; vertical-align: bottom; From 1b8a229e8508548fecdae8a1072003960dc3088c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 30 Jan 2025 16:36:44 +0000 Subject: [PATCH 04/29] Functions to get source type and usage endpoint for screens. --- packages/backend-core/src/docIds/params.ts | 6 +++++- packages/server/src/api/controllers/screen.ts | 7 +++++++ packages/server/src/api/routes/screen.ts | 5 +++++ packages/server/src/sdk/app/common/index.ts | 1 + packages/server/src/sdk/app/common/utils.ts | 15 +++++++++++++++ packages/server/src/sdk/index.ts | 2 ++ packages/types/src/api/web/app/screen.ts | 7 +++++++ packages/types/src/documents/app/datasource.ts | 7 +++++++ 8 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 packages/server/src/sdk/app/common/index.ts create mode 100644 packages/server/src/sdk/app/common/utils.ts diff --git a/packages/backend-core/src/docIds/params.ts b/packages/backend-core/src/docIds/params.ts index 016604b69b..5f1c053bde 100644 --- a/packages/backend-core/src/docIds/params.ts +++ b/packages/backend-core/src/docIds/params.ts @@ -83,11 +83,15 @@ export function isViewId(id: string): boolean { /** * Check if a given ID is that of a datasource or datasource plus. */ -export const isDatasourceId = (id: string): boolean => { +export function isDatasourceId(id: string): boolean { // this covers both datasources and datasource plus return !!id && id.startsWith(`${DocumentType.DATASOURCE}${SEPARATOR}`) } +export function isQueryId(id: string): boolean { + return !!id && id.startsWith(`${DocumentType.QUERY}${SEPARATOR}`) +} + /** * Gets parameters for retrieving workspaces. */ diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index ad000871fe..99a6b5ab52 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -16,8 +16,10 @@ import { SaveScreenRequest, SaveScreenResponse, DeleteScreenResponse, + UsageScreenResponse, } from "@budibase/types" import { builderSocket } from "../../websockets" +import sdk from "../../sdk" export async function fetch(ctx: UserCtx) { const db = context.getAppDB() @@ -140,3 +142,8 @@ function findPlugins(component: ScreenProps, foundPlugins: string[]) { } component._children.forEach(child => findPlugins(child, foundPlugins)) } + +export async function usage(ctx: UserCtx) { + const sourceId = ctx.params.sourceId + const sourceType = sdk.common.getSourceType(sourceId) +} diff --git a/packages/server/src/api/routes/screen.ts b/packages/server/src/api/routes/screen.ts index 7caa37bbfc..6329175fb6 100644 --- a/packages/server/src/api/routes/screen.ts +++ b/packages/server/src/api/routes/screen.ts @@ -19,5 +19,10 @@ router authorized(permissions.BUILDER), controller.destroy ) + .post( + "/api/screens/usage/:sourceId", + authorized(permissions.BUILDER), + controller.usage + ) export default router diff --git a/packages/server/src/sdk/app/common/index.ts b/packages/server/src/sdk/app/common/index.ts new file mode 100644 index 0000000000..3eeaeaa90c --- /dev/null +++ b/packages/server/src/sdk/app/common/index.ts @@ -0,0 +1 @@ +export * from "./utils" diff --git a/packages/server/src/sdk/app/common/utils.ts b/packages/server/src/sdk/app/common/utils.ts new file mode 100644 index 0000000000..ed4e4c9a4f --- /dev/null +++ b/packages/server/src/sdk/app/common/utils.ts @@ -0,0 +1,15 @@ +import { SourceType } from "@budibase/types" +import { docIds } from "@budibase/backend-core" + +export function getSourceType(sourceId: string): SourceType { + if (docIds.isTableId(sourceId)) { + return SourceType.TABLE + } else if (docIds.isViewId(sourceId)) { + return SourceType.VIEW + } else if (docIds.isDatasourceId(sourceId)) { + return SourceType.DATASOURCE + } else if (docIds.isQueryId(sourceId)) { + return SourceType.QUERY + } + throw new Error("Unknown source type - cannot find document type") +} diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index a871546b60..91dd3dffff 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -11,6 +11,7 @@ import { default as plugins } from "./plugins" import * as views from "./app/views" import * as permissions from "./app/permissions" import * as rowActions from "./app/rowActions" +import * as common from "./app/common" const sdk = { backups, @@ -26,6 +27,7 @@ const sdk = { permissions, links, rowActions, + common, } // default export for TS diff --git a/packages/types/src/api/web/app/screen.ts b/packages/types/src/api/web/app/screen.ts index 372d1ba2ea..f7cd5c0bf7 100644 --- a/packages/types/src/api/web/app/screen.ts +++ b/packages/types/src/api/web/app/screen.ts @@ -15,3 +15,10 @@ export interface SaveScreenResponse extends Screen {} export interface DeleteScreenResponse { message: string } + +export interface UsageScreenResponse { + screens: { + url: string + _id: string + }[] +} diff --git a/packages/types/src/documents/app/datasource.ts b/packages/types/src/documents/app/datasource.ts index a0be7bd80d..46d2a81de0 100644 --- a/packages/types/src/documents/app/datasource.ts +++ b/packages/types/src/documents/app/datasource.ts @@ -57,3 +57,10 @@ export interface RestConfig { } dynamicVariables?: DynamicVariable[] } + +export enum SourceType { + DATASOURCE = "datasource", + QUERY = "query", + TABLE = "table", + VIEW = "view", +} From 47fdd0f64ffd0ccd3ec80b8e04031565fbcffe2b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 30 Jan 2025 17:55:53 +0000 Subject: [PATCH 05/29] Still requires tests, but main body of endpoint to check screen usage. --- packages/server/src/api/controllers/screen.ts | 28 ++++++++++++------- packages/server/src/sdk/app/screens/index.ts | 1 + .../server/src/sdk/app/screens/screens.ts | 15 ++++++++++ packages/server/src/sdk/index.ts | 2 ++ .../shared-core/src/sdk/documents/index.ts | 1 + .../shared-core/src/sdk/documents/screens.ts | 24 ++++++++++++++++ packages/shared-core/src/utils.ts | 17 +++++++++++ packages/types/src/api/web/app/screen.ts | 15 ++++++---- 8 files changed, 87 insertions(+), 16 deletions(-) create mode 100644 packages/server/src/sdk/app/screens/index.ts create mode 100644 packages/server/src/sdk/app/screens/screens.ts create mode 100644 packages/shared-core/src/sdk/documents/screens.ts diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 99a6b5ab52..403059efde 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -1,4 +1,4 @@ -import { getScreenParams, generateScreenID, DocumentType } from "../../db/utils" +import { generateScreenID, DocumentType } from "../../db/utils" import { events, context, @@ -17,20 +17,14 @@ import { SaveScreenResponse, DeleteScreenResponse, UsageScreenResponse, + ScreenUsage, } from "@budibase/types" import { builderSocket } from "../../websockets" import sdk from "../../sdk" +import { sdk as sharedSdk } from "@budibase/shared-core" export async function fetch(ctx: UserCtx) { - const db = context.getAppDB() - - const screens = ( - await db.allDocs( - getScreenParams(null, { - include_docs: true, - }) - ) - ).rows.map((el: any) => el.doc) + const screens = await sdk.screens.fetch() const roleId = ctx.user?.role?._id as string if (!roleId) { @@ -146,4 +140,18 @@ function findPlugins(component: ScreenProps, foundPlugins: string[]) { export async function usage(ctx: UserCtx) { const sourceId = ctx.params.sourceId const sourceType = sdk.common.getSourceType(sourceId) + const allScreens = await sdk.screens.fetch() + const response: ScreenUsage[] = [] + for (let screen of allScreens) { + if (sharedSdk.screens.findInSettings(screen, sourceId)) { + response.push({ + url: screen.routing.route, + _id: screen._id!, + }) + } + } + ctx.body = { + sourceType, + screens: response, + } } diff --git a/packages/server/src/sdk/app/screens/index.ts b/packages/server/src/sdk/app/screens/index.ts new file mode 100644 index 0000000000..84f4bad6bb --- /dev/null +++ b/packages/server/src/sdk/app/screens/index.ts @@ -0,0 +1 @@ +export * from "./screens" diff --git a/packages/server/src/sdk/app/screens/screens.ts b/packages/server/src/sdk/app/screens/screens.ts new file mode 100644 index 0000000000..c600825efb --- /dev/null +++ b/packages/server/src/sdk/app/screens/screens.ts @@ -0,0 +1,15 @@ +import { getScreenParams } from "../../../db/utils" +import { context } from "@budibase/backend-core" +import { Screen } from "@budibase/types" + +export async function fetch(): Promise { + const db = context.getAppDB() + + return ( + await db.allDocs( + getScreenParams(null, { + include_docs: true, + }) + ) + ).rows.map(el => el.doc!) +} diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index 91dd3dffff..e3e88c25c4 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -11,6 +11,7 @@ import { default as plugins } from "./plugins" import * as views from "./app/views" import * as permissions from "./app/permissions" import * as rowActions from "./app/rowActions" +import * as screens from "./app/screens" import * as common from "./app/common" const sdk = { @@ -23,6 +24,7 @@ const sdk = { datasources, queries, plugins, + screens, views, permissions, links, diff --git a/packages/shared-core/src/sdk/documents/index.ts b/packages/shared-core/src/sdk/documents/index.ts index 4b17c1ea08..502e968a15 100644 --- a/packages/shared-core/src/sdk/documents/index.ts +++ b/packages/shared-core/src/sdk/documents/index.ts @@ -1,3 +1,4 @@ export * as applications from "./applications" export * as automations from "./automations" export * as users from "./users" +export * as screens from "./screens" diff --git a/packages/shared-core/src/sdk/documents/screens.ts b/packages/shared-core/src/sdk/documents/screens.ts new file mode 100644 index 0000000000..8fdd7087c8 --- /dev/null +++ b/packages/shared-core/src/sdk/documents/screens.ts @@ -0,0 +1,24 @@ +import { Screen } from "@budibase/types" +import { flattenObject } from "../../utils" + +export function findInSettings(screen: Screen, toFind: string) { + const flattened = flattenObject(screen.props) + const foundIn: { setting: string; value: string }[] = [] + for (let key of Object.keys(flattened)) { + let found = false + if (typeof flattened[key] === "string") { + found = flattened[key].includes(toFind) + } else if (Array.isArray(flattened[key])) { + found = flattened[key].find( + (el: any) => typeof el === "string" && el.includes(toFind) + ) + } + if (found) { + foundIn.push({ + setting: key, + value: flattened[key], + }) + } + } + return foundIn +} diff --git a/packages/shared-core/src/utils.ts b/packages/shared-core/src/utils.ts index fac8fa61ee..afc1c8872b 100644 --- a/packages/shared-core/src/utils.ts +++ b/packages/shared-core/src/utils.ts @@ -173,3 +173,20 @@ export function processSearchFilters( ], } } + +export function flattenObject( + obj: Record, + parentKey: string = "", + state: Record = {} +) { + for (const key of Object.keys(obj)) { + const newKey = parentKey.length ? `${parentKey}.${key}` : key + const value = obj[key] + if (value && typeof value === "object" && !Array.isArray(value)) { + flattenObject(value, newKey, state) + } else { + state[newKey] = value + } + } + return state +} diff --git a/packages/types/src/api/web/app/screen.ts b/packages/types/src/api/web/app/screen.ts index f7cd5c0bf7..280c623216 100644 --- a/packages/types/src/api/web/app/screen.ts +++ b/packages/types/src/api/web/app/screen.ts @@ -1,4 +1,4 @@ -import { ScreenRoutingJson, Screen } from "../../../documents" +import { ScreenRoutingJson, Screen, SourceType } from "../../../documents" export interface FetchScreenRoutingResponse { routes: ScreenRoutingJson @@ -16,9 +16,12 @@ export interface DeleteScreenResponse { message: string } -export interface UsageScreenResponse { - screens: { - url: string - _id: string - }[] +export interface ScreenUsage { + url: string + _id: string +} + +export interface UsageScreenResponse { + sourceType: SourceType + screens: ScreenUsage[] } From d6fb87dce04008d7a12a4e2f38481d605161b4b2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 31 Jan 2025 17:46:43 +0000 Subject: [PATCH 06/29] Creating structures for testing usage endpoint. --- .../src/api/routes/tests/screen.spec.ts | 10 +- packages/server/src/constants/screens.ts | 176 +++++++++++++++++- .../server/src/tests/utilities/structures.ts | 20 +- 3 files changed, 203 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 82a550f2fd..7d3049faca 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -3,7 +3,8 @@ import * as setup from "./utilities" import { events, roles } from "@budibase/backend-core" import { Screen, Role, BuiltinPermissionID } from "@budibase/types" -const { basicScreen } = setup.structures +const { basicScreen, tableScreen, viewScreen, queryScreen, basicTable } = + setup.structures describe("/screens", () => { let config = setup.getConfig() @@ -186,4 +187,11 @@ describe("/screens", () => { }) }) }) + + describe("usage", () => { + it("should find table usage", async () => { + const table = await config.api.table.save(basicTable(), { status: 200 }) + const tableScreen = await config.api.screen.save(tableScreen()) + }) + }) }) diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index 1107289ea0..52f0b3d9ff 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -1,6 +1,6 @@ import { roles } from "@budibase/backend-core" import { BASE_LAYOUT_PROP_IDS } from "./layouts" -import { Screen } from "@budibase/types" +import { Screen, Table, Query, ViewV2 } from "@budibase/types" export function createHomeScreen( config: { @@ -53,3 +53,177 @@ export function createHomeScreen( name: "home-screen", } } + +function heading(text: string) { + return { + _id: "c1bff24cd821e41d18c894ac77a80ef99", + _component: "@budibase/standard-components/heading", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "Table heading", + _children: [], + text, + } +} + +export function createTableScreen( + datasourceName: string, + table: Table +): Screen { + return { + props: { + _id: "cad0a0904cacd4678a2ac094e293db1a5", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + heading("table"), + { + _id: "ca6304be2079147bb9933092c4f8ce6fa", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "table - Table", + _children: [], + table: { + label: table.name, + tableId: table._id!, + type: "table", + datasourceName, + }, + }, + ], + _instanceName: "table - List", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/table", + roleId: "ADMIN", + homeScreen: false, + }, + name: "screen-id", + } +} + +export function createViewScreen(datasourceName: string, view: ViewV2): Screen { + return { + props: { + _id: "cc359092bbd6c4e10b57827155edb7872", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + heading("view"), + { + _id: "ccb4a9e3734794864b5c65b012a0bdc5a", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _instanceName: "view - Table", + _children: [], + table: { + ...view, + name: view.name, + tableId: view.tableId, + id: view.id, + label: view.name, + type: "viewV2", + }, + }, + ], + _instanceName: "view - List", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/view", + roleId: "ADMIN", + homeScreen: false, + }, + name: "view-id", + } +} + +export function createQueryScreen(datasourceId: string, query: Query): Screen { + return { + props: { + _id: "cc59b217aed264939a6c5249eee39cb25", + _component: "@budibase/standard-components/container", + _styles: { + normal: {}, + hover: {}, + active: {}, + selected: {}, + }, + _children: [ + { + _id: "c33a4a6e3cb5343158a08625c06b5cd7c", + _component: "@budibase/standard-components/gridblock", + _styles: { + normal: {}, + hover: {}, + active: {}, + }, + _instanceName: "New Table", + table: { + ...query, + label: query.name, + _id: query._id!, + name: query.name, + datasourceId: datasourceId, + type: "query", + }, + initialSortOrder: "Ascending", + allowAddRows: true, + allowEditRows: true, + allowDeleteRows: true, + stripeRows: false, + quiet: false, + columns: null, + }, + ], + _instanceName: "Blank screen", + layout: "grid", + direction: "column", + hAlign: "stretch", + vAlign: "top", + size: "grow", + gap: "M", + }, + routing: { + route: "/query", + roleId: "BASIC", + homeScreen: false, + }, + name: "screen-id", + } +} diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 0c74a0faa2..79b1773afe 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -1,5 +1,10 @@ import { roles, utils } from "@budibase/backend-core" -import { createHomeScreen } from "../../constants/screens" +import { + createHomeScreen, + createTableScreen, + createQueryScreen, + createViewScreen, +} from "../../constants/screens" import { EMPTY_LAYOUT } from "../../constants/layouts" import { cloneDeep } from "lodash/fp" import { @@ -35,6 +40,7 @@ import { WebhookActionType, BuiltinPermissionID, DeepPartial, + ViewV2, } from "@budibase/types" import { LoopInput } from "../../definitions/automations" import { merge } from "lodash" @@ -599,6 +605,18 @@ export function basicScreen(route = "/") { }) } +export function tableScreen(datasourceName: string, table: Table) { + return createTableScreen(datasourceName, table) +} + +export function viewScreen(datasourceName: string, view: ViewV2) { + return createViewScreen(datasourceName, view) +} + +export function queryScreen(datasourceId: string, query: Query) { + return createQueryScreen(datasourceId, query) +} + export function powerScreen(route = "/") { return createHomeScreen({ roleId: BUILTIN_ROLE_IDS.POWER, From 5a737ff5c4299cc0792f9e69d8b7a3074e72f826 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 31 Jan 2025 18:39:48 +0000 Subject: [PATCH 07/29] Completing test cases for usage endpoint. --- packages/server/src/api/controllers/screen.ts | 3 +- .../src/api/routes/tests/screen.spec.ts | 86 ++++++++++++++++++- .../server/src/tests/utilities/api/screen.ts | 11 ++- .../shared-core/src/sdk/documents/screens.ts | 31 ++++--- packages/shared-core/src/utils.ts | 17 ---- 5 files changed, 109 insertions(+), 39 deletions(-) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 403059efde..93a70d5e36 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -143,7 +143,8 @@ export async function usage(ctx: UserCtx) { const allScreens = await sdk.screens.fetch() const response: ScreenUsage[] = [] for (let screen of allScreens) { - if (sharedSdk.screens.findInSettings(screen, sourceId)) { + const found = sharedSdk.screens.findInSettings(screen, sourceId) + if (found.length !== 0) { response.push({ url: screen.routing.route, _id: screen._id!, diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 7d3049faca..610c07f36a 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -1,10 +1,24 @@ import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" import { events, roles } from "@budibase/backend-core" -import { Screen, Role, BuiltinPermissionID } from "@budibase/types" +import { + Screen, + Role, + BuiltinPermissionID, + SourceType, + UsageScreenResponse, +} from "@budibase/types" -const { basicScreen, tableScreen, viewScreen, queryScreen, basicTable } = - setup.structures +const { + basicScreen, + tableScreen, + viewScreen, + queryScreen, + basicTable, + viewV2, + basicQuery, + basicDatasource, +} = setup.structures describe("/screens", () => { let config = setup.getConfig() @@ -189,9 +203,73 @@ describe("/screens", () => { }) describe("usage", () => { + beforeEach(async () => { + await config.init() + await config.api.screen.save(basicScreen(), { + status: 200, + }) + }) + + function confirmScreen(usage: UsageScreenResponse, screen: Screen) { + expect(usage.screens.length).toEqual(1) + expect(usage.screens[0].url).toEqual(screen.routing.route) + expect(usage.screens[0]._id).toEqual(screen._id!) + } + it("should find table usage", async () => { const table = await config.api.table.save(basicTable(), { status: 200 }) - const tableScreen = await config.api.screen.save(tableScreen()) + const screen = await config.api.screen.save( + tableScreen("BudibaseDB", table), + { status: 200 } + ) + const usage = await config.api.screen.usage(table._id!, { status: 200 }) + expect(usage.sourceType).toEqual(SourceType.TABLE) + confirmScreen(usage, screen) + }) + + it("should find view usage", async () => { + const table = await config.api.table.save(basicTable(), { status: 200 }) + const view = await config.api.viewV2.create( + viewV2.createRequest(table._id!), + { status: 201 } + ) + const screen = await config.api.screen.save( + viewScreen("BudibaseDB", view), + { + status: 200, + } + ) + const usage = await config.api.screen.usage(view.id, { status: 200 }) + expect(usage.sourceType).toEqual(SourceType.VIEW) + confirmScreen(usage, screen) + }) + + it("should find datasource/query usage", async () => { + const datasource = await config.api.datasource.create( + basicDatasource().datasource, + { + status: 200, + } + ) + const query = await config.api.query.save(basicQuery(datasource._id!), { + status: 200, + }) + const screen = await config.api.screen.save( + queryScreen(datasource._id!, query), + { + status: 200, + } + ) + const dsUsage = await config.api.screen.usage(datasource._id!, { + status: 200, + }) + expect(dsUsage.sourceType).toEqual(SourceType.DATASOURCE) + confirmScreen(dsUsage, screen) + const queryUsage = await config.api.screen.usage(query._id!, { + status: 200, + }) + expect(queryUsage.sourceType).toEqual(SourceType.QUERY) + confirmScreen(queryUsage, screen) }) }) }) diff --git a/packages/server/src/tests/utilities/api/screen.ts b/packages/server/src/tests/utilities/api/screen.ts index 8b7d7f92c1..7f7f73fb84 100644 --- a/packages/server/src/tests/utilities/api/screen.ts +++ b/packages/server/src/tests/utilities/api/screen.ts @@ -1,4 +1,4 @@ -import { Screen } from "@budibase/types" +import { Screen, UsageScreenResponse } from "@budibase/types" import { Expectations, TestAPI } from "./base" export class ScreenAPI extends TestAPI { @@ -28,4 +28,13 @@ export class ScreenAPI extends TestAPI { } ) } + + usage = async ( + sourceId: string, + expectations?: Expectations + ): Promise => { + return this._post(`/api/screens/usage/${sourceId}`, { + expectations, + }) + } } diff --git a/packages/shared-core/src/sdk/documents/screens.ts b/packages/shared-core/src/sdk/documents/screens.ts index 8fdd7087c8..8b6ea276b2 100644 --- a/packages/shared-core/src/sdk/documents/screens.ts +++ b/packages/shared-core/src/sdk/documents/screens.ts @@ -1,24 +1,23 @@ import { Screen } from "@budibase/types" -import { flattenObject } from "../../utils" export function findInSettings(screen: Screen, toFind: string) { - const flattened = flattenObject(screen.props) const foundIn: { setting: string; value: string }[] = [] - for (let key of Object.keys(flattened)) { - let found = false - if (typeof flattened[key] === "string") { - found = flattened[key].includes(toFind) - } else if (Array.isArray(flattened[key])) { - found = flattened[key].find( - (el: any) => typeof el === "string" && el.includes(toFind) - ) - } - if (found) { - foundIn.push({ - setting: key, - value: flattened[key], - }) + function recurse(props: Record, parentKey = "") { + for (let key of Object.keys(props)) { + if (!props[key]) { + continue + } + if (typeof props[key] === "string" && props[key].includes(toFind)) { + foundIn.push({ + setting: parentKey ? `${parentKey}.${key}` : key, + value: props[key], + }) + } else if (typeof props[key] === "object") { + recurse(props[key], key) + } } } + + recurse(screen.props) return foundIn } diff --git a/packages/shared-core/src/utils.ts b/packages/shared-core/src/utils.ts index afc1c8872b..fac8fa61ee 100644 --- a/packages/shared-core/src/utils.ts +++ b/packages/shared-core/src/utils.ts @@ -173,20 +173,3 @@ export function processSearchFilters( ], } } - -export function flattenObject( - obj: Record, - parentKey: string = "", - state: Record = {} -) { - for (const key of Object.keys(obj)) { - const newKey = parentKey.length ? `${parentKey}.${key}` : key - const value = obj[key] - if (value && typeof value === "object" && !Array.isArray(value)) { - flattenObject(value, newKey, state) - } else { - state[newKey] = value - } - } - return state -} From b214019dbe53049cffdf3c9d08b3410160f8c90e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 3 Feb 2025 14:14:08 +0000 Subject: [PATCH 08/29] Frontend usage of API. --- packages/frontend-core/src/api/screens.ts | 8 ++++++++ packages/server/src/api/controllers/screen.ts | 4 ++-- packages/server/src/api/routes/tests/screen.spec.ts | 4 ++-- packages/server/src/tests/utilities/api/screen.ts | 13 ++++++++----- packages/types/src/api/web/app/screen.ts | 2 +- 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/packages/frontend-core/src/api/screens.ts b/packages/frontend-core/src/api/screens.ts index bdb01e1427..8147db738d 100644 --- a/packages/frontend-core/src/api/screens.ts +++ b/packages/frontend-core/src/api/screens.ts @@ -2,12 +2,14 @@ import { DeleteScreenResponse, SaveScreenRequest, SaveScreenResponse, + UsageOfScreensResponse, } from "@budibase/types" import { BaseAPIClient } from "./types" export interface ScreenEndpoints { saveScreen: (screen: SaveScreenRequest) => Promise deleteScreen: (id: string, rev: string) => Promise + usageOfScreens: (sourceId: string) => Promise } export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ @@ -32,4 +34,10 @@ export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ url: `/api/screens/${id}/${rev}`, }) }, + + usageOfScreens: async sourceId => { + return await API.post({ + url: `/api/screens/usage/${sourceId}`, + }) + }, }) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 93a70d5e36..1bf800691c 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -16,7 +16,7 @@ import { SaveScreenRequest, SaveScreenResponse, DeleteScreenResponse, - UsageScreenResponse, + UsageOfScreensResponse, ScreenUsage, } from "@budibase/types" import { builderSocket } from "../../websockets" @@ -137,7 +137,7 @@ function findPlugins(component: ScreenProps, foundPlugins: string[]) { component._children.forEach(child => findPlugins(child, foundPlugins)) } -export async function usage(ctx: UserCtx) { +export async function usage(ctx: UserCtx) { const sourceId = ctx.params.sourceId const sourceType = sdk.common.getSourceType(sourceId) const allScreens = await sdk.screens.fetch() diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 610c07f36a..90ec4a567f 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -6,7 +6,7 @@ import { Role, BuiltinPermissionID, SourceType, - UsageScreenResponse, + UsageOfScreensResponse, } from "@budibase/types" const { @@ -210,7 +210,7 @@ describe("/screens", () => { }) }) - function confirmScreen(usage: UsageScreenResponse, screen: Screen) { + function confirmScreen(usage: UsageOfScreensResponse, screen: Screen) { expect(usage.screens.length).toEqual(1) expect(usage.screens[0].url).toEqual(screen.routing.route) expect(usage.screens[0]._id).toEqual(screen._id!) diff --git a/packages/server/src/tests/utilities/api/screen.ts b/packages/server/src/tests/utilities/api/screen.ts index 7f7f73fb84..bbbe704a29 100644 --- a/packages/server/src/tests/utilities/api/screen.ts +++ b/packages/server/src/tests/utilities/api/screen.ts @@ -1,4 +1,4 @@ -import { Screen, UsageScreenResponse } from "@budibase/types" +import { Screen, UsageOfScreensResponse } from "@budibase/types" import { Expectations, TestAPI } from "./base" export class ScreenAPI extends TestAPI { @@ -32,9 +32,12 @@ export class ScreenAPI extends TestAPI { usage = async ( sourceId: string, expectations?: Expectations - ): Promise => { - return this._post(`/api/screens/usage/${sourceId}`, { - expectations, - }) + ): Promise => { + return this._post( + `/api/screens/usage/${sourceId}`, + { + expectations, + } + ) } } diff --git a/packages/types/src/api/web/app/screen.ts b/packages/types/src/api/web/app/screen.ts index 280c623216..0550c5206a 100644 --- a/packages/types/src/api/web/app/screen.ts +++ b/packages/types/src/api/web/app/screen.ts @@ -21,7 +21,7 @@ export interface ScreenUsage { _id: string } -export interface UsageScreenResponse { +export interface UsageOfScreensResponse { sourceType: SourceType screens: ScreenUsage[] } From a1409f43faea7046c74e5ab5a6f5d5aa7141b4a6 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 3 Feb 2025 14:34:54 +0000 Subject: [PATCH 09/29] implementing usage API in the modal. --- .../modals/DeleteDataConfirmationModal.svelte | 21 ++++++++++++++----- .../builder/src/stores/builder/screens.ts | 7 +++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index d9e55a4cf2..62d55d2283 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -1,10 +1,10 @@ - - -
    -

    - Are you sure you wish to delete the table - - - - {table.name} - ? - -

    - -

    All table data will be deleted{viewsMessage}.

    -

    This action cannot be undone.

    - - {#if screensPossiblyAffected.length > 0} -
    - -
      - {#each screensPossiblyAffected as item} -
    • - {item.text} -
    • - {/each} -
    -
    -
    - {/if} -

    Please enter the table name below to confirm.

    - -
    -
    - - diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavItem/TableNavItem.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavItem/TableNavItem.svelte index f3deccb3c9..fe9d02af40 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavItem/TableNavItem.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavItem/TableNavItem.svelte @@ -8,7 +8,7 @@ import NavItem from "@/components/common/NavItem.svelte" import { isActive } from "@roxi/routify" import EditModal from "./EditModal.svelte" - import DeleteConfirmationModal from "./DeleteConfirmationModal.svelte" + import DeleteConfirmationModal from "../../modals/DeleteDataConfirmationModal.svelte" import { Icon } from "@budibase/bbui" import { DB_TYPE_EXTERNAL } from "@/constants/backend" @@ -65,4 +65,4 @@ {/if} - + diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 62d55d2283..033358b7b3 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -1,16 +1,24 @@

    - Are you sure you wish to delete the {type} + Are you sure you wish to delete the {sourceType} @@ -83,13 +141,15 @@

    -

    All {type} data will be deleted{viewsMessage}.

    +

    + All {sourceType} data will be deleted{viewsMessage}. +

    This action cannot be undone.

    {#if affectedScreens.length > 0}
      {#each affectedScreens as item} diff --git a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/DeleteViewModal.svelte b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/DeleteViewModal.svelte deleted file mode 100644 index 5a23c976f8..0000000000 --- a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/DeleteViewModal.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - diff --git a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/ViewNavBar.svelte b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/ViewNavBar.svelte index e9c5745cc6..f32767451a 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/ViewNavBar.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/_components/ViewNavBar.svelte @@ -10,9 +10,8 @@ import { Icon, ActionButton, ActionMenu, MenuItem } from "@budibase/bbui" import { params, url } from "@roxi/routify" import EditViewModal from "./EditViewModal.svelte" - import DeleteViewModal from "./DeleteViewModal.svelte" import EditTableModal from "@/components/backend/TableNavigator/TableNavItem/EditModal.svelte" - import DeleteTableModal from "@/components/backend/TableNavigator/TableNavItem/DeleteConfirmationModal.svelte" + import DeleteConfirmationModal from "@/components/backend/modals/DeleteDataConfirmationModal.svelte" import { UserAvatars } from "@budibase/frontend-core" import { DB_TYPE_EXTERNAL } from "@/constants/backend" import { TableNames } from "@/constants" @@ -314,12 +313,12 @@ {#if table && tableEditable} - + {/if} {#if editableView} - + {/if} diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 033358b7b3..ace6dd696f 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -3,6 +3,7 @@ import { appStore, datasources, + queries, screenStore, tables, views, @@ -106,6 +107,20 @@ } } + async function deleteQuery(query: Query) { + try { + // Go back to the datasource if we are deleting the active query + if ($queries.selectedQueryId === query._id) { + $goto(`./datasource/${query.datasourceId}`) + } + await queries.delete(query) + await datasources.fetch() + notifications.success("Query deleted") + } catch (error) { + notifications.error("Error deleting query") + } + } + async function deleteSource() { if (!source || !sourceType) { throw new Error("Unable to delete - no data source found.") @@ -116,8 +131,9 @@ return await deleteTable(source as Table) case SourceType.VIEW: return await deleteView(source as ViewV2) - case SourceType.DATASOURCE: case SourceType.QUERY: + return await deleteQuery(source as Query) + case SourceType.DATASOURCE: } } @@ -164,7 +180,9 @@
    {/if}

    - Please enter the "{source?.name}" below to confirm. + Please enter the "{source?.name}" below to confirm.

    From cbc13ad567342d6dc0a4b071d2dc1614305677a3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 4 Feb 2025 17:10:22 +0000 Subject: [PATCH 12/29] Updating datasource to delete through it. --- .../DatasourceNavItem.svelte | 7 +++- .../DeleteConfirmationModal.svelte | 37 ------------------- .../modals/DeleteDataConfirmationModal.svelte | 18 ++++++++- 3 files changed, 22 insertions(+), 40 deletions(-) delete mode 100644 packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavItem/DeleteConfirmationModal.svelte diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavItem/DatasourceNavItem.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavItem/DatasourceNavItem.svelte index d538a9b1b7..16423335a6 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavItem/DatasourceNavItem.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavItem/DatasourceNavItem.svelte @@ -7,7 +7,7 @@ import IntegrationIcon from "@/components/backend/DatasourceNavigator/IntegrationIcon.svelte" import { Icon } from "@budibase/bbui" import UpdateDatasourceModal from "@/components/backend/DatasourceNavigator/modals/UpdateDatasourceModal.svelte" - import DeleteConfirmationModal from "./DeleteConfirmationModal.svelte" + import DeleteDataConfirmModal from "@/components/backend/modals/DeleteDataConfirmationModal.svelte" export let datasource @@ -71,7 +71,10 @@ {/if} - + From 877f30b7a0abf6c398e820057e979f7d1042060c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 5 Feb 2025 16:29:21 +0000 Subject: [PATCH 17/29] No input required for deletion. --- .../modals/DeleteDataConfirmationModal.svelte | 82 ++++++++----------- .../components/common/ConfirmDialog.svelte | 2 +- 2 files changed, 36 insertions(+), 48 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 2e7c8d5078..db13558713 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -1,5 +1,5 @@ - -
    -

    - - {@html buildMessage()} -

    +{#if sourceType} + +
    +

    + + {@html buildMessage()} +

    - {#if affectedScreens.length > 0} -
    - -
      - {#each affectedScreens as item} -
    • - {item.text} -
    • - {/each} -
    -
    -
    - {/if} -
    -
    + {#if affectedScreens.length > 0} +
    + +
      + {#each affectedScreens as item} +
    • + {item.text} +
    • + {/each} +
    +
    +
    + {/if} +
    +
    +{/if} From 880f27e0ff6193c913015e0dd8dde1f65b312933 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 5 Feb 2025 17:39:31 +0000 Subject: [PATCH 19/29] Changing how messaging is generated. --- .../modals/DeleteDataConfirmationModal.svelte | 74 +++++++++---------- 1 file changed, 33 insertions(+), 41 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 0649e40c09..4d2a60d2f5 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -25,22 +25,7 @@ $: isInternalTable = isInternal && sourceType === SourceType.TABLE - const getViewsMessage = () => { - if (!source || !("views" in source)) { - return "" - } - const views = Object.values(source?.views ?? []) - if (views.length < 1) { - return "" - } - if (views.length === 1) { - return "1 view" - } - - return `${views.length} views` - } - - const getQueriesMessage = () => { + const getDatasourceQueries = () => { if (sourceType !== SourceType.DATASOURCE) { return "" } @@ -48,14 +33,7 @@ const queryList = get(queries).list.filter( query => query.datasourceId === sourceId ) - if (queryList.length < 1) { - return "" - } - if (queryList.length === 1) { - return "1 query" - } - - return `${queryList.length} queries` + return queryList } function getSourceID(): string { @@ -168,21 +146,35 @@ } } - function buildMessage() { - let message = "" - if (isInternalTable) { - message = `All ${sourceType} data will also be deleted` - const viewsMessage = getViewsMessage() - if (viewsMessage) { - message += `, including ${viewsMessage}. ` - } else { - message += ". " + function buildMessage(sourceType: string) { + if (!source) { + return "" + } + let message = `Removing ${source?.name} ` + let initialLength = message.length + if (sourceType === SourceType.TABLE) { + const views = "views" in source ? Object.values(source?.views ?? []) : [] + if (isInternalTable) { + message += `will delete its data${ + views.length ? `, views (${views.length})` : "" + }` + } else if (views.length) { + message += `will delete its views (${views.length})` } } else if (sourceType === SourceType.DATASOURCE) { - const queriesMessage = getQueriesMessage() - message = `This will include deleting ${queriesMessage}. ` + const queryList = getDatasourceQueries() + if (queryList.length) { + message += `will delete its queries (${queryList.length})` + } + } + if (affectedScreens.length) { + message += + initialLength !== message.length + ? ", and break connected screens:" + : "will break connected screens:" + } else { + message += "." } - message += "This action cannot be undone." return message } @@ -192,12 +184,12 @@ okText="Delete" onOk={deleteSource} onCancel={hideDeleteDialog} - title={`Are you sure you want to delete ${source?.name}?`} + title={`Are you sure you want to delete this ${sourceType}?`} >
    - {#if affectedScreens.length > 0} + {#if affectedScreens.length > 0 && sourceType}

    - Removing this {sourceType} will break the following screens: + {buildMessage(sourceType)} {#each affectedScreens as item, idx} {/if}

    - - {@html buildMessage()} + This action cannot be undone.

    @@ -228,6 +219,7 @@ .screens { display: flex; flex-direction: row; + padding-bottom: var(--spacing-l); gap: var(--spacing-xs); } From fdf82a62760fa79e2f74edf3a1f7e50f37319595 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 15:30:30 +0000 Subject: [PATCH 20/29] Some updates, deleting external tables does delete data so warn about this. --- .../modals/DeleteDataConfirmationModal.svelte | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 4d2a60d2f5..0ef177db64 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -150,24 +150,23 @@ if (!source) { return "" } + const screenCount = affectedScreens.length let message = `Removing ${source?.name} ` let initialLength = message.length if (sourceType === SourceType.TABLE) { const views = "views" in source ? Object.values(source?.views ?? []) : [] - if (isInternalTable) { - message += `will delete its data${ - views.length ? `, views (${views.length})` : "" - }` - } else if (views.length) { - message += `will delete its views (${views.length})` - } + message += `will delete its data${ + views.length + ? `${screenCount ? "," : " and"} views (${views.length})` + : "" + }` } else if (sourceType === SourceType.DATASOURCE) { const queryList = getDatasourceQueries() if (queryList.length) { message += `will delete its queries (${queryList.length})` } } - if (affectedScreens.length) { + if (screenCount) { message += initialLength !== message.length ? ", and break connected screens:" @@ -187,16 +186,20 @@ title={`Are you sure you want to delete this ${sourceType}?`} >
    - {#if affectedScreens.length > 0 && sourceType} + {#if sourceType}

    {buildMessage(sourceType)} - - {#each affectedScreens as item, idx} - {item.text}{idx !== affectedScreens.length - 1 ? "," : ""} - {/each} - + {#if affectedScreens.length > 0} + + {#each affectedScreens as item, idx} + {item.text}{idx !== affectedScreens.length - 1 + ? "," + : ""} + {/each} + + {/if}

    {/if}

    From 399cf0ef6c9189553bb31ca508ad5da910682892 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 16:48:17 +0000 Subject: [PATCH 21/29] Removing unused elements. --- .../backend/modals/DeleteDataConfirmationModal.svelte | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 0ef177db64..39832cd491 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -1,5 +1,5 @@ From e8c79bc72008d757e8cbef488c66c96f9384c27f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 17:24:04 +0000 Subject: [PATCH 22/29] Most PR comments. --- .../modals/DeleteDataConfirmationModal.svelte | 7 +- .../src/api/routes/tests/screen.spec.ts | 99 +++++++------------ packages/server/src/constants/screens.ts | 4 +- packages/server/src/sdk/app/common/utils.ts | 2 +- .../shared-core/src/sdk/documents/screens.ts | 12 +-- 5 files changed, 46 insertions(+), 78 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index 39832cd491..f55e0e7013 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -10,6 +10,7 @@ viewsV2, } from "@/stores/builder" import ConfirmDialog from "@/components/common/ConfirmDialog.svelte" + import { helpers } from "@budibase/shared-core" import { SourceType } from "@budibase/types" import { goto, params } from "@roxi/routify" import { DB_TYPE_EXTERNAL } from "@/constants/backend" @@ -85,7 +86,7 @@ async function deleteView(view: ViewV2 | View) { try { - if ("version" in view && view.version === 2) { + if ("version" in view && helpers.views.isCalculationView(view)) { await viewsV2.delete(view as ViewV2) } else { await views.delete(view as View) @@ -99,10 +100,10 @@ async function deleteDatasource(datasource: Datasource) { try { - const isSelected = - get(datasources).selectedDatasourceId === datasource._id await datasources.delete(datasource) notifications.success("Datasource deleted") + const isSelected = + get(datasources).selectedDatasourceId === datasource._id if (isSelected) { $goto("./datasource") } diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 90ec4a567f..b6d6ceda3b 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -33,7 +33,7 @@ describe("/screens", () => { describe("fetch", () => { it("should be able to create a layout", async () => { - const screens = await config.api.screen.list({ status: 200 }) + const screens = await config.api.screen.list() expect(screens.length).toEqual(1) expect(screens.some(s => s._id === screen._id)).toEqual(true) }) @@ -67,28 +67,22 @@ describe("/screens", () => { inherits: [role1._id!, role2._id!], permissionId: BuiltinPermissionID.WRITE, }) - screen1 = await config.api.screen.save( - { - ...basicScreen(), - routing: { - roleId: role1._id!, - route: "/foo", - homeScreen: false, - }, + screen1 = await config.api.screen.save({ + ...basicScreen(), + routing: { + roleId: role1._id!, + route: "/foo", + homeScreen: false, }, - { status: 200 } - ) - screen2 = await config.api.screen.save( - { - ...basicScreen(), - routing: { - roleId: role2._id!, - route: "/bar", - homeScreen: false, - }, + }) + screen2 = await config.api.screen.save({ + ...basicScreen(), + routing: { + roleId: role2._id!, + route: "/bar", + homeScreen: false, }, - { status: 200 } - ) + }) // get into prod app await config.publish() }) @@ -96,10 +90,7 @@ describe("/screens", () => { async function checkScreens(roleId: string, screenIds: string[]) { await config.loginAsRole(roleId, async () => { const res = await config.api.application.getDefinition( - config.prodAppId!, - { - status: 200, - } + config.prodAppId! ) expect(res.screens.length).toEqual(screenIds.length) expect(res.screens.map(s => s._id).sort()).toEqual(screenIds.sort()) @@ -129,10 +120,7 @@ describe("/screens", () => { }, async () => { const res = await config.api.application.getDefinition( - config.prodAppId!, - { - status: 200, - } + config.prodAppId! ) const screenIds = [screen._id!, screen1._id!] expect(res.screens.length).toEqual(screenIds.length) @@ -149,9 +137,7 @@ describe("/screens", () => { it("should be able to create a screen", async () => { const screen = basicScreen() - const responseScreen = await config.api.screen.save(screen, { - status: 200, - }) + const responseScreen = await config.api.screen.save(screen) expect(responseScreen._rev).toBeDefined() expect(responseScreen.name).toEqual(screen.name) @@ -160,13 +146,13 @@ describe("/screens", () => { it("should be able to update a screen", async () => { const screen = basicScreen() - let responseScreen = await config.api.screen.save(screen, { status: 200 }) + let responseScreen = await config.api.screen.save(screen) screen._id = responseScreen._id screen._rev = responseScreen._rev screen.name = "edit" jest.clearAllMocks() - responseScreen = await config.api.screen.save(screen, { status: 200 }) + responseScreen = await config.api.screen.save(screen) expect(responseScreen._rev).toBeDefined() expect(responseScreen.name).toEqual(screen.name) @@ -186,8 +172,7 @@ describe("/screens", () => { it("should be able to delete the screen", async () => { const response = await config.api.screen.destroy( screen._id!, - screen._rev!, - { status: 200 } + screen._rev! ) expect(response.message).toBeDefined() expect(events.screen.deleted).toHaveBeenCalledTimes(1) @@ -205,9 +190,7 @@ describe("/screens", () => { describe("usage", () => { beforeEach(async () => { await config.init() - await config.api.screen.save(basicScreen(), { - status: 200, - }) + await config.api.screen.save(basicScreen()) }) function confirmScreen(usage: UsageOfScreensResponse, screen: Screen) { @@ -217,57 +200,41 @@ describe("/screens", () => { } it("should find table usage", async () => { - const table = await config.api.table.save(basicTable(), { status: 200 }) + const table = await config.api.table.save(basicTable()) const screen = await config.api.screen.save( - tableScreen("BudibaseDB", table), - { status: 200 } + tableScreen("BudibaseDB", table) ) - const usage = await config.api.screen.usage(table._id!, { status: 200 }) + const usage = await config.api.screen.usage(table._id!) expect(usage.sourceType).toEqual(SourceType.TABLE) confirmScreen(usage, screen) }) it("should find view usage", async () => { - const table = await config.api.table.save(basicTable(), { status: 200 }) + const table = await config.api.table.save(basicTable()) const view = await config.api.viewV2.create( viewV2.createRequest(table._id!), { status: 201 } ) const screen = await config.api.screen.save( - viewScreen("BudibaseDB", view), - { - status: 200, - } + viewScreen("BudibaseDB", view) ) - const usage = await config.api.screen.usage(view.id, { status: 200 }) + const usage = await config.api.screen.usage(view.id) expect(usage.sourceType).toEqual(SourceType.VIEW) confirmScreen(usage, screen) }) it("should find datasource/query usage", async () => { const datasource = await config.api.datasource.create( - basicDatasource().datasource, - { - status: 200, - } + basicDatasource().datasource ) - const query = await config.api.query.save(basicQuery(datasource._id!), { - status: 200, - }) + const query = await config.api.query.save(basicQuery(datasource._id!)) const screen = await config.api.screen.save( - queryScreen(datasource._id!, query), - { - status: 200, - } + queryScreen(datasource._id!, query) ) - const dsUsage = await config.api.screen.usage(datasource._id!, { - status: 200, - }) + const dsUsage = await config.api.screen.usage(datasource._id!) expect(dsUsage.sourceType).toEqual(SourceType.DATASOURCE) confirmScreen(dsUsage, screen) - const queryUsage = await config.api.screen.usage(query._id!, { - status: 200, - }) + const queryUsage = await config.api.screen.usage(query._id!) expect(queryUsage.sourceType).toEqual(SourceType.QUERY) confirmScreen(queryUsage, screen) }) diff --git a/packages/server/src/constants/screens.ts b/packages/server/src/constants/screens.ts index 52f0b3d9ff..1cbc049e42 100644 --- a/packages/server/src/constants/screens.ts +++ b/packages/server/src/constants/screens.ts @@ -1,6 +1,6 @@ import { roles } from "@budibase/backend-core" import { BASE_LAYOUT_PROP_IDS } from "./layouts" -import { Screen, Table, Query, ViewV2 } from "@budibase/types" +import { Screen, Table, Query, ViewV2, Component } from "@budibase/types" export function createHomeScreen( config: { @@ -54,7 +54,7 @@ export function createHomeScreen( } } -function heading(text: string) { +function heading(text: string): Component { return { _id: "c1bff24cd821e41d18c894ac77a80ef99", _component: "@budibase/standard-components/heading", diff --git a/packages/server/src/sdk/app/common/utils.ts b/packages/server/src/sdk/app/common/utils.ts index ed4e4c9a4f..6dac729872 100644 --- a/packages/server/src/sdk/app/common/utils.ts +++ b/packages/server/src/sdk/app/common/utils.ts @@ -11,5 +11,5 @@ export function getSourceType(sourceId: string): SourceType { } else if (docIds.isQueryId(sourceId)) { return SourceType.QUERY } - throw new Error("Unknown source type - cannot find document type") + throw new Error(`Unknown source type for source "${sourceId}"`) } diff --git a/packages/shared-core/src/sdk/documents/screens.ts b/packages/shared-core/src/sdk/documents/screens.ts index 8b6ea276b2..3ceffd26a6 100644 --- a/packages/shared-core/src/sdk/documents/screens.ts +++ b/packages/shared-core/src/sdk/documents/screens.ts @@ -3,17 +3,17 @@ import { Screen } from "@budibase/types" export function findInSettings(screen: Screen, toFind: string) { const foundIn: { setting: string; value: string }[] = [] function recurse(props: Record, parentKey = "") { - for (let key of Object.keys(props)) { - if (!props[key]) { + for (const [key, value] of Object.entries(props)) { + if (!value) { continue } - if (typeof props[key] === "string" && props[key].includes(toFind)) { + if (typeof value === "string" && value.includes(toFind)) { foundIn.push({ setting: parentKey ? `${parentKey}.${key}` : key, - value: props[key], + value: value, }) - } else if (typeof props[key] === "object") { - recurse(props[key], key) + } else if (typeof value === "object") { + recurse(value, key) } } } From 83fdbccd6b1f4aaa8f84495e165ad56ab0015004 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 17:34:44 +0000 Subject: [PATCH 23/29] export directly. --- .../src/api/routes/tests/screen.spec.ts | 12 +++++----- .../server/src/tests/utilities/structures.ts | 24 +++++-------------- 2 files changed, 12 insertions(+), 24 deletions(-) diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index b6d6ceda3b..261c287087 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -11,9 +11,9 @@ import { const { basicScreen, - tableScreen, - viewScreen, - queryScreen, + createTableScreen, + createViewScreen, + createQueryScreen, basicTable, viewV2, basicQuery, @@ -202,7 +202,7 @@ describe("/screens", () => { it("should find table usage", async () => { const table = await config.api.table.save(basicTable()) const screen = await config.api.screen.save( - tableScreen("BudibaseDB", table) + createTableScreen("BudibaseDB", table) ) const usage = await config.api.screen.usage(table._id!) expect(usage.sourceType).toEqual(SourceType.TABLE) @@ -216,7 +216,7 @@ describe("/screens", () => { { status: 201 } ) const screen = await config.api.screen.save( - viewScreen("BudibaseDB", view) + createViewScreen("BudibaseDB", view) ) const usage = await config.api.screen.usage(view.id) expect(usage.sourceType).toEqual(SourceType.VIEW) @@ -229,7 +229,7 @@ describe("/screens", () => { ) const query = await config.api.query.save(basicQuery(datasource._id!)) const screen = await config.api.screen.save( - queryScreen(datasource._id!, query) + createQueryScreen(datasource._id!, query) ) const dsUsage = await config.api.screen.usage(datasource._id!) expect(dsUsage.sourceType).toEqual(SourceType.DATASOURCE) diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 864b9f5399..7ca326e825 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -1,10 +1,5 @@ import { roles, utils } from "@budibase/backend-core" -import { - createHomeScreen, - createTableScreen, - createQueryScreen, - createViewScreen, -} from "../../constants/screens" +import { createHomeScreen } from "../../constants/screens" import { EMPTY_LAYOUT } from "../../constants/layouts" import { cloneDeep } from "lodash/fp" import { @@ -45,6 +40,11 @@ import { import { LoopInput } from "../../definitions/automations" import { merge } from "lodash" import { generator } from "@budibase/backend-core/tests" +export { + createTableScreen, + createQueryScreen, + createViewScreen, +} from "../../constants/screens" const { BUILTIN_ROLE_IDS } = roles @@ -523,18 +523,6 @@ export function basicScreen(route = "/") { }) } -export function tableScreen(datasourceName: string, table: Table) { - return createTableScreen(datasourceName, table) -} - -export function viewScreen(datasourceName: string, view: ViewV2) { - return createViewScreen(datasourceName, view) -} - -export function queryScreen(datasourceId: string, query: Query) { - return createQueryScreen(datasourceId, query) -} - export function powerScreen(route = "/") { return createHomeScreen({ roleId: BUILTIN_ROLE_IDS.POWER, From a5f2a050617495a847917ca1e14b7decdccdfb38 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 17:42:06 +0000 Subject: [PATCH 24/29] Linting. --- packages/server/src/tests/utilities/structures.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index 7ca326e825..3055b9c760 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -35,7 +35,6 @@ import { WebhookActionType, BuiltinPermissionID, DeepPartial, - ViewV2, } from "@budibase/types" import { LoopInput } from "../../definitions/automations" import { merge } from "lodash" From f9c75a213534a2ca7a8b5188328f871b462ade76 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 17:53:23 +0000 Subject: [PATCH 25/29] Final comments. --- .../backend/modals/DeleteDataConfirmationModal.svelte | 2 +- packages/server/src/api/routes/tests/screen.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index f55e0e7013..e3656328d7 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -86,7 +86,7 @@ async function deleteView(view: ViewV2 | View) { try { - if ("version" in view && helpers.views.isCalculationView(view)) { + if ("version" in view && helpers.views.isV2(view)) { await viewsV2.delete(view as ViewV2) } else { await views.delete(view as View) diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index 261c287087..a4c0b23232 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -90,7 +90,7 @@ describe("/screens", () => { async function checkScreens(roleId: string, screenIds: string[]) { await config.loginAsRole(roleId, async () => { const res = await config.api.application.getDefinition( - config.prodAppId! + config.getProdAppId() ) expect(res.screens.length).toEqual(screenIds.length) expect(res.screens.map(s => s._id).sort()).toEqual(screenIds.sort()) From c09809932d0cd91ef17c073273bd09adad82e11c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 6 Feb 2025 17:54:24 +0000 Subject: [PATCH 26/29] cleanup. --- .../backend/modals/DeleteDataConfirmationModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index e3656328d7..b52d3bce3c 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -86,7 +86,7 @@ async function deleteView(view: ViewV2 | View) { try { - if ("version" in view && helpers.views.isV2(view)) { + if (helpers.views.isV2(view)) { await viewsV2.delete(view as ViewV2) } else { await views.delete(view as View) From 85b2db1f023d49bc066315ee020c01db4ab4f8c9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 7 Feb 2025 11:52:31 +0000 Subject: [PATCH 27/29] Majority of comments. --- .../backend/modals/DeleteDataConfirmationModal.svelte | 7 ++++--- packages/builder/src/stores/builder/screens.ts | 4 ++-- packages/frontend-core/src/api/screens.ts | 6 +++--- packages/server/src/api/controllers/screen.ts | 10 ++-------- packages/server/src/api/routes/tests/screen.spec.ts | 4 ++-- packages/server/src/tests/utilities/api/screen.ts | 6 +++--- packages/shared-core/src/sdk/documents/screens.ts | 4 ++-- packages/types/src/api/web/app/screen.ts | 3 +-- 8 files changed, 19 insertions(+), 25 deletions(-) diff --git a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte index b52d3bce3c..82271bd066 100644 --- a/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte +++ b/packages/builder/src/components/backend/modals/DeleteDataConfirmationModal.svelte @@ -10,7 +10,7 @@ viewsV2, } from "@/stores/builder" import ConfirmDialog from "@/components/common/ConfirmDialog.svelte" - import { helpers } from "@budibase/shared-core" + import { helpers, utils } from "@budibase/shared-core" import { SourceType } from "@budibase/types" import { goto, params } from "@roxi/routify" import { DB_TYPE_EXTERNAL } from "@/constants/backend" @@ -45,7 +45,7 @@ } export const show = async () => { - const usage = await screenStore.usageOfScreens(getSourceID()) + const usage = await screenStore.usageInScreens(getSourceID()) affectedScreens = processScreens(usage.screens) sourceType = usage.sourceType confirmDeleteDialog.show() @@ -93,7 +93,6 @@ } notifications.success("View deleted") } catch (error) { - console.error(error) notifications.error("Error deleting view") } } @@ -140,6 +139,8 @@ return await deleteQuery(source as Query) case SourceType.DATASOURCE: return await deleteDatasource(source as Datasource) + default: + utils.unreachable(sourceType) } } diff --git a/packages/builder/src/stores/builder/screens.ts b/packages/builder/src/stores/builder/screens.ts index 1f23f9b1d1..b7d9a8be30 100644 --- a/packages/builder/src/stores/builder/screens.ts +++ b/packages/builder/src/stores/builder/screens.ts @@ -504,8 +504,8 @@ export class ScreenStore extends BudiStore { /** * Provides a list of screens that are used by a given source ID (table, view, datasource, query) */ - async usageOfScreens(sourceId: string) { - return API.usageOfScreens(sourceId) + async usageInScreens(sourceId: string) { + return API.usageInScreens(sourceId) } } diff --git a/packages/frontend-core/src/api/screens.ts b/packages/frontend-core/src/api/screens.ts index 8147db738d..0bcca9cfd3 100644 --- a/packages/frontend-core/src/api/screens.ts +++ b/packages/frontend-core/src/api/screens.ts @@ -2,14 +2,14 @@ import { DeleteScreenResponse, SaveScreenRequest, SaveScreenResponse, - UsageOfScreensResponse, + UsageInScreensResponse, } from "@budibase/types" import { BaseAPIClient } from "./types" export interface ScreenEndpoints { saveScreen: (screen: SaveScreenRequest) => Promise deleteScreen: (id: string, rev: string) => Promise - usageOfScreens: (sourceId: string) => Promise + usageInScreens: (sourceId: string) => Promise } export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ @@ -35,7 +35,7 @@ export const buildScreenEndpoints = (API: BaseAPIClient): ScreenEndpoints => ({ }) }, - usageOfScreens: async sourceId => { + usageInScreens: async sourceId => { return await API.post({ url: `/api/screens/usage/${sourceId}`, }) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index ea00e60e1a..456d5729db 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -17,7 +17,7 @@ import { ScreenProps, ScreenUsage, SourceType, - UsageOfScreensResponse, + UsageInScreensResponse, UserCtx, } from "@budibase/types" import { builderSocket } from "../../websockets" @@ -138,7 +138,7 @@ function findPlugins(component: ScreenProps, foundPlugins: string[]) { component._children.forEach(child => findPlugins(child, foundPlugins)) } -export async function usage(ctx: UserCtx) { +export async function usage(ctx: UserCtx) { const sourceId = ctx.params.sourceId const sourceType = sdk.common.getSourceType(sourceId) const allScreens = await sdk.screens.fetch() @@ -152,14 +152,8 @@ export async function usage(ctx: UserCtx) { }) } } - const isInternalTable = - sourceType === SourceType.TABLE && - sdk.tables.isInternal({ tableId: sourceId }) - const isInternalView = - sourceType === SourceType.VIEW && sdk.views.isInternal(sourceId) ctx.body = { sourceType, - internal: isInternalTable || isInternalView, screens: response, } } diff --git a/packages/server/src/api/routes/tests/screen.spec.ts b/packages/server/src/api/routes/tests/screen.spec.ts index a4c0b23232..32f4e4b361 100644 --- a/packages/server/src/api/routes/tests/screen.spec.ts +++ b/packages/server/src/api/routes/tests/screen.spec.ts @@ -6,7 +6,7 @@ import { Role, BuiltinPermissionID, SourceType, - UsageOfScreensResponse, + UsageInScreensResponse, } from "@budibase/types" const { @@ -193,7 +193,7 @@ describe("/screens", () => { await config.api.screen.save(basicScreen()) }) - function confirmScreen(usage: UsageOfScreensResponse, screen: Screen) { + function confirmScreen(usage: UsageInScreensResponse, screen: Screen) { expect(usage.screens.length).toEqual(1) expect(usage.screens[0].url).toEqual(screen.routing.route) expect(usage.screens[0]._id).toEqual(screen._id!) diff --git a/packages/server/src/tests/utilities/api/screen.ts b/packages/server/src/tests/utilities/api/screen.ts index bbbe704a29..8d4c0d9ac4 100644 --- a/packages/server/src/tests/utilities/api/screen.ts +++ b/packages/server/src/tests/utilities/api/screen.ts @@ -1,4 +1,4 @@ -import { Screen, UsageOfScreensResponse } from "@budibase/types" +import { Screen, UsageInScreensResponse } from "@budibase/types" import { Expectations, TestAPI } from "./base" export class ScreenAPI extends TestAPI { @@ -32,8 +32,8 @@ export class ScreenAPI extends TestAPI { usage = async ( sourceId: string, expectations?: Expectations - ): Promise => { - return this._post( + ): Promise => { + return this._post( `/api/screens/usage/${sourceId}`, { expectations, diff --git a/packages/shared-core/src/sdk/documents/screens.ts b/packages/shared-core/src/sdk/documents/screens.ts index 3ceffd26a6..218c6673aa 100644 --- a/packages/shared-core/src/sdk/documents/screens.ts +++ b/packages/shared-core/src/sdk/documents/screens.ts @@ -1,8 +1,8 @@ -import { Screen } from "@budibase/types" +import { Screen, Component } from "@budibase/types" export function findInSettings(screen: Screen, toFind: string) { const foundIn: { setting: string; value: string }[] = [] - function recurse(props: Record, parentKey = "") { + function recurse(props: Component, parentKey = "") { for (const [key, value] of Object.entries(props)) { if (!value) { continue diff --git a/packages/types/src/api/web/app/screen.ts b/packages/types/src/api/web/app/screen.ts index 9fc201d651..2ddac2f19a 100644 --- a/packages/types/src/api/web/app/screen.ts +++ b/packages/types/src/api/web/app/screen.ts @@ -21,8 +21,7 @@ export interface ScreenUsage { _id: string } -export interface UsageOfScreensResponse { +export interface UsageInScreensResponse { sourceType: SourceType - internal: boolean screens: ScreenUsage[] } From 126befde3ea1d40245eab697e0156211c31088ef Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 7 Feb 2025 12:02:58 +0000 Subject: [PATCH 28/29] Updating UIEvent to an interface. --- packages/types/src/ui/common.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/types/src/ui/common.ts b/packages/types/src/ui/common.ts index 2f5d864ca0..0f5a2590b9 100644 --- a/packages/types/src/ui/common.ts +++ b/packages/types/src/ui/common.ts @@ -1,3 +1,5 @@ -export type UIEvent = Event & { +export interface UIEvent extends Omit { currentTarget: EventTarget & HTMLInputElement -} & { key?: string } & { target?: any } + key?: string + target?: any +} From 6435c9b529bb6411734a6338ddec8488e9db7a0b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 7 Feb 2025 12:05:45 +0000 Subject: [PATCH 29/29] Linting. --- packages/server/src/api/controllers/screen.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts index 456d5729db..038d87d548 100644 --- a/packages/server/src/api/controllers/screen.ts +++ b/packages/server/src/api/controllers/screen.ts @@ -16,7 +16,6 @@ import { Screen, ScreenProps, ScreenUsage, - SourceType, UsageInScreensResponse, UserCtx, } from "@budibase/types"