From bb4d7e0b3270a908327f72b3d6f3488c4d2cdc71 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 2 Dec 2024 16:48:34 +0000 Subject: [PATCH 01/16] Integration API. --- .../server/src/api/controllers/integration.ts | 17 +++++++++++++---- packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/integration.ts | 8 ++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 packages/types/src/api/web/app/integration.ts diff --git a/packages/server/src/api/controllers/integration.ts b/packages/server/src/api/controllers/integration.ts index 57038f8401..9f770ad049 100644 --- a/packages/server/src/api/controllers/integration.ts +++ b/packages/server/src/api/controllers/integration.ts @@ -1,12 +1,17 @@ import { getDefinition, getDefinitions } from "../../integrations" -import { SourceName, UserCtx } from "@budibase/types" +import { + SourceName, + UserCtx, + FetchIntegrationsResponse, + FindIntegrationResponse, +} from "@budibase/types" const DISABLED_EXTERNAL_INTEGRATIONS = [ SourceName.AIRTABLE, SourceName.BUDIBASE, ] -export async function fetch(ctx: UserCtx) { +export async function fetch(ctx: UserCtx) { const definitions = await getDefinitions() for (let disabledIntegration of DISABLED_EXTERNAL_INTEGRATIONS) { delete definitions[disabledIntegration] @@ -14,10 +19,14 @@ export async function fetch(ctx: UserCtx) { ctx.body = definitions } -export async function find(ctx: UserCtx) { +export async function find(ctx: UserCtx) { const sourceType = ctx.params?.type if (DISABLED_EXTERNAL_INTEGRATIONS.indexOf(sourceType) !== -1) { ctx.throw(400, `Invalid source type - ${sourceType} is not supported.`) } - ctx.body = await getDefinition(ctx.params.type) + const integration = await getDefinition(ctx.params.type) + if (!integration) { + ctx.throw(400, "Integration not found") + } + ctx.body = integration } diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 9cc0bf36b6..614446a1b7 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -10,3 +10,4 @@ export * from "./user" export * from "./rowAction" export * from "./automation" export * from "./component" +export * from "./integration" diff --git a/packages/types/src/api/web/app/integration.ts b/packages/types/src/api/web/app/integration.ts new file mode 100644 index 0000000000..8e52a32ba9 --- /dev/null +++ b/packages/types/src/api/web/app/integration.ts @@ -0,0 +1,8 @@ +import { Integration, SourceName } from "../../../sdk" + +export type FetchIntegrationsResponse = Record< + SourceName, + Integration | undefined +> + +export type FindIntegrationResponse = Integration From 82e4e190fccb506613e266940de4e52839f35adc Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 2 Dec 2024 16:53:00 +0000 Subject: [PATCH 02/16] Layout API. --- packages/server/src/api/controllers/layout.ts | 3 ++- packages/types/src/api/web/layout.ts | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/layout.ts b/packages/server/src/api/controllers/layout.ts index c0406f50ac..447b437a23 100644 --- a/packages/server/src/api/controllers/layout.ts +++ b/packages/server/src/api/controllers/layout.ts @@ -3,6 +3,7 @@ import { generateLayoutID, getScreenParams } from "../../db/utils" import { events, context } from "@budibase/backend-core" import { BBContext, + DeleteLayoutResponse, Layout, SaveLayoutRequest, SaveLayoutResponse, @@ -32,7 +33,7 @@ export async function save( ctx.status = 200 } -export async function destroy(ctx: BBContext) { +export async function destroy(ctx: UserCtx) { const db = context.getAppDB() const layoutId = ctx.params.layoutId, layoutRev = ctx.params.layoutRev diff --git a/packages/types/src/api/web/layout.ts b/packages/types/src/api/web/layout.ts index 50512777ef..45f7fb9d0a 100644 --- a/packages/types/src/api/web/layout.ts +++ b/packages/types/src/api/web/layout.ts @@ -3,3 +3,7 @@ import { Layout } from "../../documents" export interface SaveLayoutRequest extends Layout {} export interface SaveLayoutResponse extends Layout {} + +export interface DeleteLayoutResponse { + message: string +} From c073500d502643ee7227ce98802cd98685d75997 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 2 Dec 2024 17:42:46 +0000 Subject: [PATCH 03/16] Metadata API. --- .../server/src/api/controllers/metadata.ts | 37 ++++++++++--------- .../{metadata.spec.js => metadata.spec.ts} | 24 ++++++------ packages/server/src/automations/utils.ts | 4 +- packages/server/src/constants/index.ts | 5 --- .../server/src/sdk/app/automations/crud.ts | 11 ++---- packages/server/src/utilities/index.ts | 2 +- packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/metadata.ts | 14 +++++++ packages/types/src/documents/app/index.ts | 1 + packages/types/src/documents/app/metadata.ts | 4 ++ 10 files changed, 59 insertions(+), 44 deletions(-) rename packages/server/src/api/routes/tests/{metadata.spec.js => metadata.spec.ts} (69%) create mode 100644 packages/types/src/api/web/app/metadata.ts create mode 100644 packages/types/src/documents/app/metadata.ts diff --git a/packages/server/src/api/controllers/metadata.ts b/packages/server/src/api/controllers/metadata.ts index f579b14499..85c1ab2be8 100644 --- a/packages/server/src/api/controllers/metadata.ts +++ b/packages/server/src/api/controllers/metadata.ts @@ -1,24 +1,35 @@ -import { MetadataTypes } from "../../constants" import { generateMetadataID } from "../../db/utils" import { saveEntityMetadata, deleteEntityMetadata } from "../../utilities" import { context } from "@budibase/backend-core" -import { BBContext } from "@budibase/types" +import { + UserCtx, + MetadataType, + GetMetadataTypesResponse, + SaveMetadataRequest, + SaveMetadataResponse, + DeleteMetadataResponse, + FindMetadataResponse, +} from "@budibase/types" -export async function getTypes(ctx: BBContext) { +export async function getTypes(ctx: UserCtx) { ctx.body = { - types: MetadataTypes, + types: MetadataType, } } -export async function saveMetadata(ctx: BBContext) { +export async function saveMetadata( + ctx: UserCtx +) { const { type, entityId } = ctx.params - if (type === MetadataTypes.AUTOMATION_TEST_HISTORY) { + if (type === MetadataType.AUTOMATION_TEST_HISTORY) { ctx.throw(400, "Cannot save automation history type") } ctx.body = await saveEntityMetadata(type, entityId, ctx.request.body) } -export async function deleteMetadata(ctx: BBContext) { +export async function deleteMetadata( + ctx: UserCtx +) { const { type, entityId } = ctx.params await deleteEntityMetadata(type, entityId) ctx.body = { @@ -26,17 +37,9 @@ export async function deleteMetadata(ctx: BBContext) { } } -export async function getMetadata(ctx: BBContext) { +export async function getMetadata(ctx: UserCtx) { const { type, entityId } = ctx.params const db = context.getAppDB() const id = generateMetadataID(type, entityId) - try { - ctx.body = await db.get(id) - } catch (err: any) { - if (err.status === 404) { - ctx.body = {} - } else { - ctx.throw(err.status, err) - } - } + ctx.body = (await db.tryGet(id)) || {} } diff --git a/packages/server/src/api/routes/tests/metadata.spec.js b/packages/server/src/api/routes/tests/metadata.spec.ts similarity index 69% rename from packages/server/src/api/routes/tests/metadata.spec.js rename to packages/server/src/api/routes/tests/metadata.spec.ts index d5563f417e..6e991a272e 100644 --- a/packages/server/src/api/routes/tests/metadata.spec.js +++ b/packages/server/src/api/routes/tests/metadata.spec.ts @@ -1,11 +1,11 @@ -const { testAutomation } = require("./utilities/TestFunctions") -const setup = require("./utilities") -const { MetadataTypes } = require("../../../constants") +import { testAutomation } from "./utilities/TestFunctions" +import * as setup from "./utilities" +import { MetadataType, Automation } from "@budibase/types" describe("/metadata", () => { let request = setup.getRequest() let config = setup.getConfig() - let automation + let automation: Automation afterAll(setup.afterAll) @@ -15,8 +15,8 @@ describe("/metadata", () => { }) async function createMetadata( - data, - type = MetadataTypes.AUTOMATION_TEST_INPUT + data: Record, + type = MetadataType.AUTOMATION_TEST_INPUT ) { const res = await request .post(`/api/metadata/${type}/${automation._id}`) @@ -27,7 +27,7 @@ describe("/metadata", () => { expect(res.body._rev).toBeDefined() } - async function getMetadata(type) { + async function getMetadata(type: MetadataType) { const res = await request .get(`/api/metadata/${type}/${automation._id}`) .set(config.defaultHeaders()) @@ -39,14 +39,14 @@ describe("/metadata", () => { describe("save", () => { it("should be able to save some metadata", async () => { await createMetadata({ test: "a" }) - const testInput = await getMetadata(MetadataTypes.AUTOMATION_TEST_INPUT) + const testInput = await getMetadata(MetadataType.AUTOMATION_TEST_INPUT) expect(testInput.test).toBe("a") }) it("should save history metadata on automation run", async () => { // this should have created some history - await testAutomation(config, automation) - const metadata = await getMetadata(MetadataTypes.AUTOMATION_TEST_HISTORY) + await testAutomation(config, automation, {}) + const metadata = await getMetadata(MetadataType.AUTOMATION_TEST_HISTORY) expect(metadata).toBeDefined() expect(metadata.history.length).toBe(1) expect(typeof metadata.history[0].occurredAt).toBe("number") @@ -57,13 +57,13 @@ describe("/metadata", () => { it("should be able to delete some test inputs", async () => { const res = await request .delete( - `/api/metadata/${MetadataTypes.AUTOMATION_TEST_INPUT}/${automation._id}` + `/api/metadata/${MetadataType.AUTOMATION_TEST_INPUT}/${automation._id}` ) .set(config.defaultHeaders()) .expect("Content-Type", /json/) .expect(200) expect(res.body.message).toBeDefined() - const metadata = await getMetadata(MetadataTypes.AUTOMATION_TEST_INPUT) + const metadata = await getMetadata(MetadataType.AUTOMATION_TEST_INPUT) expect(metadata.test).toBeUndefined() }) }) diff --git a/packages/server/src/automations/utils.ts b/packages/server/src/automations/utils.ts index 3eeeae5734..292b288a70 100644 --- a/packages/server/src/automations/utils.ts +++ b/packages/server/src/automations/utils.ts @@ -2,7 +2,6 @@ import { Thread, ThreadType } from "../threads" import { definitions } from "./triggerInfo" import { automationQueue } from "./bullboard" import { updateEntityMetadata } from "../utilities" -import { MetadataTypes } from "../constants" import { context, db as dbCore, utils } from "@budibase/backend-core" import { getAutomationMetadataParams } from "../db/utils" import { cloneDeep } from "lodash/fp" @@ -14,6 +13,7 @@ import { AutomationStepDefinition, AutomationTriggerDefinition, AutomationTriggerStepId, + MetadataType, } from "@budibase/types" import { automationsEnabled } from "../features" import { helpers, REBOOT_CRON } from "@budibase/shared-core" @@ -107,7 +107,7 @@ export async function updateTestHistory( history: any ) { return updateEntityMetadata( - MetadataTypes.AUTOMATION_TEST_HISTORY, + MetadataType.AUTOMATION_TEST_HISTORY, automation._id, (metadata: any) => { if (metadata && Array.isArray(metadata.history)) { diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index 89e2f26516..fde1efd1b9 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -124,11 +124,6 @@ export enum BaseQueryVerbs { DELETE = "delete", } -export enum MetadataTypes { - AUTOMATION_TEST_INPUT = "automationTestInput", - AUTOMATION_TEST_HISTORY = "automationTestHistory", -} - export enum InvalidColumns { ID = "_id", REV = "_rev", diff --git a/packages/server/src/sdk/app/automations/crud.ts b/packages/server/src/sdk/app/automations/crud.ts index b982d5b45e..d16ae2d042 100644 --- a/packages/server/src/sdk/app/automations/crud.ts +++ b/packages/server/src/sdk/app/automations/crud.ts @@ -3,10 +3,10 @@ import { RequiredKeys, Webhook, WebhookActionType, + MetadataType, } from "@budibase/types" import { generateAutomationID, getAutomationParams } from "../../../db/utils" import { deleteEntityMetadata } from "../../../utilities" -import { MetadataTypes } from "../../../constants" import { context, events, @@ -161,7 +161,7 @@ export async function update(automation: Automation) { if (oldAutoTrigger && oldAutoTrigger.id !== newAutoTrigger?.id) { await events.automation.triggerUpdated(automation) await deleteEntityMetadata( - MetadataTypes.AUTOMATION_TEST_INPUT, + MetadataType.AUTOMATION_TEST_INPUT, automation._id! ) } @@ -183,11 +183,8 @@ export async function remove(automationId: string, rev: string) { }) // delete metadata first - await deleteEntityMetadata(MetadataTypes.AUTOMATION_TEST_INPUT, automationId) - await deleteEntityMetadata( - MetadataTypes.AUTOMATION_TEST_HISTORY, - automationId - ) + await deleteEntityMetadata(MetadataType.AUTOMATION_TEST_INPUT, automationId) + await deleteEntityMetadata(MetadataType.AUTOMATION_TEST_HISTORY, automationId) const result = await db.remove(automationId, rev) diff --git a/packages/server/src/utilities/index.ts b/packages/server/src/utilities/index.ts index 331a8e266f..db57b4ec12 100644 --- a/packages/server/src/utilities/index.ts +++ b/packages/server/src/utilities/index.ts @@ -88,7 +88,7 @@ export async function saveEntityMetadata( type: string, entityId: string, metadata: Document -) { +): Promise { return updateEntityMetadata(type, entityId, () => { return metadata }) diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 614446a1b7..40c8ebf9ca 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -11,3 +11,4 @@ export * from "./rowAction" export * from "./automation" export * from "./component" export * from "./integration" +export * from "./metadata" diff --git a/packages/types/src/api/web/app/metadata.ts b/packages/types/src/api/web/app/metadata.ts new file mode 100644 index 0000000000..5940f8e3d0 --- /dev/null +++ b/packages/types/src/api/web/app/metadata.ts @@ -0,0 +1,14 @@ +import { MetadataType, Document } from "../../../documents" + +export interface GetMetadataTypesResponse { + types: typeof MetadataType +} + +export interface SaveMetadataRequest extends Document {} +export interface SaveMetadataResponse extends Document {} + +export interface DeleteMetadataResponse { + message: string +} + +export interface FindMetadataResponse extends Document {} diff --git a/packages/types/src/documents/app/index.ts b/packages/types/src/documents/app/index.ts index 51c6889f14..d6c4fd8637 100644 --- a/packages/types/src/documents/app/index.ts +++ b/packages/types/src/documents/app/index.ts @@ -19,3 +19,4 @@ export * from "./snippet" export * from "./rowAction" export * from "./theme" export * from "./deployment" +export * from "./metadata" diff --git a/packages/types/src/documents/app/metadata.ts b/packages/types/src/documents/app/metadata.ts new file mode 100644 index 0000000000..421e8a2f0d --- /dev/null +++ b/packages/types/src/documents/app/metadata.ts @@ -0,0 +1,4 @@ +export enum MetadataType { + AUTOMATION_TEST_INPUT = "automationTestInput", + AUTOMATION_TEST_HISTORY = "automationTestHistory", +} From 582d05651d0cc7d7240d19869ff5f060f59bb872 Mon Sep 17 00:00:00 2001 From: andz-bb Date: Tue, 3 Dec 2024 15:50:51 +0000 Subject: [PATCH 04/16] sort table names alphabetically when selecting data source --- .../DataSourceSelect/DataSourceSelect.svelte | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte index 410af53648..9eef8c1c06 100644 --- a/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte @@ -52,9 +52,16 @@ let modal $: text = value?.label ?? "Choose an option" - $: tables = $tablesStore.list.map(table => - format.table(table, $datasources.list) - ) + $: tables = $tablesStore.list + .map(table => format.table(table, $datasources.list)) + .sort((a, b) => { + // sort tables alphabetically, grouped by datasource + const dsComparison = a.datasourceName.localeCompare(b.datasourceName) + if (dsComparison !== 0) { + return dsComparison + } + return a.label.localeCompare(b.label) + }) $: viewsV1 = $viewsStore.list.map(view => ({ ...view, label: view.name, From b1dc9973902551218a0427ad2c3721a4dfe017b6 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 16:28:04 +0000 Subject: [PATCH 05/16] old migration typing. --- .../server/src/api/controllers/migrations.ts | 17 +++++++++++++---- packages/types/src/api/web/global/index.ts | 1 + .../types/src/api/web/global/oldMigration.ts | 9 +++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 packages/types/src/api/web/global/oldMigration.ts diff --git a/packages/server/src/api/controllers/migrations.ts b/packages/server/src/api/controllers/migrations.ts index 89255e89b1..a3ff0c5810 100644 --- a/packages/server/src/api/controllers/migrations.ts +++ b/packages/server/src/api/controllers/migrations.ts @@ -1,24 +1,33 @@ import { context } from "@budibase/backend-core" import { migrate as migrationImpl, MIGRATIONS } from "../../migrations" -import { Ctx } from "@budibase/types" +import { + Ctx, + FetchOldMigrationResponse, + GetOldMigrationStatus, + RunOldMigrationRequest, +} from "@budibase/types" import { getAppMigrationVersion, getLatestEnabledMigrationId, } from "../../appMigrations" -export async function migrate(ctx: Ctx) { +export async function migrate(ctx: Ctx) { const options = ctx.request.body // don't await as can take a while, just return migrationImpl(options) ctx.status = 200 } -export async function fetchDefinitions(ctx: Ctx) { +export async function fetchDefinitions( + ctx: Ctx +) { ctx.body = MIGRATIONS ctx.status = 200 } -export async function getMigrationStatus(ctx: Ctx) { +export async function getMigrationStatus( + ctx: Ctx +) { const appId = context.getAppId() if (!appId) { diff --git a/packages/types/src/api/web/global/index.ts b/packages/types/src/api/web/global/index.ts index efcb6dc39c..c43f2928d8 100644 --- a/packages/types/src/api/web/global/index.ts +++ b/packages/types/src/api/web/global/index.ts @@ -4,3 +4,4 @@ export * from "./events" export * from "./configs" export * from "./scim" export * from "./license" +export * from "./oldMigration" diff --git a/packages/types/src/api/web/global/oldMigration.ts b/packages/types/src/api/web/global/oldMigration.ts new file mode 100644 index 0000000000..4b8ce9cc8d --- /dev/null +++ b/packages/types/src/api/web/global/oldMigration.ts @@ -0,0 +1,9 @@ +import { Migration, MigrationOptions } from "../../../sdk" + +export interface RunOldMigrationRequest extends MigrationOptions {} + +export type FetchOldMigrationResponse = Migration[] + +export interface GetOldMigrationStatus { + migrated: boolean +} From 4bf23b0163ace4241d3d9d0ef089f7418d6689cd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 16:36:48 +0000 Subject: [PATCH 06/16] Ops API typing --- packages/server/src/api/controllers/ops.ts | 17 ++++------------- packages/types/src/api/web/system/index.ts | 1 + packages/types/src/api/web/system/ops.ts | 8 ++++++++ 3 files changed, 13 insertions(+), 13 deletions(-) create mode 100644 packages/types/src/api/web/system/ops.ts diff --git a/packages/server/src/api/controllers/ops.ts b/packages/server/src/api/controllers/ops.ts index 89ad0ac97b..dc331bb3a3 100644 --- a/packages/server/src/api/controllers/ops.ts +++ b/packages/server/src/api/controllers/ops.ts @@ -1,16 +1,7 @@ -import { Ctx } from "@budibase/types" +import { Ctx, LogOpsRequest, ErrorOpsRequest } from "@budibase/types" import { logging } from "@budibase/backend-core" -interface LogRequest { - message: string - data?: any -} - -interface ErrorRequest { - message: string -} - -export async function log(ctx: Ctx) { +export async function log(ctx: Ctx) { const body = ctx.request.body console.trace(body.message, body.data) console.debug(body.message, body.data) @@ -20,13 +11,13 @@ export async function log(ctx: Ctx) { ctx.status = 204 } -export async function alert(ctx: Ctx) { +export async function alert(ctx: Ctx) { const body = ctx.request.body logging.logAlert(body.message, new Error(body.message)) ctx.status = 204 } -export async function error(ctx: Ctx) { +export async function error(ctx: Ctx) { const body = ctx.request.body throw new Error(body.message) } diff --git a/packages/types/src/api/web/system/index.ts b/packages/types/src/api/web/system/index.ts index 8820bf5cd1..0ce2738ddb 100644 --- a/packages/types/src/api/web/system/index.ts +++ b/packages/types/src/api/web/system/index.ts @@ -1,2 +1,3 @@ export * from "./environment" export * from "./status" +export * from "./ops" diff --git a/packages/types/src/api/web/system/ops.ts b/packages/types/src/api/web/system/ops.ts new file mode 100644 index 0000000000..390e4db006 --- /dev/null +++ b/packages/types/src/api/web/system/ops.ts @@ -0,0 +1,8 @@ +export interface LogOpsRequest { + message: string + data?: any +} + +export interface ErrorOpsRequest { + message: string +} From d0179ed815af1554acfbcd98c9fac5819a2721cc Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 16:50:13 +0000 Subject: [PATCH 07/16] Permission API typing. --- .../backend-core/src/security/permissions.ts | 58 +++++++++---------- .../src/security/tests/permissions.spec.ts | 2 +- .../server/src/api/controllers/permission.ts | 8 ++- packages/types/src/api/web/app/permission.ts | 6 +- packages/types/src/sdk/permissions.ts | 19 ++++++ 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/packages/backend-core/src/security/permissions.ts b/packages/backend-core/src/security/permissions.ts index 929ae92909..263f042a9d 100644 --- a/packages/backend-core/src/security/permissions.ts +++ b/packages/backend-core/src/security/permissions.ts @@ -2,6 +2,8 @@ import { PermissionLevel, PermissionType, BuiltinPermissionID, + Permission, + BuiltinPermissions, } from "@budibase/types" import flatten from "lodash/flatten" import cloneDeep from "lodash/fp/cloneDeep" @@ -12,7 +14,7 @@ export type RoleHierarchy = { permissionId: string }[] -export class Permission { +export class PermissionImpl implements Permission { type: PermissionType level: PermissionLevel @@ -61,68 +63,62 @@ export function getAllowedLevels(userPermLevel: PermissionLevel): string[] { } } -export const BUILTIN_PERMISSIONS: { - [key in keyof typeof BuiltinPermissionID]: { - _id: (typeof BuiltinPermissionID)[key] - name: string - permissions: Permission[] - } -} = { +export const BUILTIN_PERMISSIONS: BuiltinPermissions = { PUBLIC: { _id: BuiltinPermissionID.PUBLIC, name: "Public", permissions: [ - new Permission(PermissionType.WEBHOOK, PermissionLevel.EXECUTE), + new PermissionImpl(PermissionType.WEBHOOK, PermissionLevel.EXECUTE), ], }, READ_ONLY: { _id: BuiltinPermissionID.READ_ONLY, name: "Read only", permissions: [ - new Permission(PermissionType.QUERY, PermissionLevel.READ), - new Permission(PermissionType.TABLE, PermissionLevel.READ), - new Permission(PermissionType.APP, PermissionLevel.READ), + new PermissionImpl(PermissionType.QUERY, PermissionLevel.READ), + new PermissionImpl(PermissionType.TABLE, PermissionLevel.READ), + new PermissionImpl(PermissionType.APP, PermissionLevel.READ), ], }, WRITE: { _id: BuiltinPermissionID.WRITE, name: "Read/Write", permissions: [ - new Permission(PermissionType.QUERY, PermissionLevel.WRITE), - new Permission(PermissionType.TABLE, PermissionLevel.WRITE), - new Permission(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), - new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), - new Permission(PermissionType.APP, PermissionLevel.READ), + new PermissionImpl(PermissionType.QUERY, PermissionLevel.WRITE), + new PermissionImpl(PermissionType.TABLE, PermissionLevel.WRITE), + new PermissionImpl(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), + new PermissionImpl(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new PermissionImpl(PermissionType.APP, PermissionLevel.READ), ], }, POWER: { _id: BuiltinPermissionID.POWER, name: "Power", permissions: [ - new Permission(PermissionType.TABLE, PermissionLevel.WRITE), - new Permission(PermissionType.USER, PermissionLevel.READ), - new Permission(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), - new Permission(PermissionType.WEBHOOK, PermissionLevel.READ), - new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), - new Permission(PermissionType.APP, PermissionLevel.READ), + new PermissionImpl(PermissionType.TABLE, PermissionLevel.WRITE), + new PermissionImpl(PermissionType.USER, PermissionLevel.READ), + new PermissionImpl(PermissionType.AUTOMATION, PermissionLevel.EXECUTE), + new PermissionImpl(PermissionType.WEBHOOK, PermissionLevel.READ), + new PermissionImpl(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new PermissionImpl(PermissionType.APP, PermissionLevel.READ), ], }, ADMIN: { _id: BuiltinPermissionID.ADMIN, name: "Admin", permissions: [ - new Permission(PermissionType.TABLE, PermissionLevel.ADMIN), - new Permission(PermissionType.USER, PermissionLevel.ADMIN), - new Permission(PermissionType.AUTOMATION, PermissionLevel.ADMIN), - new Permission(PermissionType.WEBHOOK, PermissionLevel.READ), - new Permission(PermissionType.QUERY, PermissionLevel.ADMIN), - new Permission(PermissionType.LEGACY_VIEW, PermissionLevel.READ), - new Permission(PermissionType.APP, PermissionLevel.READ), + new PermissionImpl(PermissionType.TABLE, PermissionLevel.ADMIN), + new PermissionImpl(PermissionType.USER, PermissionLevel.ADMIN), + new PermissionImpl(PermissionType.AUTOMATION, PermissionLevel.ADMIN), + new PermissionImpl(PermissionType.WEBHOOK, PermissionLevel.READ), + new PermissionImpl(PermissionType.QUERY, PermissionLevel.ADMIN), + new PermissionImpl(PermissionType.LEGACY_VIEW, PermissionLevel.READ), + new PermissionImpl(PermissionType.APP, PermissionLevel.READ), ], }, } -export function getBuiltinPermissions() { +export function getBuiltinPermissions(): BuiltinPermissions { return cloneDeep(BUILTIN_PERMISSIONS) } diff --git a/packages/backend-core/src/security/tests/permissions.spec.ts b/packages/backend-core/src/security/tests/permissions.spec.ts index f98833c7cd..d268a56a1f 100644 --- a/packages/backend-core/src/security/tests/permissions.spec.ts +++ b/packages/backend-core/src/security/tests/permissions.spec.ts @@ -133,7 +133,7 @@ describe("getBuiltinPermissionByID", () => { _id: BuiltinPermissionID.PUBLIC, name: "Public", permissions: [ - new permissions.Permission( + new permissions.PermissionImpl( permissions.PermissionType.WEBHOOK, permissions.PermissionLevel.EXECUTE ), diff --git a/packages/server/src/api/controllers/permission.ts b/packages/server/src/api/controllers/permission.ts index ead48d3db8..2ef197dbca 100644 --- a/packages/server/src/api/controllers/permission.ts +++ b/packages/server/src/api/controllers/permission.ts @@ -9,6 +9,8 @@ import { RemovePermissionRequest, RemovePermissionResponse, FetchResourcePermissionInfoResponse, + FetchBuiltinPermissionsRequest, + FetchPermissionLevelsRequest, } from "@budibase/types" import { CURRENTLY_SUPPORTED_LEVELS, @@ -19,11 +21,13 @@ import { PermissionUpdateType } from "../../sdk/app/permissions" const SUPPORTED_LEVELS = CURRENTLY_SUPPORTED_LEVELS -export function fetchBuiltin(ctx: UserCtx) { +export function fetchBuiltin( + ctx: UserCtx +) { ctx.body = Object.values(permissions.getBuiltinPermissions()) } -export function fetchLevels(ctx: UserCtx) { +export function fetchLevels(ctx: UserCtx) { // for now only provide the read/write perms externally ctx.body = SUPPORTED_LEVELS } diff --git a/packages/types/src/api/web/app/permission.ts b/packages/types/src/api/web/app/permission.ts index b40310f21c..1a19fb0834 100644 --- a/packages/types/src/api/web/app/permission.ts +++ b/packages/types/src/api/web/app/permission.ts @@ -1,4 +1,8 @@ -import { PermissionLevel } from "../../../sdk" +import { BuiltinPermission, PermissionLevel } from "../../../sdk" + +export type FetchBuiltinPermissionsRequest = BuiltinPermission[] + +export type FetchPermissionLevelsRequest = string[] export interface FetchResourcePermissionInfoResponse { [key: string]: Record diff --git a/packages/types/src/sdk/permissions.ts b/packages/types/src/sdk/permissions.ts index bb36af4170..43336a3317 100644 --- a/packages/types/src/sdk/permissions.ts +++ b/packages/types/src/sdk/permissions.ts @@ -36,3 +36,22 @@ export enum PermissionSource { INHERITED = "INHERITED", BASE = "BASE", } + +export interface Permission { + type: PermissionType + level: PermissionLevel +} + +export interface BuiltinPermission { + _id: BuiltinPermissionID + name: string + permissions: Permission[] +} + +export type BuiltinPermissions = { + [key in keyof typeof BuiltinPermissionID]: { + _id: (typeof BuiltinPermissionID)[key] + name: string + permissions: Permission[] + } +} From 3446b1c67806c4e5fa10d9247b317ce770632d24 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 17:02:30 +0000 Subject: [PATCH 08/16] Plugin API typing. --- .../server/src/api/controllers/plugin/file.ts | 6 +++- .../src/api/controllers/plugin/index.ts | 33 ++++++++++++------- packages/server/src/sdk/plugins/plugins.ts | 2 +- packages/types/src/api/web/plugins.ts | 14 +++++++- packages/types/src/documents/global/plugin.ts | 4 +-- 5 files changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/server/src/api/controllers/plugin/file.ts b/packages/server/src/api/controllers/plugin/file.ts index 3b21f08387..0f40998617 100644 --- a/packages/server/src/api/controllers/plugin/file.ts +++ b/packages/server/src/api/controllers/plugin/file.ts @@ -3,8 +3,12 @@ import { getPluginMetadata, extractTarball, } from "../../../utilities/fileSystem" +import { FileType } from "@budibase/types" -export async function fileUpload(file: { name: string; path: string }) { +export async function fileUpload(file: FileType) { + if (!file.name || !file.path) { + throw new Error("File is not valid - cannot upload.") + } if (!file.name.endsWith(".tar.gz")) { throw new Error("Plugin must be compressed into a gzipped tarball.") } diff --git a/packages/server/src/api/controllers/plugin/index.ts b/packages/server/src/api/controllers/plugin/index.ts index e1c51f0219..2a4d69b671 100644 --- a/packages/server/src/api/controllers/plugin/index.ts +++ b/packages/server/src/api/controllers/plugin/index.ts @@ -2,26 +2,37 @@ import { npmUpload, urlUpload, githubUpload } from "./uploaders" import { plugins as pluginCore } from "@budibase/backend-core" import { PluginType, - FileType, PluginSource, - Ctx, CreatePluginRequest, CreatePluginResponse, + UserCtx, + UploadPluginRequest, + Plugin, + UploadPluginResponse, + FetchPluginResponse, + DeletePluginResponse, } from "@budibase/types" import env from "../../../environment" import { clientAppSocket } from "../../../websockets" import sdk from "../../../sdk" import { sdk as pro } from "@budibase/pro" -export async function upload(ctx: any) { - const plugins: FileType[] = - ctx.request.files.file.length > 1 - ? Array.from(ctx.request.files.file) - : [ctx.request.files.file] +export async function upload( + ctx: UserCtx +) { + const files = ctx.request.files + const plugins = + files && Array.isArray(files.file) && files.file.length > 1 + ? Array.from(files.file) + : [files?.file] + try { - let docs = [] + let docs: Plugin[] = [] // can do single or multiple plugins for (let plugin of plugins) { + if (!plugin || Array.isArray(plugin)) { + continue + } const doc = await sdk.plugins.processUploaded(plugin, PluginSource.FILE) docs.push(doc) } @@ -37,7 +48,7 @@ export async function upload(ctx: any) { } export async function create( - ctx: Ctx + ctx: UserCtx ) { const { source, url, headers, githubToken } = ctx.request.body @@ -91,11 +102,11 @@ export async function create( } } -export async function fetch(ctx: any) { +export async function fetch(ctx: UserCtx) { ctx.body = await sdk.plugins.fetch() } -export async function destroy(ctx: any) { +export async function destroy(ctx: UserCtx) { const { pluginId } = ctx.params try { diff --git a/packages/server/src/sdk/plugins/plugins.ts b/packages/server/src/sdk/plugins/plugins.ts index f6c26ca339..650065b40c 100644 --- a/packages/server/src/sdk/plugins/plugins.ts +++ b/packages/server/src/sdk/plugins/plugins.ts @@ -10,7 +10,7 @@ import env from "../../environment" import { clientAppSocket } from "../../websockets" import { sdk as pro } from "@budibase/pro" -export async function fetch(type?: PluginType) { +export async function fetch(type?: PluginType): Promise { const db = tenancy.getGlobalDB() const response = await db.allDocs( dbCore.getPluginParams(null, { diff --git a/packages/types/src/api/web/plugins.ts b/packages/types/src/api/web/plugins.ts index 458ad3f6ce..4dd47550e5 100644 --- a/packages/types/src/api/web/plugins.ts +++ b/packages/types/src/api/web/plugins.ts @@ -1,4 +1,10 @@ -import { PluginSource } from "../../documents" +import { PluginSource, Plugin } from "../../documents" + +export interface UploadPluginRequest {} +export interface UploadPluginResponse { + message: string + plugins: Plugin[] +} export interface CreatePluginRequest { source: PluginSource @@ -10,3 +16,9 @@ export interface CreatePluginRequest { export interface CreatePluginResponse { plugin: any } + +export type FetchPluginResponse = Plugin[] + +export interface DeletePluginResponse { + message: string +} diff --git a/packages/types/src/documents/global/plugin.ts b/packages/types/src/documents/global/plugin.ts index dd96bc20db..5a7c701ae5 100644 --- a/packages/types/src/documents/global/plugin.ts +++ b/packages/types/src/documents/global/plugin.ts @@ -13,8 +13,8 @@ export enum PluginSource { FILE = "File Upload", } export interface FileType { - path: string - name: string + path: string | null + name: string | null } export interface Plugin extends Document { From 23d9808cb61cf045e9347863df1ebaf4bfb7bd71 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 17:29:41 +0000 Subject: [PATCH 09/16] Query API typing. --- .../server/src/api/controllers/query/index.ts | 79 ++++++++++++------- packages/server/src/api/routes/query.ts | 2 +- .../src/automations/steps/executeQuery.ts | 3 +- .../server/src/tests/utilities/api/query.ts | 6 +- packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/query.ts | 47 +++++++++++ packages/types/src/api/web/index.ts | 1 - packages/types/src/api/web/query.ts | 20 ----- 8 files changed, 104 insertions(+), 55 deletions(-) create mode 100644 packages/types/src/api/web/app/query.ts delete mode 100644 packages/types/src/api/web/query.ts diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts index 15c60bcf47..d73bb8c024 100644 --- a/packages/server/src/api/controllers/query/index.ts +++ b/packages/server/src/api/controllers/query/index.ts @@ -4,26 +4,38 @@ import { save as saveDatasource } from "../datasource" import { RestImporter } from "./import" import { invalidateCachedVariable } from "../../../threads/utils" import env from "../../../environment" -import { events, context, utils, constants } from "@budibase/backend-core" +import { constants, context, events, utils } from "@budibase/backend-core" import sdk from "../../../sdk" import { QueryEvent, QueryEventParameters } from "../../../threads/definitions" import { ConfigType, - Query, - UserCtx, - SessionCookie, - JsonFieldSubType, - QueryResponse, - QuerySchema, - FieldType, + CreateDatasourceRequest, + Datasource, ExecuteQueryRequest, - ExecuteQueryResponse, + ExecuteV2QueryResponse, + ExecuteV1QueryResponse, + FetchQueriesResponse, + FieldType, + FindQueryResponse, + ImportRestQueryRequest, + ImportRestQueryResponse, + JsonFieldSubType, PreviewQueryRequest, PreviewQueryResponse, + Query, + QueryResponse, + QuerySchema, + SaveQueryRequest, + SaveQueryResponse, + SessionCookie, + SourceName, + UserCtx, + DeleteQueryResponse, } from "@budibase/types" -import { ValidQueryNameRegex, utils as JsonUtils } from "@budibase/shared-core" +import { utils as JsonUtils, ValidQueryNameRegex } from "@budibase/shared-core" import { findHBSBlocks } from "@budibase/string-templates" import { ObjectId } from "mongodb" +import { merge } from "lodash" const Runner = new Thread(ThreadType.QUERY, { timeoutMs: env.QUERY_THREAD_TIMEOUT, @@ -43,11 +55,13 @@ function validateQueryInputs(parameters: QueryEventParameters) { } } -export async function fetch(ctx: UserCtx) { +export async function fetch(ctx: UserCtx) { ctx.body = await sdk.queries.fetch() } -const _import = async (ctx: UserCtx) => { +const _import = async ( + ctx: UserCtx +) => { const body = ctx.request.body const data = body.data @@ -58,9 +72,9 @@ const _import = async (ctx: UserCtx) => { if (!body.datasourceId) { // construct new datasource const info: any = await importer.getInfo() - let datasource = { + let datasource: Datasource = { type: "datasource", - source: "REST", + source: SourceName.REST, config: { url: info.url, defaultHeaders: [], @@ -69,8 +83,14 @@ const _import = async (ctx: UserCtx) => { name: info.name, } // save the datasource - const datasourceCtx = { ...ctx } - datasourceCtx.request.body.datasource = datasource + const datasourceCtx: UserCtx = merge(ctx, { + request: { + body: { + datasource, + tablesFilter: [], + }, + }, + }) await saveDatasource(datasourceCtx) datasourceId = datasourceCtx.body.datasource._id } else { @@ -88,7 +108,7 @@ const _import = async (ctx: UserCtx) => { } export { _import as import } -export async function save(ctx: UserCtx) { +export async function save(ctx: UserCtx) { const db = context.getAppDB() const query: Query = ctx.request.body @@ -119,10 +139,9 @@ export async function save(ctx: UserCtx) { query._rev = response.rev ctx.body = query - ctx.message = `Query ${query.name} saved successfully.` } -export async function find(ctx: UserCtx) { +export async function find(ctx: UserCtx) { const queryId = ctx.params.queryId ctx.body = await sdk.queries.find(queryId) } @@ -335,7 +354,7 @@ export async function preview( async function execute( ctx: UserCtx< ExecuteQueryRequest, - ExecuteQueryResponse | Record[] + ExecuteV2QueryResponse | ExecuteV1QueryResponse >, opts: any = { rowsOnly: false, isAutomation: false } ) { @@ -390,19 +409,21 @@ async function execute( } export async function executeV1( - ctx: UserCtx[]> + ctx: UserCtx ) { return execute(ctx, { rowsOnly: true, isAutomation: false }) } export async function executeV2( - ctx: UserCtx< - ExecuteQueryRequest, - ExecuteQueryResponse | Record[] - >, - { isAutomation }: { isAutomation?: boolean } = {} + ctx: UserCtx ) { - return execute(ctx, { rowsOnly: false, isAutomation }) + return execute(ctx, { rowsOnly: false }) +} + +export async function executeV2AsAutomation( + ctx: UserCtx +) { + return execute(ctx, { rowsOnly: false, isAutomation: true }) } const removeDynamicVariables = async (queryId: string) => { @@ -426,14 +447,14 @@ const removeDynamicVariables = async (queryId: string) => { } } -export async function destroy(ctx: UserCtx) { +export async function destroy(ctx: UserCtx) { const db = context.getAppDB() const queryId = ctx.params.queryId as string await removeDynamicVariables(queryId) const query = await db.get(queryId) const datasource = await sdk.datasources.get(query.datasourceId) await db.remove(ctx.params.queryId, ctx.params.revId) - ctx.message = `Query deleted.` + ctx.body = { message: `Query deleted.` } ctx.status = 200 await events.query.deleted(datasource, query) } diff --git a/packages/server/src/api/routes/query.ts b/packages/server/src/api/routes/query.ts index eb857d0637..89d3651227 100644 --- a/packages/server/src/api/routes/query.ts +++ b/packages/server/src/api/routes/query.ts @@ -56,7 +56,7 @@ router "/api/v2/queries/:queryId", paramResource("queryId"), authorized(PermissionType.QUERY, PermissionLevel.WRITE), - queryController.executeV2 as any + queryController.executeV2 ) export default router diff --git a/packages/server/src/automations/steps/executeQuery.ts b/packages/server/src/automations/steps/executeQuery.ts index a42182df37..095c91a44d 100644 --- a/packages/server/src/automations/steps/executeQuery.ts +++ b/packages/server/src/automations/steps/executeQuery.ts @@ -12,6 +12,7 @@ import { ExecuteQueryStepInputs, ExecuteQueryStepOutputs, } from "@budibase/types" +import { executeV2AsAutomation } from "../../api/controllers/query" export const definition: AutomationStepDefinition = { name: "External Data Connector", @@ -94,7 +95,7 @@ export async function run({ }) try { - await queryController.executeV2(ctx, { isAutomation: true }) + await queryController.executeV2AsAutomation(ctx) const { data, ...rest } = ctx.body return { diff --git a/packages/server/src/tests/utilities/api/query.ts b/packages/server/src/tests/utilities/api/query.ts index 2d5f7970cd..fc81fc4561 100644 --- a/packages/server/src/tests/utilities/api/query.ts +++ b/packages/server/src/tests/utilities/api/query.ts @@ -1,7 +1,7 @@ import { Query, ExecuteQueryRequest, - ExecuteQueryResponse, + ExecuteV2QueryResponse, PreviewQueryRequest, PreviewQueryResponse, } from "@budibase/types" @@ -17,8 +17,8 @@ export class QueryAPI extends TestAPI { queryId: string, body?: ExecuteQueryRequest, expectations?: Expectations - ): Promise => { - return await this._post( + ): Promise => { + return await this._post( `/api/v2/queries/${queryId}`, { body, diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 40c8ebf9ca..82770543df 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -12,3 +12,4 @@ export * from "./automation" export * from "./component" export * from "./integration" export * from "./metadata" +export * from "./query" diff --git a/packages/types/src/api/web/app/query.ts b/packages/types/src/api/web/app/query.ts new file mode 100644 index 0000000000..302f0d03e5 --- /dev/null +++ b/packages/types/src/api/web/app/query.ts @@ -0,0 +1,47 @@ +import { + Datasource, + Query, + QueryPreview, + QuerySchema, +} from "../../../documents" + +export type FetchQueriesResponse = Query[] + +export interface SaveQueryRequest extends Query {} +export interface SaveQueryResponse extends Query {} + +export interface ImportRestQueryRequest { + datasourceId: string + data: string + datasource: Datasource +} +export interface ImportRestQueryResponse { + errorQueries: Query[] + queries: Query[] + datasourceId: string +} + +export interface FindQueryResponse extends Query {} + +export interface PreviewQueryRequest extends QueryPreview {} + +export interface PreviewQueryResponse { + rows: any[] + nestedSchemaFields: { [key: string]: { [key: string]: string | QuerySchema } } + schema: { [key: string]: string | QuerySchema } + info: any + extra: any +} + +export interface ExecuteQueryRequest { + parameters?: Record + pagination?: any +} +export type ExecuteV1QueryResponse = Record[] +export interface ExecuteV2QueryResponse { + data: Record[] +} + +export interface DeleteQueryResponse { + message: string +} diff --git a/packages/types/src/api/web/index.ts b/packages/types/src/api/web/index.ts index 4021eafee8..dd2ee3beae 100644 --- a/packages/types/src/api/web/index.ts +++ b/packages/types/src/api/web/index.ts @@ -13,7 +13,6 @@ export * from "./searchFilter" export * from "./cookies" export * from "./automation" export * from "./layout" -export * from "./query" export * from "./role" export * from "./plugins" export * from "./apikeys" diff --git a/packages/types/src/api/web/query.ts b/packages/types/src/api/web/query.ts deleted file mode 100644 index 66f0248647..0000000000 --- a/packages/types/src/api/web/query.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { QueryPreview, QuerySchema } from "../../documents" - -export interface PreviewQueryRequest extends QueryPreview {} - -export interface PreviewQueryResponse { - rows: any[] - nestedSchemaFields: { [key: string]: { [key: string]: string | QuerySchema } } - schema: { [key: string]: string | QuerySchema } - info: any - extra: any -} - -export interface ExecuteQueryRequest { - parameters?: Record - pagination?: any -} - -export interface ExecuteQueryResponse { - data: Record[] -} From efcdd360e3d02b3992ae796fa8d16ecab73d4426 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 17:42:31 +0000 Subject: [PATCH 10/16] Routing API typing. --- packages/server/src/api/controllers/role.ts | 4 ++-- packages/server/src/api/controllers/routing.ts | 18 +++++++++++++----- packages/server/src/utilities/routing/index.ts | 13 ++++--------- packages/types/src/api/web/app/index.ts | 1 + packages/types/src/api/web/app/screen.ts | 8 ++++++++ packages/types/src/api/web/role.ts | 2 +- packages/types/src/documents/app/screen.ts | 17 +++++++++++++++++ 7 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 packages/types/src/api/web/app/screen.ts diff --git a/packages/server/src/api/controllers/role.ts b/packages/server/src/api/controllers/role.ts index 7f5ac6b92f..30ca70b505 100644 --- a/packages/server/src/api/controllers/role.ts +++ b/packages/server/src/api/controllers/role.ts @@ -9,7 +9,7 @@ import { getUserMetadataParams, InternalTables } from "../../db/utils" import { AccessibleRolesResponse, Database, - DestroyRoleResponse, + DeleteRoleResponse, FetchRolesResponse, FindRoleResponse, Role, @@ -199,7 +199,7 @@ export async function save(ctx: UserCtx) { builderSocket?.emitRoleUpdate(ctx, role) } -export async function destroy(ctx: UserCtx) { +export async function destroy(ctx: UserCtx) { const db = context.getAppDB() let roleId = ctx.params.roleId as string if (roles.isBuiltin(roleId)) { diff --git a/packages/server/src/api/controllers/routing.ts b/packages/server/src/api/controllers/routing.ts index 040cda4dd0..fc9e58def5 100644 --- a/packages/server/src/api/controllers/routing.ts +++ b/packages/server/src/api/controllers/routing.ts @@ -1,11 +1,17 @@ import { getRoutingInfo } from "../../utilities/routing" import { roles } from "@budibase/backend-core" -import { UserCtx } from "@budibase/types" +import { + FetchClientScreenRoutingResponse, + FetchScreenRoutingResponse, + ScreenRoutingJson, + UserCtx, +} from "@budibase/types" const URL_SEPARATOR = "/" class Routing { - json: any + json: ScreenRoutingJson + constructor() { this.json = {} } @@ -43,7 +49,7 @@ class Routing { * @returns The routing structure, this is the full structure designed for use in the builder, * if the client routing is required then the updateRoutingStructureForUserRole should be used. */ -async function getRoutingStructure() { +async function getRoutingStructure(): Promise<{ routes: ScreenRoutingJson }> { const screenRoutes = await getRoutingInfo() const routing = new Routing() @@ -56,11 +62,13 @@ async function getRoutingStructure() { return { routes: routing.json } } -export async function fetch(ctx: UserCtx) { +export async function fetch(ctx: UserCtx) { ctx.body = await getRoutingStructure() } -export async function clientFetch(ctx: UserCtx) { +export async function clientFetch( + ctx: UserCtx +) { const routing = await getRoutingStructure() let roleId = ctx.user?.role?._id const roleIds = roleId ? await roles.getUserRoleIdHierarchy(roleId) : [] diff --git a/packages/server/src/utilities/routing/index.ts b/packages/server/src/utilities/routing/index.ts index 82d45743ce..f0086daa72 100644 --- a/packages/server/src/utilities/routing/index.ts +++ b/packages/server/src/utilities/routing/index.ts @@ -1,24 +1,19 @@ import { createRoutingView } from "../../db/views/staticViews" import { ViewName, getQueryIndex, UNICODE_MAX } from "../../db/utils" import { context } from "@budibase/backend-core" -import { ScreenRouting, Document } from "@budibase/types" +import { ScreenRoutesViewOutput } from "@budibase/types" -interface ScreenRoutesView extends Document { - id: string - routing: ScreenRouting -} - -export async function getRoutingInfo(): Promise { +export async function getRoutingInfo(): Promise { const db = context.getAppDB() try { - const allRouting = await db.query( + const allRouting = await db.query( getQueryIndex(ViewName.ROUTING), { startkey: "", endkey: UNICODE_MAX, } ) - return allRouting.rows.map(row => row.value as ScreenRoutesView) + return allRouting.rows.map(row => row.value) } catch (err: any) { // check if the view doesn't exist, it should for all new instances /* istanbul ignore next */ diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 82770543df..15c9c8929d 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -13,3 +13,4 @@ export * from "./component" export * from "./integration" export * from "./metadata" export * from "./query" +export * from "./screen" diff --git a/packages/types/src/api/web/app/screen.ts b/packages/types/src/api/web/app/screen.ts new file mode 100644 index 0000000000..fc9eb63975 --- /dev/null +++ b/packages/types/src/api/web/app/screen.ts @@ -0,0 +1,8 @@ +import { ScreenRoutingJson } from "../../../documents" + +export interface FetchScreenRoutingResponse { + routes: ScreenRoutingJson +} + +export interface FetchClientScreenRoutingResponse + extends FetchScreenRoutingResponse {} diff --git a/packages/types/src/api/web/role.ts b/packages/types/src/api/web/role.ts index f1c573c849..eeedaea163 100644 --- a/packages/types/src/api/web/role.ts +++ b/packages/types/src/api/web/role.ts @@ -18,7 +18,7 @@ export interface FindRoleResponse extends Role {} export type FetchRolesResponse = Role[] -export interface DestroyRoleResponse { +export interface DeleteRoleResponse { message: string } diff --git a/packages/types/src/documents/app/screen.ts b/packages/types/src/documents/app/screen.ts index 4977c79b0b..b2cedf31a9 100644 --- a/packages/types/src/documents/app/screen.ts +++ b/packages/types/src/documents/app/screen.ts @@ -24,3 +24,20 @@ export interface Screen extends Document { name?: string pluginAdded?: boolean } + +export interface ScreenRoutesViewOutput extends Document { + id: string + routing: ScreenRouting +} + +export type ScreenRoutingJson = Record< + string, + { + subpaths: Record< + string, + { + screens: Record + } + > + } +> From efb99c6446711a1fad35ce8194902047e4fc1bca Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 17:50:51 +0000 Subject: [PATCH 11/16] Row API typing. --- .../server/src/api/controllers/row/index.ts | 28 +++++++++++++------ .../server/src/tests/utilities/api/row.ts | 6 ++-- packages/types/src/api/web/app/index.ts | 1 - packages/types/src/api/web/app/row.ts | 18 ------------ packages/types/src/api/web/app/rows/index.ts | 28 ++++++++++++++++++- 5 files changed, 50 insertions(+), 31 deletions(-) delete mode 100644 packages/types/src/api/web/app/row.ts diff --git a/packages/server/src/api/controllers/row/index.ts b/packages/server/src/api/controllers/row/index.ts index cc5491e54f..0463c0a565 100644 --- a/packages/server/src/api/controllers/row/index.ts +++ b/packages/server/src/api/controllers/row/index.ts @@ -11,11 +11,14 @@ import { DeleteRow, DeleteRowRequest, DeleteRows, + DownloadAttachmentResponse, EventType, ExportRowsRequest, ExportRowsResponse, + FetchEnrichedRowResponse, + FetchRowsResponse, FieldType, - GetRowResponse, + FindRowResponse, isRelationshipField, PatchRowRequest, PatchRowResponse, @@ -23,12 +26,15 @@ import { Row, RowAttachment, RowSearchParams, + SaveRowRequest, + SaveRowResponse, SearchFilters, SearchRowRequest, SearchRowResponse, Table, UserCtx, - ValidateResponse, + ValidateRowRequest, + ValidateRowResponse, } from "@budibase/types" import * as utils from "./utils" import { gridSocket } from "../../../websockets" @@ -83,7 +89,7 @@ export async function patch( } } -export const save = async (ctx: UserCtx) => { +export const save = async (ctx: UserCtx) => { const { tableId, viewId } = utils.getSourceId(ctx) const sourceId = viewId || tableId @@ -131,12 +137,12 @@ export async function fetchLegacyView(ctx: any) { }) } -export async function fetch(ctx: any) { +export async function fetch(ctx: UserCtx) { const { tableId } = utils.getSourceId(ctx) ctx.body = await sdk.rows.fetch(tableId) } -export async function find(ctx: UserCtx) { +export async function find(ctx: UserCtx) { const { tableId, viewId } = utils.getSourceId(ctx) const sourceId = viewId || tableId const rowId = ctx.params.rowId @@ -314,7 +320,9 @@ function replaceTableNamesInFilters( }) } -export async function validate(ctx: Ctx) { +export async function validate( + ctx: Ctx +) { const source = await utils.getSource(ctx) const table = await utils.getTableFromSource(source) // external tables are hard to validate currently @@ -328,7 +336,9 @@ export async function validate(ctx: Ctx) { } } -export async function fetchEnrichedRow(ctx: UserCtx) { +export async function fetchEnrichedRow( + ctx: UserCtx +) { const { tableId } = utils.getSourceId(ctx) ctx.body = await pickApi(tableId).fetchEnrichedRow(ctx) } @@ -366,7 +376,9 @@ export const exportRows = async ( ctx.body = apiFileReturn(content) } -export async function downloadAttachment(ctx: UserCtx) { +export async function downloadAttachment( + ctx: UserCtx +) { const { columnName } = ctx.params const { tableId } = utils.getSourceId(ctx) diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index 52317e142a..f68b41aca9 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -2,7 +2,7 @@ import { PatchRowRequest, SaveRowRequest, Row, - ValidateResponse, + ValidateRowResponse, ExportRowsRequest, BulkImportRequest, BulkImportResponse, @@ -51,8 +51,8 @@ export class RowAPI extends TestAPI { sourceId: string, row: SaveRowRequest, expectations?: Expectations - ): Promise => { - return await this._post( + ): Promise => { + return await this._post( `/api/${sourceId}/rows/validate`, { body: row, diff --git a/packages/types/src/api/web/app/index.ts b/packages/types/src/api/web/app/index.ts index 15c9c8929d..d130d19a42 100644 --- a/packages/types/src/api/web/app/index.ts +++ b/packages/types/src/api/web/app/index.ts @@ -1,6 +1,5 @@ export * from "./backup" export * from "./datasource" -export * from "./row" export * from "./view" export * from "./rows" export * from "./table" diff --git a/packages/types/src/api/web/app/row.ts b/packages/types/src/api/web/app/row.ts deleted file mode 100644 index 4ab4090461..0000000000 --- a/packages/types/src/api/web/app/row.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Row } from "../../../documents/app/row" - -export interface GetRowResponse extends Row {} - -export interface DeleteRows { - rows: (Row | string)[] -} - -export interface DeleteRow { - _id: string -} - -export type DeleteRowRequest = DeleteRows | DeleteRow - -export interface ValidateResponse { - valid: boolean - errors: Record -} diff --git a/packages/types/src/api/web/app/rows/index.ts b/packages/types/src/api/web/app/rows/index.ts index 2642a8b04e..7994f9736b 100644 --- a/packages/types/src/api/web/app/rows/index.ts +++ b/packages/types/src/api/web/app/rows/index.ts @@ -1,11 +1,17 @@ import { SearchFilters } from "../../../../sdk" import { Row } from "../../../../documents" -import { SortOrder } from "../../../../api/web/pagination" +import { SortOrder } from "../../pagination" import { ReadStream } from "fs" +import stream from "node:stream" export * from "./search" +export interface FetchEnrichedRowResponse extends Row {} + +export type FetchRowsResponse = Row[] + export interface SaveRowRequest extends Row {} +export interface SaveRowResponse extends Row {} export interface PatchRowRequest extends Row { _id: string @@ -26,3 +32,23 @@ export interface ExportRowsRequest { } export type ExportRowsResponse = ReadStream + +export type DownloadAttachmentResponse = stream.PassThrough | stream.Readable + +export interface FindRowResponse extends Row {} + +export interface DeleteRows { + rows: (Row | string)[] +} + +export interface DeleteRow { + _id: string +} + +export type DeleteRowRequest = DeleteRows | DeleteRow + +export interface ValidateRowRequest extends Row {} +export interface ValidateRowResponse { + valid: boolean + errors: Record +} From 0b8f2c111d998ce10341742dafd517c93a05d5bd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 3 Dec 2024 18:07:21 +0000 Subject: [PATCH 12/16] Linting. --- packages/server/src/api/controllers/layout.ts | 1 - packages/server/src/automations/steps/executeQuery.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/server/src/api/controllers/layout.ts b/packages/server/src/api/controllers/layout.ts index 447b437a23..a0eee000aa 100644 --- a/packages/server/src/api/controllers/layout.ts +++ b/packages/server/src/api/controllers/layout.ts @@ -2,7 +2,6 @@ import { EMPTY_LAYOUT } from "../../constants/layouts" import { generateLayoutID, getScreenParams } from "../../db/utils" import { events, context } from "@budibase/backend-core" import { - BBContext, DeleteLayoutResponse, Layout, SaveLayoutRequest, diff --git a/packages/server/src/automations/steps/executeQuery.ts b/packages/server/src/automations/steps/executeQuery.ts index 095c91a44d..7af540093f 100644 --- a/packages/server/src/automations/steps/executeQuery.ts +++ b/packages/server/src/automations/steps/executeQuery.ts @@ -12,7 +12,6 @@ import { ExecuteQueryStepInputs, ExecuteQueryStepOutputs, } from "@budibase/types" -import { executeV2AsAutomation } from "../../api/controllers/query" export const definition: AutomationStepDefinition = { name: "External Data Connector", From da81381d62e08af19b1635afe1233dd6a3feb864 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 4 Dec 2024 13:48:30 +0000 Subject: [PATCH 13/16] PR review comments. --- packages/server/src/api/controllers/integration.ts | 2 +- packages/server/src/api/controllers/plugin/file.ts | 4 ++-- packages/server/src/sdk/plugins/plugins.ts | 4 ++-- packages/types/src/documents/global/plugin.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/server/src/api/controllers/integration.ts b/packages/server/src/api/controllers/integration.ts index 9f770ad049..f26f8dcc52 100644 --- a/packages/server/src/api/controllers/integration.ts +++ b/packages/server/src/api/controllers/integration.ts @@ -26,7 +26,7 @@ export async function find(ctx: UserCtx) { } const integration = await getDefinition(ctx.params.type) if (!integration) { - ctx.throw(400, "Integration not found") + ctx.throw(404, "Integration not found") } ctx.body = integration } diff --git a/packages/server/src/api/controllers/plugin/file.ts b/packages/server/src/api/controllers/plugin/file.ts index 0f40998617..4b1f098939 100644 --- a/packages/server/src/api/controllers/plugin/file.ts +++ b/packages/server/src/api/controllers/plugin/file.ts @@ -3,9 +3,9 @@ import { getPluginMetadata, extractTarball, } from "../../../utilities/fileSystem" -import { FileType } from "@budibase/types" +import { KoaFile } from "@budibase/types" -export async function fileUpload(file: FileType) { +export async function fileUpload(file: KoaFile) { if (!file.name || !file.path) { throw new Error("File is not valid - cannot upload.") } diff --git a/packages/server/src/sdk/plugins/plugins.ts b/packages/server/src/sdk/plugins/plugins.ts index 650065b40c..63f2f22cd9 100644 --- a/packages/server/src/sdk/plugins/plugins.ts +++ b/packages/server/src/sdk/plugins/plugins.ts @@ -1,4 +1,4 @@ -import { FileType, Plugin, PluginSource, PluginType } from "@budibase/types" +import { KoaFile, Plugin, PluginSource, PluginType } from "@budibase/types" import { db as dbCore, objectStore, @@ -26,7 +26,7 @@ export async function fetch(type?: PluginType): Promise { } } -export async function processUploaded(plugin: FileType, source?: PluginSource) { +export async function processUploaded(plugin: KoaFile, source?: PluginSource) { const { metadata, directory } = await fileUpload(plugin) pluginCore.validate(metadata?.schema) diff --git a/packages/types/src/documents/global/plugin.ts b/packages/types/src/documents/global/plugin.ts index 5a7c701ae5..eeddc04e58 100644 --- a/packages/types/src/documents/global/plugin.ts +++ b/packages/types/src/documents/global/plugin.ts @@ -12,7 +12,7 @@ export enum PluginSource { URL = "URL", FILE = "File Upload", } -export interface FileType { +export interface KoaFile { path: string | null name: string | null } From bc8cd6723d0757defa0982d67434e0e7c5f8916e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 4 Dec 2024 15:54:07 +0000 Subject: [PATCH 14/16] Add automation survey popup --- .../[application]/automation/_layout.svelte | 74 ++++++++++++++++++- 1 file changed, 70 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte index acc1b8e2d9..cc2746306c 100644 --- a/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte @@ -1,5 +1,5 @@