From 678033cc8b7276e5823536925cfebc577527f764 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Thu, 5 Oct 2023 17:39:40 +0100 Subject: [PATCH 001/427] License Key - Activate & Manage Tests There are two test files, license.activate.spec.ts and license.manage.spec.ts These test files each contain a test: - Creates, activates, and deletes an online license for a self hosted account - license.activate.spec.ts - Retrieves plans, creates checkout session, and updates license - license.manage.spec.ts Updated and created API files - StripeAPI - LicenseAPI - internal-api LicenseAPI - index & AccountInternalAPI also updated to reflect API file changes --- .../src/account-api/api/AccountInternalAPI.ts | 4 +- .../src/account-api/api/apis/LicenseAPI.ts | 137 +++++++++++++----- qa-core/src/account-api/api/apis/StripeAPI.ts | 64 ++++++++ qa-core/src/account-api/api/apis/index.ts | 1 + .../tests/licensing/license.activate.spec.ts | 74 ++++++++++ .../tests/licensing/license.manage.spec.ts | 57 ++++++++ .../src/internal-api/api/apis/LicenseAPI.ts | 40 +++-- 7 files changed, 329 insertions(+), 48 deletions(-) create mode 100644 qa-core/src/account-api/api/apis/StripeAPI.ts create mode 100644 qa-core/src/account-api/tests/licensing/license.activate.spec.ts create mode 100644 qa-core/src/account-api/tests/licensing/license.manage.spec.ts diff --git a/qa-core/src/account-api/api/AccountInternalAPI.ts b/qa-core/src/account-api/api/AccountInternalAPI.ts index 3813ad2c9e..ef2c39d5a4 100644 --- a/qa-core/src/account-api/api/AccountInternalAPI.ts +++ b/qa-core/src/account-api/api/AccountInternalAPI.ts @@ -1,5 +1,5 @@ import AccountInternalAPIClient from "./AccountInternalAPIClient" -import { AccountAPI, LicenseAPI, AuthAPI } from "./apis" +import { AccountAPI, LicenseAPI, AuthAPI, StripeAPI } from "./apis" import { State } from "../../types" export default class AccountInternalAPI { @@ -8,11 +8,13 @@ export default class AccountInternalAPI { auth: AuthAPI accounts: AccountAPI licenses: LicenseAPI + stripe: StripeAPI constructor(state: State) { this.client = new AccountInternalAPIClient(state) this.auth = new AuthAPI(this.client) this.accounts = new AccountAPI(this.client) this.licenses = new LicenseAPI(this.client) + this.stripe = new StripeAPI((this.client)) } } diff --git a/qa-core/src/account-api/api/apis/LicenseAPI.ts b/qa-core/src/account-api/api/apis/LicenseAPI.ts index 44579f867b..9f06ec7198 100644 --- a/qa-core/src/account-api/api/apis/LicenseAPI.ts +++ b/qa-core/src/account-api/api/apis/LicenseAPI.ts @@ -2,25 +2,23 @@ import AccountInternalAPIClient from "../AccountInternalAPIClient" import { Account, CreateOfflineLicenseRequest, + GetLicenseKeyResponse, GetOfflineLicenseResponse, UpdateLicenseRequest, } from "@budibase/types" import { Response } from "node-fetch" import BaseAPI from "./BaseAPI" import { APIRequestOpts } from "../../../types" - export default class LicenseAPI extends BaseAPI { client: AccountInternalAPIClient - constructor(client: AccountInternalAPIClient) { super() this.client = client } - async updateLicense( - accountId: string, - body: UpdateLicenseRequest, - opts: APIRequestOpts = { status: 200 } + accountId: string, + body: UpdateLicenseRequest, + opts: APIRequestOpts = { status: 200 } ): Promise<[Response, Account]> { return this.doRequest(() => { return this.client.put(`/api/accounts/${accountId}/license`, { @@ -29,44 +27,111 @@ export default class LicenseAPI extends BaseAPI { }) }, opts) } - // TODO: Better approach for setting tenant id header - async createOfflineLicense( - accountId: string, - tenantId: string, - body: CreateOfflineLicenseRequest, - opts: { status?: number } = {} + accountId: string, + tenantId: string, + body: CreateOfflineLicenseRequest, + opts: { status?: number } = {} ): Promise { const [response, json] = await this.client.post( - `/api/internal/accounts/${accountId}/license/offline`, - { - body, - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, + `/api/internal/accounts/${accountId}/license/offline`, + { + body, + internal: true, + headers: { + "x-budibase-tenant-id": tenantId, + }, + } + ) + expect(response.status).toBe(opts.status ? opts.status : 201) + return response + } + async getOfflineLicense( + accountId: string, + tenantId: string, + opts: { status?: number } = {} + ): Promise<[Response, GetOfflineLicenseResponse]> { + const [response, json] = await this.client.get( + `/api/internal/accounts/${accountId}/license/offline`, + { + internal: true, + headers: { + "x-budibase-tenant-id": tenantId, + }, + } + ) + expect(response.status).toBe(opts.status ? opts.status : 200) + return [response, json] + } + async getLicenseKey( + opts: { status?: number } = {} + ): Promise<[Response, GetLicenseKeyResponse]> { + const [response, json] = await this.client.get(`/api/license/key`) + expect(response.status).toBe(opts.status ? opts.status : 200) + return [response, json] + } + async activateLicense( + apiKey: string, + tenantId: string, + licenseKey: string, + opts: APIRequestOpts = { status: 200 } + ) { + return this.doRequest(() => { + return this.client.post(`/api/license/activate`, { + body: { + apiKey: apiKey, + tenantId: tenantId, + licenseKey: licenseKey, }, - } + }) + }, opts) + } + async regenerateLicenseKey(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.post(`/api/license/key/regenerate`, {}) + }, opts) + } + + async getPlans(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/plans`) + }, opts) + } + + async updatePlan(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.put(`/api/license/plan`) + }, opts) + } + + async refreshAccountLicense( + accountId: string, + opts: { status?: number } = {} + ): Promise { + const [response, json] = await this.client.post( + `/api/accounts/${accountId}/license/refresh`, + { + internal: true, + } ) expect(response.status).toBe(opts.status ? opts.status : 201) return response } - async getOfflineLicense( - accountId: string, - tenantId: string, - opts: { status?: number } = {} - ): Promise<[Response, GetOfflineLicenseResponse]> { - const [response, json] = await this.client.get( - `/api/internal/accounts/${accountId}/license/offline`, - { - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, - }, - } - ) - expect(response.status).toBe(opts.status ? opts.status : 200) - return [response, json] + async getLicenseUsage(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/license/usage`) + }, opts) } -} + + async licenseUsageTriggered( + opts: { status?: number } = {} + ): Promise { + const [response, json] = await this.client.post( + `/api/license/usage/triggered` + ) + expect(response.status).toBe(opts.status ? opts.status : 201) + return response + } +} \ No newline at end of file diff --git a/qa-core/src/account-api/api/apis/StripeAPI.ts b/qa-core/src/account-api/api/apis/StripeAPI.ts new file mode 100644 index 0000000000..ffa96b3c2b --- /dev/null +++ b/qa-core/src/account-api/api/apis/StripeAPI.ts @@ -0,0 +1,64 @@ +import AccountInternalAPIClient from "../AccountInternalAPIClient" +import BaseAPI from "./BaseAPI" +import { APIRequestOpts } from "../../../types" + +export default class StripeAPI extends BaseAPI { + client: AccountInternalAPIClient + + constructor(client: AccountInternalAPIClient) { + super() + this.client = client + } + + async createCheckoutSession( + priceId: string, + opts: APIRequestOpts = { status: 200 } + ) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/checkout-session`, { + body: { priceId }, + }) + }, opts) + } + + async checkoutSuccess(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/checkout-success`) + }, opts) + } + + async createPortalSession( + stripeCustomerId: string, + opts: APIRequestOpts = { status: 200 } + ) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/portal-session`, { + body: { stripeCustomerId }, + }) + }, opts) + } + + async linkStripeCustomer(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/link`) + }, opts) + } + + async getInvoices(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/invoices`) + }, opts) + } + + async getUpcomingInvoice(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/upcoming-invoice`) + }, opts) + } + + async getStripeCustomers(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/customers`) + }, opts) + } +} diff --git a/qa-core/src/account-api/api/apis/index.ts b/qa-core/src/account-api/api/apis/index.ts index 1137ac3e36..5b0cf55110 100644 --- a/qa-core/src/account-api/api/apis/index.ts +++ b/qa-core/src/account-api/api/apis/index.ts @@ -1,3 +1,4 @@ export { default as AuthAPI } from "./AuthAPI" export { default as AccountAPI } from "./AccountAPI" export { default as LicenseAPI } from "./LicenseAPI" +export { default as StripeAPI } from "./StripeAPI" diff --git a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts new file mode 100644 index 0000000000..709e2c33f0 --- /dev/null +++ b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts @@ -0,0 +1,74 @@ +import TestConfiguration from "../../config/TestConfiguration" +import * as fixures from "../../fixtures" +import { Feature, Hosting } from "@budibase/types" + +describe("license activation", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("creates, activates and deletes online license - self host", async () => { + // Remove existing license key + await config.internalApi.license.deleteLicenseKey() + + // Verify license key not found + await config.internalApi.license.getLicenseKey({ status: 404 }) + + // Create self host account + const createAccountRequest = fixures.accounts.generateAccount({ + hosting: Hosting.SELF, + }) + const [createAccountRes, account] = + await config.accountsApi.accounts.create(createAccountRequest, { autoVerify: true }) + + let licenseKey: string = " " + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + // Retrieve license key + const [res, body] = + await config.accountsApi.licenses.getLicenseKey() + licenseKey = body.licenseKey + }) + + const accountId = account.accountId! + + // Update license to have paid feature + const [res, acc] = await config.accountsApi.licenses.updateLicense( + accountId, + { + overrides: { + features: [Feature.APP_BACKUPS], + }, + } + ) + + // Activate license key + await config.internalApi.license.activateLicenseKey({licenseKey}) + + // Verify license updated with new feature + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + const [selfRes, body] = await config.api.accounts.self() + expect(body.license.features[0]).toBe("appBackups") + }) + + // Remove license key + await config.internalApi.license.deleteLicenseKey() + + // Verify license key not found + await config.internalApi.license.getLicenseKey({ status: 404 }) + + // Verify user downgraded to free license + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + const [selfRes, body] = await config.api.accounts.self() + expect(body.license.plan.type).toBe("free") + }) + }) +}) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts new file mode 100644 index 0000000000..967252a0f9 --- /dev/null +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -0,0 +1,57 @@ +import TestConfiguration from "../../config/TestConfiguration" +import * as fixtures from "../../fixtures" +import { Hosting, PlanType } from "@budibase/types" + +describe("license management", () => { + const config = new TestConfiguration() + + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("retrieves plans, creates checkout session, and updates license", async () => { + // Create cloud account + const createAccountRequest = fixtures.accounts.generateAccount({ + hosting: Hosting.CLOUD, + }) + + // Self response has free license + const [selfRes, selfBody] = await config.api.accounts.self() + expect(selfBody.license.plan.type).toBe(PlanType.FREE) + + // Retrieve plans + const [plansRes, planBody] = await config.api.licenses.getPlans() + + // Select priceId from premium plan + let premiumPriceId = null + for (const plan of planBody) { + if (plan.type === PlanType.PREMIUM) { + premiumPriceId = plan.prices[0].priceId + break + } + } + + // Create checkout session for price + const checkoutSessionRes = await config.api.stripe.createCheckoutSession( + premiumPriceId + ) + const checkoutSessionUrl = checkoutSessionRes[1].url + expect(checkoutSessionUrl).toContain("checkout.stripe.com") + + // TODO: Mimic checkout success + // Create stripe customer + // Create subscription for premium plan + // Assert license updated from free to premium + + // Create portal session + //await config.api.stripe.createPortalSession() + + // Update from free to business license + + // License updated + }) +}) diff --git a/qa-core/src/internal-api/api/apis/LicenseAPI.ts b/qa-core/src/internal-api/api/apis/LicenseAPI.ts index 4c9d14c55e..268f8781c3 100644 --- a/qa-core/src/internal-api/api/apis/LicenseAPI.ts +++ b/qa-core/src/internal-api/api/apis/LicenseAPI.ts @@ -1,45 +1,63 @@ import { Response } from "node-fetch" import { + ActivateLicenseKeyRequest, ActivateOfflineLicenseTokenRequest, + GetLicenseKeyResponse, GetOfflineIdentifierResponse, GetOfflineLicenseTokenResponse, } from "@budibase/types" import BudibaseInternalAPIClient from "../BudibaseInternalAPIClient" import BaseAPI from "./BaseAPI" +import { APIRequestOpts } from "../../../types" export default class LicenseAPI extends BaseAPI { constructor(client: BudibaseInternalAPIClient) { super(client) } - async getOfflineLicenseToken( - opts: { status?: number } = {} + opts: { status?: number } = {} ): Promise<[Response, GetOfflineLicenseTokenResponse]> { const [response, body] = await this.get( - `/global/license/offline`, - opts.status + `/global/license/offline`, + opts.status ) return [response, body] } - async deleteOfflineLicenseToken(): Promise<[Response]> { const [response] = await this.del(`/global/license/offline`, 204) return [response] } - async activateOfflineLicenseToken( - body: ActivateOfflineLicenseTokenRequest + body: ActivateOfflineLicenseTokenRequest ): Promise<[Response]> { const [response] = await this.post(`/global/license/offline`, body) return [response] } - async getOfflineIdentifier(): Promise< - [Response, GetOfflineIdentifierResponse] + [Response, GetOfflineIdentifierResponse] > { const [response, body] = await this.get( - `/global/license/offline/identifier` + `/global/license/offline/identifier` ) return [response, body] } -} + + async getLicenseKey( + opts: { status?: number } = {} + ): Promise<[Response, GetLicenseKeyResponse]> { + const [response, body] = await this.get(`/global/license/key`, opts.status) + return [response, body] + } + + async activateLicenseKey( + body: ActivateLicenseKeyRequest + ): Promise<[Response]> { + const [response] = await this.post(`/global/license/key`, body) + return [response] + } + + async deleteLicenseKey(): Promise<[Response]> { + const [response] = await this.del(`/global/license/key`, 204) + return [response] + } +} \ No newline at end of file From 5e16d0451936ed2b1c2c0c6d1928c146b45c65c1 Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Thu, 5 Oct 2023 17:43:25 +0100 Subject: [PATCH 002/427] lint --- .../src/account-api/api/AccountInternalAPI.ts | 2 +- .../src/account-api/api/apis/LicenseAPI.ts | 78 +++++------ qa-core/src/account-api/api/apis/StripeAPI.ts | 100 +++++++-------- .../tests/licensing/license.activate.spec.ts | 121 +++++++++--------- .../tests/licensing/license.manage.spec.ts | 82 ++++++------ .../src/internal-api/api/apis/LicenseAPI.ts | 18 +-- 6 files changed, 201 insertions(+), 200 deletions(-) diff --git a/qa-core/src/account-api/api/AccountInternalAPI.ts b/qa-core/src/account-api/api/AccountInternalAPI.ts index ef2c39d5a4..f89bf556f2 100644 --- a/qa-core/src/account-api/api/AccountInternalAPI.ts +++ b/qa-core/src/account-api/api/AccountInternalAPI.ts @@ -15,6 +15,6 @@ export default class AccountInternalAPI { this.auth = new AuthAPI(this.client) this.accounts = new AccountAPI(this.client) this.licenses = new LicenseAPI(this.client) - this.stripe = new StripeAPI((this.client)) + this.stripe = new StripeAPI(this.client) } } diff --git a/qa-core/src/account-api/api/apis/LicenseAPI.ts b/qa-core/src/account-api/api/apis/LicenseAPI.ts index 9f06ec7198..dba1a661d4 100644 --- a/qa-core/src/account-api/api/apis/LicenseAPI.ts +++ b/qa-core/src/account-api/api/apis/LicenseAPI.ts @@ -16,9 +16,9 @@ export default class LicenseAPI extends BaseAPI { this.client = client } async updateLicense( - accountId: string, - body: UpdateLicenseRequest, - opts: APIRequestOpts = { status: 200 } + accountId: string, + body: UpdateLicenseRequest, + opts: APIRequestOpts = { status: 200 } ): Promise<[Response, Account]> { return this.doRequest(() => { return this.client.put(`/api/accounts/${accountId}/license`, { @@ -29,53 +29,53 @@ export default class LicenseAPI extends BaseAPI { } // TODO: Better approach for setting tenant id header async createOfflineLicense( - accountId: string, - tenantId: string, - body: CreateOfflineLicenseRequest, - opts: { status?: number } = {} + accountId: string, + tenantId: string, + body: CreateOfflineLicenseRequest, + opts: { status?: number } = {} ): Promise { const [response, json] = await this.client.post( - `/api/internal/accounts/${accountId}/license/offline`, - { - body, - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, - }, - } + `/api/internal/accounts/${accountId}/license/offline`, + { + body, + internal: true, + headers: { + "x-budibase-tenant-id": tenantId, + }, + } ) expect(response.status).toBe(opts.status ? opts.status : 201) return response } async getOfflineLicense( - accountId: string, - tenantId: string, - opts: { status?: number } = {} + accountId: string, + tenantId: string, + opts: { status?: number } = {} ): Promise<[Response, GetOfflineLicenseResponse]> { const [response, json] = await this.client.get( - `/api/internal/accounts/${accountId}/license/offline`, - { - internal: true, - headers: { - "x-budibase-tenant-id": tenantId, - }, - } + `/api/internal/accounts/${accountId}/license/offline`, + { + internal: true, + headers: { + "x-budibase-tenant-id": tenantId, + }, + } ) expect(response.status).toBe(opts.status ? opts.status : 200) return [response, json] } async getLicenseKey( - opts: { status?: number } = {} + opts: { status?: number } = {} ): Promise<[Response, GetLicenseKeyResponse]> { const [response, json] = await this.client.get(`/api/license/key`) expect(response.status).toBe(opts.status ? opts.status : 200) return [response, json] } async activateLicense( - apiKey: string, - tenantId: string, - licenseKey: string, - opts: APIRequestOpts = { status: 200 } + apiKey: string, + tenantId: string, + licenseKey: string, + opts: APIRequestOpts = { status: 200 } ) { return this.doRequest(() => { return this.client.post(`/api/license/activate`, { @@ -106,14 +106,14 @@ export default class LicenseAPI extends BaseAPI { } async refreshAccountLicense( - accountId: string, - opts: { status?: number } = {} + accountId: string, + opts: { status?: number } = {} ): Promise { const [response, json] = await this.client.post( - `/api/accounts/${accountId}/license/refresh`, - { - internal: true, - } + `/api/accounts/${accountId}/license/refresh`, + { + internal: true, + } ) expect(response.status).toBe(opts.status ? opts.status : 201) return response @@ -126,12 +126,12 @@ export default class LicenseAPI extends BaseAPI { } async licenseUsageTriggered( - opts: { status?: number } = {} + opts: { status?: number } = {} ): Promise { const [response, json] = await this.client.post( - `/api/license/usage/triggered` + `/api/license/usage/triggered` ) expect(response.status).toBe(opts.status ? opts.status : 201) return response } -} \ No newline at end of file +} diff --git a/qa-core/src/account-api/api/apis/StripeAPI.ts b/qa-core/src/account-api/api/apis/StripeAPI.ts index ffa96b3c2b..c9c776e89b 100644 --- a/qa-core/src/account-api/api/apis/StripeAPI.ts +++ b/qa-core/src/account-api/api/apis/StripeAPI.ts @@ -3,62 +3,62 @@ import BaseAPI from "./BaseAPI" import { APIRequestOpts } from "../../../types" export default class StripeAPI extends BaseAPI { - client: AccountInternalAPIClient + client: AccountInternalAPIClient - constructor(client: AccountInternalAPIClient) { - super() - this.client = client - } + constructor(client: AccountInternalAPIClient) { + super() + this.client = client + } - async createCheckoutSession( - priceId: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/checkout-session`, { - body: { priceId }, - }) - }, opts) - } + async createCheckoutSession( + priceId: string, + opts: APIRequestOpts = { status: 200 } + ) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/checkout-session`, { + body: { priceId }, + }) + }, opts) + } - async checkoutSuccess(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/checkout-success`) - }, opts) - } + async checkoutSuccess(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/checkout-success`) + }, opts) + } - async createPortalSession( - stripeCustomerId: string, - opts: APIRequestOpts = { status: 200 } - ) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/portal-session`, { - body: { stripeCustomerId }, - }) - }, opts) - } + async createPortalSession( + stripeCustomerId: string, + opts: APIRequestOpts = { status: 200 } + ) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/portal-session`, { + body: { stripeCustomerId }, + }) + }, opts) + } - async linkStripeCustomer(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.post(`/api/stripe/link`) - }, opts) - } + async linkStripeCustomer(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.post(`/api/stripe/link`) + }, opts) + } - async getInvoices(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/invoices`) - }, opts) - } + async getInvoices(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/invoices`) + }, opts) + } - async getUpcomingInvoice(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/upcoming-invoice`) - }, opts) - } + async getUpcomingInvoice(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/upcoming-invoice`) + }, opts) + } - async getStripeCustomers(opts: APIRequestOpts = { status: 200 }) { - return this.doRequest(() => { - return this.client.get(`/api/stripe/customers`) - }, opts) - } + async getStripeCustomers(opts: APIRequestOpts = { status: 200 }) { + return this.doRequest(() => { + return this.client.get(`/api/stripe/customers`) + }, opts) + } } diff --git a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts index 709e2c33f0..a494ceb354 100644 --- a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts @@ -3,72 +3,73 @@ import * as fixures from "../../fixtures" import { Feature, Hosting } from "@budibase/types" describe("license activation", () => { - const config = new TestConfiguration() + const config = new TestConfiguration() - beforeAll(async () => { - await config.beforeAll() + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("creates, activates and deletes online license - self host", async () => { + // Remove existing license key + await config.internalApi.license.deleteLicenseKey() + + // Verify license key not found + await config.internalApi.license.getLicenseKey({ status: 404 }) + + // Create self host account + const createAccountRequest = fixures.accounts.generateAccount({ + hosting: Hosting.SELF, + }) + const [createAccountRes, account] = + await config.accountsApi.accounts.create(createAccountRequest, { + autoVerify: true, + }) + + let licenseKey: string = " " + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + // Retrieve license key + const [res, body] = await config.accountsApi.licenses.getLicenseKey() + licenseKey = body.licenseKey }) - afterAll(async () => { - await config.afterAll() + const accountId = account.accountId! + + // Update license to have paid feature + const [res, acc] = await config.accountsApi.licenses.updateLicense( + accountId, + { + overrides: { + features: [Feature.APP_BACKUPS], + }, + } + ) + + // Activate license key + await config.internalApi.license.activateLicenseKey({ licenseKey }) + + // Verify license updated with new feature + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + const [selfRes, body] = await config.api.accounts.self() + expect(body.license.features[0]).toBe("appBackups") }) - it("creates, activates and deletes online license - self host", async () => { - // Remove existing license key - await config.internalApi.license.deleteLicenseKey() + // Remove license key + await config.internalApi.license.deleteLicenseKey() - // Verify license key not found - await config.internalApi.license.getLicenseKey({ status: 404 }) + // Verify license key not found + await config.internalApi.license.getLicenseKey({ status: 404 }) - // Create self host account - const createAccountRequest = fixures.accounts.generateAccount({ - hosting: Hosting.SELF, - }) - const [createAccountRes, account] = - await config.accountsApi.accounts.create(createAccountRequest, { autoVerify: true }) - - let licenseKey: string = " " - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - // Retrieve license key - const [res, body] = - await config.accountsApi.licenses.getLicenseKey() - licenseKey = body.licenseKey - }) - - const accountId = account.accountId! - - // Update license to have paid feature - const [res, acc] = await config.accountsApi.licenses.updateLicense( - accountId, - { - overrides: { - features: [Feature.APP_BACKUPS], - }, - } - ) - - // Activate license key - await config.internalApi.license.activateLicenseKey({licenseKey}) - - // Verify license updated with new feature - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - const [selfRes, body] = await config.api.accounts.self() - expect(body.license.features[0]).toBe("appBackups") - }) - - // Remove license key - await config.internalApi.license.deleteLicenseKey() - - // Verify license key not found - await config.internalApi.license.getLicenseKey({ status: 404 }) - - // Verify user downgraded to free license - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - const [selfRes, body] = await config.api.accounts.self() - expect(body.license.plan.type).toBe("free") - }) + // Verify user downgraded to free license + await config.doInNewState(async () => { + await config.loginAsAccount(createAccountRequest) + const [selfRes, body] = await config.api.accounts.self() + expect(body.license.plan.type).toBe("free") }) + }) }) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts index 967252a0f9..3f87838ee4 100644 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -3,55 +3,55 @@ import * as fixtures from "../../fixtures" import { Hosting, PlanType } from "@budibase/types" describe("license management", () => { - const config = new TestConfiguration() + const config = new TestConfiguration() - beforeAll(async () => { - await config.beforeAll() + beforeAll(async () => { + await config.beforeAll() + }) + + afterAll(async () => { + await config.afterAll() + }) + + it("retrieves plans, creates checkout session, and updates license", async () => { + // Create cloud account + const createAccountRequest = fixtures.accounts.generateAccount({ + hosting: Hosting.CLOUD, }) - afterAll(async () => { - await config.afterAll() - }) + // Self response has free license + const [selfRes, selfBody] = await config.api.accounts.self() + expect(selfBody.license.plan.type).toBe(PlanType.FREE) - it("retrieves plans, creates checkout session, and updates license", async () => { - // Create cloud account - const createAccountRequest = fixtures.accounts.generateAccount({ - hosting: Hosting.CLOUD, - }) + // Retrieve plans + const [plansRes, planBody] = await config.api.licenses.getPlans() - // Self response has free license - const [selfRes, selfBody] = await config.api.accounts.self() - expect(selfBody.license.plan.type).toBe(PlanType.FREE) + // Select priceId from premium plan + let premiumPriceId = null + for (const plan of planBody) { + if (plan.type === PlanType.PREMIUM) { + premiumPriceId = plan.prices[0].priceId + break + } + } - // Retrieve plans - const [plansRes, planBody] = await config.api.licenses.getPlans() + // Create checkout session for price + const checkoutSessionRes = await config.api.stripe.createCheckoutSession( + premiumPriceId + ) + const checkoutSessionUrl = checkoutSessionRes[1].url + expect(checkoutSessionUrl).toContain("checkout.stripe.com") - // Select priceId from premium plan - let premiumPriceId = null - for (const plan of planBody) { - if (plan.type === PlanType.PREMIUM) { - premiumPriceId = plan.prices[0].priceId - break - } - } + // TODO: Mimic checkout success + // Create stripe customer + // Create subscription for premium plan + // Assert license updated from free to premium - // Create checkout session for price - const checkoutSessionRes = await config.api.stripe.createCheckoutSession( - premiumPriceId - ) - const checkoutSessionUrl = checkoutSessionRes[1].url - expect(checkoutSessionUrl).toContain("checkout.stripe.com") + // Create portal session + //await config.api.stripe.createPortalSession() - // TODO: Mimic checkout success - // Create stripe customer - // Create subscription for premium plan - // Assert license updated from free to premium + // Update from free to business license - // Create portal session - //await config.api.stripe.createPortalSession() - - // Update from free to business license - - // License updated - }) + // License updated + }) }) diff --git a/qa-core/src/internal-api/api/apis/LicenseAPI.ts b/qa-core/src/internal-api/api/apis/LicenseAPI.ts index 268f8781c3..ef322e069a 100644 --- a/qa-core/src/internal-api/api/apis/LicenseAPI.ts +++ b/qa-core/src/internal-api/api/apis/LicenseAPI.ts @@ -15,11 +15,11 @@ export default class LicenseAPI extends BaseAPI { super(client) } async getOfflineLicenseToken( - opts: { status?: number } = {} + opts: { status?: number } = {} ): Promise<[Response, GetOfflineLicenseTokenResponse]> { const [response, body] = await this.get( - `/global/license/offline`, - opts.status + `/global/license/offline`, + opts.status ) return [response, body] } @@ -28,29 +28,29 @@ export default class LicenseAPI extends BaseAPI { return [response] } async activateOfflineLicenseToken( - body: ActivateOfflineLicenseTokenRequest + body: ActivateOfflineLicenseTokenRequest ): Promise<[Response]> { const [response] = await this.post(`/global/license/offline`, body) return [response] } async getOfflineIdentifier(): Promise< - [Response, GetOfflineIdentifierResponse] + [Response, GetOfflineIdentifierResponse] > { const [response, body] = await this.get( - `/global/license/offline/identifier` + `/global/license/offline/identifier` ) return [response, body] } async getLicenseKey( - opts: { status?: number } = {} + opts: { status?: number } = {} ): Promise<[Response, GetLicenseKeyResponse]> { const [response, body] = await this.get(`/global/license/key`, opts.status) return [response, body] } async activateLicenseKey( - body: ActivateLicenseKeyRequest + body: ActivateLicenseKeyRequest ): Promise<[Response]> { const [response] = await this.post(`/global/license/key`, body) return [response] @@ -60,4 +60,4 @@ export default class LicenseAPI extends BaseAPI { const [response] = await this.del(`/global/license/key`, 204) return [response] } -} \ No newline at end of file +} From f3234f6bd63e0bf5307ffc752c9992bfc4813b0d Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Fri, 6 Oct 2023 16:22:56 +0100 Subject: [PATCH 003/427] Update license activate test I have removed the end of the test which was to 'Verify user downgraded to free license' - This is not needed I have also updated getLicenseKey - specifically how it handles the expected 200 response --- qa-core/src/account-api/api/apis/LicenseAPI.ts | 2 +- .../account-api/tests/licensing/license.activate.spec.ts | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/qa-core/src/account-api/api/apis/LicenseAPI.ts b/qa-core/src/account-api/api/apis/LicenseAPI.ts index dba1a661d4..b371f00f05 100644 --- a/qa-core/src/account-api/api/apis/LicenseAPI.ts +++ b/qa-core/src/account-api/api/apis/LicenseAPI.ts @@ -68,7 +68,7 @@ export default class LicenseAPI extends BaseAPI { opts: { status?: number } = {} ): Promise<[Response, GetLicenseKeyResponse]> { const [response, json] = await this.client.get(`/api/license/key`) - expect(response.status).toBe(opts.status ? opts.status : 200) + expect(response.status).toBe(opts.status || 200) return [response, json] } async activateLicense( diff --git a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts index a494ceb354..96c6eaea2a 100644 --- a/qa-core/src/account-api/tests/licensing/license.activate.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.activate.spec.ts @@ -64,12 +64,5 @@ describe("license activation", () => { // Verify license key not found await config.internalApi.license.getLicenseKey({ status: 404 }) - - // Verify user downgraded to free license - await config.doInNewState(async () => { - await config.loginAsAccount(createAccountRequest) - const [selfRes, body] = await config.api.accounts.self() - expect(body.license.plan.type).toBe("free") - }) }) }) From b4a8f22b2ee54914294eadb0259fde5f1006879a Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 11 Oct 2023 10:09:14 +0100 Subject: [PATCH 004/427] Initial commit --- .../design/settings/componentSettings.js | 2 + .../ButtonConfiguration.svelte | 104 ++++++++++ .../ButtonConfiguration/ButtonSetting.svelte | 61 ++++++ .../controls/FieldConfiguration.svelte | 91 --------- .../EditFieldPopover.svelte | 38 ++-- .../FieldConfiguration.svelte | 4 +- .../new/_components/componentStructure.json | 1 + packages/client/manifest.json | 183 ++++++++++++++++++ .../components/app/forms/ButtonGroup.svelte | 38 ++++ .../client/src/components/app/forms/index.js | 1 + 10 files changed, 412 insertions(+), 111 deletions(-) create mode 100644 packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte create mode 100644 packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte delete mode 100644 packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte create mode 100644 packages/client/src/components/app/forms/ButtonGroup.svelte diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index 4c49587372..232b4bef31 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -23,6 +23,7 @@ import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte" import GridColumnEditor from "./controls/ColumnEditor/GridColumnEditor.svelte" import BarButtonList from "./controls/BarButtonList.svelte" import FieldConfiguration from "./controls/FieldConfiguration/FieldConfiguration.svelte" +import ButtonConfiguration from "./controls/ButtonConfiguration/ButtonConfiguration.svelte" import RelationshipFilterEditor from "./controls/RelationshipFilterEditor.svelte" const componentMap = { @@ -48,6 +49,7 @@ const componentMap = { "filter/relationship": RelationshipFilterEditor, url: URLSelect, fieldConfiguration: FieldConfiguration, + buttonConfiguration: ButtonConfiguration, columns: ColumnEditor, "columns/basic": BasicColumnEditor, "columns/grid": GridColumnEditor, diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte new file mode 100644 index 0000000000..baa8ad733c --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -0,0 +1,104 @@ + + +
+ {#if buttonList?.length} + + {/if} +
+ + diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte new file mode 100644 index 0000000000..851cbe289c --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonSetting.svelte @@ -0,0 +1,61 @@ + + +
+
+ +
{item.text}
+
+
+ {console.log("REMOVE ME")}} + /> +
+
+ + diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte deleted file mode 100644 index 80f4829d71..0000000000 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration.svelte +++ /dev/null @@ -1,91 +0,0 @@ - - -Configure columns - - - Configure the columns in your {subject.toLowerCase()}. - - - - diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/EditFieldPopover.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/EditFieldPopover.svelte index 7d2eaae478..72e7784727 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/EditFieldPopover.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/EditFieldPopover.svelte @@ -61,6 +61,25 @@ dispatch("change", update) } + + const customPositionHandler = (anchorBounds, eleBounds, cfg) => { + let { left, top } = cfg + let percentageOffset = 30 + // left-outside + left = anchorBounds.left - eleBounds.width - 18 + + // shift up from the anchor, if space allows + let offsetPos = Math.floor(eleBounds.height / 100) * percentageOffset + let defaultTop = anchorBounds.top - offsetPos + + if (window.innerHeight - defaultTop < eleBounds.height) { + top = window.innerHeight - eleBounds.height - 5 + } else { + top = anchorBounds.top - offsetPos + } + + return { ...cfg, left, top } + } 0} maxHeight={600} - handlePostionUpdate={(anchorBounds, eleBounds, cfg) => { - let { left, top } = cfg - let percentageOffset = 30 - // left-outside - left = anchorBounds.left - eleBounds.width - 18 - - // shift up from the anchor, if space allows - let offsetPos = Math.floor(eleBounds.height / 100) * percentageOffset - let defaultTop = anchorBounds.top - offsetPos - - if (window.innerHeight - defaultTop < eleBounds.height) { - top = window.innerHeight - eleBounds.height - 5 - } else { - top = anchorBounds.top - offsetPos - } - - return { ...cfg, left, top } - }} + handlePostionUpdate={customPositionHandler} > diff --git a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte index 4169cb7d3d..42651a4d84 100644 --- a/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/FieldConfiguration/FieldConfiguration.svelte @@ -50,7 +50,7 @@ updateSanitsedFields(sanitisedValue) unconfigured = buildUnconfiguredOptions(schema, sanitisedFields) fieldList = [...sanitisedFields, ...unconfigured] - .map(buildSudoInstance) + .map(buildPseudoInstance) .filter(x => x != null) } @@ -104,7 +104,7 @@ }) } - const buildSudoInstance = instance => { + const buildPseudoInstance = instance => { if (instance._component) { return instance } diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json index 11a130490a..dd129be11e 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/new/_components/componentStructure.json @@ -36,6 +36,7 @@ "heading", "text", "button", + "buttongroup", "tag", "spectrumcard", "cardstat", diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 4e56ca758d..27e56d94db 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -258,6 +258,189 @@ "description": "Contains your app screens", "static": true }, + + "buttongroup": { + "name": "Button group", + "icon": "Button", + "hasChildren": false, + "settings": [ + { + "section": true, + "name": "Buttons", + "settings": [ + { + "type": "buttonConfiguration", + "key": "buttons", + "nested": true, + "defaultValue" : [{ + "component" : "button", + "props" : { + "type": "cta" + } + },{ + "component" : "button", + "props" : { + "type" : "primary" + } + }] + } + ] + }, + { + "section": true, + "name": "Layout", + "settings": [ + { + "type": "select", + "label": "Direction", + "key": "direction", + "showInBar": true, + "barStyle": "buttons", + "options": [ + { + "label": "Column", + "value": "column", + "barIcon": "ViewColumn", + "barTitle": "Column layout" + }, + { + "label": "Row", + "value": "row", + "barIcon": "ViewRow", + "barTitle": "Row layout" + } + ], + "defaultValue": "column" + }, + { + "type": "select", + "label": "Horiz. align", + "key": "hAlign", + "showInBar": true, + "barStyle": "buttons", + "options": [ + { + "label": "Left", + "value": "left", + "barIcon": "AlignLeft", + "barTitle": "Align left" + }, + { + "label": "Center", + "value": "center", + "barIcon": "AlignCenter", + "barTitle": "Align center" + }, + { + "label": "Right", + "value": "right", + "barIcon": "AlignRight", + "barTitle": "Align right" + }, + { + "label": "Stretch", + "value": "stretch", + "barIcon": "MoveLeftRight", + "barTitle": "Align stretched horizontally" + } + ], + "defaultValue": "left" + }, + { + "type": "select", + "label": "Vert. align", + "key": "vAlign", + "showInBar": true, + "barStyle": "buttons", + "options": [ + { + "label": "Top", + "value": "top", + "barIcon": "AlignTop", + "barTitle": "Align top" + }, + { + "label": "Middle", + "value": "middle", + "barIcon": "AlignMiddle", + "barTitle": "Align middle" + }, + { + "label": "Bottom", + "value": "bottom", + "barIcon": "AlignBottom", + "barTitle": "Align bottom" + }, + { + "label": "Stretch", + "value": "stretch", + "barIcon": "MoveUpDown", + "barTitle": "Align stretched vertically" + } + ], + "defaultValue": "top" + }, + { + "type": "select", + "label": "Size", + "key": "size", + "showInBar": true, + "barStyle": "buttons", + "options": [ + { + "label": "Shrink", + "value": "shrink", + "barIcon": "Minimize", + "barTitle": "Shrink container" + }, + { + "label": "Grow", + "value": "grow", + "barIcon": "Maximize", + "barTitle": "Grow container" + } + ], + "defaultValue": "shrink" + }, + { + "type": "select", + "label": "Gap", + "key": "gap", + "showInBar": true, + "barStyle": "picker", + "options": [ + { + "label": "None", + "value": "N" + }, + { + "label": "Small", + "value": "S" + }, + { + "label": "Medium", + "value": "M" + }, + { + "label": "Large", + "value": "L" + } + ], + "defaultValue": "M" + }, + { + "type": "boolean", + "label": "Wrap", + "key": "wrap", + "showInBar": true, + "barIcon": "ModernGridView", + "barTitle": "Wrap" + } + ] + } + ] + }, + "button": { "name": "Button", "description": "A basic html button that is ready for styling", diff --git a/packages/client/src/components/app/forms/ButtonGroup.svelte b/packages/client/src/components/app/forms/ButtonGroup.svelte new file mode 100644 index 0000000000..222e91a55f --- /dev/null +++ b/packages/client/src/components/app/forms/ButtonGroup.svelte @@ -0,0 +1,38 @@ + + + + + + {#each buttons as { text, type, quiet, disabled, onClick, size }} + + {/each} + + + diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 5804d3a79d..24d7f11c0c 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -16,3 +16,4 @@ export { default as jsonfield } from "./JSONField.svelte" export { default as s3upload } from "./S3Upload.svelte" export { default as codescanner } from "./CodeScannerField.svelte" export { default as bbreferencefield } from "./BBReferenceField.svelte" +export { default as buttongroup } from "./ButtonGroup.svelte" \ No newline at end of file From 789bb528f43f7cd28eca2e52a309eddcbb3c370b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Oct 2023 11:58:25 +0100 Subject: [PATCH 005/427] Add basic inline searching and fix create first row popup --- .../components/grid/cells/HeaderCell.svelte | 164 ++++++++++++++---- .../src/components/grid/layout/NewRow.svelte | 6 +- .../src/components/grid/stores/filter.js | 40 +++++ .../src/components/grid/stores/rows.js | 6 + 4 files changed, 185 insertions(+), 31 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 5ac70c93c8..1abddfe1ff 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -3,6 +3,7 @@ import GridCell from "./GridCell.svelte" import { Icon, Popover, Menu, MenuItem, clickOutside } from "@budibase/bbui" import { getColumnIcon } from "../lib/utils" + import { debounce } from "../../../utils/utils" export let column export let idx @@ -23,6 +24,8 @@ definition, datasource, schema, + focusedCellId, + filter, } = getContext("grid") const bannedDisplayColumnTypes = [ @@ -32,12 +35,15 @@ "boolean", "json", ] + const searchableTypes = ["string", "options", "number"] let anchor let open = false let editIsOpen = false let timeout let popover + let searchValue + let input $: sortedBy = column.name === $sort.column $: canMoveLeft = orderable && idx > 0 @@ -48,6 +54,9 @@ $: descendingLabel = ["number", "bigint"].includes(column.schema?.type) ? "high-low" : "Z-A" + $: searchable = searchableTypes.includes(column.schema.type) + $: searching = searchValue != null + $: debouncedUpdateFilter(searchValue) const editColumn = async () => { editIsOpen = true @@ -148,12 +157,46 @@ }) } + const startSearching = async () => { + $focusedCellId = null + searchValue = "" + await tick() + input?.focus() + } + + const onInputKeyDown = e => { + if (e.key === "Enter") { + updateFilter() + } else if (e.key === "Escape") { + input?.blur() + } + } + + const stopSearching = () => { + searchValue = null + updateFilter() + } + + const onBlurInput = () => { + if (searchValue === "") { + searchValue = null + } + updateFilter() + } + + const updateFilter = () => { + filter.actions.addInlineFilter(column, searchValue) + } + const debouncedUpdateFilter = debounce(updateFilter, 250) + onMount(() => subscribe("close-edit-column", cancelEdit))
- + {#if searching} + focusedCellId.set(null)} + on:keydown={onInputKeyDown} + /> + {/if} + +
+ +
+
+ +
+
{column.label}
- {#if sortedBy} -
- + + {#if searching} +
+ +
+ {:else} + {#if sortedBy} +
+ +
+ {/if} +
(open = true)}> +
{/if} -
(open = true)}> - -
@@ -289,6 +350,29 @@ background: var(--grid-background-alt); } + /* Icon colors */ + .header-cell :global(.spectrum-Icon) { + color: var(--spectrum-global-color-gray-600); + } + .header-cell :global(.spectrum-Icon.hoverable:hover) { + color: var(--spectrum-global-color-gray-800) !important; + cursor: pointer; + } + + /* Search icon */ + .search-icon { + display: none; + } + .header-cell.searchable:not(.open):hover .search-icon, + .header-cell.searchable.searching .search-icon { + display: block; + } + .header-cell.searchable:not(.open):hover .column-icon, + .header-cell.searchable.searching .column-icon { + display: none; + } + + /* Main center content */ .name { flex: 1 1 auto; width: 0; @@ -296,23 +380,45 @@ text-overflow: ellipsis; overflow: hidden; } + .header-cell.searching .name { + opacity: 0; + pointer-events: none; + } + input { + display: none; + font-family: var(--font-sans); + outline: none; + border: 1px solid transparent; + background: transparent; + color: var(--ink); + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: 0 30px; + border-radius: 2px; + } + input:focus { + border: 1px solid var(--accent-color); + } + input:not(:focus) { + background: var(--spectrum-global-color-gray-200); + } + .header-cell.searching input { + display: block; + } - .more { + /* Right icons */ + .more-icon { display: none; padding: 4px; margin: 0 -4px; } - .header-cell.open .more, - .header-cell:hover .more { + .header-cell.open .more-icon, + .header-cell:hover .more-icon { display: block; } - .more:hover { - cursor: pointer; - } - .more:hover :global(.spectrum-Icon) { - color: var(--spectrum-global-color-gray-800) !important; - } - .header-cell.open .sort-indicator, .header-cell:hover .sort-indicator { display: none; diff --git a/packages/frontend-core/src/components/grid/layout/NewRow.svelte b/packages/frontend-core/src/components/grid/layout/NewRow.svelte index aa9b6fa051..440e15ee0c 100644 --- a/packages/frontend-core/src/components/grid/layout/NewRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/NewRow.svelte @@ -27,8 +27,10 @@ rowVerticalInversionIndex, columnHorizontalInversionIndex, selectedRows, - loading, + loaded, + refreshing, config, + filter, } = getContext("grid") let visible = false @@ -153,7 +155,7 @@ {#if !visible && !selectedRowCount && $config.canAddRows} diff --git a/packages/frontend-core/src/components/grid/stores/filter.js b/packages/frontend-core/src/components/grid/stores/filter.js index a59c98ccdd..a2de0ca2d0 100644 --- a/packages/frontend-core/src/components/grid/stores/filter.js +++ b/packages/frontend-core/src/components/grid/stores/filter.js @@ -11,6 +11,46 @@ export const createStores = context => { } } +export const createActions = context => { + const { filter } = context + + const addInlineFilter = (column, value) => { + const filterId = `inline-${column}` + + const inlineFilter = { + field: column.name, + id: filterId, + operator: "equal", + type: "string", + valueType: "value", + value, + } + + filter.update($filter => { + // Remove any existing inline filter + if ($filter?.length) { + $filter = $filter?.filter(x => x.id !== filterId) + } + + // Add new one if a value exists + if (value) { + $filter = [...($filter || []), inlineFilter] + } + + return $filter + }) + } + + return { + filter: { + ...filter, + actions: { + addInlineFilter, + }, + }, + } +} + export const initialise = context => { const { filter, initialFilter } = context diff --git a/packages/frontend-core/src/components/grid/stores/rows.js b/packages/frontend-core/src/components/grid/stores/rows.js index 49adb62936..98e64d7acb 100644 --- a/packages/frontend-core/src/components/grid/stores/rows.js +++ b/packages/frontend-core/src/components/grid/stores/rows.js @@ -8,6 +8,7 @@ export const createStores = () => { const rows = writable([]) const loading = writable(false) const loaded = writable(false) + const refreshing = writable(false) const rowChangeCache = writable({}) const inProgressChanges = writable({}) const hasNextPage = writable(false) @@ -53,6 +54,7 @@ export const createStores = () => { fetch, rowLookupMap, loaded, + refreshing, loading, rowChangeCache, inProgressChanges, @@ -82,6 +84,7 @@ export const createActions = context => { notifications, fetch, isDatasourcePlus, + refreshing, } = context const instanceLoaded = writable(false) @@ -176,6 +179,9 @@ export const createActions = context => { // Notify that we're loaded loading.set(false) } + + // Update refreshing state + refreshing.set($fetch.loading) }) fetch.set(newFetch) From 2ef2d07cab6f3f563d0d3afd62e6e7e5bf201f42 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Oct 2023 14:28:05 +0100 Subject: [PATCH 006/427] Add inline searching for formula and longform columns, and improve searching operators where possible --- .../components/grid/cells/HeaderCell.svelte | 12 ++++++++-- .../src/components/grid/stores/filter.js | 22 ++++++++++++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 1abddfe1ff..314db21fc5 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -35,7 +35,7 @@ "boolean", "json", ] - const searchableTypes = ["string", "options", "number"] + const searchableTypes = ["string", "options", "number", "array", "longform"] let anchor let open = false @@ -54,10 +54,18 @@ $: descendingLabel = ["number", "bigint"].includes(column.schema?.type) ? "high-low" : "Z-A" - $: searchable = searchableTypes.includes(column.schema.type) + $: searchable = isColumnSearchable(column) $: searching = searchValue != null $: debouncedUpdateFilter(searchValue) + const isColumnSearchable = col => { + const type = col.schema.type + return ( + searchableTypes.includes(type) || + (type === "formula" && col.schema.formulaType === "static") + ) + } + const editColumn = async () => { editIsOpen = true await tick() diff --git a/packages/frontend-core/src/components/grid/stores/filter.js b/packages/frontend-core/src/components/grid/stores/filter.js index a2de0ca2d0..25b61161fa 100644 --- a/packages/frontend-core/src/components/grid/stores/filter.js +++ b/packages/frontend-core/src/components/grid/stores/filter.js @@ -16,27 +16,39 @@ export const createActions = context => { const addInlineFilter = (column, value) => { const filterId = `inline-${column}` - - const inlineFilter = { + let inlineFilter = { field: column.name, id: filterId, operator: "equal", - type: "string", + type: column.schema.type, valueType: "value", value, } + // Add overrides specific so the certain column type + switch (column.schema.type) { + case "string": + case "formula": + case "longform": + inlineFilter.operator = "string" + break + case "number": + inlineFilter.value = parseFloat(value) + break + case "array": + inlineFilter.operator = "contains" + } + + // Add this filter filter.update($filter => { // Remove any existing inline filter if ($filter?.length) { $filter = $filter?.filter(x => x.id !== filterId) } - // Add new one if a value exists if (value) { $filter = [...($filter || []), inlineFilter] } - return $filter }) } From cfdaa3564c9b4e507a79fa75e85eb22778628626 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Oct 2023 14:30:41 +0100 Subject: [PATCH 007/427] Improve options inline searching --- .../src/components/grid/stores/filter.js | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/packages/frontend-core/src/components/grid/stores/filter.js b/packages/frontend-core/src/components/grid/stores/filter.js index 25b61161fa..7e8cb364a8 100644 --- a/packages/frontend-core/src/components/grid/stores/filter.js +++ b/packages/frontend-core/src/components/grid/stores/filter.js @@ -16,27 +16,22 @@ export const createActions = context => { const addInlineFilter = (column, value) => { const filterId = `inline-${column}` + const type = column.schema.type let inlineFilter = { field: column.name, id: filterId, - operator: "equal", - type: column.schema.type, + operator: "string", valueType: "value", + type, value, } // Add overrides specific so the certain column type - switch (column.schema.type) { - case "string": - case "formula": - case "longform": - inlineFilter.operator = "string" - break - case "number": - inlineFilter.value = parseFloat(value) - break - case "array": - inlineFilter.operator = "contains" + if (type === "number") { + inlineFilter.value = parseFloat(value) + inlineFilter.operator = "equal" + } else if (type === "array") { + inlineFilter.operator = "contains" } // Add this filter From c906efb972b211ba02dbd2e1f64c6e9a8002ec61 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Oct 2023 14:37:13 +0100 Subject: [PATCH 008/427] Fix text colour for inline searching in grid block --- .../frontend-core/src/components/grid/cells/HeaderCell.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 573030b7b4..cdd8afb57e 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -398,7 +398,7 @@ outline: none; border: 1px solid transparent; background: transparent; - color: var(--ink); + color: var(--spectrum-global-color-gray-800); position: absolute; top: 0; left: 0; From 6dfe2c22af340af73429121cf35c86874c60c3cc Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 12 Oct 2023 15:46:18 +0100 Subject: [PATCH 009/427] Fix issue with multiple filters at the same time and remove unused variable --- .../src/components/grid/cells/HeaderCell.svelte | 7 ------- .../frontend-core/src/components/grid/stores/filter.js | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index cdd8afb57e..7d2b5d5941 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -29,13 +29,6 @@ filter, } = getContext("grid") - const bannedDisplayColumnTypes = [ - "link", - "array", - "attachment", - "boolean", - "json", - ] const searchableTypes = ["string", "options", "number", "array", "longform"] let anchor diff --git a/packages/frontend-core/src/components/grid/stores/filter.js b/packages/frontend-core/src/components/grid/stores/filter.js index 7e8cb364a8..984c2115ee 100644 --- a/packages/frontend-core/src/components/grid/stores/filter.js +++ b/packages/frontend-core/src/components/grid/stores/filter.js @@ -15,7 +15,7 @@ export const createActions = context => { const { filter } = context const addInlineFilter = (column, value) => { - const filterId = `inline-${column}` + const filterId = `inline-${column.name}` const type = column.schema.type let inlineFilter = { field: column.name, From 804aab3e43a0f9a4bb154d54925bdfa29ea37d41 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Fri, 13 Oct 2023 09:36:50 +0100 Subject: [PATCH 010/427] Refactor to use types for fields and add support for searching bigint columns --- .../components/grid/cells/HeaderCell.svelte | 25 ++++++++++++------- .../src/components/grid/stores/filter.js | 7 ++++-- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 7d2b5d5941..d4ed41efd3 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -5,6 +5,7 @@ import GridCell from "./GridCell.svelte" import { getColumnIcon } from "../lib/utils" import { debounce } from "../../../utils/utils" + import { FieldType, FormulaTypes } from "@budibase/types" export let column export let idx @@ -29,7 +30,14 @@ filter, } = getContext("grid") - const searchableTypes = ["string", "options", "number", "array", "longform"] + const searchableTypes = [ + FieldType.STRING, + FieldType.OPTIONS, + FieldType.NUMBER, + FieldType.BIGINT, + FieldType.ARRAY, + FieldType.LONGFORM, + ] let anchor let open = false @@ -42,21 +50,20 @@ $: sortedBy = column.name === $sort.column $: canMoveLeft = orderable && idx > 0 $: canMoveRight = orderable && idx < $renderedColumns.length - 1 - $: ascendingLabel = ["number", "bigint"].includes(column.schema?.type) - ? "low-high" - : "A-Z" - $: descendingLabel = ["number", "bigint"].includes(column.schema?.type) - ? "high-low" - : "Z-A" + $: numericType = [FieldType.NUMBER, FieldType.BIGINT].includes( + column.schema?.type + ) + $: ascendingLabel = numericType ? "low-high" : "A-Z" + $: descendingLabel = numericType ? "high-low" : "Z-A" $: searchable = isColumnSearchable(column) $: searching = searchValue != null $: debouncedUpdateFilter(searchValue) const isColumnSearchable = col => { - const type = col.schema.type + const { type, formulaType } = col.schema return ( searchableTypes.includes(type) || - (type === "formula" && col.schema.formulaType === "static") + (type === FieldType.FORMULA && formulaType === FormulaTypes.STATIC) ) } diff --git a/packages/frontend-core/src/components/grid/stores/filter.js b/packages/frontend-core/src/components/grid/stores/filter.js index 984c2115ee..76c8c5d3ec 100644 --- a/packages/frontend-core/src/components/grid/stores/filter.js +++ b/packages/frontend-core/src/components/grid/stores/filter.js @@ -1,4 +1,5 @@ import { writable, get } from "svelte/store" +import { FieldType } from "@budibase/types" export const createStores = context => { const { props } = context @@ -27,10 +28,12 @@ export const createActions = context => { } // Add overrides specific so the certain column type - if (type === "number") { + if (type === FieldType.NUMBER) { inlineFilter.value = parseFloat(value) inlineFilter.operator = "equal" - } else if (type === "array") { + } else if (type === FieldType.BIGINT) { + inlineFilter.operator = "equal" + } else if (type === FieldType.ARRAY) { inlineFilter.operator = "contains" } From b8c87224f76b0ec77e65072fd78df917bff7a7bf Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 13 Oct 2023 14:53:39 +0100 Subject: [PATCH 011/427] Add/Remove button behaviour, binding label support and default text behaviour. Refactoring and renaming of EditComponentPopoever component --- .../ButtonConfiguration.svelte | 112 ++++++++++++++---- .../ButtonConfiguration/ButtonSetting.svelte | 33 +++--- .../{ => DraggableList}/DraggableList.svelte | 32 ++++- .../controls/DraggableList/drag-handle.svelte | 31 +++++ ...ver.svelte => EditComponentPopover.svelte} | 65 ++++------ .../FieldConfiguration.svelte | 2 +- .../FieldConfiguration/FieldSetting.svelte | 56 ++++++++- packages/client/manifest.json | 22 ++-- .../src/components/app/ButtonGroup.svelte | 37 ++++++ .../components/app/forms/ButtonGroup.svelte | 38 ------ .../client/src/components/app/forms/index.js | 3 +- packages/client/src/components/app/index.js | 1 + 12 files changed, 281 insertions(+), 151 deletions(-) rename packages/builder/src/components/design/settings/controls/{ => DraggableList}/DraggableList.svelte (82%) create mode 100644 packages/builder/src/components/design/settings/controls/DraggableList/drag-handle.svelte rename packages/builder/src/components/design/settings/controls/{FieldConfiguration/EditFieldPopover.svelte => EditComponentPopover.svelte} (63%) create mode 100644 packages/client/src/components/app/ButtonGroup.svelte delete mode 100644 packages/client/src/components/app/forms/ButtonGroup.svelte diff --git a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte index baa8ad733c..fde888d17b 100644 --- a/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte +++ b/packages/builder/src/components/design/settings/controls/ButtonConfiguration/ButtonConfiguration.svelte @@ -1,5 +1,5 @@
- -
{item.text}
+
{readableText || "Button"}
- {console.log("REMOVE ME")}} - /> + removeButton(item._id)} + />
diff --git a/packages/builder/src/components/design/settings/controls/DraggableList.svelte b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte similarity index 82% rename from packages/builder/src/components/design/settings/controls/DraggableList.svelte rename to packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte index c8395b2a1f..1992299e90 100644 --- a/packages/builder/src/components/design/settings/controls/DraggableList.svelte +++ b/packages/builder/src/components/design/settings/controls/DraggableList/DraggableList.svelte @@ -1,10 +1,10 @@
- -
{item.label || item.field}
+ > +
+ + {item.field} +
+ +
{readableText}
@@ -53,4 +81,20 @@ .list-item-body { justify-content: space-between; } + .type-icon { + display: flex; + gap: var(--spacing-m); + margin: var(--spacing-xl); + margin-bottom: 0px; + height: var(--spectrum-alias-item-height-m); + padding: 0px var(--spectrum-alias-item-padding-m); + border-width: var(--spectrum-actionbutton-border-size); + border-radius: var(--spectrum-alias-border-radius-regular); + border: 1px solid + var( + --spectrum-actionbutton-m-border-color, + var(--spectrum-alias-border-color) + ); + align-items: center; + } diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 27e56d94db..a8559d4f79 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -258,7 +258,6 @@ "description": "Contains your app screens", "static": true }, - "buttongroup": { "name": "Button group", "icon": "Button", @@ -272,17 +271,14 @@ "type": "buttonConfiguration", "key": "buttons", "nested": true, - "defaultValue" : [{ - "component" : "button", - "props" : { + "defaultValue": [ + { "type": "cta" + }, + { + "type": "primary" } - },{ - "component" : "button", - "props" : { - "type" : "primary" - } - }] + ] } ] }, @@ -310,7 +306,7 @@ "barTitle": "Row layout" } ], - "defaultValue": "column" + "defaultValue": "row" }, { "type": "select", @@ -440,7 +436,6 @@ } ] }, - "button": { "name": "Button", "description": "A basic html button that is ready for styling", @@ -2592,7 +2587,6 @@ "key": "disabled", "defaultValue": false }, - { "type": "text", "label": "Initial form step", @@ -5875,4 +5869,4 @@ } ] } -} +} \ No newline at end of file diff --git a/packages/client/src/components/app/ButtonGroup.svelte b/packages/client/src/components/app/ButtonGroup.svelte new file mode 100644 index 0000000000..8d6cacd7a5 --- /dev/null +++ b/packages/client/src/components/app/ButtonGroup.svelte @@ -0,0 +1,37 @@ + + + + + {#each buttons as { text, type, quiet, disabled, onClick, size }, idx} + + {/each} + + diff --git a/packages/client/src/components/app/forms/ButtonGroup.svelte b/packages/client/src/components/app/forms/ButtonGroup.svelte deleted file mode 100644 index 222e91a55f..0000000000 --- a/packages/client/src/components/app/forms/ButtonGroup.svelte +++ /dev/null @@ -1,38 +0,0 @@ - - - - - - {#each buttons as { text, type, quiet, disabled, onClick, size }} - - {/each} - - - diff --git a/packages/client/src/components/app/forms/index.js b/packages/client/src/components/app/forms/index.js index 24d7f11c0c..38372f32d4 100644 --- a/packages/client/src/components/app/forms/index.js +++ b/packages/client/src/components/app/forms/index.js @@ -15,5 +15,4 @@ export { default as formstep } from "./FormStep.svelte" export { default as jsonfield } from "./JSONField.svelte" export { default as s3upload } from "./S3Upload.svelte" export { default as codescanner } from "./CodeScannerField.svelte" -export { default as bbreferencefield } from "./BBReferenceField.svelte" -export { default as buttongroup } from "./ButtonGroup.svelte" \ No newline at end of file +export { default as bbreferencefield } from "./BBReferenceField.svelte" \ No newline at end of file diff --git a/packages/client/src/components/app/index.js b/packages/client/src/components/app/index.js index 060c15a857..97df3741e1 100644 --- a/packages/client/src/components/app/index.js +++ b/packages/client/src/components/app/index.js @@ -19,6 +19,7 @@ export { default as dataprovider } from "./DataProvider.svelte" export { default as divider } from "./Divider.svelte" export { default as screenslot } from "./ScreenSlot.svelte" export { default as button } from "./Button.svelte" +export { default as buttongroup } from "./ButtonGroup.svelte" export { default as repeater } from "./Repeater.svelte" export { default as text } from "./Text.svelte" export { default as layout } from "./Layout.svelte" From 166c517ff82d4644f37f5e6fe3b4ff2fef2b0d94 Mon Sep 17 00:00:00 2001 From: Dean Date: Fri, 13 Oct 2023 15:02:18 +0100 Subject: [PATCH 012/427] Fix Block import locations --- packages/client/src/components/app/ButtonGroup.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/components/app/ButtonGroup.svelte b/packages/client/src/components/app/ButtonGroup.svelte index 8d6cacd7a5..0af16e6950 100644 --- a/packages/client/src/components/app/ButtonGroup.svelte +++ b/packages/client/src/components/app/ButtonGroup.svelte @@ -1,6 +1,6 @@
- {#if buttonList?.length} + {#if buttonCount} - {#each buttons as { text, type, quiet, disabled, onClick, size }, idx} + {#each buttons as { text, type, quiet, disabled, onClick, size }} Date: Mon, 16 Oct 2023 17:12:25 +0100 Subject: [PATCH 018/427] Ensure keyboard events while inline searching are not captured by the main grid keyboard manager --- .../frontend-core/src/components/grid/cells/HeaderCell.svelte | 1 + .../src/components/grid/overlays/KeyboardManager.svelte | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index a053b2a6f0..6648ba1a69 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -234,6 +234,7 @@ on:blur={onBlurInput} on:click={() => focusedCellId.set(null)} on:keydown={onInputKeyDown} + data-grid-ignore /> {/if} diff --git a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte index cd23f154b5..8b0a0f0942 100644 --- a/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte +++ b/packages/frontend-core/src/components/grid/overlays/KeyboardManager.svelte @@ -21,6 +21,7 @@ const ignoredOriginSelectors = [ ".spectrum-Modal", "#builder-side-panel-container", + "[data-grid-ignore]", ] // Global key listener which intercepts all key events From 74cab111917c91c64b39f8d913be228259d42982 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 16 Oct 2023 17:17:14 +0100 Subject: [PATCH 019/427] Improve grid sorting labels to account for date types and provide better labels --- .../components/grid/cells/HeaderCell.svelte | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index 6648ba1a69..f367e3427f 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -51,16 +51,33 @@ $: sortedBy = column.name === $sort.column $: canMoveLeft = orderable && idx > 0 $: canMoveRight = orderable && idx < $renderedColumns.length - 1 - $: numericType = [FieldType.NUMBER, FieldType.BIGINT].includes( - column.schema?.type - ) - $: ascendingLabel = numericType ? "low-high" : "A-Z" - $: descendingLabel = numericType ? "high-low" : "Z-A" + $: sortingLabels = getSortingLabels(column.schema?.type) $: searchable = isColumnSearchable(column) $: resetSearchValue(column.name) $: searching = searchValue != null $: debouncedUpdateFilter(searchValue) + const getSortingLabels = type => { + switch (type) { + case FieldType.NUMBER: + case FieldType.BIGINT: + return { + ascending: "low-high", + descending: "high-low", + } + case FieldType.DATETIME: + return { + ascending: "old-new", + descending: "new-old", + } + default: + return { + ascending: "A-Z", + descending: "Z-A", + } + } + } + const resetSearchValue = name => { searchValue = $inlineFilters?.find(x => x.id === `inline-${name}`)?.value } @@ -318,14 +335,14 @@ on:click={sortAscending} disabled={column.name === $sort.column && $sort.order === "ascending"} > - Sort {ascendingLabel} + Sort {sortingLabels.ascending} - Sort {descendingLabel} + Sort {sortingLabels.descending} Move left From 71712d422a99531fe531a86978eadd62c16e6693 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 17 Oct 2023 10:51:06 +0100 Subject: [PATCH 020/427] Fix alert when duplicate auto columns exist --- .../app/[application]/data/table/[tableId]/index.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/index.svelte index 414722a177..a68a782bed 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/table/[tableId]/index.svelte @@ -53,7 +53,8 @@ } .alert-wrap { display: flex; - width: 100%; + flex: 0 0 auto; + margin: -28px -40px 14px -40px; } .alert-wrap :global(> *) { flex: 1; From 5e8e4add4a650604357d5c78c77c7ee22a25c399 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 17 Oct 2023 10:53:08 +0100 Subject: [PATCH 021/427] Make it less painful to delete columns --- .../backend/DataTable/modals/CreateEditColumn.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 7b51e6c839..ff9500f226 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -777,7 +777,8 @@ disabled={deleteColName !== originalName} >

- Are you sure you wish to delete the column {originalName}? + Are you sure you wish to delete the column + (deleteColName = originalName)}>{originalName}? Your data will be deleted and this action cannot be undone - enter the column name to confirm.

From 4e703fdfcc0e644d08fc6de3c73633dcf39bb39b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 17 Oct 2023 11:16:15 +0100 Subject: [PATCH 022/427] Remove concept of rendered columns from grid and instead render all columns --- .../components/grid/cells/HeaderCell.svelte | 4 +- .../grid/cells/RelationshipCell.svelte | 7 ++ .../components/grid/layout/GridBody.svelte | 6 +- .../src/components/grid/layout/GridRow.svelte | 4 +- .../grid/layout/GridScrollWrapper.svelte | 7 +- .../components/grid/layout/HeaderRow.svelte | 4 +- .../grid/layout/NewColumnButton.svelte | 9 +-- .../src/components/grid/layout/NewRow.svelte | 46 ++++++------ .../grid/overlays/ResizeOverlay.svelte | 4 +- .../src/components/grid/stores/viewport.js | 75 ++----------------- 10 files changed, 53 insertions(+), 113 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index f367e3427f..2397f964e8 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -17,7 +17,7 @@ isResizing, rand, sort, - renderedColumns, + visibleColumns, dispatch, subscribe, config, @@ -50,7 +50,7 @@ $: sortedBy = column.name === $sort.column $: canMoveLeft = orderable && idx > 0 - $: canMoveRight = orderable && idx < $renderedColumns.length - 1 + $: canMoveRight = orderable && idx < $visibleColumns.length - 1 $: sortingLabels = getSortingLabels(column.schema?.type) $: searchable = isColumnSearchable(column) $: resetSearchValue(column.name) diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index 925c840478..d15acce4fc 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -444,6 +444,13 @@ text-decoration: underline; } + .remove { + display: none; + } + .remove.visible { + display: block; + } + .add { background: var(--spectrum-global-color-gray-200); padding: 4px; diff --git a/packages/frontend-core/src/components/grid/layout/GridBody.svelte b/packages/frontend-core/src/components/grid/layout/GridBody.svelte index 762985a4db..0bb2a51fb4 100644 --- a/packages/frontend-core/src/components/grid/layout/GridBody.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridBody.svelte @@ -7,7 +7,7 @@ const { bounds, renderedRows, - renderedColumns, + visibleColumns, rowVerticalInversionIndex, hoveredRowId, dispatch, @@ -17,7 +17,7 @@ let body - $: renderColumnsWidth = $renderedColumns.reduce( + $: columnsWidth = $visibleColumns.reduce( (total, col) => (total += col.width), 0 ) @@ -47,7 +47,7 @@
($hoveredRowId = BlankRowID)} on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)} on:click={() => dispatch("add-row-inline")} diff --git a/packages/frontend-core/src/components/grid/layout/GridRow.svelte b/packages/frontend-core/src/components/grid/layout/GridRow.svelte index 4754d493bf..93dc11f6ed 100644 --- a/packages/frontend-core/src/components/grid/layout/GridRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridRow.svelte @@ -10,7 +10,7 @@ focusedCellId, reorder, selectedRows, - renderedColumns, + visibleColumns, hoveredRowId, selectedCellMap, focusedRow, @@ -34,7 +34,7 @@ on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)} on:click={() => dispatch("rowclick", rows.actions.cleanRow(row))} > - {#each $renderedColumns as column, columnIdx (column.name)} + {#each $visibleColumns as column, columnIdx} {@const cellId = `${row._id}-${column.name}`} { - const offsetX = scrollHorizontally ? -1 * scroll.left + hiddenWidths : 0 + const generateStyle = (scroll, rowHeight) => { + const offsetX = scrollHorizontally ? -1 * scroll.left : 0 const offsetY = scrollVertically ? -1 * (scroll.top % rowHeight) : 0 return `transform: translate3d(${offsetX}px, ${offsetY}px, 0);` } diff --git a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte index 97b7d054f3..b8655b98b3 100644 --- a/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/HeaderRow.svelte @@ -5,14 +5,14 @@ import HeaderCell from "../cells/HeaderCell.svelte" import { TempTooltip, TooltipType } from "@budibase/bbui" - const { renderedColumns, config, hasNonAutoColumn, datasource, loading } = + const { visibleColumns, config, hasNonAutoColumn, datasource, loading } = getContext("grid")
- {#each $renderedColumns as column, idx} + {#each $visibleColumns as column, idx} diff --git a/packages/frontend-core/src/components/grid/layout/NewColumnButton.svelte b/packages/frontend-core/src/components/grid/layout/NewColumnButton.svelte index d131df26e5..46e9b40fb6 100644 --- a/packages/frontend-core/src/components/grid/layout/NewColumnButton.svelte +++ b/packages/frontend-core/src/components/grid/layout/NewColumnButton.svelte @@ -2,17 +2,16 @@ import { getContext, onMount } from "svelte" import { Icon, Popover, clickOutside } from "@budibase/bbui" - const { renderedColumns, scroll, hiddenColumnsWidth, width, subscribe } = - getContext("grid") + const { visibleColumns, scroll, width, subscribe } = getContext("grid") let anchor let open = false - $: columnsWidth = $renderedColumns.reduce( + $: columnsWidth = $visibleColumns.reduce( (total, col) => (total += col.width), 0 ) - $: end = $hiddenColumnsWidth + columnsWidth - 1 - $scroll.left + $: end = columnsWidth - 1 - $scroll.left $: left = Math.min($width - 40, end) const close = () => { @@ -34,7 +33,7 @@
- {#each $renderedColumns as column, columnIdx} + {#each $visibleColumns as column, columnIdx} {@const cellId = `new-${column.name}`} - {#key cellId} - = $columnHorizontalInversionIndex} - {invertY} - > - {#if column?.schema?.autocolumn} -
Can't edit auto column
- {/if} - {#if isAdding} -
- {/if} - - {/key} + = $columnHorizontalInversionIndex} + {invertY} + > + {#if column?.schema?.autocolumn} +
Can't edit auto column
+ {/if} + {#if isAdding} +
+ {/if} + {/each}
diff --git a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte index 13e158b300..9e584ab610 100644 --- a/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte +++ b/packages/frontend-core/src/components/grid/overlays/ResizeOverlay.svelte @@ -2,7 +2,7 @@ import { getContext } from "svelte" import { GutterWidth } from "../lib/constants" - const { resize, renderedColumns, stickyColumn, isReordering, scrollLeft } = + const { resize, visibleColumns, stickyColumn, isReordering, scrollLeft } = getContext("grid") $: offset = GutterWidth + ($stickyColumn?.width || 0) @@ -26,7 +26,7 @@
{/if} - {#each $renderedColumns as column} + {#each $visibleColumns as column}
{ [] ) - // Derive visible columns - const scrollLeftRounded = derived(scrollLeft, $scrollLeft => { - const interval = MinColumnWidth - return Math.round($scrollLeft / interval) * interval - }) - const renderedColumns = derived( - [visibleColumns, scrollLeftRounded, width], - ([$visibleColumns, $scrollLeft, $width], set) => { - if (!$visibleColumns.length) { - set([]) - return - } - let startColIdx = 0 - let rightEdge = $visibleColumns[0].width - while ( - rightEdge < $scrollLeft && - startColIdx < $visibleColumns.length - 1 - ) { - startColIdx++ - rightEdge += $visibleColumns[startColIdx].width - } - let endColIdx = startColIdx + 1 - let leftEdge = rightEdge - while ( - leftEdge < $width + $scrollLeft && - endColIdx < $visibleColumns.length - ) { - leftEdge += $visibleColumns[endColIdx].width - endColIdx++ - } - // Render an additional column on either side to account for - // debounce column updates based on scroll position - const next = $visibleColumns.slice( - Math.max(0, startColIdx - 1), - endColIdx + 1 - ) - const current = get(renderedColumns) - if (JSON.stringify(next) !== JSON.stringify(current)) { - set(next) - } - } - ) - - const hiddenColumnsWidth = derived( - [renderedColumns, visibleColumns], - ([$renderedColumns, $visibleColumns]) => { - const idx = $visibleColumns.findIndex( - col => col.name === $renderedColumns[0]?.name - ) - let width = 0 - if (idx > 0) { - for (let i = 0; i < idx; i++) { - width += $visibleColumns[i].width - } - } - return width - }, - 0 - ) - // Determine the row index at which we should start vertically inverting cell // dropdowns const rowVerticalInversionIndex = derived( @@ -130,12 +69,12 @@ export const deriveStores = context => { // Determine the column index at which we should start horizontally inverting // cell dropdowns const columnHorizontalInversionIndex = derived( - [renderedColumns, scrollLeft, width], - ([$renderedColumns, $scrollLeft, $width]) => { + [visibleColumns, scrollLeft, width], + ([$visibleColumns, $scrollLeft, $width]) => { const cutoff = $width + $scrollLeft - ScrollBarSize * 3 - let inversionIdx = $renderedColumns.length - for (let i = $renderedColumns.length - 1; i >= 0; i--, inversionIdx--) { - const rightEdge = $renderedColumns[i].left + $renderedColumns[i].width + let inversionIdx = $visibleColumns.length + for (let i = $visibleColumns.length - 1; i >= 0; i--, inversionIdx--) { + const rightEdge = $visibleColumns[i].left + $visibleColumns[i].width if (rightEdge + MaxCellRenderWidthOverflow <= cutoff) { break } @@ -148,8 +87,6 @@ export const deriveStores = context => { scrolledRowCount, visualRowCapacity, renderedRows, - renderedColumns, - hiddenColumnsWidth, rowVerticalInversionIndex, columnHorizontalInversionIndex, } From c37538d61125734a32aeb0a178843c61f5b63e25 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 17 Oct 2023 11:51:42 +0100 Subject: [PATCH 023/427] Use CSS content-visibility to improve rendering performance by hiding offscreen grid cells --- .../src/components/grid/cells/DataCell.svelte | 2 + .../src/components/grid/cells/GridCell.svelte | 5 +++ .../src/components/grid/layout/GridRow.svelte | 2 + .../src/components/grid/layout/NewRow.svelte | 2 + .../src/components/grid/stores/viewport.js | 45 ++++++++++++++++++- 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/cells/DataCell.svelte b/packages/frontend-core/src/components/grid/cells/DataCell.svelte index f9cdef3756..cdaf28978a 100644 --- a/packages/frontend-core/src/components/grid/cells/DataCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/DataCell.svelte @@ -21,6 +21,7 @@ export let invertX = false export let invertY = false export let contentLines = 1 + export let hidden = false const emptyError = writable(null) @@ -78,6 +79,7 @@ {focused} {selectedUser} {readonly} + {hidden} error={$error} on:click={() => focusedCellId.set(cellId)} on:contextmenu={e => menu.actions.open(cellId, e)} diff --git a/packages/frontend-core/src/components/grid/cells/GridCell.svelte b/packages/frontend-core/src/components/grid/cells/GridCell.svelte index fe4bd70ba4..dcc76b9c75 100644 --- a/packages/frontend-core/src/components/grid/cells/GridCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/GridCell.svelte @@ -10,6 +10,7 @@ export let defaultHeight = false export let center = false export let readonly = false + export let hidden = false $: style = getStyle(width, selectedUser) @@ -30,6 +31,7 @@ class:error class:center class:readonly + class:hidden class:default-height={defaultHeight} class:selected-other={selectedUser != null} class:alt={rowIdx % 2 === 1} @@ -81,6 +83,9 @@ .cell.center { align-items: center; } + .cell.hidden { + content-visibility: hidden; + } /* Cell border */ .cell.focused:after, diff --git a/packages/frontend-core/src/components/grid/layout/GridRow.svelte b/packages/frontend-core/src/components/grid/layout/GridRow.svelte index 93dc11f6ed..4a0db40ee8 100644 --- a/packages/frontend-core/src/components/grid/layout/GridRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/GridRow.svelte @@ -19,6 +19,7 @@ isDragging, dispatch, rows, + columnRenderMap, } = getContext("grid") $: rowSelected = !!$selectedRows[row._id] @@ -51,6 +52,7 @@ selectedUser={$selectedCellMap[cellId]} width={column.width} contentLines={$contentLines} + hidden={!$columnRenderMap[column.name]} /> {/each}
diff --git a/packages/frontend-core/src/components/grid/layout/NewRow.svelte b/packages/frontend-core/src/components/grid/layout/NewRow.svelte index cc2d76f536..980f86326d 100644 --- a/packages/frontend-core/src/components/grid/layout/NewRow.svelte +++ b/packages/frontend-core/src/components/grid/layout/NewRow.svelte @@ -31,6 +31,7 @@ refreshing, config, filter, + columnRenderMap, } = getContext("grid") let visible = false @@ -224,6 +225,7 @@ topRow={offset === 0} invertX={columnIdx >= $columnHorizontalInversionIndex} {invertY} + hidden={!$columnRenderMap[column.name]} > {#if column?.schema?.autocolumn}
Can't edit auto column
diff --git a/packages/frontend-core/src/components/grid/stores/viewport.js b/packages/frontend-core/src/components/grid/stores/viewport.js index 3a1b65e2b9..0890792989 100644 --- a/packages/frontend-core/src/components/grid/stores/viewport.js +++ b/packages/frontend-core/src/components/grid/stores/viewport.js @@ -1,7 +1,8 @@ -import { derived } from "svelte/store" +import { derived, get } from "svelte/store" import { MaxCellRenderHeight, MaxCellRenderWidthOverflow, + MinColumnWidth, ScrollBarSize, } from "../lib/constants" @@ -44,6 +45,47 @@ export const deriveStores = context => { [] ) + // Derive visible columns + const scrollLeftRounded = derived(scrollLeft, $scrollLeft => { + const interval = MinColumnWidth + return Math.round($scrollLeft / interval) * interval + }) + const columnRenderMap = derived( + [visibleColumns, scrollLeftRounded, width], + ([$visibleColumns, $scrollLeft, $width]) => { + if (!$visibleColumns.length) { + return {} + } + let startColIdx = 0 + let rightEdge = $visibleColumns[0].width + while ( + rightEdge < $scrollLeft && + startColIdx < $visibleColumns.length - 1 + ) { + startColIdx++ + rightEdge += $visibleColumns[startColIdx].width + } + let endColIdx = startColIdx + 1 + let leftEdge = rightEdge + while ( + leftEdge < $width + $scrollLeft && + endColIdx < $visibleColumns.length + ) { + leftEdge += $visibleColumns[endColIdx].width + endColIdx++ + } + + // Only update the store if different + let next = {} + $visibleColumns + .slice(Math.max(0, startColIdx), endColIdx) + .forEach(col => { + next[col.name] = true + }) + return next + } + ) + // Determine the row index at which we should start vertically inverting cell // dropdowns const rowVerticalInversionIndex = derived( @@ -87,6 +129,7 @@ export const deriveStores = context => { scrolledRowCount, visualRowCapacity, renderedRows, + columnRenderMap, rowVerticalInversionIndex, columnHorizontalInversionIndex, } From dadb36827932c16bd5f71ea1a65ed469488855bb Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 18 Oct 2023 08:31:29 +0100 Subject: [PATCH 024/427] Lint and add hover styles for deleting prompts --- .../backend/DataTable/modals/CreateEditColumn.svelte | 7 +++++++ .../src/components/grid/cells/RelationshipCell.svelte | 7 ------- .../frontend-core/src/components/grid/stores/viewport.js | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index ff9500f226..467ae413c3 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -811,4 +811,11 @@ gap: 8px; display: flex; } + b { + transition: color 130ms ease-out; + } + b:hover { + cursor: pointer; + color: var(--spectrum-global-color-gray-900); + } diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index d15acce4fc..925c840478 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -444,13 +444,6 @@ text-decoration: underline; } - .remove { - display: none; - } - .remove.visible { - display: block; - } - .add { background: var(--spectrum-global-color-gray-200); padding: 4px; diff --git a/packages/frontend-core/src/components/grid/stores/viewport.js b/packages/frontend-core/src/components/grid/stores/viewport.js index 0890792989..8df8acd0f4 100644 --- a/packages/frontend-core/src/components/grid/stores/viewport.js +++ b/packages/frontend-core/src/components/grid/stores/viewport.js @@ -1,4 +1,4 @@ -import { derived, get } from "svelte/store" +import { derived } from "svelte/store" import { MaxCellRenderHeight, MaxCellRenderWidthOverflow, From 36346874428a999206f79d5a396dbeaefa15743c Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 18 Oct 2023 10:31:36 +0100 Subject: [PATCH 025/427] Create endpoint and controller function for user column migration. --- packages/server/src/api/controllers/table/index.ts | 2 ++ packages/server/src/api/routes/table.ts | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 44f673f284..2780185ed7 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -158,3 +158,5 @@ export async function validateExistingTableImport(ctx: UserCtx) { ctx.status = 422 } } + +export async function migrate(ctx: UserCtx) {} diff --git a/packages/server/src/api/routes/table.ts b/packages/server/src/api/routes/table.ts index 7ffa5acb3e..1aa23257fb 100644 --- a/packages/server/src/api/routes/table.ts +++ b/packages/server/src/api/routes/table.ts @@ -167,4 +167,11 @@ router tableController.bulkImport ) + .post( + "/api/tables/:tableId/migrate", + paramResource("tableId"), + authorized(BUILDER), + tableController.migrate + ) + export default router From a06451222400312d9928e67618e870cc28f2dbf7 Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 18 Oct 2023 11:14:16 +0100 Subject: [PATCH 026/427] Added description field to the formblock --- packages/client/manifest.json | 6 ++ .../app/blocks/form/FormBlock.svelte | 2 + .../app/blocks/form/InnerFormBlock.svelte | 101 ++++++++++-------- 3 files changed, 67 insertions(+), 42 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 8d0a4e456f..ed69b8868f 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5305,6 +5305,12 @@ "key": "title", "nested": true }, + { + "type": "text", + "label": "Description", + "key": "description", + "nested": true + }, { "section": true, "dependsOn": { diff --git a/packages/client/src/components/app/blocks/form/FormBlock.svelte b/packages/client/src/components/app/blocks/form/FormBlock.svelte index 5d57d10ab6..f905227af9 100644 --- a/packages/client/src/components/app/blocks/form/FormBlock.svelte +++ b/packages/client/src/components/app/blocks/form/FormBlock.svelte @@ -12,6 +12,7 @@ export let fields export let labelPosition export let title + export let description export let showDeleteButton export let showSaveButton export let saveButtonLabel @@ -98,6 +99,7 @@ fields: fieldsOrDefault, labelPosition, title, + description, saveButtonLabel: saveLabel, deleteButtonLabel: deleteLabel, schema, diff --git a/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte b/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte index ec5daa21b1..e65d2cf90b 100644 --- a/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte +++ b/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte @@ -11,6 +11,7 @@ export let fields export let labelPosition export let title + export let description export let saveButtonLabel export let deleteButtonLabel export let schema @@ -160,55 +161,71 @@ - {#if renderButtons} + > + {#if renderButtons} + + {#if renderDeleteButton} + + {/if} + {#if renderSaveButton} + + {/if} + + {/if} + + {#if description} + - {#if renderDeleteButton} - - {/if} - {#if renderSaveButton} - - {/if} - + /> {/if} {/if} From 5747f30b5f5cccd7ea5b78a6565e56edfc919400 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 18 Oct 2023 12:04:50 +0100 Subject: [PATCH 027/427] Precondition checks to make sure the migration is from the right column to the right column. --- .../server/src/api/controllers/table/index.ts | 56 ++++++++++++++++++- packages/types/src/api/web/app/table.ts | 11 ++++ .../types/src/documents/app/table/schema.ts | 30 ++++++++++ 3 files changed, 96 insertions(+), 1 deletion(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 2780185ed7..36dee4811e 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -11,11 +11,16 @@ import { BulkImportRequest, BulkImportResponse, FetchTablesResponse, + InternalTable, + MigrateRequest, + MigrateResponse, SaveTableRequest, SaveTableResponse, Table, TableResponse, UserCtx, + isBBReferenceField, + isRelationshipField, } from "@budibase/types" import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" @@ -159,4 +164,53 @@ export async function validateExistingTableImport(ctx: UserCtx) { } } -export async function migrate(ctx: UserCtx) {} +function error(ctx: UserCtx, message: string, status = 400) { + ctx.status = status + ctx.body = { message } +} + +export async function migrate(ctx: UserCtx) { + const { tableId, oldColumn, newColumn } = ctx.request.body + + // For now we're only supporting migrations of user relationships to user + // columns in internal tables. In future we may want to support other + // migrations but for now return an error if we aren't migrating a user + // relationship. + if (isExternalTable(tableId)) { + return error(ctx, "External tables cannot be migrated") + } + + const table = await sdk.tables.getTable(tableId) + + if (!(oldColumn.name in table.schema)) { + return error( + ctx, + `Column "${oldColumn.name}" does not exist on table "${table.name}"` + ) + } + + if (newColumn.name in table.schema) { + return error( + ctx, + `Column "${newColumn.name}" already exists on table "${table.name}"` + ) + } + + if (!isBBReferenceField(newColumn)) { + return error(ctx, `Column "${newColumn.name}" is not a user column`) + } + + if (newColumn.subtype !== "user" && newColumn.subtype !== "users") { + return error(ctx, `Column "${newColumn.name}" is not a user column`) + } + + if (!isRelationshipField(oldColumn)) { + return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) + } + + if (oldColumn.tableId !== InternalTable.USER_METADATA) { + return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) + } + + let rows = await sdk.rows.fetch(tableId) +} diff --git a/packages/types/src/api/web/app/table.ts b/packages/types/src/api/web/app/table.ts index cb5faaa9ea..61da12d041 100644 --- a/packages/types/src/api/web/app/table.ts +++ b/packages/types/src/api/web/app/table.ts @@ -1,4 +1,5 @@ import { + FieldSchema, Row, Table, TableRequest, @@ -33,3 +34,13 @@ export interface BulkImportRequest { export interface BulkImportResponse { message: string } + +export interface MigrateRequest { + tableId: string + oldColumn: FieldSchema + newColumn: FieldSchema +} + +export interface MigrateResponse { + message: string +} diff --git a/packages/types/src/documents/app/table/schema.ts b/packages/types/src/documents/app/table/schema.ts index e529a8e8b7..755ccf61e7 100644 --- a/packages/types/src/documents/app/table/schema.ts +++ b/packages/types/src/documents/app/table/schema.ts @@ -164,3 +164,33 @@ export type FieldSchema = export interface TableSchema { [key: string]: FieldSchema } + +export function isRelationshipField( + field: FieldSchema +): field is RelationshipFieldMetadata { + return field.type === FieldType.LINK +} + +export function isManyToMany( + field: RelationshipFieldMetadata +): field is ManyToManyRelationshipFieldMetadata { + return field.relationshipType === RelationshipType.MANY_TO_MANY +} + +export function isOneToMany( + field: RelationshipFieldMetadata +): field is OneToManyRelationshipFieldMetadata { + return field.relationshipType === RelationshipType.ONE_TO_MANY +} + +export function isManyToOne( + field: RelationshipFieldMetadata +): field is ManyToOneRelationshipFieldMetadata { + return field.relationshipType === RelationshipType.MANY_TO_ONE +} + +export function isBBReferenceField( + field: FieldSchema +): field is BBReferenceFieldMetadata { + return field.type === FieldType.BB_REFERENCE +} From 0d67f000f03e1c27692e92dcf866cd36b55435cd Mon Sep 17 00:00:00 2001 From: Dean Date: Wed, 18 Oct 2023 12:19:24 +0100 Subject: [PATCH 028/427] Initial reflow of the block settings --- packages/client/manifest.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 8d0a4e456f..d5fca15fd7 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5288,17 +5288,17 @@ }, "settings": [ { - "type": "select", + "type": "table", + "label": "Data", + "key": "dataSource" + }, + { + "type": "radio", "label": "Type", "key": "actionType", "options": ["Create", "Update", "View"], "defaultValue": "Create" }, - { - "type": "table", - "label": "Data", - "key": "dataSource" - }, { "type": "text", "label": "Title", @@ -5323,7 +5323,7 @@ }, { "type": "text", - "label": "Empty text", + "label": "No rows found", "key": "noRowsMessage", "defaultValue": "We couldn't find a row to display", "nested": true From c25de74e174c9e2fa895f5aa8e2a5cc51f9968bf Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 18 Oct 2023 15:14:34 +0100 Subject: [PATCH 029/427] Action Michael's feedback about the structure of this feature. --- .../server/src/api/controllers/table/index.ts | 50 +---------- packages/server/src/sdk/app/tables/index.ts | 12 +++ .../server/src/sdk/app/tables/migration.ts | 84 +++++++++++++++++++ 3 files changed, 97 insertions(+), 49 deletions(-) create mode 100644 packages/server/src/sdk/app/tables/migration.ts diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 36dee4811e..132bae3d81 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -11,7 +11,6 @@ import { BulkImportRequest, BulkImportResponse, FetchTablesResponse, - InternalTable, MigrateRequest, MigrateResponse, SaveTableRequest, @@ -19,8 +18,6 @@ import { Table, TableResponse, UserCtx, - isBBReferenceField, - isRelationshipField, } from "@budibase/types" import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" @@ -164,53 +161,8 @@ export async function validateExistingTableImport(ctx: UserCtx) { } } -function error(ctx: UserCtx, message: string, status = 400) { - ctx.status = status - ctx.body = { message } -} - export async function migrate(ctx: UserCtx) { const { tableId, oldColumn, newColumn } = ctx.request.body - - // For now we're only supporting migrations of user relationships to user - // columns in internal tables. In future we may want to support other - // migrations but for now return an error if we aren't migrating a user - // relationship. - if (isExternalTable(tableId)) { - return error(ctx, "External tables cannot be migrated") - } - const table = await sdk.tables.getTable(tableId) - - if (!(oldColumn.name in table.schema)) { - return error( - ctx, - `Column "${oldColumn.name}" does not exist on table "${table.name}"` - ) - } - - if (newColumn.name in table.schema) { - return error( - ctx, - `Column "${newColumn.name}" already exists on table "${table.name}"` - ) - } - - if (!isBBReferenceField(newColumn)) { - return error(ctx, `Column "${newColumn.name}" is not a user column`) - } - - if (newColumn.subtype !== "user" && newColumn.subtype !== "users") { - return error(ctx, `Column "${newColumn.name}" is not a user column`) - } - - if (!isRelationshipField(oldColumn)) { - return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) - } - - if (oldColumn.tableId !== InternalTable.USER_METADATA) { - return error(ctx, `Column "${oldColumn.name}" is not a user relationship`) - } - - let rows = await sdk.rows.fetch(tableId) + await sdk.tables.migrate(table, oldColumn, newColumn) } diff --git a/packages/server/src/sdk/app/tables/index.ts b/packages/server/src/sdk/app/tables/index.ts index 64fcde4bff..e4884b2ec4 100644 --- a/packages/server/src/sdk/app/tables/index.ts +++ b/packages/server/src/sdk/app/tables/index.ts @@ -7,6 +7,7 @@ import { } from "../../../integrations/utils" import { Database, + FieldSchema, Table, TableResponse, TableViewsResponse, @@ -14,6 +15,7 @@ import { import datasources from "../datasources" import { populateExternalTableSchemas } from "./validation" import sdk from "../../../sdk" +import { migrate } from "./migration" async function getAllInternalTables(db?: Database): Promise { if (!db) { @@ -84,6 +86,14 @@ async function saveTable(table: Table) { } } +async function addColumn(table: Table, newColumn: FieldSchema) { + if (newColumn.name in table.schema) { + throw `Column "${newColumn.name}" already exists on table "${table.name}"` + } + table.schema[newColumn.name] = newColumn + await saveTable(table) +} + export default { getAllInternalTables, getAllExternalTables, @@ -92,4 +102,6 @@ export default { populateExternalTableSchemas, enrichViewSchemas, saveTable, + addColumn, + migrate, } diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts new file mode 100644 index 0000000000..f727b5ea9d --- /dev/null +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -0,0 +1,84 @@ +import { BadRequestError } from "@budibase/backend-core" +import { + BBReferenceFieldMetadata, + FieldSchema, + InternalTable, + RelationshipFieldMetadata, + Table, + isBBReferenceField, + isRelationshipField, +} from "@budibase/types" +import { isExternalTable } from "src/integrations/utils" +import sdk from "../../../sdk" + +export async function migrate( + table: Table, + oldColumn: FieldSchema, + newColumn: FieldSchema +) { + let migrator = getColumnMigrator(table, oldColumn, newColumn) + + await sdk.tables.addColumn(table, newColumn) + + migrator.doMigration() +} + +interface ColumnMigrator { + doMigration(): Promise +} + +function getColumnMigrator( + table: Table, + oldColumn: FieldSchema, + newColumn: FieldSchema +): ColumnMigrator { + // For now we're only supporting migrations of user relationships to user + // columns in internal tables. In future we may want to support other + // migrations but for now return an error if we aren't migrating a user + // relationship. + if (isExternalTable(table._id!)) { + throw new BadRequestError("External tables cannot be migrated") + } + + if (!(oldColumn.name in table.schema)) { + throw new BadRequestError(`Column "${oldColumn.name}" does not exist`) + } + + if (newColumn.name in table.schema) { + throw new BadRequestError(`Column "${newColumn.name}" already exists`) + } + + if (!isBBReferenceField(newColumn)) { + throw new BadRequestError(`Column "${newColumn.name}" is not a user column`) + } + + if (newColumn.subtype !== "user" && newColumn.subtype !== "users") { + throw new BadRequestError(`Column "${newColumn.name}" is not a user column`) + } + + if (!isRelationshipField(oldColumn)) { + throw new BadRequestError( + `Column "${oldColumn.name}" is not a user relationship` + ) + } + + if (oldColumn.tableId !== InternalTable.USER_METADATA) { + throw new BadRequestError( + `Column "${oldColumn.name}" is not a user relationship` + ) + } + + return new UserColumnMigrator(table, oldColumn, newColumn) +} + +class UserColumnMigrator implements ColumnMigrator { + constructor( + private table: Table, + private oldColumn: RelationshipFieldMetadata, + private newColumn: BBReferenceFieldMetadata + ) {} + + async doMigration() { + let rows = await sdk.rows.fetch(this.table._id!) + } +} From 8d700655ba66aae7bc4397d1eabd06741fc7b5cf Mon Sep 17 00:00:00 2001 From: Duarte Velez Grilo Date: Wed, 18 Oct 2023 16:06:51 +0100 Subject: [PATCH 030/427] Update manifest.json Added used camera preference option. --- packages/client/manifest.json | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 8d0a4e456f..f294dbbc80 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3419,6 +3419,22 @@ "value": "custom" } }, + { + "type": "select", + "label": "Preferred camera", + "key": "preferredCamera", + "defaultValue": "environment", + "options": [ + { + "label": "Front", + "value": "user" + }, + { + "label": "Back", + "value": "environment" + } + ] + }, { "type": "event", "label": "On change", From 5e4821e6a5e22473e869bf71f2499649ae453d26 Mon Sep 17 00:00:00 2001 From: Duarte Velez Grilo Date: Wed, 18 Oct 2023 16:12:12 +0100 Subject: [PATCH 031/427] Update CodeScannerField.svelte Added preferred camera setting. --- .../client/src/components/app/forms/CodeScannerField.svelte | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/client/src/components/app/forms/CodeScannerField.svelte b/packages/client/src/components/app/forms/CodeScannerField.svelte index c408f78d7c..1a57d8fbe2 100644 --- a/packages/client/src/components/app/forms/CodeScannerField.svelte +++ b/packages/client/src/components/app/forms/CodeScannerField.svelte @@ -14,6 +14,7 @@ export let beepOnScan export let beepFrequency export let customFrequency + export let preferredCamera let fieldState let fieldApi @@ -48,6 +49,7 @@ {beepOnScan} {beepFrequency} {customFrequency} + {preferredCamera} /> {/if} From ad3a80c629e4748bc1f27bb4193f8912100b9d33 Mon Sep 17 00:00:00 2001 From: Duarte Velez Grilo Date: Wed, 18 Oct 2023 16:13:50 +0100 Subject: [PATCH 032/427] Update CodeScanner.svelte Added preferred camera setting. --- packages/client/src/components/app/forms/CodeScanner.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/client/src/components/app/forms/CodeScanner.svelte b/packages/client/src/components/app/forms/CodeScanner.svelte index 04d6919157..891154939f 100644 --- a/packages/client/src/components/app/forms/CodeScanner.svelte +++ b/packages/client/src/components/app/forms/CodeScanner.svelte @@ -11,6 +11,7 @@ export let beepOnScan = false export let beepFrequency = 2637 export let customFrequency = 1046 + export let preferredCamera = "environment" const dispatch = createEventDispatcher() @@ -20,7 +21,7 @@ let cameraEnabled let cameraStarted = false let html5QrCode - let cameraSetting = { facingMode: "environment" } + let cameraSetting = { facingMode: "{preferredCamera}" } let cameraConfig = { fps: 25, qrbox: { width: 250, height: 250 }, From 6ae5451fdf46db2dce172047a310b64ebbf596a7 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 18 Oct 2023 16:56:55 +0100 Subject: [PATCH 033/427] Create failing test. --- .../server/src/api/controllers/table/index.ts | 6 ++- .../server/src/api/routes/tests/table.spec.ts | 44 +++++++++++++++++++ packages/server/src/sdk/app/links/index.ts | 5 +++ packages/server/src/sdk/app/links/links.ts | 32 ++++++++++++++ packages/server/src/sdk/app/rows/search.ts | 6 +-- .../server/src/sdk/app/tables/migration.ts | 3 +- packages/server/src/sdk/index.ts | 2 + .../server/src/tests/utilities/api/table.ts | 22 +++++++++- packages/types/src/api/web/app/table.ts | 1 - 9 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 packages/server/src/sdk/app/links/index.ts create mode 100644 packages/server/src/sdk/app/links/links.ts diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index 132bae3d81..75532dbfdf 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -162,7 +162,11 @@ export async function validateExistingTableImport(ctx: UserCtx) { } export async function migrate(ctx: UserCtx) { - const { tableId, oldColumn, newColumn } = ctx.request.body + const { oldColumn, newColumn } = ctx.request.body + let tableId = ctx.params.tableId as string const table = await sdk.tables.getTable(tableId) await sdk.tables.migrate(table, oldColumn, newColumn) + + ctx.status = 200 + ctx.body = { message: `Column ${oldColumn.name} migrated.` } } diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index ded54729b9..eaf3757ea8 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -6,6 +6,8 @@ import { Table, ViewCalculation, AutoFieldSubTypes, + InternalTable, + FieldSubtype, } from "@budibase/types" import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" @@ -417,4 +419,46 @@ describe("/tables", () => { }) }) }) + + describe("migrate", () => { + it("should successfully migrate a user relationship to a user column", async () => { + const users = await config.api.row.fetch(InternalTable.USER_METADATA) + const table = await config.api.table.create({ + name: "table", + type: "table", + schema: { + "user relationship": { + type: FieldType.LINK, + fieldName: "test", + name: "user relationship", + constraints: { + type: "array", + presence: false, + }, + relationshipType: RelationshipType.ONE_TO_MANY, + tableId: InternalTable.USER_METADATA, + }, + }, + }) + + await config.api.row.save(table._id!, { + "user relationship": users, + }) + + await config.api.table.migrate(table._id!, { + oldColumn: table.schema["user relationship"], + newColumn: { + name: "user column", + type: FieldType.BB_REFERENCE, + subtype: FieldSubtype.USER, + }, + }) + + const migratedTable = await config.api.table.get(table._id!) + expect(migratedTable.schema["user column"]).toBeDefined() + + const rows = await config.api.row.fetch(table._id!) + expect(rows[0]["user column"]).toBeDefined() + }) + }) }) diff --git a/packages/server/src/sdk/app/links/index.ts b/packages/server/src/sdk/app/links/index.ts new file mode 100644 index 0000000000..6655a76656 --- /dev/null +++ b/packages/server/src/sdk/app/links/index.ts @@ -0,0 +1,5 @@ +import * as links from "./links" + +export default { + ...links, +} diff --git a/packages/server/src/sdk/app/links/links.ts b/packages/server/src/sdk/app/links/links.ts new file mode 100644 index 0000000000..7f3dcbd4ac --- /dev/null +++ b/packages/server/src/sdk/app/links/links.ts @@ -0,0 +1,32 @@ +import { context } from "@budibase/backend-core" +import { isTableId } from "@budibase/backend-core/src/docIds" +import { LinkDocument, LinkDocumentValue } from "@budibase/types" +import { ViewName, getQueryIndex } from "../../../../src/db/utils" + +export async function fetch(tableId: string): Promise { + if (!isTableId(tableId)) { + throw new Error(`Invalid tableId: ${tableId}`) + } + + const db = context.getAppDB() + const params: any = { startkey: [tableId], endkey: [tableId, {}] } + const linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows + return linkRows.map(row => row.value as LinkDocumentValue) +} + +export async function fetchWithDocument( + tableId: string +): Promise { + if (!isTableId(tableId)) { + throw new Error(`Invalid tableId: ${tableId}`) + } + + const db = context.getAppDB() + const params: any = { + startkey: [tableId], + endkey: [tableId, {}], + include_docs: true, + } + const linkRows = (await db.query(getQueryIndex(ViewName.LINK), params)).rows + return linkRows.map(row => row.doc as LinkDocument) +} diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts index ced35db9be..f75bd07437 100644 --- a/packages/server/src/sdk/app/rows/search.ts +++ b/packages/server/src/sdk/app/rows/search.ts @@ -1,4 +1,4 @@ -import { SearchFilters, SearchParams, Row } from "@budibase/types" +import { SearchFilters, SearchParams } from "@budibase/types" import { isExternalTable } from "../../../integrations/utils" import * as internal from "./search/internal" import * as external from "./search/external" @@ -45,7 +45,7 @@ export async function exportRows( return pickApi(options.tableId).exportRows(options) } -export async function fetch(tableId: string): Promise { +export async function fetch(tableId: string) { return pickApi(tableId).fetch(tableId) } @@ -53,6 +53,6 @@ export async function fetchView( tableId: string, viewName: string, params: ViewParams -): Promise { +) { return pickApi(tableId).fetchView(viewName, params) } diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index f727b5ea9d..ac7f3a3765 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -8,8 +8,8 @@ import { isBBReferenceField, isRelationshipField, } from "@budibase/types" -import { isExternalTable } from "src/integrations/utils" import sdk from "../../../sdk" +import { isExternalTable } from "../../../../src/integrations/utils" export async function migrate( table: Table, @@ -80,5 +80,6 @@ class UserColumnMigrator implements ColumnMigrator { async doMigration() { let rows = await sdk.rows.fetch(this.table._id!) + let links = await sdk.links.fetchWithDocument(this.table._id!) } } diff --git a/packages/server/src/sdk/index.ts b/packages/server/src/sdk/index.ts index 24eb1ebf3c..c3057e3d4f 100644 --- a/packages/server/src/sdk/index.ts +++ b/packages/server/src/sdk/index.ts @@ -5,6 +5,7 @@ import { default as applications } from "./app/applications" import { default as datasources } from "./app/datasources" import { default as queries } from "./app/queries" import { default as rows } from "./app/rows" +import { default as links } from "./app/links" import { default as users } from "./users" import { default as plugins } from "./plugins" import * as views from "./app/views" @@ -22,6 +23,7 @@ const sdk = { plugins, views, permissions, + links, } // default export for TS diff --git a/packages/server/src/tests/utilities/api/table.ts b/packages/server/src/tests/utilities/api/table.ts index 04432a788a..501841f6e7 100644 --- a/packages/server/src/tests/utilities/api/table.ts +++ b/packages/server/src/tests/utilities/api/table.ts @@ -1,4 +1,10 @@ -import { SaveTableRequest, SaveTableResponse, Table } from "@budibase/types" +import { + MigrateRequest, + MigrateResponse, + SaveTableRequest, + SaveTableResponse, + Table, +} from "@budibase/types" import TestConfiguration from "../TestConfiguration" import { TestAPI } from "./base" @@ -42,4 +48,18 @@ export class TableAPI extends TestAPI { .expect(expectStatus) return res.body } + + migrate = async ( + tableId: string, + data: MigrateRequest, + { expectStatus } = { expectStatus: 200 } + ): Promise => { + const res = await this.request + .post(`/api/tables/${tableId}/migrate`) + .send(data) + .set(this.config.defaultHeaders()) + .expect("Content-Type", /json/) + .expect(expectStatus) + return res.body + } } diff --git a/packages/types/src/api/web/app/table.ts b/packages/types/src/api/web/app/table.ts index 61da12d041..f4d6720516 100644 --- a/packages/types/src/api/web/app/table.ts +++ b/packages/types/src/api/web/app/table.ts @@ -36,7 +36,6 @@ export interface BulkImportResponse { } export interface MigrateRequest { - tableId: string oldColumn: FieldSchema newColumn: FieldSchema } From 1771b5905a37e544261a2fb8b2deca58786aeabc Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 18 Oct 2023 18:02:10 +0100 Subject: [PATCH 034/427] Most of the way to getting my first test passing. --- .../server/src/api/routes/tests/table.spec.ts | 2 + packages/server/src/sdk/app/tables/index.ts | 17 ++++++--- .../server/src/sdk/app/tables/migration.ts | 38 +++++++++++++++++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index eaf3757ea8..dcf1704ed0 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -456,9 +456,11 @@ describe("/tables", () => { const migratedTable = await config.api.table.get(table._id!) expect(migratedTable.schema["user column"]).toBeDefined() + expect(migratedTable.schema["user relationship"]).not.toBeDefined() const rows = await config.api.row.fetch(table._id!) expect(rows[0]["user column"]).toBeDefined() + expect(rows[0]["user relationship"]).not.toBeDefined() }) }) }) diff --git a/packages/server/src/sdk/app/tables/index.ts b/packages/server/src/sdk/app/tables/index.ts index e4884b2ec4..fab8c7d198 100644 --- a/packages/server/src/sdk/app/tables/index.ts +++ b/packages/server/src/sdk/app/tables/index.ts @@ -16,6 +16,8 @@ import datasources from "../datasources" import { populateExternalTableSchemas } from "./validation" import sdk from "../../../sdk" import { migrate } from "./migration" +import { DocumentInsertResponse } from "@budibase/nano" +import { cloneDeep } from "lodash" async function getAllInternalTables(db?: Database): Promise { if (!db) { @@ -75,23 +77,28 @@ function enrichViewSchemas(table: Table): TableResponse { } } -async function saveTable(table: Table) { +async function saveTable(table: Table): Promise { const db = context.getAppDB() + let resp: DocumentInsertResponse if (isExternalTable(table._id!)) { const datasource = await sdk.datasources.get(table.sourceId!) datasource.entities![table.name] = table - await db.put(datasource) + resp = await db.put(datasource) } else { - await db.put(table) + resp = await db.put(table) } + + let tableClone = cloneDeep(table) + tableClone._rev = resp.rev + return tableClone } -async function addColumn(table: Table, newColumn: FieldSchema) { +async function addColumn(table: Table, newColumn: FieldSchema): Promise
{ if (newColumn.name in table.schema) { throw `Column "${newColumn.name}" already exists on table "${table.name}"` } table.schema[newColumn.name] = newColumn - await saveTable(table) + return await saveTable(table) } export default { diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index ac7f3a3765..a08b56826d 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -1,15 +1,19 @@ -import { BadRequestError } from "@budibase/backend-core" +import { BadRequestError, context } from "@budibase/backend-core" import { BBReferenceFieldMetadata, FieldSchema, InternalTable, RelationshipFieldMetadata, + Row, Table, isBBReferenceField, isRelationshipField, } from "@budibase/types" import sdk from "../../../sdk" import { isExternalTable } from "../../../../src/integrations/utils" +import { db as dbCore } from "@budibase/backend-core" +import { EventType, updateLinks } from "../../../../src/db/linkedRows" +import { cloneDeep } from "lodash" export async function migrate( table: Table, @@ -17,10 +21,15 @@ export async function migrate( newColumn: FieldSchema ) { let migrator = getColumnMigrator(table, oldColumn, newColumn) + let oldTable = cloneDeep(table) - await sdk.tables.addColumn(table, newColumn) + table = await sdk.tables.addColumn(table, newColumn) - migrator.doMigration() + await migrator.doMigration() + + delete table.schema[oldColumn.name] + await sdk.tables.saveTable(table) + await updateLinks({ eventType: EventType.TABLE_UPDATED, table, oldTable }) } interface ColumnMigrator { @@ -80,6 +89,29 @@ class UserColumnMigrator implements ColumnMigrator { async doMigration() { let rows = await sdk.rows.fetch(this.table._id!) + let rowsById = rows.reduce((acc, row) => { + acc[row._id!] = row + return acc + }, {} as Record) + let links = await sdk.links.fetchWithDocument(this.table._id!) + for (let link of links) { + if (link.doc1.tableId !== this.table._id) { + continue + } + if (link.doc1.fieldName !== this.oldColumn.name) { + continue + } + if (link.doc2.tableId !== InternalTable.USER_METADATA) { + continue + } + + let userId = dbCore.getGlobalIDFromUserMetadataID(link.doc2.rowId) + let row = rowsById[link.doc1.rowId] + row[this.newColumn.name] = userId + } + + let db = context.getAppDB() + await db.bulkDocs(rows) } } From 77729737bce3cceabcef7290bbf46321939bccd4 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Thu, 19 Oct 2023 09:47:50 +0100 Subject: [PATCH 035/427] First test passes! --- packages/server/src/sdk/app/rows/external.ts | 2 +- packages/server/src/sdk/app/rows/search.ts | 8 ++++++-- packages/server/src/sdk/app/rows/search/external.ts | 6 ++++++ packages/server/src/sdk/app/rows/search/internal.ts | 9 ++++----- packages/server/src/sdk/app/tables/migration.ts | 4 ++-- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/server/src/sdk/app/rows/external.ts b/packages/server/src/sdk/app/rows/external.ts index 8bcf89a3f5..beae02e134 100644 --- a/packages/server/src/sdk/app/rows/external.ts +++ b/packages/server/src/sdk/app/rows/external.ts @@ -1,4 +1,4 @@ -import { IncludeRelationship, Operation, Row } from "@budibase/types" +import { IncludeRelationship, Operation } from "@budibase/types" import { handleRequest } from "../../../api/controllers/row/external" import { breakRowIdField } from "../../../integrations/utils" diff --git a/packages/server/src/sdk/app/rows/search.ts b/packages/server/src/sdk/app/rows/search.ts index f75bd07437..221aa6486c 100644 --- a/packages/server/src/sdk/app/rows/search.ts +++ b/packages/server/src/sdk/app/rows/search.ts @@ -1,4 +1,4 @@ -import { SearchFilters, SearchParams } from "@budibase/types" +import { Row, SearchFilters, SearchParams } from "@budibase/types" import { isExternalTable } from "../../../integrations/utils" import * as internal from "./search/internal" import * as external from "./search/external" @@ -45,10 +45,14 @@ export async function exportRows( return pickApi(options.tableId).exportRows(options) } -export async function fetch(tableId: string) { +export async function fetch(tableId: string): Promise { return pickApi(tableId).fetch(tableId) } +export async function fetchRaw(tableId: string): Promise { + return pickApi(tableId).fetchRaw(tableId) +} + export async function fetchView( tableId: string, viewName: string, diff --git a/packages/server/src/sdk/app/rows/search/external.ts b/packages/server/src/sdk/app/rows/search/external.ts index c41efad171..981ae1bf8d 100644 --- a/packages/server/src/sdk/app/rows/search/external.ts +++ b/packages/server/src/sdk/app/rows/search/external.ts @@ -186,6 +186,12 @@ export async function fetch(tableId: string): Promise { }) } +export async function fetchRaw(tableId: string): Promise { + return await handleRequest(Operation.READ, tableId, { + includeSqlRelationships: IncludeRelationship.INCLUDE, + }) +} + export async function fetchView(viewName: string) { // there are no views in external datasources, shouldn't ever be called // for now just fetch diff --git a/packages/server/src/sdk/app/rows/search/internal.ts b/packages/server/src/sdk/app/rows/search/internal.ts index 779ff5f777..58611c8849 100644 --- a/packages/server/src/sdk/app/rows/search/internal.ts +++ b/packages/server/src/sdk/app/rows/search/internal.ts @@ -140,14 +140,13 @@ export async function exportRows( } export async function fetch(tableId: string): Promise { - const db = context.getAppDB() - const table = await sdk.tables.getTable(tableId) - const rows = await getRawTableData(db, tableId) + const rows = await fetchRaw(tableId) return await outputProcessing(table, rows) } -async function getRawTableData(db: Database, tableId: string) { +export async function fetchRaw(tableId: string): Promise { + const db = context.getAppDB() let rows if (tableId === InternalTables.USER_METADATA) { rows = await sdk.users.fetchMetadata() @@ -182,7 +181,7 @@ export async function fetchView( }) } else { const tableId = viewInfo.meta.tableId - const data = await getRawTableData(db, tableId) + const data = await fetchRaw(tableId) response = await inMemoryViews.runView( viewInfo, calculation as string, diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index a08b56826d..cea15114a1 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -28,7 +28,7 @@ export async function migrate( await migrator.doMigration() delete table.schema[oldColumn.name] - await sdk.tables.saveTable(table) + table = await sdk.tables.saveTable(table) await updateLinks({ eventType: EventType.TABLE_UPDATED, table, oldTable }) } @@ -88,7 +88,7 @@ class UserColumnMigrator implements ColumnMigrator { ) {} async doMigration() { - let rows = await sdk.rows.fetch(this.table._id!) + let rows = await sdk.rows.fetchRaw(this.table._id!) let rowsById = rows.reduce((acc, row) => { acc[row._id!] = row return acc From 92e091818b20113ecc332da17d227ca0247e1217 Mon Sep 17 00:00:00 2001 From: Duarte Velez Grilo Date: Thu, 19 Oct 2023 11:11:10 +0100 Subject: [PATCH 036/427] Update CodeScanner.svelte Removed quotes ("") from variable reference. I believe this is what is causing the test failure. --- packages/client/src/components/app/forms/CodeScanner.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/components/app/forms/CodeScanner.svelte b/packages/client/src/components/app/forms/CodeScanner.svelte index 891154939f..f68f0791f6 100644 --- a/packages/client/src/components/app/forms/CodeScanner.svelte +++ b/packages/client/src/components/app/forms/CodeScanner.svelte @@ -21,7 +21,7 @@ let cameraEnabled let cameraStarted = false let html5QrCode - let cameraSetting = { facingMode: "{preferredCamera}" } + let cameraSetting = { facingMode: {preferredCamera} } let cameraConfig = { fps: 25, qrbox: { width: 250, height: 250 }, From a069b343e9137a441d5af9305efc83e7ab23c42f Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 19 Oct 2023 11:29:37 +0100 Subject: [PATCH 037/427] Allow settings sections to be collapsible --- .../_components/Component/ComponentSettingsSection.svelte | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte index b4ed8995a0..a1ea13ce2b 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte @@ -123,10 +123,7 @@ {#each sections as section, idx (section.name)} {#if section.visible} - + {#if section.info} From a209280daa33c248f78844685ddca66692499c70 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 24 Oct 2023 15:17:22 +0100 Subject: [PATCH 129/427] naming automations --- .../builderStore/store/automation/index.js | 24 ++++++++ .../FlowChart/FlowItemHeader.svelte | 56 +++++++++++++------ 2 files changed, 63 insertions(+), 17 deletions(-) diff --git a/packages/builder/src/builderStore/store/automation/index.js b/packages/builder/src/builderStore/store/automation/index.js index c22240370b..dd28d14461 100644 --- a/packages/builder/src/builderStore/store/automation/index.js +++ b/packages/builder/src/builderStore/store/automation/index.js @@ -221,6 +221,30 @@ const automationActions = store => ({ newAutomation.definition.steps.splice(blockIdx, 0, block) await store.actions.save(newAutomation) }, + saveAutomationName: async (stepId, name) => { + const automation = get(selectedAutomation) + let newAutomation = cloneDeep(automation) + if (!automation) { + return + } + newAutomation.definition.stepNames = { + ...newAutomation.definition.stepNames, + [stepId]: name, + } + + await store.actions.save(newAutomation) + }, + deleteAutomationName: async stepId => { + const automation = get(selectedAutomation) + let newAutomation = cloneDeep(automation) + if (!automation) { + return + } + delete newAutomation.definition.stepNames[stepId] + + await store.actions.save(newAutomation) + }, + deleteAutomationBlock: async block => { const automation = get(selectedAutomation) let newAutomation = cloneDeep(automation) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index 0bbd2d2318..7cc124f44c 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -3,6 +3,7 @@ import { Icon, Body, StatusLight, AbsTooltip } from "@budibase/bbui" import { externalActions } from "./ExternalActions" import { createEventDispatcher } from "svelte" + import { Features } from "constants/backend/automations" export let block export let open @@ -10,16 +11,19 @@ export let testResult export let isTrigger export let idx + export let addLooping + export let deleteStep - $: console.log($selectedAutomation) let validRegex = /^[A-Za-z0-9_\s]+$/ let typing = false - let automationName = block?.name || "" - - $: automationNameError = getAutomationNameError(automationName) const dispatch = createEventDispatcher() + $: stepNames = $selectedAutomation.definition.stepNames + $: automationName = stepNames[block.id] || block.name || "" + $: automationNameError = getAutomationNameError(automationName) + $: status = updateStatus(testResult, isTrigger) + $: isTrigger = isTrigger || block.type === "TRIGGER" $: { if (!testResult) { testResult = $automationStore.testResults?.steps?.filter(step => @@ -27,9 +31,9 @@ )?.[0] } } - $: isTrigger = isTrigger || block.type === "TRIGGER" - - $: status = updateStatus(testResult, isTrigger) + $: loopBlock = $selectedAutomation?.definition.steps.find( + x => x.blockToLoop === block.id + ) async function onSelect(block) { await automationStore.update(state => { @@ -53,9 +57,11 @@ } const getAutomationNameError = name => { - let invalidRoleName = !validRegex.test(name) - if (invalidRoleName) { - return "Please enter a role name consisting of only alphanumeric symbols and underscores" + if (name.length > 0) { + let invalidRoleName = !validRegex.test(name) + if (invalidRoleName) { + return "Please enter a role name consisting of only alphanumeric symbols and underscores" + } } return null } @@ -63,6 +69,18 @@ const startTyping = async () => { typing = true } + + const saveName = async () => { + if (automationNameError) { + return + } + + if (automationName.length === 0) { + automationStore.actions.deleteAutomationName(block.id) + } else { + automationStore.actions.saveAutomationName(block.id, automationName) + } + }
{ automationNameError = getAutomationNameError(e.target.value) automationName = e.target.value @@ -106,13 +125,14 @@ automationNameError = false // Reset the error when input is valid } }} - value={automationName} on:click={startTyping} - on:blur={() => { + on:blur={async () => { typing = false if (automationNameError) { - automationName = block?.name + automationName = stepNames[block.id] automationNameError = null + } else { + await saveName() } }} /> @@ -137,11 +157,13 @@ }} > {#if !showTestStatus} - - - + {#if !isTrigger && !loopBlock && (block?.features?.[Features.LOOPING] || !block.features)} + + + + {/if} - + {/if} Date: Tue, 24 Oct 2023 15:17:39 +0100 Subject: [PATCH 130/427] update bindings to use names --- .../SetupPanel/AutomationBlockSetup.svelte | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index ce8c5c344c..fa1f0c76c9 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -183,20 +183,17 @@ } } const outputs = Object.entries(schema) - let bindingIcon = "" - let bindindingRank = 0 - + let bindingRank = 0 if (idx === 0) { bindingIcon = automation.trigger.icon } else if (isLoopBlock) { bindingIcon = "Reuse" - bindindingRank = idx + 1 + bindingRank = idx + 1 } else { bindingIcon = allSteps[idx].icon - bindindingRank = idx - loopBlockCount + bindingRank = idx - loopBlockCount } - bindings = bindings.concat( outputs.map(([name, value]) => { let runtimeName = isLoopBlock @@ -205,12 +202,21 @@ ? `steps[${idx - loopBlockCount}].${name}` : `steps.${idx - loopBlockCount}.${name}` const runtime = idx === 0 ? `trigger.${name}` : runtimeName - const categoryName = - idx === 0 - ? "Trigger outputs" - : isLoopBlock - ? "Loop Outputs" - : `Step ${idx - loopBlockCount} outputs` + + let bindingName = + automation.stepNames[allSteps[bindingRank - loopBlockCount].id] + + let categoryName + if (idx === 0) { + categoryName = "Trigger outputs" + } else if (isLoopBlock) { + categoryName = "Loop Outputs" + } else if (bindingName) { + categoryName = `${bindingName} outputs` + } else { + categoryName = `Step ${idx - loopBlockCount} outputs` + } + return { readableBinding: runtime, runtimeBinding: runtime, @@ -221,7 +227,7 @@ display: { type: value.type, name: name, - rank: bindindingRank, + rank: bindingRank, }, } }) From 791020580720c7098274329b64a7e82895363a11 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 24 Oct 2023 15:17:57 +0100 Subject: [PATCH 131/427] update styling of flow items --- .../FlowChart/FlowChart.svelte | 139 +++++++++++------- .../FlowChart/FlowItem.svelte | 24 +-- .../[application]/automation/_layout.svelte | 1 - 3 files changed, 92 insertions(+), 72 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte index 63a3478ef3..784cdf2357 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowChart.svelte @@ -5,13 +5,7 @@ import TestDataModal from "./TestDataModal.svelte" import { flip } from "svelte/animate" import { fly } from "svelte/transition" - import { - Heading, - Icon, - ActionButton, - notifications, - Modal, - } from "@budibase/bbui" + import { Icon, notifications, Modal } from "@budibase/bbui" import { ActionStepID } from "constants/backend/automations" import UndoRedoControl from "components/common/UndoRedoControl.svelte" import { automationHistoryStore } from "builderStore" @@ -20,7 +14,8 @@ let testDataModal let confirmDeleteDialog - + let scrolling = false + let hasScrolled = false $: blocks = getBlocks(automation) const getBlocks = automation => { @@ -32,58 +27,74 @@ return blocks } - async function deleteAutomation() { + const deleteAutomation = async () => { try { await automationStore.actions.delete($selectedAutomation) } catch (error) { notifications.error("Error deleting automation") } } + + const handleScroll = e => { + if (e.target.scrollTop >= 30 && !hasScrolled) { + scrolling = true + hasScrolled = true + } else if (e.target.scrollTop < 30 && hasScrolled) { + // Set scrolling back to false if scrolled back to less than 100px + scrolling = false + hasScrolled = false + } + } -
-
- {automation.name} -
- +
+
+ +
+
+
+ +
{ + testDataModal.show() + }} + > + Run test +
+
+
-
- { - testDataModal.show() - }} - icon="MultipleCheck" - size="M">Run test - { - $automationStore.showTestPanel = true - }} - size="M">Test Details +
{ + $automationStore.showTestPanel = true + }} + > + Test details
-
- {#each blocks as block, idx (block.id)} -
- {#if block.stepId !== ActionStepID.LOOP} - - {/if} -
- {/each} +
+
+ {#each blocks as block, idx (block.id)} +
+ {#if block.stepId !== ActionStepID.LOOP} + + {/if} +
+ {/each} +
.canvas { padding: var(--spacing-l) var(--spacing-xl); + overflow-y: auto; + max-height: 100%; + } + + .header-left :global(div) { + border-right: none; } /* Fix for firefox not respecting bottom padding in scrolling containers */ .canvas > *:last-child { @@ -117,23 +134,45 @@ } .content { - display: inline-block; - text-align: left; + flex-grow: 1; + padding: 23px 23px 80px; + box-sizing: border-box; + } + + .header.scrolling { + background: var(--background); + z-index: -1; + border-bottom: var(--border-light); + background: var(--background); } .header { + z-index: 1; display: flex; justify-content: space-between; align-items: center; + padding-left: var(--spacing-l); + transition: background 130ms ease-out; + flex: 0 0 48px; + padding-right: var(--spacing-xl); + } + .controls { + display: flex; + gap: var(--spacing-xl); } - .controls, .buttons { display: flex; justify-content: flex-end; align-items: center; - gap: var(--spacing-xl); - } - .buttons { gap: var(--spacing-s); } + + .buttons:hover { + cursor: pointer; + } + + .disabled { + pointer-events: none; + color: var(--spectrum-global-color-gray-500) !important; + } diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index 0517fe1034..7d00ee4ac1 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -16,11 +16,7 @@ import ActionModal from "./ActionModal.svelte" import FlowItemHeader from "./FlowItemHeader.svelte" import RoleSelect from "components/design/settings/controls/RoleSelect.svelte" - import { - ActionStepID, - TriggerStepID, - Features, - } from "constants/backend/automations" + import { ActionStepID, TriggerStepID } from "constants/backend/automations" import { permissions } from "stores/backend" export let block @@ -172,28 +168,14 @@ {block} {testDataModal} {idx} + {addLooping} + {deleteStep} on:toggle={() => (open = !open)} /> {#if open}
- {#if !isTrigger} -
-
- {#if !loopBlock && (block?.features?.[Features.LOOPING] || !block.features)} - addLooping()} icon="Reuse"> - Add Looping - - {/if} - deleteStep()} - icon="DeleteOutline" - /> -
-
- {/if} - {#if isAppAction} 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 0afe257e60..dd18dbba82 100644 --- a/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/automation/_layout.svelte @@ -91,7 +91,6 @@ flex-direction: column; justify-content: flex-start; align-items: stretch; - gap: var(--spacing-l); overflow: auto; } .centered { From edb511cbce84e9ed22dd8a502d0a66353516c693 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Wed, 25 Oct 2023 11:57:52 +0100 Subject: [PATCH 132/427] update field styling and ux for individual blocks --- packages/builder/.gitignore | 3 +- .../FlowChart/FlowItem.svelte | 14 +- .../SetupPanel/AutomationBlockSetup.svelte | 434 +++++++++--------- .../automation/SetupPanel/RowSelector.svelte | 122 ++--- .../SetupPanel/RowSelectorTypes.svelte | 4 - 5 files changed, 297 insertions(+), 280 deletions(-) diff --git a/packages/builder/.gitignore b/packages/builder/.gitignore index e5c961d509..abc5671984 100644 --- a/packages/builder/.gitignore +++ b/packages/builder/.gitignore @@ -5,4 +5,5 @@ package-lock.json release/ dist/ routify -.routify/ \ No newline at end of file +.routify/ +svelte.config.js \ No newline at end of file diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index 7d00ee4ac1..2cdb1e1e72 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -100,19 +100,13 @@ } -
{}} - on:click={() => {}} -> +
{}}> {#if loopBlock}
{ showLooping = !showLooping }} - on:keydown={() => {}} class="splitHeader" >
@@ -131,11 +125,7 @@
-
{}} - on:click={() => {}} - > +
{}}>
diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index fa1f0c76c9..3a96363751 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -295,245 +295,248 @@
{#each schemaProperties as [key, value]} {#if canShowField(key, value)} -
- {#if key !== "fields" && value.type !== "boolean"} +
+ {#if key !== "fields" && value.type !== "boolean" && value.customType !== "row"} {/if} - {#if value.type === "string" && value.enum && canShowField(key, value)} - onChange(e, key)} - /> -
- {:else if value.type === "date"} - onChange(e, key)} - {bindings} - allowJS={true} - updateOnChange={false} - drawerLeft="260px" - > - onChange(e, key)} + placeholder={false} + options={value.enum} + getOptionLabel={(x, idx) => + value.pretty ? value.pretty[idx] : x} /> - - {:else if value.customType === "column"} - onChange(e, key)} - value={inputData[key]} - /> - {:else if value.customType === "email"} - {#if isTestModal} - onChange(e, key)} - {bindings} - fillWidth - updateOnChange={false} - /> - {:else} - + onChange(e, key)} + /> +
+ {:else if value.type === "date"} + onChange(e, key)} {bindings} - allowJS={false} + allowJS={true} updateOnChange={false} drawerLeft="260px" - /> - {/if} - {:else if value.customType === "query"} - onChange(e, key)} - value={inputData[key]} - /> - {:else if value.customType === "cron"} - onChange(e, key)} - value={inputData[key]} - /> - {:else if value.customType === "queryParams"} - onChange(e, key)} - value={inputData[key]} - {bindings} - /> - {:else if value.customType === "table"} - onChange(e, key)} - /> - {:else if value.customType === "row"} - { - if (e.detail?.key) { - onChange(e, e.detail.key) - } else { - onChange(e, key) - } - }} - {bindings} - {isTestModal} - {isUpdateRow} - /> - {:else if value.customType === "webhookUrl"} - onChange(e, key)} - value={inputData[key]} - /> - {:else if value.customType === "fields"} - onChange(e, key)} - {bindings} - {isTestModal} - /> - {:else if value.customType === "triggerSchema"} - onChange(e, key)} - value={inputData[key]} - /> - {:else if value.customType === "code"} - - {#if codeMode == EditorModes.JS} - (codeBindingOpen = !codeBindingOpen)} - quiet - icon={codeBindingOpen ? "ChevronDown" : "ChevronRight"} - > - Bindings - - {#if codeBindingOpen} -
{JSON.stringify(bindings, null, 2)}
- {/if} - {/if} - { - // need to pass without the value inside - onChange({ detail: e.detail }, key) - inputData[key] = e.detail - }} - completions={stepCompletions} - mode={codeMode} - autocompleteEnabled={codeMode != EditorModes.JS} - height={500} - /> -
- {#if codeMode == EditorModes.Handlebars} - -
-
- Add available bindings by typing - }} - -
-
- {/if} -
-
- {:else if value.customType === "loopOption"} - onChange(e, key)} - {bindings} - updateOnChange={false} + value={inputData[key]} + options={Object.keys(table?.schema || {})} /> - {:else} -
+ {:else if value.customType === "filters"} + Define filters + + + (tempFilters = e.detail)} + /> + + {:else if value.customType === "password"} + onChange(e, key)} + value={inputData[key]} + /> + {:else if value.customType === "email"} + {#if isTestModal} + onChange(e, key)} + {bindings} + fillWidth + updateOnChange={false} + /> + {:else} onChange(e, key)} {bindings} + allowJS={false} updateOnChange={false} - placeholder={value.customType === "queryLimit" - ? queryLimit - : ""} drawerLeft="260px" /> -
+ {/if} + {:else if value.customType === "query"} + onChange(e, key)} + value={inputData[key]} + /> + {:else if value.customType === "cron"} + onChange(e, key)} + value={inputData[key]} + /> + {:else if value.customType === "queryParams"} + onChange(e, key)} + value={inputData[key]} + {bindings} + /> + {:else if value.customType === "table"} + onChange(e, key)} + /> + {:else if value.customType === "row"} + { + if (e.detail?.key) { + onChange(e, e.detail.key) + } else { + onChange(e, key) + } + }} + {bindings} + {isTestModal} + {isUpdateRow} + /> + {:else if value.customType === "webhookUrl"} + onChange(e, key)} + value={inputData[key]} + /> + {:else if value.customType === "fields"} + onChange(e, key)} + {bindings} + {isTestModal} + /> + {:else if value.customType === "triggerSchema"} + onChange(e, key)} + value={inputData[key]} + /> + {:else if value.customType === "code"} + + {#if codeMode == EditorModes.JS} + (codeBindingOpen = !codeBindingOpen)} + quiet + icon={codeBindingOpen ? "ChevronDown" : "ChevronRight"} + > + Bindings + + {#if codeBindingOpen} +
{JSON.stringify(bindings, null, 2)}
+ {/if} + {/if} + { + // need to pass without the value inside + onChange({ detail: e.detail }, key) + inputData[key] = e.detail + }} + completions={stepCompletions} + mode={codeMode} + autocompleteEnabled={codeMode != EditorModes.JS} + height={500} + /> +
+ {#if codeMode == EditorModes.Handlebars} + +
+
+ Add available bindings by typing + }} + +
+
+ {/if} +
+
+ {:else if value.customType === "loopOption"} + table.name} - getOptionValue={table => table._id} -/> +
+ +
+ onChange(e, field)} - label={field} value={value[field]} options={schema.constraints.inclusion} /> @@ -46,7 +45,6 @@ {:else if schema.type === "boolean"} { automationNameError = getAutomationNameError(e.target.value) From 70f39b6b6bc86ad34ef7f80826b1d6e4707ef39f Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 14:41:23 +0100 Subject: [PATCH 134/427] Fix missing sourceId on Table sent to the websocket. --- packages/builder/src/builderStore/websocket.js | 1 + packages/server/src/api/controllers/table/index.ts | 5 ++++- packages/server/src/sdk/app/tables/getters.ts | 12 ++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/builderStore/websocket.js b/packages/builder/src/builderStore/websocket.js index 6121831c38..2aed5a3f8f 100644 --- a/packages/builder/src/builderStore/websocket.js +++ b/packages/builder/src/builderStore/websocket.js @@ -47,6 +47,7 @@ export const createBuilderWebsocket = appId => { // Data section events socket.onOther(BuilderSocketEvent.TableChange, ({ id, table }) => { + console.log("Table change", id, table) tables.replaceTable(id, table) }) socket.onOther(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => { diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index af3c2c0c15..f139313086 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -24,6 +24,7 @@ import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" import { builderSocket } from "../../../websockets" import { cloneDeep, isEqual } from "lodash" +import { processInternalTable } from "../../../sdk/app/tables/getters" function pickApi({ tableId, table }: { tableId?: string; table?: Table }) { if (table && !tableId) { @@ -168,7 +169,9 @@ export async function migrate(ctx: UserCtx) { let result = await sdk.tables.migrate(table, oldColumn, newColumn) for (let table of result.tablesUpdated) { - builderSocket?.emitTableUpdate(ctx, table, { includeSelf: true }) + builderSocket?.emitTableUpdate(ctx, processInternalTable(table), { + includeSelf: true, + }) } ctx.status = 200 diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 02cef748c5..34cddc8dc7 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -20,11 +20,15 @@ import datasources from "../datasources" import sdk from "../../../sdk" function processInternalTables(docs: AllDocsResponse): Table[] { - return docs.rows.map((tableDoc: any) => ({ - ...tableDoc.doc, + return docs.rows.map(tableDoc => processInternalTable(tableDoc.doc)) +} + +export function processInternalTable(table: Table): Table { + return { + ...table, type: "internal", - sourceId: tableDoc.doc.sourceId || BudibaseInternalDB._id, - })) + sourceId: table.sourceId || BudibaseInternalDB._id, + } } export async function getAllInternalTables(db?: Database): Promise { From 007e919d0edf97bd138c101981d02a5c06fc8153 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 14:53:51 +0100 Subject: [PATCH 135/427] Spring cleaning before review. --- packages/server/src/api/controllers/table/index.ts | 4 ++-- packages/server/src/integrations/utils.ts | 4 ++++ packages/server/src/websockets/builder.ts | 9 +++++++++ packages/server/src/websockets/websocket.ts | 8 +++++--- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index f139313086..d2ad63c13e 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -169,8 +169,8 @@ export async function migrate(ctx: UserCtx) { let result = await sdk.tables.migrate(table, oldColumn, newColumn) for (let table of result.tablesUpdated) { - builderSocket?.emitTableUpdate(ctx, processInternalTable(table), { - includeSelf: true, + builderSocket?.emitTableUpdate(ctx, table, { + includeOriginator: true, }) } diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index 79b18e767c..b37fe9f0ed 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -87,6 +87,10 @@ export function isExternalTable(tableId: string) { return tableId.includes(DocumentType.DATASOURCE) } +export function isInternalTable(tableId: string) { + return !isExternalTable(tableId) +} + export function buildExternalTableId(datasourceId: string, tableName: string) { // encode spaces if (tableName.includes(" ")) { diff --git a/packages/server/src/websockets/builder.ts b/packages/server/src/websockets/builder.ts index b1ef473d6c..2cff24e635 100644 --- a/packages/server/src/websockets/builder.ts +++ b/packages/server/src/websockets/builder.ts @@ -16,6 +16,8 @@ import { gridSocket } from "./index" import { clearLock, updateLock } from "../utilities/redis" import { Socket } from "socket.io" import { BuilderSocketEvent } from "@budibase/shared-core" +import { processInternalTable } from "../sdk/app/tables/getters" +import { isExternalTable, isInternalTable } from "../integrations/utils" export default class BuilderSocket extends BaseSocket { constructor(app: Koa, server: http.Server) { @@ -101,6 +103,13 @@ export default class BuilderSocket extends BaseSocket { } emitTableUpdate(ctx: any, table: Table, options?: EmitOptions) { + // This was added to make sure that sourceId is always present when + // sending this message to clients. Without this, tables without a + // sourceId (e.g. ta_users) won't get correctly updated client-side. + if (isInternalTable(table._id!)) { + table = processInternalTable(table) + } + this.emitToRoom( ctx, ctx.appId, diff --git a/packages/server/src/websockets/websocket.ts b/packages/server/src/websockets/websocket.ts index a0dc388d4f..1dba108d24 100644 --- a/packages/server/src/websockets/websocket.ts +++ b/packages/server/src/websockets/websocket.ts @@ -13,8 +13,10 @@ import { createContext, runMiddlewares } from "./middleware" export interface EmitOptions { // Whether to include the originator of the request from the broadcast, - // defaults to false. - includeSelf?: boolean + // defaults to false because it is assumed that the user who triggered + // an action will already have the changes of that action reflected in their + // own UI, so there is no need to send them again. + includeOriginator?: boolean } const anonUser = () => ({ @@ -284,7 +286,7 @@ export class BaseSocket { options?: EmitOptions ) { let emitPayload = { ...payload } - if (!options?.includeSelf) { + if (!options?.includeOriginator) { emitPayload.apiSessionId = ctx.headers?.[Header.SESSION_ID] } this.io.in(room).emit(event, emitPayload) From 5779e97e6eaf50687a0829592246d3081fc73a03 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 15:29:56 +0100 Subject: [PATCH 136/427] Add column name input validation. --- .../builder/src/builderStore/websocket.js | 1 - .../grid/controls/MigrationModal.svelte | 24 +++++++++++-------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/packages/builder/src/builderStore/websocket.js b/packages/builder/src/builderStore/websocket.js index 2aed5a3f8f..6121831c38 100644 --- a/packages/builder/src/builderStore/websocket.js +++ b/packages/builder/src/builderStore/websocket.js @@ -47,7 +47,6 @@ export const createBuilderWebsocket = appId => { // Data section events socket.onOther(BuilderSocketEvent.TableChange, ({ id, table }) => { - console.log("Table change", id, table) tables.replaceTable(id, table) }) socket.onOther(BuilderSocketEvent.DatasourceChange, ({ id, datasource }) => { diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index f4070e7a98..ee7fd7bfbe 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -6,13 +6,23 @@ InlineAlert, } from "@budibase/bbui" import { getContext } from "svelte" + import { ValidColumnNameRegex } from "@budibase/shared-core" const { API, dispatch, definition, rows } = getContext("grid") export let column - let newColumnName = `${column.schema.name} (migrated)` - $: newAndOldNameMatch = column.schema.name === newColumnName + let newColumnName = `${column.schema.name} migrated` + $: error = checkNewColumnName(newColumnName) + + const checkNewColumnName = newColumnName => { + if (column.schema.name === newColumnName) { + return "New column name can't be the same as the existing column name." + } + if (newColumnName.match(ValidColumnNameRegex) === null) { + return "Illegal character; must be alpha-numeric." + } + } const migrateUserColumn = async () => { let subtype = "users" @@ -44,7 +54,7 @@ confirmText="Continue" cancelText="Cancel" onConfirm={migrateUserColumn} - disabled={newAndOldNameMatch} + disabled={error !== undefined} size="M" > This operation will kick off a migration of the column "{column.schema.name}" @@ -56,11 +66,5 @@ header="Are you sure?" message="This will leave bindings which utilised the user relationship column in a state where they will need to be updated to use the new column instead." /> - + From 402a53cacb3e413f64a0473247e63e78355132c9 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 15:33:12 +0100 Subject: [PATCH 137/427] User constant for user table ID. --- .../frontend-core/src/components/grid/cells/HeaderCell.svelte | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte index dee73d17e9..02cf751e30 100644 --- a/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/HeaderCell.svelte @@ -14,6 +14,7 @@ import MigrationModal from "../controls/MigrationModal.svelte" import { debounce } from "../../../utils/utils" import { FieldType, FormulaTypes } from "@budibase/types" + import { TableNames } from "../../../constants" export let column export let idx @@ -381,7 +382,7 @@ > Hide column - {#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === "ta_users"} + {#if $config.canEditColumns && column.schema.type === "link" && column.schema.tableId === TableNames.USERS} Migrate to user column From 0ac6b17ff09441c06ae7d19fe7c51843bf99c069 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 15:33:53 +0100 Subject: [PATCH 138/427] Remove stray console.log --- .../src/components/backend/DataTable/TableDataTable.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 40bcf2b74e..22deacbe03 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -55,7 +55,6 @@ } const refreshDefinitions = async () => { - console.log("woot") await tables.fetch() await datasources.fetch() } From c9e3b7a348ce41bd3e9e4bc1bf406a83463f6d35 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:00:14 +0100 Subject: [PATCH 139/427] Remove the previous attempt at syncing grid state, it is no longer needed. --- .../src/components/backend/DataTable/TableDataTable.svelte | 6 ------ .../src/components/grid/controls/MigrationModal.svelte | 1 - 2 files changed, 7 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 22deacbe03..5fee849afb 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -53,11 +53,6 @@ await datasources.fetch() } } - - const refreshDefinitions = async () => { - await tables.fetch() - await datasources.fetch() - }
@@ -71,7 +66,6 @@ schemaOverrides={isUsersTable ? userSchemaOverrides : null} showAvatars={false} on:updatedatasource={handleGridTableUpdate} - on:refreshdefinitions={refreshDefinitions} > {#if isUsersTable && $store.features.disableUserMetadata} diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index ee7fd7bfbe..111ff86170 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -45,7 +45,6 @@ notifications.error(`Failed to migrate: ${e.message}`) } await rows.actions.refreshData() - dispatch("refreshdefintions") } From 7acce7b7c0b0a8ca4363263a8d728007c0eecfb8 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:01:08 +0100 Subject: [PATCH 140/427] Remove unused dispatch import. --- .../src/components/grid/controls/MigrationModal.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index 111ff86170..ec2972a03e 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -8,7 +8,7 @@ import { getContext } from "svelte" import { ValidColumnNameRegex } from "@budibase/shared-core" - const { API, dispatch, definition, rows } = getContext("grid") + const { API, definition, rows } = getContext("grid") export let column From 2c5dd99da202a45dfb97a5cb87351862ecc3627e Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:37:15 +0100 Subject: [PATCH 141/427] Use FieldSubtype enum instead of raw strings. --- .../src/components/grid/controls/MigrationModal.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index ec2972a03e..0f173da8b9 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -7,6 +7,7 @@ } from "@budibase/bbui" import { getContext } from "svelte" import { ValidColumnNameRegex } from "@budibase/shared-core" + import { FieldSubtype } from "@budibase/types" const { API, definition, rows } = getContext("grid") @@ -25,9 +26,9 @@ } const migrateUserColumn = async () => { - let subtype = "users" + let subtype = FieldSubtype.USERS if (column.schema.relationshipType === "one-to-many") { - subtype = "user" + subtype = FieldSubtype.USER } try { From ef84e96f98c7a38a6145fb47beee4bdbf5237a73 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:38:14 +0100 Subject: [PATCH 142/427] Use RelationshipType enum instead of raw string. --- .../src/components/grid/controls/MigrationModal.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index 0f173da8b9..c6a49c6a48 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -7,7 +7,7 @@ } from "@budibase/bbui" import { getContext } from "svelte" import { ValidColumnNameRegex } from "@budibase/shared-core" - import { FieldSubtype } from "@budibase/types" + import { FieldSubtype, RelationshipType } from "@budibase/types" const { API, definition, rows } = getContext("grid") @@ -27,7 +27,7 @@ const migrateUserColumn = async () => { let subtype = FieldSubtype.USERS - if (column.schema.relationshipType === "one-to-many") { + if (column.schema.relationshipType === RelationshipType.ONE_TO_MANY) { subtype = FieldSubtype.USER } From c5097487e22438a0e4fbc961f2cb422c07329a00 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:38:55 +0100 Subject: [PATCH 143/427] Use FieldType constant instead of raw string. --- .../src/components/grid/controls/MigrationModal.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index c6a49c6a48..73ea32408b 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -7,7 +7,7 @@ } from "@budibase/bbui" import { getContext } from "svelte" import { ValidColumnNameRegex } from "@budibase/shared-core" - import { FieldSubtype, RelationshipType } from "@budibase/types" + import { FieldSubtype, FieldType, RelationshipType } from "@budibase/types" const { API, definition, rows } = getContext("grid") @@ -37,7 +37,7 @@ oldColumn: column.schema, newColumn: { name: newColumnName, - type: "bb_reference", + type: FieldType.BB_REFERENCE, subtype, }, }) From e03b1be9d1b2c7c3e8c3ded8f0b3e304a23446bc Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:41:37 +0100 Subject: [PATCH 144/427] Make sure new column name cannot be the same as an existing column name. --- .../src/components/grid/controls/MigrationModal.svelte | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte index 73ea32408b..1957c3259f 100644 --- a/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte +++ b/packages/frontend-core/src/components/grid/controls/MigrationModal.svelte @@ -17,8 +17,8 @@ $: error = checkNewColumnName(newColumnName) const checkNewColumnName = newColumnName => { - if (column.schema.name === newColumnName) { - return "New column name can't be the same as the existing column name." + if (newColumnName in $definition.schema) { + return "New column name can't be the same as an existing column name." } if (newColumnName.match(ValidColumnNameRegex) === null) { return "Illegal character; must be alpha-numeric." From 4a00649f7f7c01d1280a08ff35cedd4963779021 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:46:14 +0100 Subject: [PATCH 145/427] Simplify the function signature of processInternalTables --- packages/server/src/sdk/app/tables/getters.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 34cddc8dc7..47da0beb40 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -19,8 +19,8 @@ import { import datasources from "../datasources" import sdk from "../../../sdk" -function processInternalTables(docs: AllDocsResponse): Table[] { - return docs.rows.map(tableDoc => processInternalTable(tableDoc.doc)) +function processInternalTables(tables: Table[]): Table[] { + return tables.map(processInternalTable) } export function processInternalTable(table: Table): Table { @@ -40,7 +40,7 @@ export async function getAllInternalTables(db?: Database): Promise { include_docs: true, }) ) - return processInternalTables(internalTables) + return processInternalTables(internalTables.rows.map(row => row.doc!)) } async function getAllExternalTables(): Promise { @@ -110,7 +110,9 @@ export async function getTables(tableIds: string[]): Promise { const internalTableDocs = await db.allDocs( getMultiIDParams(internalTableIds) ) - tables = tables.concat(processInternalTables(internalTableDocs)) + tables = tables.concat( + processInternalTables(internalTableDocs.rows.map(row => row.doc!)) + ) } return tables } From 6c3b535863878c521bb312606218b89fa2f0f787 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Wed, 25 Oct 2023 16:49:29 +0100 Subject: [PATCH 146/427] Simplify try-catch in the migrate function. --- packages/server/src/sdk/app/tables/migration.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index b678b64318..293f9184d6 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -34,10 +34,8 @@ export async function migrate( table = await sdk.tables.saveTable(table) let migrator = getColumnMigrator(table, oldColumn, newColumn) - let result: MigrationResult - try { - result = await migrator.doMigration() + return await migrator.doMigration() } catch (e) { // If the migration fails then we need to roll back the table schema // change. @@ -45,8 +43,6 @@ export async function migrate( await sdk.tables.saveTable(table) throw e } - - return result } interface ColumnMigrator { From 455b26bac95917be3b5d15176248498fd55635e9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 25 Oct 2023 19:00:25 +0100 Subject: [PATCH 147/427] Making sure the source ID is always set when creating a table - the frontend expects this to be set for every table so making the type represent this correctly. --- .../server/src/api/controllers/datasource.ts | 3 +- .../server/src/api/controllers/table/index.ts | 3 +- .../server/src/api/routes/tests/row.spec.ts | 6 +- .../server/src/api/routes/tests/table.spec.ts | 4 ++ .../src/api/routes/tests/viewV2.spec.ts | 2 + packages/server/src/constants/index.ts | 14 ++--- packages/server/src/db/utils.ts | 3 +- .../server/src/integrations/googlesheets.ts | 8 +-- .../src/integrations/microsoftSqlServer.ts | 6 +- packages/server/src/integrations/mysql.ts | 6 +- packages/server/src/integrations/oracle.ts | 6 +- packages/server/src/integrations/postgres.ts | 6 +- .../integrations/tests/googlesheets.spec.ts | 2 + packages/server/src/integrations/utils.ts | 9 ++- .../middleware/tests/trimViewRowInfo.spec.ts | 9 ++- .../src/sdk/app/rows/search/internal.ts | 6 +- .../app/rows/search/tests/external.spec.ts | 62 ++++++++++--------- .../app/rows/search/tests/internal.spec.ts | 9 ++- .../sdk/app/rows/search/tests/utils.spec.ts | 3 + .../src/sdk/app/tables/external/index.ts | 2 +- .../src/sdk/app/tables/external/utils.ts | 4 +- packages/server/src/sdk/app/tables/getters.ts | 10 +-- .../src/sdk/app/tables/tests/tables.spec.ts | 8 ++- .../sdk/app/tables/tests/validation.spec.ts | 41 ++++++++---- .../src/sdk/app/views/tests/views.spec.ts | 2 + .../src/tests/utilities/TestConfiguration.ts | 27 ++++++-- .../server/src/tests/utilities/structures.ts | 2 + .../tests/inputProcessing.spec.ts | 11 +++- .../tests/outputProcessing.spec.ts | 4 ++ packages/server/src/websockets/builder.ts | 2 +- .../types/src/documents/app/table/table.ts | 8 +-- packages/types/src/sdk/datasources.ts | 6 +- 32 files changed, 185 insertions(+), 109 deletions(-) diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts index b50c2464f0..5d024d51b6 100644 --- a/packages/server/src/api/controllers/datasource.ts +++ b/packages/server/src/api/controllers/datasource.ts @@ -12,7 +12,6 @@ import { CreateDatasourceResponse, Datasource, DatasourcePlus, - ExternalTable, FetchDatasourceInfoRequest, FetchDatasourceInfoResponse, IntegrationBase, @@ -59,7 +58,7 @@ async function buildSchemaHelper(datasource: Datasource): Promise { const connector = (await getConnector(datasource)) as DatasourcePlus return await connector.buildSchema( datasource._id!, - datasource.entities! as Record + datasource.entities! as Record ) } diff --git a/packages/server/src/api/controllers/table/index.ts b/packages/server/src/api/controllers/table/index.ts index d2ad63c13e..c814a37ad9 100644 --- a/packages/server/src/api/controllers/table/index.ts +++ b/packages/server/src/api/controllers/table/index.ts @@ -24,7 +24,6 @@ import sdk from "../../../sdk" import { jsonFromCsvString } from "../../../utilities/csv" import { builderSocket } from "../../../websockets" import { cloneDeep, isEqual } from "lodash" -import { processInternalTable } from "../../../sdk/app/tables/getters" function pickApi({ tableId, table }: { tableId?: string; table?: Table }) { if (table && !tableId) { @@ -50,7 +49,7 @@ export async function fetch(ctx: UserCtx) { return Object.values(entities).map
((entity: Table) => ({ ...entity, type: "external", - sourceId: datasource._id, + sourceId: datasource._id!, sql: isSQL(datasource), })) } else { diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 4c2e7a7494..e5a24c27e0 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -21,6 +21,7 @@ import { SortType, StaticQuotaName, Table, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import { expectAnyExternalColsAttributes, @@ -65,6 +66,7 @@ describe.each([ type: "table", primary: ["id"], primaryDisplay: "name", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { id: { type: FieldType.AUTO, @@ -880,6 +882,7 @@ describe.each([ async function userTable(): Promise
{ return { name: `users_${generator.word()}`, + sourceId: INTERNAL_TABLE_SOURCE_ID, type: "table", primary: ["id"], schema: { @@ -1062,6 +1065,7 @@ describe.each([ async function userTable(): Promise
{ return { name: `users_${generator.word()}`, + sourceId: INTERNAL_TABLE_SOURCE_ID, type: "table", primary: ["id"], schema: { @@ -1597,7 +1601,7 @@ describe.each([ const tableConfig = generateTableConfig() if (config.datasource) { - tableConfig.sourceId = config.datasource._id + tableConfig.sourceId = config.datasource._id! if (config.datasource.plus) { tableConfig.type = "external" } diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index 420717f7f0..f988b44e0f 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -9,6 +9,7 @@ import { InternalTable, FieldSubtype, Row, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import { checkBuilderEndpoint } from "./utilities/TestFunctions" import * as setup from "./utilities" @@ -432,6 +433,7 @@ describe("/tables", () => { const table = await config.api.table.create({ name: "table", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { "user relationship": { type: FieldType.LINK, @@ -491,6 +493,7 @@ describe("/tables", () => { const table = await config.api.table.create({ name: "table", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { "user relationship": { type: FieldType.LINK, @@ -552,6 +555,7 @@ describe("/tables", () => { const table = await config.api.table.create({ name: "table", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { "user relationship": { type: FieldType.LINK, diff --git a/packages/server/src/api/routes/tests/viewV2.spec.ts b/packages/server/src/api/routes/tests/viewV2.spec.ts index 40060aef48..a1ce5ec10e 100644 --- a/packages/server/src/api/routes/tests/viewV2.spec.ts +++ b/packages/server/src/api/routes/tests/viewV2.spec.ts @@ -10,6 +10,7 @@ import { UIFieldMetadata, UpdateViewRequest, ViewV2, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import { generator } from "@budibase/backend-core/tests" import { generateDatasourceID } from "../../../db/utils" @@ -18,6 +19,7 @@ function priceTable(): Table { return { name: "table", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { Price: { type: FieldType.NUMBER, diff --git a/packages/server/src/constants/index.ts b/packages/server/src/constants/index.ts index b37a4b36c1..fe69b3c9c8 100644 --- a/packages/server/src/constants/index.ts +++ b/packages/server/src/constants/index.ts @@ -1,5 +1,9 @@ import { objectStore, roles, constants } from "@budibase/backend-core" -import { FieldType as FieldTypes } from "@budibase/types" +import { + FieldType as FieldTypes, + Table, + INTERNAL_TABLE_SOURCE_ID, +} from "@budibase/types" export { FieldType as FieldTypes, RelationshipType, @@ -70,9 +74,10 @@ export enum SortDirection { DESCENDING = "DESCENDING", } -export const USERS_TABLE_SCHEMA = { +export const USERS_TABLE_SCHEMA: Table = { _id: "ta_users", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, views: {}, name: "Users", // TODO: ADMIN PANEL - when implemented this doesn't need to be carried out @@ -87,12 +92,10 @@ export const USERS_TABLE_SCHEMA = { }, presence: true, }, - fieldName: "email", name: "email", }, firstName: { name: "firstName", - fieldName: "firstName", type: FieldTypes.STRING, constraints: { type: FieldTypes.STRING, @@ -101,7 +104,6 @@ export const USERS_TABLE_SCHEMA = { }, lastName: { name: "lastName", - fieldName: "lastName", type: FieldTypes.STRING, constraints: { type: FieldTypes.STRING, @@ -109,7 +111,6 @@ export const USERS_TABLE_SCHEMA = { }, }, roleId: { - fieldName: "roleId", name: "roleId", type: FieldTypes.OPTIONS, constraints: { @@ -119,7 +120,6 @@ export const USERS_TABLE_SCHEMA = { }, }, status: { - fieldName: "status", name: "status", type: FieldTypes.OPTIONS, constraints: { diff --git a/packages/server/src/db/utils.ts b/packages/server/src/db/utils.ts index 2c07bd8d22..d532d8a8b2 100644 --- a/packages/server/src/db/utils.ts +++ b/packages/server/src/db/utils.ts @@ -5,6 +5,7 @@ import { FieldSchema, RelationshipFieldMetadata, VirtualDocumentType, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import { FieldTypes } from "../constants" export { DocumentType, VirtualDocumentType } from "@budibase/types" @@ -18,7 +19,7 @@ export const enum AppStatus { } export const BudibaseInternalDB = { - _id: "bb_internal", + _id: INTERNAL_TABLE_SOURCE_ID, type: dbCore.BUDIBASE_DATASOURCE_TYPE, name: "Budibase DB", source: "BUDIBASE", diff --git a/packages/server/src/integrations/googlesheets.ts b/packages/server/src/integrations/googlesheets.ts index 57b6682cc8..4433f45863 100644 --- a/packages/server/src/integrations/googlesheets.ts +++ b/packages/server/src/integrations/googlesheets.ts @@ -12,7 +12,7 @@ import { Row, SearchFilters, SortJson, - ExternalTable, + Table, TableRequest, Schema, } from "@budibase/types" @@ -262,7 +262,7 @@ class GoogleSheetsIntegration implements DatasourcePlus { id?: string ) { // base table - const table: ExternalTable = { + const table: Table = { name: title, primary: [GOOGLE_SHEETS_PRIMARY_KEY], schema: {}, @@ -283,7 +283,7 @@ class GoogleSheetsIntegration implements DatasourcePlus { async buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise { // not fully configured yet if (!this.config.auth) { @@ -291,7 +291,7 @@ class GoogleSheetsIntegration implements DatasourcePlus { } await this.connect() const sheets = this.client.sheetsByIndex - const tables: Record = {} + const tables: Record = {} let errors: Record = {} await utils.parallelForeach( sheets, diff --git a/packages/server/src/integrations/microsoftSqlServer.ts b/packages/server/src/integrations/microsoftSqlServer.ts index ff68026369..b86286756c 100644 --- a/packages/server/src/integrations/microsoftSqlServer.ts +++ b/packages/server/src/integrations/microsoftSqlServer.ts @@ -2,7 +2,7 @@ import { DatasourceFieldType, Integration, Operation, - ExternalTable, + Table, TableSchema, QueryJson, QueryType, @@ -380,7 +380,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus { */ async buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise { await this.connect() let tableInfo: MSSQLTablesResponse[] = await this.runSQL(this.TABLES_SQL) @@ -394,7 +394,7 @@ class SqlServerIntegration extends Sql implements DatasourcePlus { .map((record: any) => record.TABLE_NAME) .filter((name: string) => this.MASTER_TABLES.indexOf(name) === -1) - const tables: Record = {} + const tables: Record = {} for (let tableName of tableNames) { // get the column definition (type) const definition = await this.runSQL( diff --git a/packages/server/src/integrations/mysql.ts b/packages/server/src/integrations/mysql.ts index 3a954da9bd..fe7eae51be 100644 --- a/packages/server/src/integrations/mysql.ts +++ b/packages/server/src/integrations/mysql.ts @@ -4,7 +4,7 @@ import { QueryType, QueryJson, SqlQuery, - ExternalTable, + Table, TableSchema, DatasourcePlus, DatasourceFeature, @@ -278,9 +278,9 @@ class MySQLIntegration extends Sql implements DatasourcePlus { async buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise { - const tables: { [key: string]: ExternalTable } = {} + const tables: { [key: string]: Table } = {} await this.connect() try { diff --git a/packages/server/src/integrations/oracle.ts b/packages/server/src/integrations/oracle.ts index b3936320ac..5fde565180 100644 --- a/packages/server/src/integrations/oracle.ts +++ b/packages/server/src/integrations/oracle.ts @@ -5,7 +5,7 @@ import { QueryJson, QueryType, SqlQuery, - ExternalTable, + Table, DatasourcePlus, DatasourceFeature, ConnectionInfo, @@ -263,14 +263,14 @@ class OracleIntegration extends Sql implements DatasourcePlus { */ async buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise { const columnsResponse = await this.internalQuery({ sql: this.COLUMNS_SQL, }) const oracleTables = this.mapColumns(columnsResponse) - const tables: { [key: string]: ExternalTable } = {} + const tables: { [key: string]: Table } = {} // iterate each table Object.values(oracleTables).forEach(oracleTable => { diff --git a/packages/server/src/integrations/postgres.ts b/packages/server/src/integrations/postgres.ts index 8479cd05d8..38339499b2 100644 --- a/packages/server/src/integrations/postgres.ts +++ b/packages/server/src/integrations/postgres.ts @@ -5,7 +5,7 @@ import { QueryType, QueryJson, SqlQuery, - ExternalTable, + Table, DatasourcePlus, DatasourceFeature, ConnectionInfo, @@ -273,7 +273,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus { */ async buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise { let tableKeys: { [key: string]: string[] } = {} await this.openConnection() @@ -300,7 +300,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus { const columnsResponse: { rows: PostgresColumn[] } = await this.client.query(this.COLUMNS_SQL) - const tables: { [key: string]: ExternalTable } = {} + const tables: { [key: string]: Table } = {} for (let column of columnsResponse.rows) { const tableName: string = column.table_name diff --git a/packages/server/src/integrations/tests/googlesheets.spec.ts b/packages/server/src/integrations/tests/googlesheets.spec.ts index 748baddc39..842b681867 100644 --- a/packages/server/src/integrations/tests/googlesheets.spec.ts +++ b/packages/server/src/integrations/tests/googlesheets.spec.ts @@ -31,6 +31,7 @@ import { structures } from "@budibase/backend-core/tests" import TestConfiguration from "../../tests/utilities/TestConfiguration" import GoogleSheetsIntegration from "../googlesheets" import { FieldType, Table, TableSchema } from "@budibase/types" +import { generateDatasourceID } from "../../db/utils" describe("Google Sheets Integration", () => { let integration: any, @@ -61,6 +62,7 @@ describe("Google Sheets Integration", () => { function createBasicTable(name: string, columns: string[]): Table { return { name, + sourceId: generateDatasourceID(), schema: { ...columns.reduce((p, c) => { p[c] = { diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index b37fe9f0ed..9e6c5ca3af 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -4,7 +4,6 @@ import { SearchFilters, Datasource, FieldType, - ExternalTable, } from "@budibase/types" import { DocumentType, SEPARATOR } from "../db/utils" import { InvalidColumns, NoEmptyFilterStrings } from "../constants" @@ -301,9 +300,9 @@ function copyExistingPropsOver( * @param entities The old list of tables, if there was any to look for definitions in. */ export function finaliseExternalTables( - tables: Record, - entities: Record -): Record { + tables: Record, + entities: Record +): Record { let finalTables: Record = {} const tableIds = Object.values(tables).map(table => table._id!) for (let [name, table] of Object.entries(tables)) { @@ -316,7 +315,7 @@ export function finaliseExternalTables( } export function checkExternalTables( - tables: Record + tables: Record ): Record { const invalidColumns = Object.values(InvalidColumns) as string[] const errors: Record = {} diff --git a/packages/server/src/middleware/tests/trimViewRowInfo.spec.ts b/packages/server/src/middleware/tests/trimViewRowInfo.spec.ts index bf717d5828..106129f6c9 100644 --- a/packages/server/src/middleware/tests/trimViewRowInfo.spec.ts +++ b/packages/server/src/middleware/tests/trimViewRowInfo.spec.ts @@ -1,5 +1,11 @@ import { generator } from "@budibase/backend-core/tests" -import { BBRequest, FieldType, Row, Table } from "@budibase/types" +import { + BBRequest, + FieldType, + Row, + Table, + INTERNAL_TABLE_SOURCE_ID, +} from "@budibase/types" import * as utils from "../../db/utils" import trimViewRowInfoMiddleware from "../trimViewRowInfo" @@ -73,6 +79,7 @@ describe("trimViewRowInfo middleware", () => { const table: Table = { _id: tableId, name: generator.word(), + sourceId: INTERNAL_TABLE_SOURCE_ID, type: "table", schema: { name: { diff --git a/packages/server/src/sdk/app/rows/search/internal.ts b/packages/server/src/sdk/app/rows/search/internal.ts index 58611c8849..1aec8a321e 100644 --- a/packages/server/src/sdk/app/rows/search/internal.ts +++ b/packages/server/src/sdk/app/rows/search/internal.ts @@ -197,11 +197,7 @@ export async function fetchView( try { table = await sdk.tables.getTable(viewInfo.meta.tableId) } catch (err) { - /* istanbul ignore next */ - table = { - name: "", - schema: {}, - } + throw new Error("Unable to retrieve view table.") } rows = await outputProcessing(table, response.rows) } diff --git a/packages/server/src/sdk/app/rows/search/tests/external.spec.ts b/packages/server/src/sdk/app/rows/search/tests/external.spec.ts index b3bddfbc97..1afdca35fa 100644 --- a/packages/server/src/sdk/app/rows/search/tests/external.spec.ts +++ b/packages/server/src/sdk/app/rows/search/tests/external.spec.ts @@ -15,6 +15,7 @@ import { expectAnyExternalColsAttributes, generator, } from "@budibase/backend-core/tests" +import datasource from "../../../../../api/routes/datasource" jest.unmock("mysql2/promise") @@ -23,36 +24,7 @@ jest.setTimeout(30000) describe.skip("external", () => { const config = new TestConfiguration() - let externalDatasource: Datasource - - const tableData: Table = { - name: generator.word(), - type: "external", - primary: ["id"], - schema: { - id: { - name: "id", - type: FieldType.AUTO, - autocolumn: true, - }, - name: { - name: "name", - type: FieldType.STRING, - }, - surname: { - name: "surname", - type: FieldType.STRING, - }, - age: { - name: "age", - type: FieldType.NUMBER, - }, - address: { - name: "address", - type: FieldType.STRING, - }, - }, - } + let externalDatasource: Datasource, tableData: Table beforeAll(async () => { const container = await new GenericContainer("mysql") @@ -84,6 +56,36 @@ describe.skip("external", () => { }, }, }) + + tableData = { + name: generator.word(), + type: "external", + primary: ["id"], + sourceId: externalDatasource._id!, + schema: { + id: { + name: "id", + type: FieldType.AUTO, + autocolumn: true, + }, + name: { + name: "name", + type: FieldType.STRING, + }, + surname: { + name: "surname", + type: FieldType.STRING, + }, + age: { + name: "age", + type: FieldType.NUMBER, + }, + address: { + name: "address", + type: FieldType.STRING, + }, + }, + } }) describe("search", () => { diff --git a/packages/server/src/sdk/app/rows/search/tests/internal.spec.ts b/packages/server/src/sdk/app/rows/search/tests/internal.spec.ts index b3e98a1149..b3cac2321e 100644 --- a/packages/server/src/sdk/app/rows/search/tests/internal.spec.ts +++ b/packages/server/src/sdk/app/rows/search/tests/internal.spec.ts @@ -1,4 +1,10 @@ -import { FieldType, Row, Table, SearchParams } from "@budibase/types" +import { + FieldType, + Row, + Table, + SearchParams, + INTERNAL_TABLE_SOURCE_ID, +} from "@budibase/types" import TestConfiguration from "../../../../../tests/utilities/TestConfiguration" import { search } from "../internal" import { @@ -12,6 +18,7 @@ describe("internal", () => { const tableData: Table = { name: generator.word(), type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { name: "name", diff --git a/packages/server/src/sdk/app/rows/search/tests/utils.spec.ts b/packages/server/src/sdk/app/rows/search/tests/utils.spec.ts index d946eea432..428c57be64 100644 --- a/packages/server/src/sdk/app/rows/search/tests/utils.spec.ts +++ b/packages/server/src/sdk/app/rows/search/tests/utils.spec.ts @@ -5,12 +5,14 @@ import { FieldTypeSubtypes, Table, SearchParams, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" const tableId = "ta_a" const tableWithUserCol: Table = { _id: tableId, name: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { user: { name: "user", @@ -23,6 +25,7 @@ const tableWithUserCol: Table = { const tableWithUsersCol: Table = { _id: tableId, name: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { user: { name: "user", diff --git a/packages/server/src/sdk/app/tables/external/index.ts b/packages/server/src/sdk/app/tables/external/index.ts index 402baada78..f445fcaf08 100644 --- a/packages/server/src/sdk/app/tables/external/index.ts +++ b/packages/server/src/sdk/app/tables/external/index.ts @@ -35,10 +35,10 @@ export async function save( opts?: { tableId?: string; renaming?: RenameColumn } ) { let tableToSave: TableRequest = { + ...update, type: "table", _id: buildExternalTableId(datasourceId, update.name), sourceId: datasourceId, - ...update, } const tableId = opts?.tableId || update._id diff --git a/packages/server/src/sdk/app/tables/external/utils.ts b/packages/server/src/sdk/app/tables/external/utils.ts index 10c755a7d6..a60667f44f 100644 --- a/packages/server/src/sdk/app/tables/external/utils.ts +++ b/packages/server/src/sdk/app/tables/external/utils.ts @@ -76,12 +76,14 @@ export function generateManyLinkSchema( const primary = table.name + table.primary[0] const relatedPrimary = relatedTable.name + relatedTable.primary[0] const jcTblName = generateJunctionTableName(column, table, relatedTable) + const datasourceId = datasource._id! // first create the new table const junctionTable = { - _id: buildExternalTableId(datasource._id!, jcTblName), + _id: buildExternalTableId(datasourceId, jcTblName), name: jcTblName, primary: [primary, relatedPrimary], constrained: [primary, relatedPrimary], + sourceId: datasourceId, schema: { [primary]: foreignKeyStructure(primary, { toTable: table.name, diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index 47da0beb40..af779bcc2b 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -1,20 +1,16 @@ import { context } from "@budibase/backend-core" -import { - BudibaseInternalDB, - getMultiIDParams, - getTableParams, -} from "../../../db/utils" +import { getMultiIDParams, getTableParams } from "../../../db/utils" import { breakExternalTableId, isExternalTable, isSQL, } from "../../../integrations/utils" import { - AllDocsResponse, Database, Table, TableResponse, TableViewsResponse, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import datasources from "../datasources" import sdk from "../../../sdk" @@ -27,7 +23,7 @@ export function processInternalTable(table: Table): Table { return { ...table, type: "internal", - sourceId: table.sourceId || BudibaseInternalDB._id, + sourceId: table.sourceId || INTERNAL_TABLE_SOURCE_ID, } } diff --git a/packages/server/src/sdk/app/tables/tests/tables.spec.ts b/packages/server/src/sdk/app/tables/tests/tables.spec.ts index 78ebe59f01..ab45eeb8bb 100644 --- a/packages/server/src/sdk/app/tables/tests/tables.spec.ts +++ b/packages/server/src/sdk/app/tables/tests/tables.spec.ts @@ -1,4 +1,9 @@ -import { FieldType, Table, ViewV2 } from "@budibase/types" +import { + FieldType, + INTERNAL_TABLE_SOURCE_ID, + Table, + ViewV2, +} from "@budibase/types" import { generator } from "@budibase/backend-core/tests" import sdk from "../../.." @@ -13,6 +18,7 @@ describe("table sdk", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, diff --git a/packages/server/src/sdk/app/tables/tests/validation.spec.ts b/packages/server/src/sdk/app/tables/tests/validation.spec.ts index 5347eede90..62a9c61dbb 100644 --- a/packages/server/src/sdk/app/tables/tests/validation.spec.ts +++ b/packages/server/src/sdk/app/tables/tests/validation.spec.ts @@ -1,36 +1,50 @@ import { populateExternalTableSchemas } from "../validation" import { cloneDeep } from "lodash/fp" -import { AutoReason, Datasource, Table } from "@budibase/types" +import { + AutoReason, + Datasource, + FieldType, + RelationshipType, + SourceName, + Table, +} from "@budibase/types" import { isEqual } from "lodash" +import { generateDatasourceID } from "../../../../db/utils" -const SCHEMA = { +const datasourceId = generateDatasourceID() + +const SCHEMA: Datasource = { + source: SourceName.POSTGRES, + type: "datasource", + _id: datasourceId, entities: { client: { _id: "tableA", name: "client", primary: ["idC"], primaryDisplay: "Name", + sourceId: datasourceId, schema: { idC: { autocolumn: true, externalType: "int unsigned", name: "idC", - type: "number", + type: FieldType.NUMBER, }, Name: { autocolumn: false, externalType: "varchar(255)", name: "Name", - type: "string", + type: FieldType.STRING, }, project: { fieldName: "idC", foreignKey: "idC", main: true, name: "project", - relationshipType: "many-to-one", + relationshipType: RelationshipType.MANY_TO_ONE, tableId: "tableB", - type: "link", + type: FieldType.LINK, }, }, }, @@ -39,31 +53,32 @@ const SCHEMA = { name: "project", primary: ["idP"], primaryDisplay: "Name", + sourceId: datasourceId, schema: { idC: { externalType: "int unsigned", name: "idC", - type: "number", + type: FieldType.NUMBER, }, idP: { autocolumn: true, externalType: "int unsigned", name: "idProject", - type: "number", + type: FieldType.NUMBER, }, Name: { autocolumn: false, externalType: "varchar(255)", name: "Name", - type: "string", + type: FieldType.STRING, }, client: { fieldName: "idC", foreignKey: "idC", name: "client", - relationshipType: "one-to-many", + relationshipType: RelationshipType.ONE_TO_MANY, tableId: "tableA", - type: "link", + type: FieldType.LINK, }, }, sql: true, @@ -95,12 +110,12 @@ describe("validation and update of external table schemas", () => { function noOtherTableChanges(response: any) { checkOtherColumns( response.entities!.client!, - SCHEMA.entities.client as Table, + SCHEMA.entities!.client, OTHER_CLIENT_COLS ) checkOtherColumns( response.entities!.project!, - SCHEMA.entities.project as Table, + SCHEMA.entities!.project, OTHER_PROJECT_COLS ) } diff --git a/packages/server/src/sdk/app/views/tests/views.spec.ts b/packages/server/src/sdk/app/views/tests/views.spec.ts index 8fcc6405ef..2068eda4c3 100644 --- a/packages/server/src/sdk/app/views/tests/views.spec.ts +++ b/packages/server/src/sdk/app/views/tests/views.spec.ts @@ -2,6 +2,7 @@ import _ from "lodash" import { FieldSchema, FieldType, + INTERNAL_TABLE_SOURCE_ID, Table, TableSchema, ViewV2, @@ -14,6 +15,7 @@ describe("table sdk", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, diff --git a/packages/server/src/tests/utilities/TestConfiguration.ts b/packages/server/src/tests/utilities/TestConfiguration.ts index 5f5b211975..3d4046b91b 100644 --- a/packages/server/src/tests/utilities/TestConfiguration.ts +++ b/packages/server/src/tests/utilities/TestConfiguration.ts @@ -56,6 +56,7 @@ import { CreateViewRequest, RelationshipFieldMetadata, User, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" import API from "./api" @@ -68,6 +69,10 @@ type DefaultUserValues = { csrfToken: string } +interface TableToBuild extends Omit { + sourceId?: string +} + class TestConfiguration { server: any request: supertest.SuperTest | undefined @@ -538,10 +543,13 @@ class TestConfiguration { // TABLE async updateTable( - config?: Table, + config?: TableToBuild, { skipReassigning } = { skipReassigning: false } ): Promise
{ config = config || basicTable() + if (!config.sourceId) { + config.sourceId = INTERNAL_TABLE_SOURCE_ID + } const response = await this._req(config, null, controllers.table.save) if (!skipReassigning) { this.table = response @@ -549,13 +557,19 @@ class TestConfiguration { return response } - async createTable(config?: Table, options = { skipReassigning: false }) { + async createTable( + config?: TableToBuild, + options = { skipReassigning: false } + ) { if (config != null && config._id) { delete config._id } config = config || basicTable() + if (!config.sourceId) { + config.sourceId = INTERNAL_TABLE_SOURCE_ID + } if (this.datasource && !config.sourceId) { - config.sourceId = this.datasource._id + config.sourceId = this.datasource._id || INTERNAL_TABLE_SOURCE_ID if (this.datasource.plus) { config.type = "external" } @@ -572,12 +586,15 @@ class TestConfiguration { async createLinkedTable( relationshipType = RelationshipType.ONE_TO_MANY, links: any = ["link"], - config?: Table + config?: TableToBuild ) { if (!this.table) { throw "Must have created a table first." } const tableConfig = config || basicTable() + if (!tableConfig.sourceId) { + tableConfig.sourceId = INTERNAL_TABLE_SOURCE_ID + } tableConfig.primaryDisplay = "name" for (let link of links) { tableConfig.schema[link] = { @@ -590,7 +607,7 @@ class TestConfiguration { } if (this.datasource && !tableConfig.sourceId) { - tableConfig.sourceId = this.datasource._id + tableConfig.sourceId = this.datasource._id || INTERNAL_TABLE_SOURCE_ID if (this.datasource.plus) { tableConfig.type = "external" } diff --git a/packages/server/src/tests/utilities/structures.ts b/packages/server/src/tests/utilities/structures.ts index d3e92ea34d..52f3450454 100644 --- a/packages/server/src/tests/utilities/structures.ts +++ b/packages/server/src/tests/utilities/structures.ts @@ -19,12 +19,14 @@ import { FieldType, SourceName, Table, + INTERNAL_TABLE_SOURCE_ID, } from "@budibase/types" export function basicTable(): Table { return { name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, diff --git a/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts index 18d5128986..10274ed4d9 100644 --- a/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/inputProcessing.spec.ts @@ -1,6 +1,11 @@ import { inputProcessing } from ".." import { generator, structures } from "@budibase/backend-core/tests" -import { FieldType, FieldTypeSubtypes, Table } from "@budibase/types" +import { + FieldType, + FieldTypeSubtypes, + INTERNAL_TABLE_SOURCE_ID, + Table, +} from "@budibase/types" import * as bbReferenceProcessor from "../bbReferenceProcessor" jest.mock("../bbReferenceProcessor", (): typeof bbReferenceProcessor => ({ @@ -20,6 +25,7 @@ describe("rowProcessor - inputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, @@ -70,6 +76,7 @@ describe("rowProcessor - inputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, @@ -110,6 +117,7 @@ describe("rowProcessor - inputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, @@ -150,6 +158,7 @@ describe("rowProcessor - inputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, diff --git a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts index ecb8856c88..56db285f8b 100644 --- a/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts +++ b/packages/server/src/utilities/rowProcessor/tests/outputProcessing.spec.ts @@ -2,6 +2,7 @@ import { FieldSubtype, FieldType, FieldTypeSubtypes, + INTERNAL_TABLE_SOURCE_ID, Table, } from "@budibase/types" import { outputProcessing } from ".." @@ -26,6 +27,7 @@ describe("rowProcessor - outputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, @@ -71,6 +73,7 @@ describe("rowProcessor - outputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, @@ -108,6 +111,7 @@ describe("rowProcessor - outputProcessing", () => { _id: generator.guid(), name: "TestTable", type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, schema: { name: { type: FieldType.STRING, diff --git a/packages/server/src/websockets/builder.ts b/packages/server/src/websockets/builder.ts index 2cff24e635..0428561bfe 100644 --- a/packages/server/src/websockets/builder.ts +++ b/packages/server/src/websockets/builder.ts @@ -17,7 +17,7 @@ import { clearLock, updateLock } from "../utilities/redis" import { Socket } from "socket.io" import { BuilderSocketEvent } from "@budibase/shared-core" import { processInternalTable } from "../sdk/app/tables/getters" -import { isExternalTable, isInternalTable } from "../integrations/utils" +import { isInternalTable } from "../integrations/utils" export default class BuilderSocket extends BaseSocket { constructor(app: Koa, server: http.Server) { diff --git a/packages/types/src/documents/app/table/table.ts b/packages/types/src/documents/app/table/table.ts index 5174ec608f..df17351c12 100644 --- a/packages/types/src/documents/app/table/table.ts +++ b/packages/types/src/documents/app/table/table.ts @@ -3,14 +3,16 @@ import { View, ViewV2 } from "../view" import { RenameColumn } from "../../../sdk" import { TableSchema } from "./schema" +export const INTERNAL_TABLE_SOURCE_ID = "bb_internal" + export interface Table extends Document { type?: string views?: { [key: string]: View | ViewV2 } name: string + sourceId: string primary?: string[] schema: TableSchema primaryDisplay?: string - sourceId?: string relatedFormula?: string[] constrained?: string[] sql?: boolean @@ -19,10 +21,6 @@ export interface Table extends Document { rowHeight?: number } -export interface ExternalTable extends Table { - sourceId: string -} - export interface TableRequest extends Table { _rename?: RenameColumn created?: boolean diff --git a/packages/types/src/sdk/datasources.ts b/packages/types/src/sdk/datasources.ts index 39a10961de..7a335eb3b9 100644 --- a/packages/types/src/sdk/datasources.ts +++ b/packages/types/src/sdk/datasources.ts @@ -1,4 +1,4 @@ -import { ExternalTable, Table } from "../documents" +import { Table } from "../documents" export const PASSWORD_REPLACEMENT = "--secret-value--" @@ -176,7 +176,7 @@ export interface IntegrationBase { } export interface Schema { - tables: Record + tables: Record errors: Record } @@ -187,7 +187,7 @@ export interface DatasourcePlus extends IntegrationBase { getStringConcat(parts: string[]): string buildSchema( datasourceId: string, - entities: Record + entities: Record ): Promise getTableNames(): Promise } From fd0d8f17f2a3ac4c4413c5b64ed0c730d3f75bcd Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 25 Oct 2023 19:07:51 +0100 Subject: [PATCH 148/427] Making sure single table get also includes sourceId. --- packages/server/src/sdk/app/tables/getters.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/server/src/sdk/app/tables/getters.ts b/packages/server/src/sdk/app/tables/getters.ts index af779bcc2b..b9c67c573b 100644 --- a/packages/server/src/sdk/app/tables/getters.ts +++ b/packages/server/src/sdk/app/tables/getters.ts @@ -67,7 +67,7 @@ export async function getTable(tableId: string): Promise
{ const table = await getExternalTable(datasourceId!, tableName!) return { ...table, sql: isSQL(datasource) } } else { - return db.get(tableId) + return processInternalTable(await db.get
(tableId)) } } From 4bcc49b692674094a3cf16bea5e17f2a4669d0eb Mon Sep 17 00:00:00 2001 From: andz-bb Date: Thu, 26 Oct 2023 07:33:53 +0100 Subject: [PATCH 149/427] add cloudfront to script csp to fix cloud plugins --- hosting/proxy/nginx.prod.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/proxy/nginx.prod.conf b/hosting/proxy/nginx.prod.conf index 365765ccbb..6da2e4a1c3 100644 --- a/hosting/proxy/nginx.prod.conf +++ b/hosting/proxy/nginx.prod.conf @@ -51,7 +51,7 @@ http { proxy_buffering off; set $csp_default "default-src 'self'"; - set $csp_script "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.budibase.net https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io"; + set $csp_script "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://*.budibase.net https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io https://d2l5prqdbvm3op.cloudfront.net"; set $csp_style "style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com"; set $csp_object "object-src 'none'"; set $csp_base_uri "base-uri 'self'"; From dccc60c9b083a5a03aa2bca491474ec311330ded Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 26 Oct 2023 07:05:00 +0000 Subject: [PATCH 150/427] Bump version to 2.11.45 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 7d14875c97..384473120b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.11.44", + "version": "2.11.45", "npmClient": "yarn", "packages": [ "packages/*" From 7ddaf6479f934af59832269f06bc2f4c144aad17 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 26 Oct 2023 09:54:38 +0200 Subject: [PATCH 151/427] Clean unused pipelines --- .../workflows/release-singleimage-test.yml | 72 ------------------- package.json | 1 - 2 files changed, 73 deletions(-) delete mode 100644 .github/workflows/release-singleimage-test.yml diff --git a/.github/workflows/release-singleimage-test.yml b/.github/workflows/release-singleimage-test.yml deleted file mode 100644 index c3a14226ce..0000000000 --- a/.github/workflows/release-singleimage-test.yml +++ /dev/null @@ -1,72 +0,0 @@ -name: Test - -on: - workflow_dispatch: - -env: - CI: true - PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - REGISTRY_URL: registry.hub.docker.com - NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }} -jobs: - build: - name: "build" - runs-on: ubuntu-latest - strategy: - matrix: - node-version: [18.x] - steps: - - name: "Checkout" - uses: actions/checkout@v4 - with: - submodules: true - token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - cache: "yarn" - - name: Setup QEMU - uses: docker/setup-qemu-action@v3 - - name: Setup Docker Buildx - id: buildx - uses: docker/setup-buildx-action@v3 - - name: Run Yarn - run: yarn - - name: Run Yarn Build - run: yarn build --scope @budibase/server --scope @budibase/worker - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_API_KEY }} - - name: Get the latest release version - id: version - run: | - release_version=$(cat lerna.json | jq -r '.version') - echo $release_version - echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - - name: Tag and release Budibase service docker image - uses: docker/build-push-action@v5 - with: - context: . - push: true - pull: true - platforms: linux/amd64,linux/arm64 - build-args: BUDIBASE_VERSION=0.0.0+test - tags: budibase/budibase-test:test - file: ./hosting/single/Dockerfile.v2 - cache-from: type=registry,ref=budibase/budibase-test:test - cache-to: type=inline - - name: Tag and release Budibase Azure App Service docker image - uses: docker/build-push-action@v2 - with: - context: . - push: true - platforms: linux/amd64 - build-args: | - TARGETBUILD=aas - BUDIBASE_VERSION=0.0.0+test - tags: budibase/budibase-test:aas - file: ./hosting/single/Dockerfile.v2 diff --git a/package.json b/package.json index 100a306a35..d3f4903e6c 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,6 @@ "build:sdk": "lerna run --stream build:sdk", "deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular", "release": "lerna publish from-package --yes --force-publish --no-git-tag-version --no-push --no-git-reset", - "release:develop": "yarn release --dist-tag develop", "restore": "yarn run clean && yarn && yarn run build", "nuke": "yarn run nuke:packages && yarn run nuke:docker", "nuke:packages": "yarn run restore", From 389848efbefe4e37d3bf397d16fda5ad04e9abdc Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 26 Oct 2023 11:05:07 +0200 Subject: [PATCH 152/427] Add update versions v2 --- scripts/updateWorkepaceVersions.V2.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100755 scripts/updateWorkepaceVersions.V2.sh diff --git a/scripts/updateWorkepaceVersions.V2.sh b/scripts/updateWorkepaceVersions.V2.sh new file mode 100755 index 0000000000..634bcbcfb0 --- /dev/null +++ b/scripts/updateWorkepaceVersions.V2.sh @@ -0,0 +1,8 @@ +#!/bin/bash +version=$1 +echo "Setting version $version" +yarn lerna exec "yarn version --no-git-tag-version --new-version=$version" +echo "Updating dependencies" +node scripts/syncLocalDependencies.js $version +echo "Syncing yarn workspace" +yarn From 230dc169fa86bdaa63976b03407e1d3ab166db4b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Thu, 26 Oct 2023 10:11:34 +0100 Subject: [PATCH 153/427] Revert previous fix for relationship column dragging --- .../grid/cells/RelationshipCell.svelte | 50 +++++++++---------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte index e6d83e0bea..925c840478 100644 --- a/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte +++ b/packages/frontend-core/src/components/grid/cells/RelationshipCell.svelte @@ -260,31 +260,29 @@ class:wrap={editable || contentLines > 1} on:wheel={e => (focused ? e.stopPropagation() : null)} > - {#if Array.isArray(value) && value.length} - {#each value as relationship} - {#if relationship[primaryDisplay] || relationship.primaryDisplay} -
- showRelationship(relationship._id) - : null} - > - {readable( - relationship[primaryDisplay] || relationship.primaryDisplay - )} - - {#if editable} - toggleRow(relationship)} - /> - {/if} -
- {/if} - {/each} - {/if} + {#each value || [] as relationship} + {#if relationship[primaryDisplay] || relationship.primaryDisplay} +
+ showRelationship(relationship._id) + : null} + > + {readable( + relationship[primaryDisplay] || relationship.primaryDisplay + )} + + {#if editable} + toggleRow(relationship)} + /> + {/if} +
+ {/if} + {/each} {#if editable}
@@ -320,7 +318,7 @@
- {:else if Array.isArray(searchResults) && searchResults.length} + {:else if searchResults?.length}
{#each searchResults as row, idx}
Date: Thu, 26 Oct 2023 10:13:22 +0100 Subject: [PATCH 154/427] Added fix for scenario where componentdefinition was queried befor it existed. Fix to ensure an empty general settings block is not shown --- .../Component/ComponentSettingsSection.svelte | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte index cd91e974ce..6dc9078f2c 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/[componentId]/_components/Component/ComponentSettingsSection.svelte @@ -34,10 +34,14 @@ setting => setting.section && setting.tag === tag ) let sections = [ - { - name: "General", - settings: generalSettings, - }, + ...(generalSettings?.length + ? [ + { + name: "General", + settings: generalSettings, + }, + ] + : []), ...(customSections || []), ] @@ -142,7 +146,7 @@ - {:else if idx === 0 && section.name === "General" && componentDefinition.info && !tag} + {:else if idx === 0 && section.name === "General" && componentDefinition?.info && !tag} Date: Thu, 26 Oct 2023 10:23:51 +0100 Subject: [PATCH 155/427] fix bug with smtp name containing parentheses --- .../AutomationBuilder/FlowChart/FlowItemHeader.svelte | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index 87934d6520..0d35f955ef 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -57,14 +57,15 @@ } const getAutomationNameError = name => { - if (name.length > 0) { + if (name !== block.name && block.name.includes(name)) { + if (name?.length > 0) { let invalidRoleName = !validRegex.test(name) if (invalidRoleName) { return "Please enter a role name consisting of only alphanumeric symbols and underscores" } } return null - } + }} const startTyping = async () => { typing = true From 980e223aef9e9aab8545b5339a9776cce75b7a82 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 26 Oct 2023 11:24:02 +0200 Subject: [PATCH 156/427] Fix typo --- ...pdateWorkepaceVersions.V2.sh => updateWorkspaceVersions.V2.sh} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename scripts/{updateWorkepaceVersions.V2.sh => updateWorkspaceVersions.V2.sh} (100%) diff --git a/scripts/updateWorkepaceVersions.V2.sh b/scripts/updateWorkspaceVersions.V2.sh similarity index 100% rename from scripts/updateWorkepaceVersions.V2.sh rename to scripts/updateWorkspaceVersions.V2.sh From 5909b4a7c723c8e13fc735e7844fcf00331f8b0b Mon Sep 17 00:00:00 2001 From: Gerard Burns Date: Thu, 26 Oct 2023 11:24:43 +0100 Subject: [PATCH 157/427] Add template for grid blocks (#12086) * wip * linting --------- Co-authored-by: Mihail Hadzhiev <102024164+MihailHadzhiev2022@users.noreply.github.com> --- .../store/screenTemplates/rowListScreen.js | 22 ++++++++++++++---- .../NewScreen/CreateScreenModal.svelte | 8 +++++-- .../NewScreen/DatasourceModal.svelte | 6 ++++- .../design/_components/NewScreen/grid.png | Bin 0 -> 24819 bytes .../design/_components/NewScreen/index.svelte | 11 +++++++++ 5 files changed, 40 insertions(+), 7 deletions(-) create mode 100644 packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/grid.png diff --git a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js index b17bd99e10..59bcd0d5e8 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js +++ b/packages/builder/src/builderStore/store/screenTemplates/rowListScreen.js @@ -2,14 +2,14 @@ import sanitizeUrl from "./utils/sanitizeUrl" import { Screen } from "./utils/Screen" import { Component } from "./utils/Component" -export default function (datasources) { +export default function (datasources, mode = "table") { if (!Array.isArray(datasources)) { return [] } return datasources.map(datasource => { return { name: `${datasource.label} - List`, - create: () => createScreen(datasource), + create: () => createScreen(datasource, mode), id: ROW_LIST_TEMPLATE, resourceId: datasource.resourceId, } @@ -40,10 +40,24 @@ const generateTableBlock = datasource => { return tableBlock } -const createScreen = datasource => { +const generateGridBlock = datasource => { + const gridBlock = new Component("@budibase/standard-components/gridblock") + gridBlock + .customProps({ + table: datasource, + }) + .instanceName(`${datasource.label} - Grid block`) + return gridBlock +} + +const createScreen = (datasource, mode) => { return new Screen() .route(rowListUrl(datasource)) .instanceName(`${datasource.label} - List`) - .addChild(generateTableBlock(datasource)) + .addChild( + mode === "table" + ? generateTableBlock(datasource) + : generateGridBlock(datasource) + ) .json() } diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte index 9a96242b30..92ed3dcfc7 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/CreateScreenModal.svelte @@ -12,6 +12,7 @@ import { capitalise } from "helpers" import { goto } from "@roxi/routify" + let mode let pendingScreen // Modal refs @@ -100,14 +101,15 @@ } // Handler for NewScreenModal - export const show = mode => { + export const show = newMode => { + mode = newMode selectedTemplates = null blankScreenUrl = null screenMode = mode pendingScreen = null screenAccessRole = Roles.BASIC - if (mode === "table") { + if (mode === "table" || mode === "grid") { datasourceModal.show() } else if (mode === "blank") { let templates = getTemplates($tables.list) @@ -123,6 +125,7 @@ // Handler for DatasourceModal confirmation, move to screen access select const confirmScreenDatasources = async ({ templates }) => { + console.log(templates) selectedTemplates = templates screenAccessRoleModal.show() } @@ -177,6 +180,7 @@ diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/DatasourceModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/DatasourceModal.svelte index a866cd23d4..731c60a406 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/DatasourceModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/DatasourceModal.svelte @@ -7,6 +7,7 @@ import rowListScreen from "builderStore/store/screenTemplates/rowListScreen" import DatasourceTemplateRow from "./DatasourceTemplateRow.svelte" + export let mode export let onCancel export let onConfirm export let initialScreens = [] @@ -24,7 +25,10 @@ screen => screen.resourceId !== resourceId ) } else { - selectedScreens = [...selectedScreens, rowListScreen([datasource])[0]] + selectedScreens = [ + ...selectedScreens, + rowListScreen([datasource], mode)[0], + ] } } diff --git a/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/grid.png b/packages/builder/src/pages/builder/app/[application]/design/_components/NewScreen/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..c3efa30a67d733434fade938263e4aba9fd7cdee GIT binary patch literal 24819 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

K!sjL$Lv!Wkn)K>4}0WK}T!0MY-`Gz%=`-%HF zzfruL%8whFdA@Nd4cE$TNUubnlCR&8P4X$LW11t zsJkt_W%8=e&^|8*nG;K~C7M{R#^Ys(9vbIIf^B)T6MEP9iMGUNKqTkF9+gZ_XeXNiy{!2=`+vnFS*FMpuK z1z2YCh-Kfqi~HuzbqDBqq(=OH4}VDT!e$Prt!5;>*Gc?Drx{o8YD%zX57y@S9i%-N z+jvMzErUp5v5x{jw*zOO@t zUn*43a)hhkdU^@U)u8n`L2*rr+hNMCR;WhTOFdJF=kJso|Mr${Ng<( znnq!wvxC^=&1JBAWm{^OjwGe1z+bsHrs|rLZ%-*E> zm5P!0j!FyGU^I+uYSd-CLl_sR$E7fh-mwEQ!M9~rL;|!r$W3(*^|FvhbGd5`8@U&P zTq94thgOqJk%|68e6BWcS?Ujv-PLOFUIaT~4{(994WfR}$OCCPT}OT2a&HIIvS5!QPgku4Uxqgyz?gAl3n3ZxQaLULmby!A+|W3c)-8vwofTmRR>Y2UKX#G768#DG?Rxoa zXwxi!<0e;IhgMcx>U#OYMA;9Wd!HDOu6mfnf zfWDKdpr8cTc=4Q^%U2YGts|SvFKLN>e9!`uwU%m3l-fOrywL)wkT2{}c%PSQDfK5O z5DwFRZShRM4lO&)V;@}2r54^#u`M$sxP{t(#X1Hfdc}sx5M?pd>Bk8LhIHS^(jm$V z>0%SghKz|&nAnJhq5CDfb9?49{q*~@#@{T$*6QpKd2Dy7W8+!NK^H&_{+0CNlOi5w|6 zwZvh>)t5QMZ{Bm|0|-SIZ-?hXJSMH+>p?O5YCMIr`tlMVNjUZw|BV(uPvFZ*3xC5z zN^hbzcmLfn2<`~SH*>n6DqyA{Y|?jKGKPT`fA)$+CjN5XxL@xwLz=~pJfs|V!nT2g z4w;=Ls}(bGthk+*8%NoebOf7xkrK;cu$M2*^wESYI(vUZ}20f!o^-DhR0um z`iVW%IUaW7uxW9R-Gf=QlmiYVB7`x`{&p zU@(#zIXf5EIKx7(3S_|<@Ei)$UE6<^*=6qM5Vo>J1L%?vm|>VJIzNk5``dE(j0Fvh ze0I8#HYTlH&m;5*UaZvxr>Fs+)+|!Eczj8KmcsZ&f z=|0?LE6MKDEX&Ok4vK$Tt!ZS-OiNE%3w8-VYVbo4N~M7t{{m^kmz3Jp+hXVaD**CgoyJnJdon4!O)O5+t; z=Fujhh~|%1%VBcMVc8eHUOm3xi{Hzi0bkuY;s3rqyDv};Xr?vgg8?57pW^DDn_o9V z9ZJz1hS~qRUjd)u;-9@+fI4w&G2iyDr|x$&lozTm>cr=T?8C1Yz{e8yNan2|VjBVL zk|T=fovl(MSwN3VseKCZnNOp+|Lk)D}BK-y7QW_6aoKu()$(KhC|E(6)Yx3G!2esO}jHzA6C0-Nt2tn!ezL zd)dd#-ZTCTnjm1!Au?-9x#Pj%xeC{y+uw%UlQGQ)@BT63Q%{l9$6+UO`?5Ij&-=fE z6PpaJ`o`wXFR3uv#;+lQUl88sUO2$o+yIOeCu7kt80`!_WNTIqh;W?tCdIh13^VTu;20rwFJj5}4l%M*Zs*o+86GzVu{RO~FLY zzXkePKaMDY7Kim$WPP~d3LBC_iff)<5C$#eJ-kFBf2*^A*iw(qX*&>Wq$~wSZVn<< zIYl&!(D$xVuNuEM&w|MXb7Jo-}?%WMyK;cp8uckCc4srDgm9?gnO!F)dSlhD z9(%pH<6y4eL!c}?xxjR_KoHSiR4gn)ev6%wmiACpBXha~2C15bk(m>_Kw8kBAX9SDFP+uP(D0`gt7| ztY4glDLUQs`Zp1B1695839KcDyk>jw^1YZkiS>>fjH?&-5m$qGUppwpoOGT-lUzW2jtO_qL8f*9VhyREn$JR3Obn*m`tiOhVPgM_Q|-S0sXy=$4>KeT@)Sl zb{8-{{!tG+a8n-)iZSZa^-Nk`V4ni=&W}ag7-=%wBXu&-ety3$ruc@0zOcRy8tk51 zdxSni0UBzb{Q!99WUC)F#HJp84VP8sdu@&hywV7u;b@0tHpAY`%MtHF?ex`khX0mxO_<*d(mFhC;1} z453)=%>CmgN@kW|XQ>O(jUVTrPm`;kL|?le;`>j4S4nzHt?$9VCi6L`suzl&RA4dy zi14&DGmDC(5P89FX3cG?#{GmRC&LRH3iF1-#V%YfQWOLK$-|2vQMhdoRfBDjoIffl zAoms>EGoCRL?bO_YxWue0q6G1%o+F&C#L5!Wu9|s$8foNZQDXYMIe4bFqjf9Pm#ib zLpF~`7+*K1KK?KAdy<-d3qZ)%)HGV?i^HNAe%8T05;ZrV*iiVqHF%BA^)edDisPs& z{7FYj3b-G>D!4Eir>Lv3Yp&&T_yC^S8oPf>z2__J?UK7 zJ{lgb#tZKEB&j%l$iA)EHqO_ah>$QOrA6};(hvlIFe65u!{8Phzc#ev+c7YDA8S*_ zCd}N*rM6hJ8*`gs4Nx{n!$s`FPU+jC+vl)u#mtT~LJj!eEI>-P{k?-H>RjN}?B%V^ znak(H?=*>0AL#K`SHuE<37YoIFKD5_@8V#?&isnxua&zEmY1)ijQdCwh|V9Lt=3o^ zq6BX+&~a~kMbcB%SF(g-5|rOk{(xYpAQ6e;ihXy_k#X}hG}8VX`WHgNRb4o7R5MCh z?b5CyXH%_tO~>PlHOywl?RmJV`>WT2w;)wlvTxTP#HJwRNo$D!O9TO0dVnRqUEg1F zXO|Dx@DFn~hwnBrlycVl60(*V+CI@vZz=MW3Qxp-(Ns=*vB{bseWItIhZ0QOMKz!t zmQnl|zp*6V52AU@b!0{vrKG#KV~r4~`V;%It?56(qgr|qu@2!g*ovNnO8`F$VzA{% zN0pkpbGbt*0o6X^1;wTFHKuWFl>-AUn+k1!39m^jpSe#QSws?oi{dv|Du|s zzr-1yNb~kQc0m&B{F`^XLzt%I?{{Z}@prhHF<^DCQJz7&{r`i4kz%A?)Zdl6qZo1L zNX%vV<+O`B8ZD~+!UW}%F=-7@K|I4j?)?a3-Ut2L0& zxFsp=ZVMiM5kI$=MX87=Q8MzFCUJDZTb!aBJrLF4_3w{@w zc+~$UUNEvr#O`00TyulOM(@#}JEoPakjCdM2}2Vk3Tqd<6(9Xq_E%`vBRB4F!jC(N ztYwZc%KV*-(J|sgI(pbb?~)oXrl;H|SDY0$P5IX8QSekq44?XQ#yfKYF(??tRMRF# zEdKk?sp+hymY^O43v2(y@)2zWFWdl~i~Df29PrS-S8tMbqDFF5c8cjHUs{<$*4E4? zKFY37$0j0Gy$0JumQG7Fk)HkSU~+r6n7>5Te9bVR@ zAJA(hNL#dog*+#$@Mkf5uqFnQ1Q7hYP#nW-q5cXm;X0dL1J*wbCm7j7vnmrISE*-= ztc%hoCZ*h%n0=A>553iEtCItZDZLUCX)BbMD5l3;~k19D;5_o&GwnK|dyXUXBYRz-QQ!icVclWo>z_T{Ha| zsH9H0bBfZ>vr3U^bf4aE?K!Asxsjklx3-6z^q+Mc)?wk5D4n)zp(Cj#Fs2+wJB!!l z@wMbX*s>`1%?XU|tg~nl{peE?;%yKiU`Zi6M41#9Bnev$j}n#`BVzS(THsRsrod4z zDY)l;r28w*p!*mlV3BZhd_vtVIW>;#JrVej#|gKPs_$`@{rj1r9QdeOj&`EJQP`-4 zAHQ!3H*oGxg_^2c!*`u%ZK~_vUWM+eeMBtkFwPK<26~_vP}oV+=mYVh{VDI-CbRsN zm#5*@tr+(J1sJmch~$8?tGVf@p^9IkhX1eC0lhn%Y?|#s5^v z?+PwXKZ@_C{r&y-8_(9fu-8TbZdH6=H7Fayb~&(K zSUauJ;%|3em@6YYqEa-@4cDG*Wuj47r7)X9FqB2}0_#Fe8W=-kij#%z$DzC3j zW?MmskJ?=L->d$1=kx>ykk-`euQUrgb}^or$YDvik|j!T5?rqouFcktdW4V9}fPwU^UT zg^VE80bD5km1gc4qWKdH=?O~cw@z}4Mc0tStJ#O(V^_25CN>(F|WIICv?h z8w;K2n2c#qYVeJG(ba2du08}aDaRf2gP{xKZ{*+gCo_Vp2VQXZ8!REtLE(A)WpPzo z@UVV}yU)dW+Kk@`)7y>nll7VEqmTRonmf?(Bo6?1sqt$F)Iz+^99Y;rhy8uLW$<#z zW$JrXxjryH4dtw0eYWR3YTvStmK- zher{hCp}MN0e>0C>gAmccV{$56f9}t54%ZkKk&9;sopc}dj5d-#?njYx@_>ESM~u8 zA8xp|2y5AGclJ96M-h#&>LVe^l0Q2cTzqAoXYnn2mcKY*i1KNkL7f+4^MF_9g8a?A z&w6G>)8DZU|5F|Q80ejUwJ6Mkc1&$i7I2>@3QWdo#_@3F82Ozds05y|x1W1k8JpQY zzl{Y%HcXUT7TSZA($Zqr_x1w+(KlX9np|5pMRqgCd&X{~IZCpRXI4xps{qs-LOfCB zdqIY|HTaCc^d{)vM3{GbgV2Vzo8}e|45MH-0v)OQAJdkDf@2d!$>@e|cW5-?2Dx00 z=s(3XmQajcHGa^m(3mJN3c= zeZb#p`J4asEO8Vtt%re)hyo;?nGD^GI^39q!tRYRjOZCU|7dfLB<8F9tA1G|Vsk@eN(l1J-unOh_K zB_dwN+ufTYR$qD)W68$*JW$@x%WMT4-@PBX*mK%&?+gmst^A)|JR?qt-FyveX^bfG zZ&V7zRcrmXUX}bREylPEPpR3IhY&N8O^?CRjRcKj`T%a=*-6c0u7F)!IMM(#v{$iSlu66VEt zR)X((2Hn=*&U%?MrPB~LrCN2J6tnS8e#=o7KY4v4n}mLvk}PYF#z>`xky3=;Ck&${ zbUkEm+UrEidLn4W$Ea++KJy=(G9`cO6aC$;#i|$%q7(a;bNKu>-gp7~Tvn zM0x;#ujqB=oNkNpHWuth0L!y)v8?csDz}J9EfMXnpkctIIp%LdhUFrwNJf{ zA%gS(tYSx**V)6-oj}fhRvequ5zfVyl6>oC+Gxsb)wtvYXrX$uwl;Jjr9njeJDJNH z*IQn+lFJkA92QZDTw~6j>-e*X+_``|d_u~^*QWtrIV!D7o=bOjrh!IH0!cu$Dqh_z zqGbJlrnGTWiEvrPLb2@_I~nJQ%?=`_sa#4V0d{x+x{SsT!-mPNX?4uIicD z&(j+C%Ddm>a4IQl4*IpY-u}R4M4Rd;mHkNBr$Tk8ETM-QfKEsPEauS`V=4IgZW`C+#4?>~j@Wmw+rHr1AdNHT0N6VD^db zy|Y<@Kk%Dv1^qQMCz`qqJK5YdA!o!2)kHdQIICdjF>3-n-*@fl<_mMTO~z}ufwwJ@ zVx~yx!RZnGx?JQ}A!6AVM(C&3B!J|oervs(aXi1dUHnWH!#CUWNPx>wBrViz5Vk;y z1QHH(weJiw76{Ep*-vyU=BZx?6%Sv^EcEq-z+woEv0dT*7vnhy5xRBvGD@J?VH96k)vv4l(?clt~UPM8S1}rar4uXPY*rh1i^zITOS--=oRy)T>wrc z?iHuQX`bG!=xn}JE&7aoidM!X^zVjFl?t-WVK@z_xSbC|=sHm%t)f1*CJlWAN6|(O zQWpiGHZ!V0lg)v{Gt1w(qivf5Y(bpf6XVHlnL^l;pve%N@*?S*Ox-H8U+ey+4t6#< zl|ma8!|VKwv8yoqvj3EcsKnYJE*Q;d*`}b2Z}rH*30yo8WSL9DqC~Pus^G5&Z7}B~ zQl&O6X2IX#S5baIUrv^*XEil&&!}ouIj(o+6CBvN-Cj_J2Ie+GGk7wO`J8v3VCNPS z`k)M|87fGgo&?OkPV~p`=N+?Qc5NJG)Ls8g!1C25DYj5lD^coB3teURv@L3tUZLzh z+!14k0L$G-!M~92$+GS6RWEbe3Z(zUPncN?nT#H+qbb{OL)CsUXtc1g-o^C;x= z1?6y$W5(C7a4Y9IjWlD`y1Y++fhb+RL}LJGb&B|Q3KzEHLrj0ag<+ll(xn}DH*JE6 z^p&kUGUcbU1@#u~@`Q@HEYY^BB^khG^L7>xhndYE!`7f7I6-2x+RekhlOYuGpW#7yCjkE)o=T93Kd`YY3@$ZkJ_Zfhv6G!v}^sQRoy zMpdl@p6v%-iff|3z1`W&zH0h?voG5H-d39S7+p@m+iyX2^?A#+U(S$dnm;cYk0j~& zEwMh%whB4h1Frlq(S)QXD-(%6pTAMq$5J$4s-?7%cW2-Ua7{@HI0C#<>PhoGi?kYJ z*vDu!Ja#{=301H?-SQxYI8M(_*W3w**83QKr2gw9h7T}s>`u(m6E_f_=w=4qKsPu2eVL~7z6b3EvVQBj!;~b{ zHT}IUj3cu5F*JoU&d_{D!PvDBJCnso07=k0YU%i6&XOb9x47zamiqTPLFnjPZZ}cw z#e*R0f;m2?KltKj+cFP#vPYh>3yP(9rV!DTD17r2+-4K>ARXQGgCq2pSjXUAzOl+U z0vIM46uKVZBI+`6xgF*5p2eoD6%1@0EBg#M%sct+md%-rVS=||3Q9;>Pfotmcg6oc?X zT;ZY6Bg=S?)SPQ(HU?8?vnlwXdV`S||M!rL-J2O2edLYxF0M87oYwRdFC~wnUxgWE ze@EoCqyZ3mk3zz;kQmmYw!M;mr!eyD7HzFjJmJ}p9|q%Zf09C{0FB;1(qNLrjoLg9q&f_b@JZ^yNr8AQ(k;Uw(wEynTky_fBFOGMpYII3E zUn`0C&DS*vRVqo@zdc1T$u<*q#4mA&B;TRAW;8N7@K-sC`fpY38$#2Eofyj?l4shg z8`+tN(MjeR(fMf(y7Gx>!K+_ntMZO7Fap3m7=RGD`%;NiYah}IS*eQSbq(NUzO4>@ zFR8Keu*Cl+W}q*9GLXEZqLdqP3q?0+S95^~a;8Y#bX_25wH?G*niBR$OV;`7PLVe5 zng$#^Be#$4P;g^G3o5C_Q4P-AFNI7DPtzVgZxZg$E(Z4z*_?SCOFgRA425ny3t(b_ z`2;9`7S2Khvi$9TdDJH)hER7N8nYsJ^RU}GSIapRl!a%^;$!4ZE32aD{oG#r#M$6x ziP=S&KlP@pnOp2SiK#B!SOEE2B+&37_1i6+ z7*yHAvdFPG_|!hMB@6l?LO%PwRZpn&u-^S5tp0Vr8MkyxllW~LiPzRJ^!aW%skM{{rW zyQ>V|<3(dD#bL78->JPc+;?b4r+oE+^!}7Y#l~_w)$`}}o)977Ka+vZBI})0w)&;u|`*E?%Mx@aeC=takWcW1? z+}A9yN8K&K>p+q@M5ir#ZGk;viy60aSwkQM=EliJ0rV{5l6w^M)oUe|A)Mj|J~?(O z66W-?czwi11R$m-ai)$G^)X=K+0^SJN~ zPZ^9;zu;tEM!sT^d^x^OcXbmeN#x6m-If%Y$MCmervLu=##29eWf7NafEmXX-K~o- zFGYNQmM?0#6MGqYrD{V%+@z-2*;W;?u@-o!mZsJlJNz$Ej>28zLG50U>cxD>*$%zi z9QIl8+KsO}>9}L+%rHR!y<9SR{=PQWxQ)4Cf1i<$JG*^LSaWFoB^2Bx>}!|%KxMql z%}F`90Ijdj9iq<2u<~dYM>gT)))Uk?VV3Chpp+k^-QNl)y~vFaPc2SFC;?(xo1IfhCdT;K|)0AEvuTKL~zoNWnY`vu|+?^}QYh+8J zQR$4=-r)X%&^azzt!`LT7!i#SSHPNk&v}M@4*zPdr$qSKW@j%M)vBf*hdj>Leo`C3 ze$>tWvh(Bv_bTXxNfg%;%tE!Fe6T%u3oETb4`myVb2ycaU3o`fOf;B%z@9Cl>WH}7 zrHz$$SCc@f<#V?2Z(kPaC2h~n1qxDmShd$rG57aF^O~OJ=1Y4-^~7Ao-sP;edQbCo z$BdS}WI-ko0gD;rW~cZahau;MfWd$~Z01}GC3q4~O1?)%85Uc0rfrp9(d)|<()Y$D zSfaCV?>lLZ38~@FT!xg6y{C6F8IqBIB`AnY`{SqCfLKm#c|Xl!rr~(x z;fUnQ+J1BQPe2Aghjbq^<+ZRj3+zM>!NqGh67LO7LEJ?cx47yxL{n9)JF{AE^(dpL z6X9kP1gW`t<)8$NExf=AL|mL9wor=_M8r6IJ=*7FqS}`+(i9JSusvi6aDm9!;LE)K z_yR;*yuiTIvpj4fRXQ`Hv5(djNX=V@+>Yrn;iqd_dhw%o<`2d)?KA#G*s>O!kPR!v zSCmwD0myu;MJ1 z{yPLY=UhNIbk<1Yg?whh1C8v>Q}O;`To-|?F%27Dg*}ov8idCX7Exi++ zV!JXI;`e@nRw~(Gd-nclw7(WJ(a>PD>_E8Xx^bsHx@Qj_ifBsvxZ)L_oRLM?rQ#hA z6A2BCfpBofTSc4O^o;ef<$OzvO`;GLt4=piEoM0#IKG^(24C=&+3Rl-%yX( zYT`$aKPYN_AiFmV1~lw@+!^rd8uf`uUE8#9(c|P%IOs+2P*r+xu`pXTQ`q;z&wp)} znM3WZMoE6Z0j@nuzJuK3-yxIDFkff*Ic7R9%elq&X=c#3=z-B{Wjdqn>6D>YAk&}6G|!Cw4*_a| zS#urY>c-%ZEWMAw&etY<`>dZ-JnK?)p+|C{6+*n@vy!=iPv{+~twyG6eNF1u72md? za3}cdCwKi{Yt+SuExo`3qBCtzK+Hig&o5>B^SGCU0(-Y$ifv!=4N!OpZ!p%AiCLM; zkDtxLl?^J#McJ+!oLKW;x19B2TP#mZ6bqgzjXAyKFL*I4_&?u68Z6P^J=^d1O$1z* z?NR+;zg@Hz`_80Lxt+zAn!)C$r$9YqPBA8OWse|D*y;oQ%*#ME|U+rPI<=ZJSmW-puZm9v&x$ zXkP=jn`#U07S;e_fC_Nu{D~&pwpi|o=tk9yakjH!3TpLf8uw@l-qSfN`%@ahcBCa{@6yRPpW)EU6V_-pt$2^9W^ znwl&BGA!i2pc73BR_}h(97dAU%f>`Ieuiz0jiqxl}dvi|X1nmpHaJ5di;e2et@(9U3 zm7@7C!o`E9ZBr4EpZ?*ShCs2ZeAr8iGTuW!!A_Ei`rrNi{R_&m|L^_@KWV-@ru&gy zQ@d-nv1{$||BrsWe_dGB2tR)Z>=AhL)I2+y@OOUrb~0Zn9^!3?dph6yt)FcRcOUzwu2L;n||=Wi%1_-fzE&$p7+pNT<8^Kaj_n7PKD0|Nomm z+YdU*=Nx9=1%zCao8&=jJ+wC%+7~>Lh)tb=dPk8N>~Ekwa^P8H8tv)`x|iUTws6b8 zBX&012}uqE{-u&IO9=UIgBj*1RYa*!Z3Nb>*>CwbhMX+2y=a@60X)}wd^IHxWoHbJ zXYYxC#}?-zn=v&6img&MxDH6${@)kS6g=TxaB%H7%*En?Bj~m0f4Ksnc|JCuAraGc zn5C)5QZca$FQbPE<0O0kmfDQQIjl~0<$ota_qt=2d6qU^ttz#7v9^Y&KFE7Oo2M&j zPv-xGVm;$O1v1@#)wKU1zSQ@n!6BDT+8_&B0zZqU;0vl;AZ&7jR#kuYA%?`C<8(rp z{az9L3x*M48_i>KzUmbaL22|mZR-+bMa>VP#nHwT?Ju77=PYfUh4ADbV2vX;!t?oi zMq{tLjLGc{@Y!>v979v>mh5j4Ruztl8W?dHl7LeVsN@ge3IGZuQn-34}1I+;4*?wcUE z6z$JW-j72>B}x)|3s3i6>$CU zsvXQvm5k6j?Nl4h=@oM$yFK*c+!1W0P)cNX8usJl%XR31aK1Mw>hyet08`X{)y?Y3u>R(s6Enxx`DZATE$fm! ztC_#(5CzFr^d0$y=AhxarTmh(b9}gq7XtR<{$C`iqV$OEH#(vC5I!s~oARSEtFeVa zcfCt*x<30QM}lO;Va1)CIoUL zDT^I!TL%Hni9G3Qozu+Qu0i7Hor@MCjNGn4rC_`-o3XGt1htr!n@vqfHf&hEDY}fX z_{%*$Z((9y!L4{DtL;J>uRMo6O(!8?81vrt=>iIAn~7gUQ?N%=xG{nYUm+*`KP`71 zMV&}L@Jh2dA?ufApjAqgE^Ot5>r-{7Y;qz(dDchCU4%nd{w@5*gM*@X`1<#y0pKPP zw56?BgG|Oub{mFEI|N0sE=E?7QTwY2v`YkA`mgWUegkIW%H!S1NiFmRn?g z;Lr|~5I_48lVz5j_3d&~3K{>_vwQ6$65_TKd_cxlmdDusFfH0VhH{AY<6rz#zZ<#r z;gXtpH~GGbel_F5$h-J3!b)ta zL&tdBxSI|XXFF|$kq#S&$h`R{bLU%f$pqqg4h4bkZDgz%&aKfrwWq-_*Ty8Q&WZcX zKWhZfI@TKxveYzgd=fJar5RedA@n+c!Yb3*R}EZ zA-dU6rJ476GzDLXZo$&e>m_i=t(u^jH;zO&alwuYmxSx9v1F1;Ret}cZ!U*{9V4W59DOBplX@jF z*Xd7w_l@KklY~1pewb(vny~W>w4_`v0GwK+nr}X$Siutr35idCU!?h@B|mbgOAF9baT7k z<4FRolY94Qi~vEDeCx6<(5JGiS4|Nn6GNsL zZ-mHJFJu0)DA!iFulP8eUn4YliF+g3htVq7;AB-dqh|MWoDtYwvDcZVA0$tjVeBDJ zB4PftI|Wgxi{L^zuKb7G1vx*~{SUZ1W69X${$T4k=ZG@XIa_wP{hE6`5L#&`$~1Yz z>Z%L$Z}8SWhpjrD4h=SwCmVn=cI45VL(JzdHe^1zVi#I-@oQ)bzHr!p4u8iX+v4UW zk8AV?{Og%IS*t;AaI;ls3?5x(?+baKbOZ_#7@ZMFxMsYmJ8+P1Y6pf$j`v@7PX^9c za|_RnjRCQUNO{Dg8jM+pkB^Q|>qOaw^#q+RCT53|#}8`fERq!`R{l^l0^ z?bG-Mhy4aPAKOX>dEErFj(*yUEe>#x!x=hAAa1o$kak63v7v>zGVfPTY9X`=u zXNeh-HQrR%cH_s&gOH za-KomA?r5Rk=IJmJl*yvjAVF&(FMFM)O{M)e?&+KeS*RS?`<5HoAZ)eb3IGOJLS5U z0kbmeZ#ddnMt1496MynJJhc7v)rsXe&RlNg8L@bG-`#2B0PTq2Nc{ljn6$FmzbRrA z;*4}=l21manWhT>3rAxQqlvE%LF!qGCAks$X!h`r5lQzVCFSTvPgI&@!VPjk_k z6X}bj$S<)v%i)g5I5$IM-R|Vk89Y~CC01R$qnUp+03WJr;aH#5qUD*hr6Enh3s3QH zM6rewi}8#+%u2T}nkHA@$_+z7DI+YShy36K7QQUGx&*p1jXV%@-ukQLL31|QWZ$ea zBvE8p<_@>c5TApF=debq_wxP8qJHLMinlpL-hFx8Ha9$z7MDH}W5)Vx$8yCj5XM>L zZaFEvaf^0DTv#jh3x`69M)vbio&T9gvm3nU?q;h-O=xKz_B(zfQv?l60Hh%FTgG5H zr0`~AuipQa{OfHiB_ti~sH@?oAjr)<0{0Y_+d{F%@~5yQ@RIqOGnCq+%fYgqj&AqD z#h77;U6y1%+;Oj0XqqBA$BIGee>s?Mb=vvfsstn_xdYH-1EvDO6U(6dA)6iPxNKELTAi1Bcempb@09u z4j6KJDefGtf-lhiK>622pn}WoUHiK9yZ0Ks^pw^+KFY~r^nW>a_pvC@X z6j=yA--WUM z5j-NJi(U^)V@z9VWHhjWYG)N!ZU?JmbAR1zgKQJ`RX}*w*{wASGzA-+PjV|jms_33 zofeZjSuy94@hUhilbC8^GCnmN9eS@U#*cQABgoL1akpt~tbeUqyp9X{ek448See6@ zB)=Ue5BwV|rc8&%x+xrwxuf46o6*i0I3w&a8%Aq&p0qevx~H#?#B&HKM&YL}bQN6rrQjIX=b(Hhz0f*Beb z38d+2*E|_g`24m{N-lGb)*5vGtm;|PNphG@1 zW5XEh!BH$=tcy2rmY2CJn>_pFVzSK4bA#2BW0agTsHpm7nn`S%#TBVr>F zP)Dta^e$+ouFKISpb!Znivlz@2>;y7eMK~>ndEnxBhDar5 z(Gi|S_Tl^yR{RV9z^&7OyiBoNzzV87caQ8UTx@7}GK_u2R5QgX;?VltYm48q~-JhbYOed{1c}9M$zC~ zQfiH{Ju@1NrlMls_kyIUhM^u;-s|rixd8)i^HB`ePvPx#(daJV%moo&bXL46XK7Bp zE;k&^HleGjF+Sjhx-~5)4$rK;sn0TGv>yFnF`Xsh2F>VVmlF=$JPnRk!5*Ox8&!1s zV>V8?4A-?vrJxd+(dLU;Y5ZUcUJYt%+h)X=NgV9Y1drFM%eJxnA!yjT_gS>A5kkc- z06pW#M5CZiZW+9m=SpKtqKU2#jUEH2jrb(jk=hJ>QweDd1G1gMXc#Xv4778S^k^4@ zq{Y)!$=SWI(i=5e1slBJLv$gidQdjr_7>jnRu*CFk8{jLeQJ0?d>mXkF{~O)@M(v& zr1Yqo(_t)Ah;v9K8H#8(Y#XQw`PV;!5PxYy)PtxBRrbxQgxkcedzL8O(~N2T^9@Al zKJ<H|kiKok7oF%6|;zt@2hE9f%l_qZ` zXiVUe!yiSLj!Q`15Sjn1$hehw1;6hnV#X5OK~dIrT0dg!d}-3s*l0(@1$}3Mp#i!{ zuDK+E#LXX{k6-`Ey$U{my0(s<3YOs-1C(6cndq`ctzDpAJ?X@acW|4o1aDi;0+f!^ zZ_$Oo+bzRpH4k$cV=pS^#^B={GG9c-JEG*I;b5KB2G5~xkBvg=FuKL!3L!P%J(NA- z9kH=|=hsa95?TeHq84aQ>X{?9OteC1Cw2U5nCtO(*nqMB9YT60p3!Bx#|7#Fje2c! zGvrJ=pc$o{{+fhxC1)IGEMcN7qvIE6#CAn5dqBht*&5S^@`RiQr>G=$%?QefeUY-G zSL3lA!Mq(;>7wP%4ctv+t#Y26?7VJ7GzELO4*}1>7!Ce~rb#+UK6h5=T`Nu6k|EA2 zSHo3Z77Tke_Cf2yHZynlIpmexof>a4Gh64I4rsuRZNIHWZWnHr*8h40w+`P(%Iank z@HqF7Gl#U4=wo0ue69+FFQ)INqPsS>1^AkSEyA<}m&(T8rK^p$ldG^9gCFo#`-$bw zJ82dC8rl&-aLB(|i0O4DK@8iby_SiLsXl_C|1~3~tmn3=xzS>oF@K`RO<+w{dOn%J+9}=@f=h6V%HOXXhW(O@ zP7Sqzg!$MB5_Tw zbLvtFMW6rp5Ix1@w@Yx$1r&#yDfH~uqE};h$6rVjKAzQx^t}Nt8a|+kxG9JudSi*& zv3x(uN8Wtbk6L!;B18P($$`>}S>Sl&vz@^qT3)O08hjR(fo(#@wEV$NvH9eopm81Yfu@i( z3Zg>p`UxrP650^wgk28YU8Oj;2rAX(zHI}ym98S|tbQVVJ3ep-SzkooJh^%paQuD}2}*Q=7=1>M98BQJ-8Of^+yl>oW?~X_ocmrrr-;zTCBoF4ZHi8+BmEg`qpWAi1zdXAPxGy{uTZ@Ua+YWf^oQ+4> zNfp&hzlOhxFpm#ReOFymgk6I2hehN<@`H-IM%goJWM~TBz*V@clW!p~|Jxl+q1Rcg zkGbqN?bbS0%ynh*FYmicm4ww8r?XrLW)@}KSeb`C#SfFzn?IwhUS}&xSH56y{i&k@ z1^-h0l}cdi|D8{rKfC4sH6fF0VjbE{O@Y}}i2?bFoZ@}KE1l7$t#EC(?YZB_)yWu8 z4>{u9`1C>|gyKS^aPUDQ6 z8X{`$%V$U5N(gdkjU|5gDHCRONF&Q)Hyb~j5$lQ(t81P+ZRpd++~`$HbAA(rD}DLL z;hGr{#)^1ZsPv|b=qJ;Wr6+%<29RGv{0D8-gwAZ^`^5B7TGKhL|G6FZaRJn zy(7YaimBMhByt%`Zc~_}7((58W*;%6bqnaG^UvHAj{F@c{vo%v?D2$B^uSnGwxUjm zmF7Q!^D)!8*M99V5WeFukSJSfo~nzrQ^hBOx^?9?D6xhRe1eLp>?8KoYu&HeT?jOR z$^~?{0p=d{ZuAC2t6&4Sfa6`j7*otuVN~KjrW0w~vVlUJYxbw4=qp|HU6L_DqDGE= zk$1dh2@piJ&|3Hqt%5I5M%W)BfPS*2$uJFV+P)>bt%LUo0CkQoEkSB*yauxj9L+hFWNK4-?f@()t<2DMYP z3a;=DYaQ3zKbcxOrRMeTj%R1(Ztty)>G_fU-dF6>CS)n2M?qtMg=dMcfhU;nLqLJF ztU9433wle@nXqT;**g@;zYfJK7-{ud+g;|=^<)kqUpF3O4y(BFuO?`XTx)#T;|c!kJr$n=7`@Gmz?v@1^I4s1rHlP>@O0B=b|K~!BZ zwy1QGwxE#fbpQJd|Ki2i!NjfhxMU+0dn5@+dzS|ZrFp91U$l)|5j+gh=MalXy#ayt z;%6oNSKkUY5AX{B!<;&AL6)JJDgn*0@=U4>wGTI%fm_h3+~zzf4xfjPgg85CE`AFC y4eyTF0000View, edit and delete rows on a table

+ +
createScreenModal.show("grid")}> +
+ +
+
+ Grid + View and manipulate rows on a grid +
+
From 8203139c69639c48190c008336e096e9129619a2 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 26 Oct 2023 11:27:20 +0100 Subject: [PATCH 158/427] fix bug in adding trigger --- .../FlowChart/FlowItemHeader.svelte | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index 0d35f955ef..8b907ae0f6 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -20,7 +20,7 @@ const dispatch = createEventDispatcher() $: stepNames = $selectedAutomation.definition.stepNames - $: automationName = stepNames[block.id] || block.name || "" + $: automationName = stepNames?.[block.id] || block?.name || "" $: automationNameError = getAutomationNameError(automationName) $: status = updateStatus(testResult, isTrigger) $: isTrigger = isTrigger || block.type === "TRIGGER" @@ -32,7 +32,7 @@ } } $: loopBlock = $selectedAutomation?.definition.steps.find( - x => x.blockToLoop === block.id + x => x.blockToLoop === block?.id ) async function onSelect(block) { @@ -59,13 +59,14 @@ const getAutomationNameError = name => { if (name !== block.name && block.name.includes(name)) { if (name?.length > 0) { - let invalidRoleName = !validRegex.test(name) - if (invalidRoleName) { - return "Please enter a role name consisting of only alphanumeric symbols and underscores" + let invalidRoleName = !validRegex.test(name) + if (invalidRoleName) { + return "Please enter a role name consisting of only alphanumeric symbols and underscores" + } } + return null } - return null - }} + } const startTyping = async () => { typing = true From 73a7b8eb50de6f4a4660887974cea14fe4599624 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 26 Oct 2023 11:27:34 +0100 Subject: [PATCH 159/427] fix formatting of selectors --- .../SetupPanel/AutomationBlockSetup.svelte | 14 ++++- .../SetupPanel/CodeEditorModal.svelte | 9 ++- .../SetupPanel/QueryParamSelector.svelte | 63 +++++++++++-------- 3 files changed, 57 insertions(+), 29 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 3a96363751..4a0f1094ad 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -283,6 +283,14 @@ return !dependsOn || !!inputData[dependsOn] } + function shouldRenderField(value) { + return ( + value.customType !== "row" && + value.customType !== "code" && + value.customType !== "queryParams" + ) + } + onMount(async () => { try { await environment.loadVariables() @@ -295,15 +303,15 @@
{#each schemaProperties as [key, value]} {#if canShowField(key, value)} -
- {#if key !== "fields" && value.type !== "boolean" && value.customType !== "row"} +
+ {#if key !== "fields" && value.type !== "boolean" && shouldRenderField(value)} {/if} -
+
{#if value.type === "string" && value.enum && canShowField(key, value)} query._id} - getOptionLabel={query => query.name} - /> +
+ +
+
+
{/if} diff --git a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte index 5fee849afb..c2932d3b10 100644 --- a/packages/builder/src/components/backend/DataTable/TableDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/TableDataTable.svelte @@ -16,6 +16,7 @@ import GridRelationshipButton from "components/backend/DataTable/buttons/grid/GridRelationshipButton.svelte" import GridEditColumnModal from "components/backend/DataTable/modals/grid/GridEditColumnModal.svelte" import GridUsersTableButton from "components/backend/DataTable/modals/grid/GridUsersTableButton.svelte" + import { DB_TYPE_EXTERNAL } from "constants/backend" const userSchemaOverrides = { firstName: { displayName: "First name", disabled: true }, @@ -27,7 +28,7 @@ $: id = $tables.selected?._id $: isUsersTable = id === TableNames.USERS - $: isInternal = $tables.selected?.type !== "external" + $: isInternal = $tables.selected?.sourceType !== DB_TYPE_EXTERNAL $: gridDatasource = { type: "table", tableId: id, @@ -46,10 +47,7 @@ tables.replaceTable(id, e.detail) // We need to refresh datasources when an external table changes. - // Type "external" may exist - sometimes type is "table" and sometimes it - // is "external" - it has different meanings in different endpoints. - // If we check both these then we hopefully catch all external tables. - if (e.detail?.type === "external" || e.detail?.sql) { + if (e.detail?.sourceType === DB_TYPE_EXTERNAL || e.detail?.sql) { await datasources.fetch() } } diff --git a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte index f6160e3caa..bdf62f2959 100644 --- a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte @@ -17,7 +17,6 @@ let hideAutocolumns = true let data = [] let loading = false - let type = "internal" $: name = view.name $: calculation = view.calculation @@ -65,7 +64,6 @@ tableId={view.tableId} {data} {loading} - {type} rowCount={10} allowEditing={false} bind:hideAutocolumns diff --git a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte index 71d971891c..74e255cf7e 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/grid/GridImportButton.svelte @@ -10,6 +10,6 @@ diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 7b51e6c839..8c9b425708 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -26,6 +26,7 @@ ALLOWABLE_NUMBER_TYPES, SWITCHABLE_TYPES, PrettyRelationshipDefinitions, + DB_TYPE_EXTERNAL, } from "constants/backend" import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils" import ConfirmDialog from "components/common/ConfirmDialog.svelte" @@ -254,10 +255,11 @@ !uneditable && editableColumn?.type !== AUTO_TYPE && !editableColumn.autocolumn - $: external = table.type === "external" + $: externalTable = table.sourceType === DB_TYPE_EXTERNAL // in the case of internal tables the sourceId will just be undefined $: tableOptions = $tables.list.filter( - opt => opt.type === table.type && table.sourceId === opt.sourceId + opt => + opt.sourceType === table.sourceType && table.sourceId === opt.sourceId ) $: typeEnabled = !originalName || @@ -409,7 +411,7 @@ editableColumn.type === FieldType.BB_REFERENCE && editableColumn.subtype === FieldSubtype.USERS - if (!external) { + if (!externalTable) { return [ FIELDS.STRING, FIELDS.BARCODEQR, @@ -441,7 +443,7 @@ isUsers ? FIELDS.USERS : FIELDS.USER, ] // no-sql or a spreadsheet - if (!external || table.sql) { + if (!externalTable || table.sql) { fields = [...fields, FIELDS.LINK, FIELDS.ARRAY] } return fields @@ -486,7 +488,7 @@ }) } const newError = {} - if (!external && fieldInfo.name?.startsWith("_")) { + if (!externalTable && fieldInfo.name?.startsWith("_")) { newError.name = `Column name cannot start with an underscore.` } else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) { newError.name = `Illegal character; must be alpha-numeric.` @@ -498,7 +500,7 @@ newError.name = `Column name already in use.` } - if (fieldInfo.type == "auto" && !fieldInfo.subtype) { + if (fieldInfo.type === "auto" && !fieldInfo.subtype) { newError.subtype = `Auto Column requires a type` } diff --git a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte index 43751ad944..eb1e7bc7ff 100644 --- a/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/ExistingTableDataImport.svelte @@ -1,6 +1,6 @@
-
+
diff --git a/packages/client/src/components/app/forms/Field.svelte b/packages/client/src/components/app/forms/Field.svelte index d8b2c7a327..83db76b473 100644 --- a/packages/client/src/components/app/forms/Field.svelte +++ b/packages/client/src/components/app/forms/Field.svelte @@ -102,13 +102,13 @@ @@ -158,12 +160,11 @@
-

+

 

Houston we have a problem!

-

-

+

 

From 8c6365d2a3ca61b5d505745a2b3d14cf48d64a8c Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 31 Oct 2023 10:43:10 +0000 Subject: [PATCH 240/427] Add brand colour variables --- packages/bbui/src/bbui.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/bbui/src/bbui.css b/packages/bbui/src/bbui.css index 343aa77b27..9b5d89f61c 100644 --- a/packages/bbui/src/bbui.css +++ b/packages/bbui/src/bbui.css @@ -2,6 +2,15 @@ --background: #ffffff; --ink: #000000; + /* Brand colours */ + --bb-coral: #FF4E4E; + --bb-coral-light: #F97777; + --bb-indigo: #6E56FF; + --bb-indigo-light: #9F8FFF; + --bb-lime: #ECFFB5; + --bb-forest-green: #053835; + --bb-beige: #F6EFEA; + --grey-1: #fafafa; --grey-2: #f5f5f5; --grey-3: #eeeeee; From 15b1f3efe63fb8d4eceb3dc66d4c3ed3f90fd6ee Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 31 Oct 2023 10:51:46 +0000 Subject: [PATCH 241/427] Removing duplicate filtering of automation logs. --- packages/server/src/sdk/app/backups/exports.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/server/src/sdk/app/backups/exports.ts b/packages/server/src/sdk/app/backups/exports.ts index 6432bd2e0d..c349dcb927 100644 --- a/packages/server/src/sdk/app/backups/exports.ts +++ b/packages/server/src/sdk/app/backups/exports.ts @@ -26,7 +26,6 @@ export interface DBDumpOpts { export interface ExportOpts extends DBDumpOpts { tar?: boolean excludeRows?: boolean - excludeLogs?: boolean encryptPassword?: string } @@ -83,7 +82,7 @@ export async function exportDB( }) } -function defineFilter(excludeRows?: boolean, excludeLogs?: boolean) { +function defineFilter(excludeRows?: boolean) { const ids = [ USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX, @@ -92,9 +91,6 @@ function defineFilter(excludeRows?: boolean, excludeLogs?: boolean) { if (excludeRows) { ids.push(TABLE_ROW_PREFIX) } - if (excludeLogs) { - ids.push(AUTOMATION_LOG_PREFIX) - } return (doc: any) => !ids.map(key => doc._id.includes(key)).reduce((prev, curr) => prev || curr) } @@ -122,7 +118,7 @@ export async function exportApp(appId: string, config?: ExportOpts) { fs.writeFileSync(join(tmpPath, path), contents) } } - // get all of the files + // get all the files else { tmpPath = await objectStore.retrieveDirectory( ObjectStoreBuckets.APPS, @@ -145,7 +141,7 @@ export async function exportApp(appId: string, config?: ExportOpts) { // enforce an export of app DB to the tmp path const dbPath = join(tmpPath, DB_EXPORT_FILE) await exportDB(appId, { - filter: defineFilter(config?.excludeRows, config?.excludeLogs), + filter: defineFilter(config?.excludeRows), exportPath: dbPath, }) @@ -195,7 +191,6 @@ export async function streamExportApp({ }) { const tmpPath = await exportApp(appId, { excludeRows, - excludeLogs: true, tar: true, encryptPassword, }) From d9bce880ecffad25e42535a277bde8a1a4d4f44b Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 31 Oct 2023 10:58:32 +0000 Subject: [PATCH 242/427] Update BB logo across product --- packages/builder/assets/bb-emblem.svg | 89 ++---------- packages/builder/assets/bb-space-black.svg | 18 --- packages/builder/assets/bb-space-purple.svg | 25 ---- packages/builder/public/bblogo.png | Bin 787 -> 3966 bytes .../DatasourceNavigator/icons/Budibase.svelte | 134 +++--------------- 5 files changed, 33 insertions(+), 233 deletions(-) delete mode 100644 packages/builder/assets/bb-space-black.svg delete mode 100644 packages/builder/assets/bb-space-purple.svg diff --git a/packages/builder/assets/bb-emblem.svg b/packages/builder/assets/bb-emblem.svg index 7d499e4862..26d09cc97f 100644 --- a/packages/builder/assets/bb-emblem.svg +++ b/packages/builder/assets/bb-emblem.svg @@ -1,80 +1,13 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + diff --git a/packages/builder/assets/bb-space-black.svg b/packages/builder/assets/bb-space-black.svg deleted file mode 100644 index fa1743f90c..0000000000 --- a/packages/builder/assets/bb-space-black.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/builder/assets/bb-space-purple.svg b/packages/builder/assets/bb-space-purple.svg deleted file mode 100644 index ccfb8b220d..0000000000 --- a/packages/builder/assets/bb-space-purple.svg +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/packages/builder/public/bblogo.png b/packages/builder/public/bblogo.png index 8c89c12f19762f94ae12a8a14291eaae1937e138..aa5ee4466e23c768dbb6c77167c93a63784a91bd 100644 GIT binary patch literal 3966 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 diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Budibase.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Budibase.svelte index 012f54da89..afbdccd2b0 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/icons/Budibase.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Budibase.svelte @@ -4,123 +4,33 @@ - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - + + + + + From e8f5980905af428302db90bb44766bd22d746ef6 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 31 Oct 2023 11:13:19 +0000 Subject: [PATCH 243/427] Update images in emails, readme and a few other usages --- README.md | 2 +- i18n/README.de.md | 2 +- i18n/README.es.md | 2 +- i18n/README.fr.md | 2 +- i18n/README.id.md | 2 +- i18n/README.jp.md | 2 +- i18n/README.zh.md | 2 +- .../portal/apps/onboarding/_components/PanelHeader.svelte | 6 +----- .../client/src/components/app/deprecated/Navigation.svelte | 3 --- packages/worker/src/constants/templates/core.hbs | 2 +- 10 files changed, 9 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7827d4e48a..35b84a8816 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.de.md b/i18n/README.de.md index a2f4c3afb9..17d3d1ebbe 100644 --- a/i18n/README.de.md +++ b/i18n/README.de.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.es.md b/i18n/README.es.md index 21eb8caef7..227d5d5d5f 100644 --- a/i18n/README.es.md +++ b/i18n/README.es.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.fr.md b/i18n/README.fr.md index 12abd4d073..f5f9fbb25e 100644 --- a/i18n/README.fr.md +++ b/i18n/README.fr.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.id.md b/i18n/README.id.md index d4a25f569c..c2077f3922 100644 --- a/i18n/README.id.md +++ b/i18n/README.id.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.jp.md b/i18n/README.jp.md index 6fea497d53..62d0b1d3aa 100644 --- a/i18n/README.jp.md +++ b/i18n/README.jp.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/i18n/README.zh.md b/i18n/README.zh.md index 7e4dffd387..a6a9575029 100644 --- a/i18n/README.zh.md +++ b/i18n/README.zh.md @@ -1,6 +1,6 @@

- Budibase + Budibase

diff --git a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/PanelHeader.svelte b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/PanelHeader.svelte index 34d612dc9e..49b8032726 100644 --- a/packages/builder/src/pages/builder/portal/apps/onboarding/_components/PanelHeader.svelte +++ b/packages/builder/src/pages/builder/portal/apps/onboarding/_components/PanelHeader.svelte @@ -8,11 +8,7 @@
- +
{#if onBack} - +
{:else} {/if} @@ -390,12 +389,15 @@ diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 9d931af7bb..9260a197c2 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -58,7 +58,6 @@ let fillWidth = true let inputData let codeBindingOpen = false - $: console.log($selectedAutomation?.definition) $: filters = lookForFilters(schemaProperties) || [] $: tempFilters = filters $: stepId = block.stepId @@ -136,7 +135,6 @@ await automationStore.actions.addTestDataToAutomation(newTestData) } else { const data = { schema, [key]: e.detail } - console.log(data) await automationStore.actions.updateBlockInputs(block, data) } } catch (error) { @@ -156,7 +154,7 @@ } let blockIdx = allSteps.findIndex(step => step.id === block.id) - // Extract all outputs from all previous steps as available bindins + // Extract all outputs from all previous steps as available bindingsx§x let bindings = [] let loopBlockCount = 0 for (let idx = 0; idx < blockIdx; idx++) { @@ -197,7 +195,6 @@ } let bindingName = automation.stepNames?.[allSteps[idx - loopBlockCount].id] - console.log(bindingName) bindings = bindings.concat( outputs.map(([name, value]) => { let runtimeName = isLoopBlock From 36414823658c293bbac4a8ab09409778b4991c1e Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 31 Oct 2023 13:43:52 +0000 Subject: [PATCH 253/427] Update meta image tag to coral wordmark --- packages/server/src/api/controllers/static/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts index 984cb16c06..75948d3fe2 100644 --- a/packages/server/src/api/controllers/static/index.ts +++ b/packages/server/src/api/controllers/static/index.ts @@ -123,7 +123,7 @@ export const serveApp = async function (ctx: Ctx) { const { head, html, css } = App.render({ metaImage: branding?.metaImageUrl || - "https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png", + "https://res.cloudinary.com/daog6scxm/image/upload/v1698759482/meta-images/plain-branded-meta-image-coral_ocxmgu.png", metaDescription: branding?.metaDescription || "", metaTitle: branding?.metaTitle || `${appInfo.name} - built with Budibase`, @@ -161,7 +161,7 @@ export const serveApp = async function (ctx: Ctx) { metaTitle: branding?.metaTitle, metaImage: branding?.metaImageUrl || - "https://res.cloudinary.com/daog6scxm/image/upload/v1666109324/meta-images/budibase-meta-image_uukc1m.png", + "https://res.cloudinary.com/daog6scxm/image/upload/v1698759482/meta-images/plain-branded-meta-image-coral_ocxmgu.png", metaDescription: branding?.metaDescription || "", favicon: branding.faviconUrl !== "" From 82c0e3d450fad816b278ff5d538377235257f11c Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 31 Oct 2023 13:43:54 +0000 Subject: [PATCH 254/427] Fix issue with directional relationship being swapped --- .../backend/Datasources/CreateEditRelationship.svelte | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte index abec380b46..f6621c1508 100644 --- a/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte +++ b/packages/builder/src/components/backend/Datasources/CreateEditRelationship.svelte @@ -30,15 +30,15 @@ part2: PrettyRelationshipDefinitions.MANY, }, [RelationshipType.MANY_TO_ONE]: { - part1: PrettyRelationshipDefinitions.ONE, - part2: PrettyRelationshipDefinitions.MANY, + part1: PrettyRelationshipDefinitions.MANY, + part2: PrettyRelationshipDefinitions.ONE, }, } let relationshipOpts1 = Object.values(PrettyRelationshipDefinitions) let relationshipOpts2 = Object.values(PrettyRelationshipDefinitions) - let relationshipPart1 = PrettyRelationshipDefinitions.MANY - let relationshipPart2 = PrettyRelationshipDefinitions.ONE + let relationshipPart1 = PrettyRelationshipDefinitions.ONE + let relationshipPart2 = PrettyRelationshipDefinitions.MANY let originalFromColumnName = toRelationship.name, originalToColumnName = fromRelationship.name From b65b6019185bbfbd9b05cfb70d20fc4428dee1b2 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 31 Oct 2023 13:52:28 +0000 Subject: [PATCH 255/427] remove label --- .../src/components/automation/SetupPanel/RowSelector.svelte | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte index 4f96c21370..971bb5886b 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte @@ -132,7 +132,6 @@ title={value.title} panel={AutomationBindingPanel} type={schema.type} - label={field} {schema} value={value[field]} on:change={e => onChange(e, field)} From 7c1f71745e4ada6bbc9896ee68f8d31c439cab82 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Tue, 31 Oct 2023 13:58:59 +0000 Subject: [PATCH 256/427] Update email link and button colours and sort apps in portal side bar --- .../apps/_components/PortalSideBar.svelte | 18 ++++++++++++------ .../worker/src/constants/templates/base.hbs | 12 ++++++------ .../worker/src/constants/templates/core.hbs | 6 +----- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/packages/builder/src/pages/builder/portal/apps/_components/PortalSideBar.svelte b/packages/builder/src/pages/builder/portal/apps/_components/PortalSideBar.svelte index 1e21bd7a9a..7989c5f1a8 100644 --- a/packages/builder/src/pages/builder/portal/apps/_components/PortalSideBar.svelte +++ b/packages/builder/src/pages/builder/portal/apps/_components/PortalSideBar.svelte @@ -9,12 +9,18 @@ let searchString let searching = false - $: filteredApps = $apps.filter(app => { - return ( - !searchString || - app.name.toLowerCase().includes(searchString.toLowerCase()) - ) - }) + $: filteredApps = $apps + .filter(app => { + return ( + !searchString || + app.name.toLowerCase().includes(searchString.toLowerCase()) + ) + }) + .sort((a, b) => { + const lowerA = a.name.toLowerCase() + const lowerB = b.name.toLowerCase() + return lowerA > lowerB ? 1 : -1 + }) const startSearching = async () => { searching = true diff --git a/packages/worker/src/constants/templates/base.hbs b/packages/worker/src/constants/templates/base.hbs index 438197b5d2..9a7d906aa9 100644 --- a/packages/worker/src/constants/templates/base.hbs +++ b/packages/worker/src/constants/templates/base.hbs @@ -19,7 +19,7 @@ } a { - color: #3869D4 !important; + color: #6E56FF !important; } a img { @@ -109,11 +109,11 @@ /* Buttons ------------------------------ */ .button { - background-color: #3869D4; - border-top: 10px solid #3869D4; - border-right: 18px solid #3869D4; - border-bottom: 10px solid #3869D4; - border-left: 18px solid #3869D4; + background-color: #6E56FF; + border-top: 10px solid #6E56FF; + border-right: 18px solid #6E56FF; + border-bottom: 10px solid #6E56FF; + border-left: 18px solid #6E56FF; display: inline-block; color: #FFF !important; text-decoration: none !important; diff --git a/packages/worker/src/constants/templates/core.hbs b/packages/worker/src/constants/templates/core.hbs index 950731beb4..07c755b1fb 100644 --- a/packages/worker/src/constants/templates/core.hbs +++ b/packages/worker/src/constants/templates/core.hbs @@ -16,15 +16,11 @@ cellspacing="0" > Budibase Logo - - Budibase - From bb19e48a6d90f7230504ea3609c47d73afe36a1d Mon Sep 17 00:00:00 2001 From: Conor Webb Date: Tue, 31 Oct 2023 14:22:15 +0000 Subject: [PATCH 257/427] Added new DataSourceSelect component. --- .../settings/controls/DataSourceSelect.svelte | 287 ++++-------------- .../DataSourceSelectItem/SelectItem.svelte | 60 ++++ 2 files changed, 123 insertions(+), 224 deletions(-) create mode 100644 packages/builder/src/components/design/settings/controls/DataSourceSelectItem/SelectItem.svelte diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte index 545165a25c..a56614e4e5 100644 --- a/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte @@ -32,6 +32,7 @@ import IntegrationQueryEditor from "components/integration/index.svelte" import { makePropSafe as safe } from "@budibase/string-templates" import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte" + import DataSourceSelect from "components/design/settings/controls/DataSourceSelectItem/SelectItem.svelte" import { API } from "api" export let value = {} @@ -279,243 +280,81 @@
diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/SelectItem.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/SelectItem.svelte new file mode 100644 index 0000000000..1fda2da634 --- /dev/null +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/SelectItem.svelte @@ -0,0 +1,60 @@ + + +{#if dividerState} + +{/if} +{#if heading.length > 0} +
+ {heading} +
+{/if} +
    + {#each dataSet as data} +
  • onSelect(data)} + on:keydown={e => { + if (e.key === "Enter" || e.key === "Space") { + onSelect(data) + } + }} + > + + {data.label} + + +
  • + {/each} +
+ + From df688b059669dc29d5cc5e374077e033012c084f Mon Sep 17 00:00:00 2001 From: Conor Webb Date: Tue, 31 Oct 2023 14:36:50 +0000 Subject: [PATCH 258/427] Fixed linting issues. --- .../settings/controls/DataSourceSelect.svelte | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte index a56614e4e5..0d8e728403 100644 --- a/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelect.svelte @@ -7,10 +7,8 @@ import { Button, Popover, - Divider, Select, Layout, - Heading, Drawer, DrawerContent, Icon, @@ -381,27 +379,6 @@ z-index: 99999999; overflow-y: scroll; } - .title { - padding: 0 var(--spacing-m) var(--spacing-s) var(--spacing-m); - } - - ul { - list-style: none; - padding-left: 0px; - margin: 0px; - width: 100%; - } - - li { - cursor: pointer; - margin: 0px; - padding: var(--spacing-s) var(--spacing-m); - font-size: var(--font-size-m); - } - - li:hover { - background-color: var(--spectrum-global-color-gray-200); - } .icon { margin-left: 8px; From 7e33aacbb1a77b0fa989d8647d9bf680c9b9ddfe Mon Sep 17 00:00:00 2001 From: Dean Date: Tue, 31 Oct 2023 14:48:23 +0000 Subject: [PATCH 259/427] Stop the sample data being identified as an external source. --- packages/server/src/integrations/utils.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/server/src/integrations/utils.ts b/packages/server/src/integrations/utils.ts index b4fff0737a..33895e4fe1 100644 --- a/packages/server/src/integrations/utils.ts +++ b/packages/server/src/integrations/utils.ts @@ -11,6 +11,7 @@ import { InvalidColumns, NoEmptyFilterStrings } from "../constants" import { helpers } from "@budibase/shared-core" import * as external from "../api/controllers/table/external" import * as internal from "../api/controllers/table/internal" +import { DEFAULT_BB_DATASOURCE_ID } from "../db/defaultData/datasource_bb_default" const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}` const ROW_ID_REGEX = /^\[.*]$/g @@ -96,7 +97,8 @@ export function isInternalTableID(tableId: string) { export function isExternalTable(table: Table) { if ( table?.sourceId && - table.sourceId.includes(DocumentType.DATASOURCE + SEPARATOR) + table.sourceId.includes(DocumentType.DATASOURCE + SEPARATOR) && + table?.sourceId !== DEFAULT_BB_DATASOURCE_ID ) { return true } else if (table?.sourceType === TableSourceType.EXTERNAL) { From 7cba4d3fb21b59dda768581e7e2a525fafa2b70c Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Tue, 31 Oct 2023 16:29:43 +0000 Subject: [PATCH 260/427] formatting found in testing --- .../FlowChart/FlowItem.svelte | 6 ++++-- .../SetupPanel/AutomationBlockSetup.svelte | 1 + .../automation/SetupPanel/RowSelector.svelte | 1 + .../SetupPanel/RowSelectorTypes.svelte | 21 +++---------------- .../common/bindings/DrawerBindableSlot.svelte | 4 ++-- 5 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index 9692154e31..c6d38a4d2e 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -168,8 +168,10 @@
{#if isAppAction} - - +
+ + +
{/if}
diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index c573049c08..373d174541 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -1,11 +1,5 @@ From 234cdbbf2441c4f3921a7226e14b920ba4506bab Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 1 Nov 2023 10:51:34 +0000 Subject: [PATCH 268/427] Bump version to 2.12.2 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 6df4a4c4cd..cb92b3ba0d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.1", + "version": "2.12.2", "npmClient": "yarn", "packages": [ "packages/*" From 5c049e0465ca27ed078e3ffda4c5c91998ba97cd Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 1 Nov 2023 13:54:18 +0000 Subject: [PATCH 269/427] LongFormField readonly prop --- .../bbui/src/Form/Core/RichTextField.svelte | 2 ++ packages/bbui/src/Form/Core/TextArea.svelte | 2 ++ .../bbui/src/Markdown/MarkdownEditor.svelte | 5 ++- packages/client/manifest.json | 33 +++++++++++++++++++ .../components/app/forms/LongFormField.svelte | 3 ++ .../components/app/forms/StringField.svelte | 2 ++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/bbui/src/Form/Core/RichTextField.svelte b/packages/bbui/src/Form/Core/RichTextField.svelte index f964405f0d..3e0b0caf4d 100644 --- a/packages/bbui/src/Form/Core/RichTextField.svelte +++ b/packages/bbui/src/Form/Core/RichTextField.svelte @@ -4,6 +4,7 @@ export let value = "" export let placeholder = null export let disabled = false + export let readonly = false export let error = null export let height = null export let id = null @@ -20,6 +21,7 @@ {fullScreenOffset} {disabled} {easyMDEOptions} + {readonly} on:change />
diff --git a/packages/bbui/src/Form/Core/TextArea.svelte b/packages/bbui/src/Form/Core/TextArea.svelte index 465212cd44..be7eed466d 100644 --- a/packages/bbui/src/Form/Core/TextArea.svelte +++ b/packages/bbui/src/Form/Core/TextArea.svelte @@ -5,6 +5,7 @@ export let value = "" export let placeholder = null export let disabled = false + export let readonly = false export let error = null export let id = null export let height = null @@ -61,6 +62,7 @@ class="spectrum-Textfield-input" style={align ? `text-align: ${align}` : ""} {disabled} + {readonly} {id} on:focus={() => (focus = true)} on:blur={onChange} diff --git a/packages/bbui/src/Markdown/MarkdownEditor.svelte b/packages/bbui/src/Markdown/MarkdownEditor.svelte index 7fb6414ad8..27035d8033 100644 --- a/packages/bbui/src/Markdown/MarkdownEditor.svelte +++ b/packages/bbui/src/Markdown/MarkdownEditor.svelte @@ -8,6 +8,7 @@ export let id = null export let fullScreenOffset = 0 export let disabled = false + export let readonly = false export let easyMDEOptions const dispatch = createEventDispatcher() @@ -19,6 +20,7 @@ // control $: checkValue(value) $: mde?.codemirror.on("change", debouncedUpdate) + $: mde?.codemirror.setOption("readOnly", readonly) const checkValue = val => { if (mde && val !== latestValue) { @@ -43,7 +45,7 @@ const debouncedUpdate = debounce(update, 250) -{#key height} +{#key (height, readonly)} diff --git a/packages/client/manifest.json b/packages/client/manifest.json index eef1e50b7c..6e175c44de 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -2589,6 +2589,17 @@ "key": "disabled", "defaultValue": false }, + { + "type": "boolean", + "label": "Read only", + "key": "readonly", + "defaultValue": false, + "dependsOn": { + "setting": "disabled", + "value": true, + "invert": true + } + }, { "type": "text", "label": "Initial form step", @@ -2738,6 +2749,17 @@ "key": "disabled", "defaultValue": false }, + { + "type": "boolean", + "label": "Read only", + "key": "readonly", + "defaultValue": false, + "dependsOn": { + "setting": "disabled", + "value": true, + "invert": true + } + }, { "type": "validation/string", "label": "Validation", @@ -3427,6 +3449,17 @@ "key": "disabled", "defaultValue": false }, + { + "type": "boolean", + "label": "Read only", + "key": "readonly", + "defaultValue": false, + "dependsOn": { + "setting": "disabled", + "value": true, + "invert": true + } + }, { "type": "validation/string", "label": "Validation", diff --git a/packages/client/src/components/app/forms/LongFormField.svelte b/packages/client/src/components/app/forms/LongFormField.svelte index 8d94f83319..8482a6a68e 100644 --- a/packages/client/src/components/app/forms/LongFormField.svelte +++ b/packages/client/src/components/app/forms/LongFormField.svelte @@ -8,6 +8,7 @@ export let label export let placeholder export let disabled = false + export let readonly = false export let validation export let defaultValue = "" export let format = "auto" @@ -71,6 +72,7 @@ value={fieldState.value} on:change={handleChange} disabled={fieldState.disabled} + {readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} @@ -88,6 +90,7 @@ value={fieldState.value} on:change={handleChange} disabled={fieldState.disabled} + {readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} diff --git a/packages/client/src/components/app/forms/StringField.svelte b/packages/client/src/components/app/forms/StringField.svelte index 26136b5d8d..624611c733 100644 --- a/packages/client/src/components/app/forms/StringField.svelte +++ b/packages/client/src/components/app/forms/StringField.svelte @@ -11,6 +11,7 @@ export let defaultValue = "" export let align export let onChange + export let readonly = false let fieldState let fieldApi @@ -44,6 +45,7 @@ {placeholder} {type} {align} + {readonly} /> {/if} From 5c36d70a0116624acadc20865a7bba6f76d46d1d Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 1 Nov 2023 14:56:28 +0000 Subject: [PATCH 270/427] Pickers readonly prop --- .../bbui/src/Form/Core/CheckboxGroup.svelte | 6 +++++ packages/bbui/src/Form/Core/RadioGroup.svelte | 6 +++++ packages/client/manifest.json | 22 +++++++++++++++++++ .../app/forms/MultiFieldSelect.svelte | 3 +++ .../components/app/forms/OptionsField.svelte | 3 +++ 5 files changed, 40 insertions(+) diff --git a/packages/bbui/src/Form/Core/CheckboxGroup.svelte b/packages/bbui/src/Form/Core/CheckboxGroup.svelte index 2b8a1e438a..faf37f3ad8 100644 --- a/packages/bbui/src/Form/Core/CheckboxGroup.svelte +++ b/packages/bbui/src/Form/Core/CheckboxGroup.svelte @@ -8,6 +8,7 @@ export let options = [] export let error = null export let disabled = false + export let readonly = false export let getOptionLabel = option => option export let getOptionValue = option => option @@ -40,6 +41,11 @@ > { + if (readonly) { + e.preventDefault() + } + }} type="checkbox" class="spectrum-Checkbox-input" value={optionValue} diff --git a/packages/bbui/src/Form/Core/RadioGroup.svelte b/packages/bbui/src/Form/Core/RadioGroup.svelte index f7afc10bbc..fc99fafd40 100644 --- a/packages/bbui/src/Form/Core/RadioGroup.svelte +++ b/packages/bbui/src/Form/Core/RadioGroup.svelte @@ -8,6 +8,7 @@ export let options = [] export let error = null export let disabled = false + export let readonly = false export let getOptionLabel = option => option export let getOptionValue = option => option export let getOptionTitle = option => option @@ -43,6 +44,11 @@ > { + if (readonly) { + e.preventDefault() + } + }} bind:group={value} value={getOptionValue(option)} type="radio" diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 6e175c44de..90c897139c 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -3071,6 +3071,17 @@ "key": "disabled", "defaultValue": false }, + { + "type": "boolean", + "label": "Read only", + "key": "readonly", + "defaultValue": false, + "dependsOn": { + "setting": "disabled", + "value": true, + "invert": true + } + }, { "type": "select", "label": "Options source", @@ -3196,6 +3207,17 @@ "key": "disabled", "defaultValue": false }, + { + "type": "boolean", + "label": "Read only", + "key": "readonly", + "defaultValue": false, + "dependsOn": { + "setting": "disabled", + "value": true, + "invert": true + } + }, { "type": "select", "label": "Type", diff --git a/packages/client/src/components/app/forms/MultiFieldSelect.svelte b/packages/client/src/components/app/forms/MultiFieldSelect.svelte index 88e1ec5a8e..cb4879f86e 100644 --- a/packages/client/src/components/app/forms/MultiFieldSelect.svelte +++ b/packages/client/src/components/app/forms/MultiFieldSelect.svelte @@ -6,6 +6,7 @@ export let label export let placeholder export let disabled = false + export let readonly = false export let validation export let defaultValue export let optionsSource = "schema" @@ -71,6 +72,7 @@ getOptionValue={flatOptions ? x => x : x => x.value} id={fieldState.fieldId} disabled={fieldState.disabled} + {readonly} on:change={handleChange} {placeholder} {options} @@ -81,6 +83,7 @@ value={fieldState.value || []} id={fieldState.fieldId} disabled={fieldState.disabled} + {readonly} error={fieldState.error} {options} {direction} diff --git a/packages/client/src/components/app/forms/OptionsField.svelte b/packages/client/src/components/app/forms/OptionsField.svelte index 3c229c0509..c01827471a 100644 --- a/packages/client/src/components/app/forms/OptionsField.svelte +++ b/packages/client/src/components/app/forms/OptionsField.svelte @@ -6,6 +6,7 @@ export let label export let placeholder export let disabled = false + export let readonly = false export let optionsType = "select" export let validation export let defaultValue @@ -58,6 +59,7 @@ value={fieldState.value} id={fieldState.fieldId} disabled={fieldState.disabled} + {readonly} error={fieldState.error} {options} {placeholder} @@ -72,6 +74,7 @@ value={fieldState.value} id={fieldState.fieldId} disabled={fieldState.disabled} + {readonly} error={fieldState.error} {options} {direction} From 984d9497811a6daa049c743f7e4095c06eca8b5a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 1 Nov 2023 15:02:55 +0000 Subject: [PATCH 271/427] Updating single image build - the old method of installing Node 18 was deprecated, switching to the new mechanism. --- hosting/scripts/install-minio.sh | 10 ---------- hosting/single/Dockerfile.v2 | 11 +++++------ scripts/install-minio.sh | 6 +++--- scripts/install-node.sh | 8 ++++++++ 4 files changed, 16 insertions(+), 19 deletions(-) delete mode 100755 hosting/scripts/install-minio.sh create mode 100644 scripts/install-node.sh diff --git a/hosting/scripts/install-minio.sh b/hosting/scripts/install-minio.sh deleted file mode 100755 index 8297593599..0000000000 --- a/hosting/scripts/install-minio.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -if [[ $TARGETARCH == arm* ]] ; -then - echo "INSTALLING ARM64 MINIO" - wget https://dl.min.io/server/minio/release/linux-arm64/minio -else - echo "INSTALLING AMD64 MINIO" - wget https://dl.min.io/server/minio/release/linux-amd64/minio -fi -chmod +x minio diff --git a/hosting/single/Dockerfile.v2 b/hosting/single/Dockerfile.v2 index 5b07a51b27..ec03a1b5a2 100644 --- a/hosting/single/Dockerfile.v2 +++ b/hosting/single/Dockerfile.v2 @@ -42,6 +42,7 @@ COPY packages/string-templates packages/string-templates FROM budibase/couchdb as runner ARG TARGETARCH ENV TARGETARCH $TARGETARCH +ENV NODE_MAJOR 18 #TARGETBUILD can be set to single (for single docker image) or aas (for azure app service) # e.g. docker build --build-arg TARGETBUILD=aas .... ARG TARGETBUILD=single @@ -49,10 +50,10 @@ ENV TARGETBUILD $TARGETBUILD # install base dependencies RUN apt-get update && \ - apt-get install -y --no-install-recommends software-properties-common nginx uuid-runtime redis-server + apt-get install -y --no-install-recommends software-properties-common nginx uuid-runtime redis-server libaio1 # Install postgres client for pg_dump utils -RUN apt install software-properties-common apt-transport-https gpg -y \ +RUN apt install -y software-properties-common apt-transport-https ca-certificates gnupg \ && curl -fsSl https://www.postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql.gpg > /dev/null \ && echo deb [arch=amd64,arm64,ppc64el signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt/ $(lsb_release -cs)-pgdg main | tee /etc/apt/sources.list.d/postgresql.list \ && apt update -y \ @@ -61,10 +62,8 @@ RUN apt install software-properties-common apt-transport-https gpg -y \ # install other dependencies, nodejs, oracle requirements, jdk8, redis, nginx WORKDIR /nodejs -RUN curl -sL https://deb.nodesource.com/setup_18.x -o /tmp/nodesource_setup.sh && \ - bash /tmp/nodesource_setup.sh && \ - apt-get install -y --no-install-recommends libaio1 nodejs && \ - npm install --global yarn pm2 +COPY scripts/install-node.sh ./install.sh +RUN chmod +x install.sh && ./install.sh # setup nginx COPY hosting/single/nginx/nginx.conf /etc/nginx diff --git a/scripts/install-minio.sh b/scripts/install-minio.sh index 8297593599..b1e0d9ee80 100755 --- a/scripts/install-minio.sh +++ b/scripts/install-minio.sh @@ -2,9 +2,9 @@ if [[ $TARGETARCH == arm* ]] ; then echo "INSTALLING ARM64 MINIO" - wget https://dl.min.io/server/minio/release/linux-arm64/minio + wget wget https://dl.min.io/server/minio/release/linux-arm64/archive/minio.deb -O minio.deb else echo "INSTALLING AMD64 MINIO" - wget https://dl.min.io/server/minio/release/linux-amd64/minio + wget wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio.deb -O minio.deb fi -chmod +x minio +dpkg -i minio.deb diff --git a/scripts/install-node.sh b/scripts/install-node.sh new file mode 100644 index 0000000000..562bdf2cd3 --- /dev/null +++ b/scripts/install-node.sh @@ -0,0 +1,8 @@ +#!/bin/bash +apt-get install -y gnupg +curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor | tee /usr/share/keyrings/nodesource.gpg > /dev/null +echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list +apt-get update +echo "INSTALLING NODE $NODE_MAJOR" +apt-get install -y --no-install-recommends nodejs +npm install --global yarn pm2 \ No newline at end of file From cf40b87d81f7161f2294c6f00c1768ec8e7aa49f Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 1 Nov 2023 15:10:32 +0000 Subject: [PATCH 272/427] Use installed MinIO rather than binary. --- hosting/single/runner.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 9dc7aa25d8..770b23eec1 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -77,7 +77,7 @@ mkdir -p ${DATA_DIR}/minio chown -R couchdb:couchdb ${DATA_DIR}/couch redis-server --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 & /bbcouch-runner.sh & -/minio/minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & +minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & /etc/init.d/nginx restart if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then # Add monthly cron job to renew certbot certificate From 85325814d06ab47f3fb8fba6bba3717f66c779de Mon Sep 17 00:00:00 2001 From: Mitch-Budibase Date: Wed, 1 Nov 2023 15:18:20 +0000 Subject: [PATCH 273/427] Moving stripe import to top of test file --- qa-core/src/account-api/tests/licensing/license.manage.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts index a9109dad3a..9cad980038 100644 --- a/qa-core/src/account-api/tests/licensing/license.manage.spec.ts +++ b/qa-core/src/account-api/tests/licensing/license.manage.spec.ts @@ -1,6 +1,7 @@ import TestConfiguration from "../../config/TestConfiguration" import * as fixtures from "../../fixtures" import { Hosting, PlanType } from "@budibase/types" +const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY) describe("license management", () => { const config = new TestConfiguration() @@ -53,7 +54,6 @@ describe("license management", () => { expect(checkoutSessionUrl).toContain("checkout.stripe.com") // Create stripe customer - const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY) const customer = await stripe.customers.create({ email: createAccountRequest.email, }) From d6a4b71655991f27ee45986f0339bd1e33d8dc2b Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 1 Nov 2023 15:37:08 +0000 Subject: [PATCH 274/427] Bump version to 2.12.3 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index cb92b3ba0d..dc6ffb2da2 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.2", + "version": "2.12.3", "npmClient": "yarn", "packages": [ "packages/*" From 56d5a0b8f6fbc0e265bdc0068f253d601d56c62a Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 1 Nov 2023 16:01:45 +0000 Subject: [PATCH 275/427] Further read only settings --- packages/bbui/src/Form/Core/Checkbox.svelte | 6 ++ packages/bbui/src/Form/Core/DatePicker.svelte | 4 +- packages/bbui/src/Form/DatePicker.svelte | 2 + packages/client/manifest.json | 57 ++++++++++++++++++- .../components/app/forms/BooleanField.svelte | 2 + .../components/app/forms/DateTimeField.svelte | 2 + .../src/components/app/forms/JSONField.svelte | 2 + .../app/forms/RelationshipField.svelte | 2 + 8 files changed, 75 insertions(+), 2 deletions(-) diff --git a/packages/bbui/src/Form/Core/Checkbox.svelte b/packages/bbui/src/Form/Core/Checkbox.svelte index 3efc737bfb..3eaaf4dede 100644 --- a/packages/bbui/src/Form/Core/Checkbox.svelte +++ b/packages/bbui/src/Form/Core/Checkbox.svelte @@ -8,6 +8,7 @@ export let id = null export let text = null export let disabled = false + export let readonly = false export let size export let indeterminate = false @@ -29,6 +30,11 @@ checked={value} {disabled} on:change={onChange} + on:click={e => { + if (readonly) { + e.preventDefault() + } + }} type="checkbox" class="spectrum-Checkbox-input" {id} diff --git a/packages/bbui/src/Form/Core/DatePicker.svelte b/packages/bbui/src/Form/Core/DatePicker.svelte index 7ce15292be..786aee40b6 100644 --- a/packages/bbui/src/Form/Core/DatePicker.svelte +++ b/packages/bbui/src/Form/Core/DatePicker.svelte @@ -9,6 +9,7 @@ export let id = null export let disabled = false + export let readonly = false export let error = null export let enableTime = true export let value = null @@ -186,7 +187,7 @@ >
option._id} From 586d539109f38d9a48b8fe23b0d4ea9084d2320d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 1 Nov 2023 16:23:51 +0000 Subject: [PATCH 276/427] Adding release information to single image. --- .github/workflows/release-singleimage.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release-singleimage.yml b/.github/workflows/release-singleimage.yml index 4d35916f4d..a3444d5e7a 100644 --- a/.github/workflows/release-singleimage.yml +++ b/.github/workflows/release-singleimage.yml @@ -66,14 +66,21 @@ jobs: context: . push: true platforms: linux/amd64,linux/arm64 + build-args: BUDIBASE_VERSION=$BUDIBASE_VERSION tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 + env: + BUDIBASE_VERSION: ${{ env.RELEASE_VERSION }} - name: Tag and release Budibase Azure App Service docker image uses: docker/build-push-action@v2 with: context: . push: true platforms: linux/amd64 - build-args: TARGETBUILD=aas + build-args: | + TARGETBUILD=aas + BUDIBASE_VERSION=$BUDIBASE_VERSION tags: budibase/budibase-aas,budibase/budibase-aas:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 + env: + BUDIBASE_VERSION: ${{ env.RELEASE_VERSION }} From 33e37261b2e8a16404efb68467eb27eea7e42ac7 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 1 Nov 2023 16:27:52 +0000 Subject: [PATCH 277/427] Use fieldState --- .../client/src/components/app/forms/AttachmentField.svelte | 4 +++- packages/client/src/components/app/forms/BooleanField.svelte | 3 ++- .../client/src/components/app/forms/CodeScannerField.svelte | 4 +++- .../client/src/components/app/forms/DateTimeField.svelte | 3 ++- packages/client/src/components/app/forms/Field.svelte | 2 ++ packages/client/src/components/app/forms/Form.svelte | 4 +++- packages/client/src/components/app/forms/InnerForm.svelte | 3 +++ packages/client/src/components/app/forms/JSONField.svelte | 3 ++- .../client/src/components/app/forms/LongFormField.svelte | 5 +++-- .../client/src/components/app/forms/MultiFieldSelect.svelte | 5 +++-- packages/client/src/components/app/forms/OptionsField.svelte | 5 +++-- .../client/src/components/app/forms/RelationshipField.svelte | 3 ++- packages/client/src/components/app/forms/StringField.svelte | 5 +++-- 13 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/client/src/components/app/forms/AttachmentField.svelte b/packages/client/src/components/app/forms/AttachmentField.svelte index e24115ebc0..861d881733 100644 --- a/packages/client/src/components/app/forms/AttachmentField.svelte +++ b/packages/client/src/components/app/forms/AttachmentField.svelte @@ -6,6 +6,7 @@ export let field export let label export let disabled = false + export let readonly = false export let compact = false export let validation export let extensions @@ -71,6 +72,7 @@ {label} {field} {disabled} + {readonly} {validation} type="attachment" bind:fieldState @@ -81,7 +83,7 @@ {#if fieldState} { @@ -205,6 +207,7 @@ error: initialError, disabled: disabled || fieldDisabled || (isAutoColumn && !editAutoColumns), + readonly: readonly || fieldReadOnly, defaultValue, validator, lastUpdate: Date.now(), diff --git a/packages/client/src/components/app/forms/JSONField.svelte b/packages/client/src/components/app/forms/JSONField.svelte index 1ee09c8460..cf96f54a23 100644 --- a/packages/client/src/components/app/forms/JSONField.svelte +++ b/packages/client/src/components/app/forms/JSONField.svelte @@ -49,6 +49,7 @@ {label} {field} {disabled} + {readonly} {validation} {defaultValue} type="json" @@ -61,7 +62,7 @@ value={serialiseValue(fieldState.value)} on:change={handleChange} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} diff --git a/packages/client/src/components/app/forms/LongFormField.svelte b/packages/client/src/components/app/forms/LongFormField.svelte index 8482a6a68e..a9087a0a9c 100644 --- a/packages/client/src/components/app/forms/LongFormField.svelte +++ b/packages/client/src/components/app/forms/LongFormField.svelte @@ -59,6 +59,7 @@ {label} {field} {disabled} + {readonly} {validation} {defaultValue} type="longform" @@ -72,7 +73,7 @@ value={fieldState.value} on:change={handleChange} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} @@ -90,7 +91,7 @@ value={fieldState.value} on:change={handleChange} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} diff --git a/packages/client/src/components/app/forms/MultiFieldSelect.svelte b/packages/client/src/components/app/forms/MultiFieldSelect.svelte index cb4879f86e..4ee691061a 100644 --- a/packages/client/src/components/app/forms/MultiFieldSelect.svelte +++ b/packages/client/src/components/app/forms/MultiFieldSelect.svelte @@ -56,6 +56,7 @@ {field} {label} {disabled} + {readonly} {validation} defaultValue={expandedDefaultValue} type="array" @@ -72,7 +73,7 @@ getOptionValue={flatOptions ? x => x : x => x.value} id={fieldState.fieldId} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} on:change={handleChange} {placeholder} {options} @@ -83,7 +84,7 @@ value={fieldState.value || []} id={fieldState.fieldId} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} {options} {direction} diff --git a/packages/client/src/components/app/forms/OptionsField.svelte b/packages/client/src/components/app/forms/OptionsField.svelte index c01827471a..dc18df8dbe 100644 --- a/packages/client/src/components/app/forms/OptionsField.svelte +++ b/packages/client/src/components/app/forms/OptionsField.svelte @@ -46,6 +46,7 @@ {field} {label} {disabled} + {readonly} {validation} {defaultValue} type="options" @@ -59,7 +60,7 @@ value={fieldState.value} id={fieldState.fieldId} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} {options} {placeholder} @@ -74,7 +75,7 @@ value={fieldState.value} id={fieldState.fieldId} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} {options} {direction} diff --git a/packages/client/src/components/app/forms/RelationshipField.svelte b/packages/client/src/components/app/forms/RelationshipField.svelte index 9d2d6adf0f..10fcaa904f 100644 --- a/packages/client/src/components/app/forms/RelationshipField.svelte +++ b/packages/client/src/components/app/forms/RelationshipField.svelte @@ -184,6 +184,7 @@ {label} {field} {disabled} + {readonly} {validation} defaultValue={expandedDefaultValue} {type} @@ -201,7 +202,7 @@ on:loadMore={loadMore} id={fieldState.fieldId} disabled={fieldState.disabled} - {readonly} + readonly={fieldState.readonly} error={fieldState.error} getOptionLabel={getDisplayName} getOptionValue={option => option._id} diff --git a/packages/client/src/components/app/forms/StringField.svelte b/packages/client/src/components/app/forms/StringField.svelte index 624611c733..674be9f1b2 100644 --- a/packages/client/src/components/app/forms/StringField.svelte +++ b/packages/client/src/components/app/forms/StringField.svelte @@ -7,11 +7,11 @@ export let placeholder export let type = "text" export let disabled = false + export let readonly = false export let validation export let defaultValue = "" export let align export let onChange - export let readonly = false let fieldState let fieldApi @@ -28,6 +28,7 @@ {label} {field} {disabled} + {readonly} {validation} {defaultValue} type={type === "number" ? "number" : "string"} @@ -40,12 +41,12 @@ value={fieldState.value} on:change={handleChange} disabled={fieldState.disabled} + readonly={fieldState.readonly} error={fieldState.error} id={fieldState.fieldId} {placeholder} {type} {align} - {readonly} /> {/if} From 5923ae2983d588d1f1d5a5fd3550a04cda47d671 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Wed, 1 Nov 2023 16:40:23 +0000 Subject: [PATCH 278/427] Make form block view readonly --- packages/bbui/src/Markdown/MarkdownEditor.svelte | 2 +- packages/client/manifest.json | 7 +------ .../src/components/app/blocks/form/InnerFormBlock.svelte | 3 ++- 3 files changed, 4 insertions(+), 8 deletions(-) diff --git a/packages/bbui/src/Markdown/MarkdownEditor.svelte b/packages/bbui/src/Markdown/MarkdownEditor.svelte index 27035d8033..225d25a0eb 100644 --- a/packages/bbui/src/Markdown/MarkdownEditor.svelte +++ b/packages/bbui/src/Markdown/MarkdownEditor.svelte @@ -56,7 +56,7 @@ easyMDEOptions={{ initialValue: value, placeholder, - toolbar: readonly ? false : undefined, + toolbar: disabled || readonly ? false : undefined, ...easyMDEOptions, }} /> diff --git a/packages/client/manifest.json b/packages/client/manifest.json index 7559825267..749e2cf194 100644 --- a/packages/client/manifest.json +++ b/packages/client/manifest.json @@ -5640,12 +5640,7 @@ "type": "boolean", "label": "Disabled", "key": "disabled", - "defaultValue": false, - "dependsOn": { - "setting": "actionType", - "value": "View", - "invert": true - } + "defaultValue": false } ] }, diff --git a/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte b/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte index f7e9a0d2ed..b2c9888c73 100644 --- a/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte +++ b/packages/client/src/components/app/blocks/form/InnerFormBlock.svelte @@ -136,7 +136,8 @@ actionType: actionType === "Create" ? "Create" : "Update", dataSource, size, - disabled: disabled || actionType === "View", + disabled, + readonly: !disabled && actionType === "View", }} styles={{ normal: { From 0477ed64f6edd6b2a2e02a25fb1baaff591584fb Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Wed, 1 Nov 2023 16:40:29 +0000 Subject: [PATCH 279/427] Bump version to 2.12.4 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index dc6ffb2da2..f0f51242d1 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.3", + "version": "2.12.4", "npmClient": "yarn", "packages": [ "packages/*" From 17b6985f06aa5faf256c3f5f45b76a1a55278414 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 1 Nov 2023 17:48:13 +0000 Subject: [PATCH 280/427] Adding a duration utility for changing between time types. --- packages/backend-core/src/index.ts | 1 + packages/backend-core/src/queue/queue.ts | 7 +-- packages/backend-core/src/utils/Duration.ts | 49 +++++++++++++++++++ packages/backend-core/src/utils/index.ts | 1 + .../src/utils/tests/Duration.spec.ts | 19 +++++++ 5 files changed, 74 insertions(+), 3 deletions(-) create mode 100644 packages/backend-core/src/utils/Duration.ts create mode 100644 packages/backend-core/src/utils/tests/Duration.spec.ts diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index ffffd8240a..c7cf9f56cc 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -30,6 +30,7 @@ export * as timers from "./timers" export { default as env } from "./environment" export * as blacklist from "./blacklist" export * as docUpdates from "./docUpdates" +export * from "./utils/Duration" export { SearchParams } from "./db" // Add context to tenancy for backwards compatibility // only do this for external usages to prevent internal diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index c6b02921bf..c0d1861de3 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -4,15 +4,16 @@ import { JobQueue } from "./constants" import InMemoryQueue from "./inMemoryQueue" import BullQueue, { QueueOptions } from "bull" import { addListeners, StalledFn } from "./listeners" +import { Duration } from "../utils" import * as timers from "../timers" import * as Redis from "ioredis" // the queue lock is held for 5 minutes -const QUEUE_LOCK_MS = 300000 +const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs() // queue lock is refreshed every 30 seconds -const QUEUE_LOCK_RENEW_INTERNAL_MS = 30000 +const QUEUE_LOCK_RENEW_INTERNAL_MS = Duration.fromSeconds(30).toMs() // cleanup the queue every 60 seconds -const CLEANUP_PERIOD_MS = 60 * 1000 +const CLEANUP_PERIOD_MS = Duration.fromSeconds(60).toMs() let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = [] let cleanupInterval: NodeJS.Timeout diff --git a/packages/backend-core/src/utils/Duration.ts b/packages/backend-core/src/utils/Duration.ts new file mode 100644 index 0000000000..f376c2f7c7 --- /dev/null +++ b/packages/backend-core/src/utils/Duration.ts @@ -0,0 +1,49 @@ +export enum DurationType { + MILLISECONDS = "milliseconds", + SECONDS = "seconds", + MINUTES = "minutes", + HOURS = "hours", + DAYS = "days", +} + +const conversion: Record = { + milliseconds: 1, + seconds: 1000, + minutes: 60 * 1000, + hours: 60 * 60 * 1000, + days: 24 * 60 * 60 * 1000, +} + +export class Duration { + static convert(from: DurationType, to: DurationType, duration: number) { + const milliseconds = duration * conversion[from] + return milliseconds / conversion[to] + } + + static from(from: DurationType, duration: number) { + return { + to: (to: DurationType) => { + return Duration.convert(from, to, duration) + }, + toMs: () => { + return Duration.convert(from, DurationType.MILLISECONDS, duration) + }, + } + } + + static fromSeconds(duration: number) { + return Duration.from(DurationType.SECONDS, duration) + } + + static fromMinutes(duration: number) { + return Duration.from(DurationType.MINUTES, duration) + } + + static fromHours(duration: number) { + return Duration.from(DurationType.HOURS, duration) + } + + static fromDays(duration: number) { + return Duration.from(DurationType.DAYS, duration) + } +} diff --git a/packages/backend-core/src/utils/index.ts b/packages/backend-core/src/utils/index.ts index 318a7f13ba..ac17227459 100644 --- a/packages/backend-core/src/utils/index.ts +++ b/packages/backend-core/src/utils/index.ts @@ -1,3 +1,4 @@ export * from "./hashing" export * from "./utils" export * from "./stringUtils" +export * from "./Duration" diff --git a/packages/backend-core/src/utils/tests/Duration.spec.ts b/packages/backend-core/src/utils/tests/Duration.spec.ts new file mode 100644 index 0000000000..46b996f788 --- /dev/null +++ b/packages/backend-core/src/utils/tests/Duration.spec.ts @@ -0,0 +1,19 @@ +import { Duration, DurationType } from "../Duration" + +describe("duration", () => { + it("should convert minutes to milliseconds", () => { + expect(Duration.fromMinutes(5).toMs()).toBe(300000) + }) + + it("should convert seconds to milliseconds", () => { + expect(Duration.fromSeconds(30).toMs()).toBe(30000) + }) + + it("should convert days to milliseconds", () => { + expect(Duration.fromDays(1).toMs()).toBe(86400000) + }) + + it("should convert minutes to days", () => { + expect(Duration.fromMinutes(1440).to(DurationType.DAYS)).toBe(1) + }) +}) From ee9ea375f98411fab6c42ad2818c333df109f8a4 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Thu, 2 Nov 2023 09:18:45 +0000 Subject: [PATCH 281/427] use constants --- .../src/components/automation/SetupPanel/RowSelector.svelte | 4 ++-- .../src/components/automation/SetupPanel/TableSelector.svelte | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte index 5318509ec4..21d90944e8 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte @@ -5,6 +5,7 @@ import RowSelectorTypes from "./RowSelectorTypes.svelte" import DrawerBindableSlot from "../../common/bindings/DrawerBindableSlot.svelte" import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte" + import { TableNames } from "constants" const dispatch = createEventDispatcher() @@ -97,7 +98,6 @@ // Ensure any nullish tableId values get set to empty string so // that the select works $: if (value?.tableId == null) value = { tableId: "" } - $: console.log($tables.list)
@@ -106,7 +106,7 @@ table._id !== TableNames.USERS)} getOptionLabel={table => table.name} getOptionValue={table => table._id} /> From c4ff4eb8bb1255d8e268d9e0c31f03f50379bf96 Mon Sep 17 00:00:00 2001 From: Conor Webb Date: Thu, 2 Nov 2023 10:00:40 +0000 Subject: [PATCH 282/427] Updated folder name, fixed ui bug. --- .../src/components/design/settings/componentSettings.js | 2 +- .../DataSourceCategory.svelte | 0 .../DataSourceSelect.svelte | 3 +-- .../components/design/settings/controls/SchemaSelect.svelte | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) rename packages/builder/src/components/design/settings/controls/{DataSourceSelectItem => DataSourceSelect}/DataSourceCategory.svelte (100%) rename packages/builder/src/components/design/settings/controls/{DataSourceSelectItem => DataSourceSelect}/DataSourceSelect.svelte (99%) diff --git a/packages/builder/src/components/design/settings/componentSettings.js b/packages/builder/src/components/design/settings/componentSettings.js index b81361dfa5..c2bd08760a 100644 --- a/packages/builder/src/components/design/settings/componentSettings.js +++ b/packages/builder/src/components/design/settings/componentSettings.js @@ -1,5 +1,5 @@ import { Checkbox, Select, RadioGroup, Stepper, Input } from "@budibase/bbui" -import DataSourceSelect from "./controls/DataSourceSelectItem/DataSourceSelect.svelte" +import DataSourceSelect from "./controls/DataSourceSelect/DataSourceSelect.svelte" import S3DataSourceSelect from "./controls/S3DataSourceSelect.svelte" import DataProviderSelect from "./controls/DataProviderSelect.svelte" import ButtonActionEditor from "./controls/ButtonActionEditor/ButtonActionEditor.svelte" diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/DataSourceCategory.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte similarity index 100% rename from packages/builder/src/components/design/settings/controls/DataSourceSelectItem/DataSourceCategory.svelte rename to packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte diff --git a/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/DataSourceSelect.svelte b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte similarity index 99% rename from packages/builder/src/components/design/settings/controls/DataSourceSelectItem/DataSourceSelect.svelte rename to packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte index 1f059ee972..5cda0ebcca 100644 --- a/packages/builder/src/components/design/settings/controls/DataSourceSelectItem/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/DataSourceSelect/DataSourceSelect.svelte @@ -30,7 +30,7 @@ import IntegrationQueryEditor from "components/integration/index.svelte" import { makePropSafe as safe } from "@budibase/string-templates" import ClientBindingPanel from "components/common/bindings/ClientBindingPanel.svelte" - import DataSourceCategory from "components/design/settings/controls/DataSourceSelectItem/DataSourceCategory.svelte" + import DataSourceCategory from "components/design/settings/controls/DataSourceSelect/DataSourceCategory.svelte" import { API } from "api" export let value = {} @@ -376,7 +376,6 @@ .dropdown { padding: var(--spacing-m) 0; z-index: 99999999; - overflow-y: scroll; } .icon { diff --git a/packages/builder/src/components/design/settings/controls/SchemaSelect.svelte b/packages/builder/src/components/design/settings/controls/SchemaSelect.svelte index 745b623e72..dbeeec53ef 100644 --- a/packages/builder/src/components/design/settings/controls/SchemaSelect.svelte +++ b/packages/builder/src/components/design/settings/controls/SchemaSelect.svelte @@ -1,5 +1,5 @@ From e0bf0f6891e60132615742b01338fb1a2b2a3c0a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Nov 2023 11:52:18 +0100 Subject: [PATCH 283/427] Fix payloads --- .github/workflows/deploy-qa.yml | 8 +++++--- .github/workflows/release-master.yml | 10 ++++++---- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index d850d289ff..0580ac1c3a 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -11,10 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: peter-evans/repository-dispatch@v2 - env: - PAYLOAD_VERSION: ${{ github.sha }} - REF_NAME: ${{ github.ref_name}} with: repository: budibase/budibase-deploys event-type: budicloud-qa-deploy token: ${{ secrets.GH_ACCESS_TOKEN }} + client-payload: |- + { + "PAYLOAD_VERSION": "${{ github.sha }}", + "REF_NAME": "${{ github.ref_name}}" + } diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index df25182cd6..d579670cf6 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -171,11 +171,13 @@ jobs: release_version=$(cat lerna.json | jq -r '.version') echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - - uses: passeidireto/trigger-external-workflow-action@main - env: - PAYLOAD_VERSION: ${{ env.RELEASE_VERSION }} - REF_NAME: ${{ github.ref_name}} + - uses: peter-evans/repository-dispatch@v2 with: repository: budibase/budibase-deploys event: budicloud-qa-deploy github_pat: ${{ secrets.GH_ACCESS_TOKEN }} + client-payload: |- + { + "PAYLOAD_VERSION": "${{ github.sha }}", + "REF_NAME": "${{ github.ref_name}}" + } From f74556cb7aacaaa0cc9447481b830a370c49c41e Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 2 Nov 2023 11:19:26 +0000 Subject: [PATCH 284/427] Hardcoding the memory used by the build process to make sure it always tries to claim as much as it needs to run the build fully (avoid heap errors as seen on feature branch builds). --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 417fb31e0e..8a27cde104 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "scripts": { "preinstall": "node scripts/syncProPackage.js", "setup": "git config submodule.recurse true && git submodule update && node ./hosting/scripts/setup.js && yarn && yarn build && yarn dev", - "build": "lerna run build --stream", + "build": "NODE_OPTIONS=--max-old-space-size=1500 lerna run build --stream", "build:dev": "lerna run --stream prebuild && yarn nx run-many --target=build --output-style=dynamic --watch --preserveWatchOutput", "check:types": "lerna run check:types", "build:sdk": "lerna run --stream build:sdk", From f08c42f338f381951599fac6c6a17b6fe524773a Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Nov 2023 13:15:39 +0100 Subject: [PATCH 285/427] Use ref name as payload version --- .github/workflows/release-master.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index d579670cf6..7648ae32e7 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -165,11 +165,6 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Get the current budibase release version - id: version - run: | - release_version=$(cat lerna.json | jq -r '.version') - echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV - uses: peter-evans/repository-dispatch@v2 with: @@ -178,6 +173,6 @@ jobs: github_pat: ${{ secrets.GH_ACCESS_TOKEN }} client-payload: |- { - "PAYLOAD_VERSION": "${{ github.sha }}", + "PAYLOAD_VERSION": "${{ github.ref_name }}", "REF_NAME": "${{ github.ref_name}}" } From 2ac06465bfdcb6463ba5c3c484ef70da7b2682bd Mon Sep 17 00:00:00 2001 From: melohagan <101575380+melohagan@users.noreply.github.com> Date: Thu, 2 Nov 2023 13:11:16 +0000 Subject: [PATCH 286/427] Remove docs section (#12248) --- pull_request_template.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pull_request_template.md b/pull_request_template.md index 36e2f425d5..fa0f7f2143 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,18 +1,16 @@ ## Description + _Describe the problem or feature in addition to a link to the relevant github issues._ -Addresses: +Addresses: + - `` - ...more if required ## App Export + - If possible, attach an app export file along with your request template to make QA testing easier, with minimal setup. ## Screenshots + _If a UI facing feature, a short video of the happy path, and some screenshots of the new functionality._ - -## Documentation -- [ ] I have reviewed the budibase documentatation to verify if this feature requires any changes. If changes or new docs are required I have written them. - - - From 4751f9b240407abee4e60bc212ecca1994c51710 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 2 Nov 2023 13:23:31 +0000 Subject: [PATCH 287/427] Correcting BUDIBASE_VERSION in single image. --- hosting/single/Dockerfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/single/Dockerfile.v2 b/hosting/single/Dockerfile.v2 index ec03a1b5a2..272860cca2 100644 --- a/hosting/single/Dockerfile.v2 +++ b/hosting/single/Dockerfile.v2 @@ -120,7 +120,7 @@ VOLUME /data ARG BUDIBASE_VERSION # Ensuring the version argument is sent RUN test -n "$BUDIBASE_VERSION" -ENV BUDIBASE_VERSION=$BUDIBASE_VERSION +ENV BUDIBASE_VERSION $BUDIBASE_VERSION HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh" From 52510c5e48506dbd1e0cb1f9cdf03949b101fd6c Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Nov 2023 13:24:05 +0000 Subject: [PATCH 288/427] Bump version to 2.12.5 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index f0f51242d1..5dd081ec14 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.4", + "version": "2.12.5", "npmClient": "yarn", "packages": [ "packages/*" From cc37afa4efd022503049b319ff06fc24d20567ef Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Nov 2023 15:55:10 +0100 Subject: [PATCH 289/427] Fix passing BUDIBASE_VERSION arg --- .github/workflows/release-singleimage.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release-singleimage.yml b/.github/workflows/release-singleimage.yml index a3444d5e7a..16b1da186a 100644 --- a/.github/workflows/release-singleimage.yml +++ b/.github/workflows/release-singleimage.yml @@ -66,7 +66,7 @@ jobs: context: . push: true platforms: linux/amd64,linux/arm64 - build-args: BUDIBASE_VERSION=$BUDIBASE_VERSION + build-args: BUDIBASE_VERSION=${{ env.BUDIBASE_VERSION }} tags: budibase/budibase,budibase/budibase:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 env: @@ -79,7 +79,7 @@ jobs: platforms: linux/amd64 build-args: | TARGETBUILD=aas - BUDIBASE_VERSION=$BUDIBASE_VERSION + BUDIBASE_VERSION=${{ env.BUDIBASE_VERSION }} tags: budibase/budibase-aas,budibase/budibase-aas:${{ env.RELEASE_VERSION }} file: ./hosting/single/Dockerfile.v2 env: From 2ee77cda993391d59a42cbbddf2830ae3dbe8ac3 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Nov 2023 15:58:02 +0100 Subject: [PATCH 290/427] Fix docker --- hosting/single/Dockerfile.v2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/single/Dockerfile.v2 b/hosting/single/Dockerfile.v2 index 272860cca2..ec03a1b5a2 100644 --- a/hosting/single/Dockerfile.v2 +++ b/hosting/single/Dockerfile.v2 @@ -120,7 +120,7 @@ VOLUME /data ARG BUDIBASE_VERSION # Ensuring the version argument is sent RUN test -n "$BUDIBASE_VERSION" -ENV BUDIBASE_VERSION $BUDIBASE_VERSION +ENV BUDIBASE_VERSION=$BUDIBASE_VERSION HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh" From d02fffc3e1c6fb52d8216e08efae9892805627f5 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Nov 2023 15:00:07 +0000 Subject: [PATCH 291/427] Bump version to 2.12.6 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 5dd081ec14..ff0cbafedd 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.5", + "version": "2.12.6", "npmClient": "yarn", "packages": [ "packages/*" From 767124163e41e1b9dd7b465f89fb9b8ef83ea5c6 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Thu, 2 Nov 2023 16:35:20 +0100 Subject: [PATCH 292/427] Fix --- .github/workflows/release-master.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 7648ae32e7..bd1efeb2c4 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -169,7 +169,7 @@ jobs: - uses: peter-evans/repository-dispatch@v2 with: repository: budibase/budibase-deploys - event: budicloud-qa-deploy + event-type: budicloud-qa-deploy github_pat: ${{ secrets.GH_ACCESS_TOKEN }} client-payload: |- { From 738082dc27e564530e7af107c99beb7c27956733 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Thu, 2 Nov 2023 15:49:16 +0000 Subject: [PATCH 293/427] Remove readonly key --- packages/bbui/src/Markdown/MarkdownEditor.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/bbui/src/Markdown/MarkdownEditor.svelte b/packages/bbui/src/Markdown/MarkdownEditor.svelte index 225d25a0eb..6c711c9d28 100644 --- a/packages/bbui/src/Markdown/MarkdownEditor.svelte +++ b/packages/bbui/src/Markdown/MarkdownEditor.svelte @@ -45,7 +45,7 @@ const debouncedUpdate = debounce(update, 250) -{#key (height, readonly)} +{#key height} Date: Thu, 2 Nov 2023 17:04:12 +0100 Subject: [PATCH 294/427] Trigger release pipeline on tag --- .github/workflows/tag-release.yml | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index eaf71ae61a..ea154d1436 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -1,4 +1,4 @@ -name: Tag release +name: Release concurrency: group: tag-release cancel-in-progress: false @@ -19,6 +19,8 @@ on: jobs: tag-release: runs-on: ubuntu-latest + outputs: + version: ${{ steps.tag-release.outputs.version }} steps: - name: Fail if branch is not master @@ -33,6 +35,7 @@ jobs: - run: cd scripts && yarn - name: Tag release + id: tag-release run: | cd scripts # setup the username and email. @@ -41,3 +44,23 @@ jobs: BUMP_TYPE_INPUT=${{ github.event.inputs.versioning }} BUMP_TYPE=${BUMP_TYPE_INPUT:-"patch"} ./versionCommit.sh $BUMP_TYPE + + + new_version=$(./scripts/getCurrentVersion.sh) + echo "version=$new_version" >> $GITHUB_OUTPUT + + trigger-release: + needs: [tag-release] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - uses: peter-evans/repository-dispatch@v2 + with: + repository: budibase/budibase-deploys + event: release-prod + github_pat: ${{ secrets.GH_ACCESS_TOKEN }} + client-payload: |- + { + "TAG": "${{ needs.tag-release.outputs.version }}" + } From a62d82afb8c66bb40e0739cacffb54f7a7f4e878 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 2 Nov 2023 18:14:25 +0000 Subject: [PATCH 295/427] Fixing user migration issue where relationships created from the other side (from user table) where not being migrated correctly. --- .../server/src/sdk/app/tables/migration.ts | 39 ++++++++++++++----- packages/types/src/documents/app/links.ts | 18 ++++----- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index 5a6b0c5bc0..5a99d45a6f 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -7,6 +7,7 @@ import { isBBReferenceField, isRelationshipField, LinkDocument, + LinkInfo, RelationshipFieldMetadata, RelationshipType, Row, @@ -118,6 +119,22 @@ function getColumnMigrator( throw new BadRequestError(`Unknown migration type`) } +function pickUserTableLinkSide(link: LinkDocument): LinkInfo { + if (link.doc1.tableId === InternalTable.USER_METADATA) { + return link.doc1 + } else { + return link.doc2 + } +} + +function pickOtherTableLinkSide(link: LinkDocument): LinkInfo { + if (link.doc1.tableId === InternalTable.USER_METADATA) { + return link.doc2 + } else { + return link.doc1 + } +} + abstract class UserColumnMigrator implements ColumnMigrator { constructor( protected table: Table, @@ -125,7 +142,7 @@ abstract class UserColumnMigrator implements ColumnMigrator { protected newColumn: BBReferenceFieldMetadata ) {} - abstract updateRow(row: Row, link: LinkDocument): void + abstract updateRow(row: Row, linkInfo: LinkInfo): void async doMigration(): Promise { let oldTable = cloneDeep(this.table) @@ -137,15 +154,17 @@ abstract class UserColumnMigrator implements ColumnMigrator { let links = await sdk.links.fetchWithDocument(this.table._id!) for (let link of links) { + const userSide = pickUserTableLinkSide(link) + const otherSide = pickOtherTableLinkSide(link) if ( - link.doc1.tableId !== this.table._id || - link.doc1.fieldName !== this.oldColumn.name || - link.doc2.tableId !== InternalTable.USER_METADATA + otherSide.tableId !== this.table._id || + otherSide.fieldName !== this.oldColumn.name || + userSide.tableId !== InternalTable.USER_METADATA ) { continue } - let row = rowsById[link.doc1.rowId] + let row = rowsById[otherSide.rowId] if (!row) { // This can happen if the row has been deleted but the link hasn't, // which was a state that was found during the initial testing of this @@ -153,7 +172,7 @@ abstract class UserColumnMigrator implements ColumnMigrator { continue } - this.updateRow(row, link) + this.updateRow(row, userSide) } let db = context.getAppDB() @@ -175,20 +194,20 @@ abstract class UserColumnMigrator implements ColumnMigrator { } class SingleUserColumnMigrator extends UserColumnMigrator { - updateRow(row: Row, link: LinkDocument): void { + updateRow(row: Row, linkInfo: LinkInfo): void { row[this.newColumn.name] = dbCore.getGlobalIDFromUserMetadataID( - link.doc2.rowId + linkInfo.rowId ) } } class MultiUserColumnMigrator extends UserColumnMigrator { - updateRow(row: Row, link: LinkDocument): void { + updateRow(row: Row, linkInfo: LinkInfo): void { if (!row[this.newColumn.name]) { row[this.newColumn.name] = [] } row[this.newColumn.name].push( - dbCore.getGlobalIDFromUserMetadataID(link.doc2.rowId) + dbCore.getGlobalIDFromUserMetadataID(linkInfo.rowId) ) } } diff --git a/packages/types/src/documents/app/links.ts b/packages/types/src/documents/app/links.ts index d6b2adddf8..ae7e4de78e 100644 --- a/packages/types/src/documents/app/links.ts +++ b/packages/types/src/documents/app/links.ts @@ -1,17 +1,15 @@ import { Document } from "../document" +export interface LinkInfo { + rowId: string + fieldName: string + tableId: string +} + export interface LinkDocument extends Document { type: string - doc1: { - rowId: string - fieldName: string - tableId: string - } - doc2: { - rowId: string - fieldName: string - tableId: string - } + doc1: LinkInfo + doc2: LinkInfo } export interface LinkDocumentValue { From 00f1d2cbdca10277430bb9ec83b04aabd6c1d9c9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Thu, 2 Nov 2023 18:23:16 +0000 Subject: [PATCH 296/427] Moving functions inside class. --- .../server/src/sdk/app/tables/migration.ts | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/server/src/sdk/app/tables/migration.ts b/packages/server/src/sdk/app/tables/migration.ts index 5a99d45a6f..718223dbeb 100644 --- a/packages/server/src/sdk/app/tables/migration.ts +++ b/packages/server/src/sdk/app/tables/migration.ts @@ -119,22 +119,6 @@ function getColumnMigrator( throw new BadRequestError(`Unknown migration type`) } -function pickUserTableLinkSide(link: LinkDocument): LinkInfo { - if (link.doc1.tableId === InternalTable.USER_METADATA) { - return link.doc1 - } else { - return link.doc2 - } -} - -function pickOtherTableLinkSide(link: LinkDocument): LinkInfo { - if (link.doc1.tableId === InternalTable.USER_METADATA) { - return link.doc2 - } else { - return link.doc1 - } -} - abstract class UserColumnMigrator implements ColumnMigrator { constructor( protected table: Table, @@ -144,6 +128,22 @@ abstract class UserColumnMigrator implements ColumnMigrator { abstract updateRow(row: Row, linkInfo: LinkInfo): void + pickUserTableLinkSide(link: LinkDocument): LinkInfo { + if (link.doc1.tableId === InternalTable.USER_METADATA) { + return link.doc1 + } else { + return link.doc2 + } + } + + pickOtherTableLinkSide(link: LinkDocument): LinkInfo { + if (link.doc1.tableId === InternalTable.USER_METADATA) { + return link.doc2 + } else { + return link.doc1 + } + } + async doMigration(): Promise { let oldTable = cloneDeep(this.table) let rows = await sdk.rows.fetchRaw(this.table._id!) @@ -154,8 +154,8 @@ abstract class UserColumnMigrator implements ColumnMigrator { let links = await sdk.links.fetchWithDocument(this.table._id!) for (let link of links) { - const userSide = pickUserTableLinkSide(link) - const otherSide = pickOtherTableLinkSide(link) + const userSide = this.pickUserTableLinkSide(link) + const otherSide = this.pickOtherTableLinkSide(link) if ( otherSide.tableId !== this.table._id || otherSide.fieldName !== this.oldColumn.name || From f84b9385a4aae52f11b9a55dd627234b6c7df3b3 Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Thu, 2 Nov 2023 18:34:16 +0000 Subject: [PATCH 297/427] Bump version to 2.12.7 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index ff0cbafedd..2d46838e55 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.6", + "version": "2.12.7", "npmClient": "yarn", "packages": [ "packages/*" From 95b4a23fa6e6ddbf160bebce347198292b859637 Mon Sep 17 00:00:00 2001 From: Adria Navarro Date: Fri, 3 Nov 2023 10:20:26 +0100 Subject: [PATCH 298/427] Fix and unify actions --- .github/workflows/deploy-qa.yml | 2 +- .github/workflows/release-master.yml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/deploy-qa.yml b/.github/workflows/deploy-qa.yml index 0580ac1c3a..1339ad2eb9 100644 --- a/.github/workflows/deploy-qa.yml +++ b/.github/workflows/deploy-qa.yml @@ -17,6 +17,6 @@ jobs: token: ${{ secrets.GH_ACCESS_TOKEN }} client-payload: |- { - "PAYLOAD_VERSION": "${{ github.sha }}", + "VERSION": "${{ github.sha }}", "REF_NAME": "${{ github.ref_name}}" } diff --git a/.github/workflows/release-master.yml b/.github/workflows/release-master.yml index 7648ae32e7..2edb470405 100644 --- a/.github/workflows/release-master.yml +++ b/.github/workflows/release-master.yml @@ -169,10 +169,10 @@ jobs: - uses: peter-evans/repository-dispatch@v2 with: repository: budibase/budibase-deploys - event: budicloud-qa-deploy - github_pat: ${{ secrets.GH_ACCESS_TOKEN }} + event-type: budicloud-qa-deploy + token: ${{ secrets.GH_ACCESS_TOKEN }} client-payload: |- { - "PAYLOAD_VERSION": "${{ github.ref_name }}", + "VERSION": "${{ github.ref_name }}", "REF_NAME": "${{ github.ref_name}}" } From f1802eb41c8896879351caa3d39fa0de0dca5cb7 Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 3 Nov 2023 10:04:51 +0000 Subject: [PATCH 299/427] update modal sizing for automation test data --- .../automation/AutomationBuilder/FlowChart/TestDataModal.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte index 17d5b35575..5c97d77ae8 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/TestDataModal.svelte @@ -60,6 +60,7 @@ Date: Fri, 3 Nov 2023 10:24:28 +0000 Subject: [PATCH 300/427] Bump version to 2.12.8 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 2d46838e55..c87304af4e 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.7", + "version": "2.12.8", "npmClient": "yarn", "packages": [ "packages/*" From 52f3d1410bf699a2a6712c5e55b419aec683c0ef Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 3 Nov 2023 10:55:06 +0000 Subject: [PATCH 301/427] Bump version to 2.12.9 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index c87304af4e..2deb06656d 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.8", + "version": "2.12.9", "npmClient": "yarn", "packages": [ "packages/*" From 635008e1732789d0329be9389ffd2c40aceaca5d Mon Sep 17 00:00:00 2001 From: Peter Clement Date: Fri, 3 Nov 2023 11:05:05 +0000 Subject: [PATCH 302/427] unique name enforcement for automations --- .../AutomationBuilder/FlowChart/FlowItemHeader.svelte | 7 +++++++ .../automation/SetupPanel/AutomationBlockSetup.svelte | 1 - 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte index d07093ad7a..4c72c57208 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItemHeader.svelte @@ -58,11 +58,18 @@ } const getAutomationNameError = name => { + for (const [key, value] of Object.entries(stepNames)) { + if (name === value && key !== block.id) { + return "This name already exists, please enter a unique name" + } + } + if (name !== block.name && name?.length > 0) { let invalidRoleName = !validRegex.test(name) if (invalidRoleName) { return "Please enter a role name consisting of only alphanumeric symbols and underscores" } + return null } } diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index a42cc34b9d..9260a197c2 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -282,7 +282,6 @@ } function shouldRenderField(value) { - console.log(value) return ( value.customType !== "row" && value.customType !== "code" && From e316124d6e7b5fc70130368e7277e9372039ca6b Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 3 Nov 2023 11:42:39 +0000 Subject: [PATCH 303/427] Add a test for the user column migration bug fix we did last night. --- .../server/src/api/routes/tests/table.spec.ts | 61 +++++++++++++++++++ .../server/src/tests/utilities/api/row.ts | 21 +++++-- 2 files changed, 78 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/routes/tests/table.spec.ts b/packages/server/src/api/routes/tests/table.spec.ts index c239c596fe..4743bca814 100644 --- a/packages/server/src/api/routes/tests/table.spec.ts +++ b/packages/server/src/api/routes/tests/table.spec.ts @@ -492,6 +492,67 @@ describe("/tables", () => { } }) + it("should succeed when the row is created from the other side of the relationship", async () => { + // We found a bug just after releasing this feature where if the row was created from the + // users table, not the table linking to it, the migration would succeed but lose the data. + // This happened because the order of the documents in the link was reversed. + const table = await config.api.table.create({ + name: "table", + type: "table", + sourceId: INTERNAL_TABLE_SOURCE_ID, + sourceType: TableSourceType.INTERNAL, + schema: { + "user relationship": { + type: FieldType.LINK, + fieldName: "test", + name: "user relationship", + constraints: { + type: "array", + presence: false, + }, + relationshipType: RelationshipType.MANY_TO_ONE, + tableId: InternalTable.USER_METADATA, + }, + }, + }) + + let testRow = await config.api.row.save(table._id!, {}) + + await Promise.all( + users.map(u => + config.api.row.patch(InternalTable.USER_METADATA, { + tableId: InternalTable.USER_METADATA, + _rev: u._rev!, + _id: u._id!, + test: [testRow], + }) + ) + ) + + await config.api.table.migrate(table._id!, { + oldColumn: table.schema["user relationship"], + newColumn: { + name: "user column", + type: FieldType.BB_REFERENCE, + subtype: FieldSubtype.USERS, + }, + }) + + const migratedTable = await config.api.table.get(table._id!) + expect(migratedTable.schema["user column"]).toBeDefined() + expect(migratedTable.schema["user relationship"]).not.toBeDefined() + + const resp = await config.api.row.get(table._id!, testRow._id!) + const migratedRow = resp.body as Row + + expect(migratedRow["user column"]).toBeDefined() + expect(migratedRow["user relationship"]).not.toBeDefined() + expect(migratedRow["user column"]).toHaveLength(3) + expect(migratedRow["user column"].map((u: Row) => u._id)).toEqual( + expect.arrayContaining(users.map(u => u._id)) + ) + }) + it("should successfully migrate a many-to-many user relationship to a users column", async () => { const table = await config.api.table.create({ name: "table", diff --git a/packages/server/src/tests/utilities/api/row.ts b/packages/server/src/tests/utilities/api/row.ts index bb880bb7da..20b1d6f9ee 100644 --- a/packages/server/src/tests/utilities/api/row.ts +++ b/packages/server/src/tests/utilities/api/row.ts @@ -55,7 +55,13 @@ export class RowAPI extends TestAPI { .send(row) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(expectStatus) + if (resp.status !== expectStatus) { + throw new Error( + `Expected status ${expectStatus} but got ${ + resp.status + }, body: ${JSON.stringify(resp.body)}` + ) + } return resp.body as Row } @@ -77,13 +83,20 @@ export class RowAPI extends TestAPI { sourceId: string, row: PatchRowRequest, { expectStatus } = { expectStatus: 200 } - ) => { - return this.request + ): Promise => { + let resp = await this.request .patch(`/api/${sourceId}/rows`) .send(row) .set(this.config.defaultHeaders()) .expect("Content-Type", /json/) - .expect(expectStatus) + if (resp.status !== expectStatus) { + throw new Error( + `Expected status ${expectStatus} but got ${ + resp.status + }, body: ${JSON.stringify(resp.body)}` + ) + } + return resp.body as Row } delete = async ( From 96534e625f71cafc71d01a8e219ef3a9d6a57cfb Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Fri, 3 Nov 2023 12:22:32 +0000 Subject: [PATCH 304/427] Revert "Updating bull parameters - help queue stalling" --- packages/backend-core/src/index.ts | 1 - .../backend-core/src/queue/inMemoryQueue.ts | 2 +- packages/backend-core/src/queue/queue.ts | 20 ++------ packages/backend-core/src/utils/Duration.ts | 49 ------------------- packages/backend-core/src/utils/index.ts | 1 - .../src/utils/tests/Duration.spec.ts | 19 ------- 6 files changed, 4 insertions(+), 88 deletions(-) delete mode 100644 packages/backend-core/src/utils/Duration.ts delete mode 100644 packages/backend-core/src/utils/tests/Duration.spec.ts diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index c7cf9f56cc..ffffd8240a 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -30,7 +30,6 @@ export * as timers from "./timers" export { default as env } from "./environment" export * as blacklist from "./blacklist" export * as docUpdates from "./docUpdates" -export * from "./utils/Duration" export { SearchParams } from "./db" // Add context to tenancy for backwards compatibility // only do this for external usages to prevent internal diff --git a/packages/backend-core/src/queue/inMemoryQueue.ts b/packages/backend-core/src/queue/inMemoryQueue.ts index a8add7ecb6..af2ec6dbaa 100644 --- a/packages/backend-core/src/queue/inMemoryQueue.ts +++ b/packages/backend-core/src/queue/inMemoryQueue.ts @@ -36,7 +36,7 @@ class InMemoryQueue { * @param opts This is not used by the in memory queue as there is no real use * case when in memory, but is the same API as Bull */ - constructor(name: string, opts?: any) { + constructor(name: string, opts = null) { this._name = name this._opts = opts this._messages = [] diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index c0d1861de3..0658147709 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -2,18 +2,11 @@ import env from "../environment" import { getRedisOptions } from "../redis/utils" import { JobQueue } from "./constants" import InMemoryQueue from "./inMemoryQueue" -import BullQueue, { QueueOptions } from "bull" +import BullQueue from "bull" import { addListeners, StalledFn } from "./listeners" -import { Duration } from "../utils" import * as timers from "../timers" -import * as Redis from "ioredis" -// the queue lock is held for 5 minutes -const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs() -// queue lock is refreshed every 30 seconds -const QUEUE_LOCK_RENEW_INTERNAL_MS = Duration.fromSeconds(30).toMs() -// cleanup the queue every 60 seconds -const CLEANUP_PERIOD_MS = Duration.fromSeconds(60).toMs() +const CLEANUP_PERIOD_MS = 60 * 1000 let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = [] let cleanupInterval: NodeJS.Timeout @@ -28,14 +21,7 @@ export function createQueue( opts: { removeStalledCb?: StalledFn } = {} ): BullQueue.Queue { const { opts: redisOpts, redisProtocolUrl } = getRedisOptions() - const queueConfig: QueueOptions = { - redis: redisProtocolUrl! || (redisOpts as Redis.RedisOptions), - settings: { - maxStalledCount: 0, - lockDuration: QUEUE_LOCK_MS, - lockRenewTime: QUEUE_LOCK_RENEW_INTERNAL_MS, - }, - } + const queueConfig: any = redisProtocolUrl || { redis: redisOpts } let queue: any if (!env.isTest()) { queue = new BullQueue(jobQueue, queueConfig) diff --git a/packages/backend-core/src/utils/Duration.ts b/packages/backend-core/src/utils/Duration.ts deleted file mode 100644 index f376c2f7c7..0000000000 --- a/packages/backend-core/src/utils/Duration.ts +++ /dev/null @@ -1,49 +0,0 @@ -export enum DurationType { - MILLISECONDS = "milliseconds", - SECONDS = "seconds", - MINUTES = "minutes", - HOURS = "hours", - DAYS = "days", -} - -const conversion: Record = { - milliseconds: 1, - seconds: 1000, - minutes: 60 * 1000, - hours: 60 * 60 * 1000, - days: 24 * 60 * 60 * 1000, -} - -export class Duration { - static convert(from: DurationType, to: DurationType, duration: number) { - const milliseconds = duration * conversion[from] - return milliseconds / conversion[to] - } - - static from(from: DurationType, duration: number) { - return { - to: (to: DurationType) => { - return Duration.convert(from, to, duration) - }, - toMs: () => { - return Duration.convert(from, DurationType.MILLISECONDS, duration) - }, - } - } - - static fromSeconds(duration: number) { - return Duration.from(DurationType.SECONDS, duration) - } - - static fromMinutes(duration: number) { - return Duration.from(DurationType.MINUTES, duration) - } - - static fromHours(duration: number) { - return Duration.from(DurationType.HOURS, duration) - } - - static fromDays(duration: number) { - return Duration.from(DurationType.DAYS, duration) - } -} diff --git a/packages/backend-core/src/utils/index.ts b/packages/backend-core/src/utils/index.ts index ac17227459..318a7f13ba 100644 --- a/packages/backend-core/src/utils/index.ts +++ b/packages/backend-core/src/utils/index.ts @@ -1,4 +1,3 @@ export * from "./hashing" export * from "./utils" export * from "./stringUtils" -export * from "./Duration" diff --git a/packages/backend-core/src/utils/tests/Duration.spec.ts b/packages/backend-core/src/utils/tests/Duration.spec.ts deleted file mode 100644 index 46b996f788..0000000000 --- a/packages/backend-core/src/utils/tests/Duration.spec.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Duration, DurationType } from "../Duration" - -describe("duration", () => { - it("should convert minutes to milliseconds", () => { - expect(Duration.fromMinutes(5).toMs()).toBe(300000) - }) - - it("should convert seconds to milliseconds", () => { - expect(Duration.fromSeconds(30).toMs()).toBe(30000) - }) - - it("should convert days to milliseconds", () => { - expect(Duration.fromDays(1).toMs()).toBe(86400000) - }) - - it("should convert minutes to days", () => { - expect(Duration.fromMinutes(1440).to(DurationType.DAYS)).toBe(1) - }) -}) From 980b9c288299a9f356c0af00067ce57760dbe2e5 Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 3 Nov 2023 12:53:37 +0000 Subject: [PATCH 305/427] Fix tests. --- .../server/src/api/routes/tests/row.spec.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.ts b/packages/server/src/api/routes/tests/row.spec.ts index 3ae4a6c1e2..92d581d930 100644 --- a/packages/server/src/api/routes/tests/row.spec.ts +++ b/packages/server/src/api/routes/tests/row.spec.ts @@ -49,7 +49,12 @@ describe.each([ let table: Table let tableId: string - afterAll(setup.afterAll) + afterAll(async () => { + if (dsProvider) { + await dsProvider.stopContainer() + } + setup.afterAll() + }) beforeAll(async () => { await config.init() @@ -521,20 +526,17 @@ describe.each([ const rowUsage = await getRowUsage() const queryUsage = await getQueryUsage() - const res = await config.api.row.patch(table._id!, { + const row = await config.api.row.patch(table._id!, { _id: existing._id!, _rev: existing._rev!, tableId: table._id!, name: "Updated Name", }) - expect((res as any).res.statusMessage).toEqual( - `${table.name} updated successfully.` - ) - expect(res.body.name).toEqual("Updated Name") - expect(res.body.description).toEqual(existing.description) + expect(row.name).toEqual("Updated Name") + expect(row.description).toEqual(existing.description) - const savedRow = await loadRow(res.body._id, table._id!) + const savedRow = await loadRow(row._id!, table._id!) expect(savedRow.body.description).toEqual(existing.description) expect(savedRow.body.name).toEqual("Updated Name") From 12a7811847fd15cdd8e2e5b3c64e939460f54a56 Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Fri, 3 Nov 2023 13:05:23 +0000 Subject: [PATCH 306/427] Revert "Reverting changes to bull parameters" --- packages/backend-core/src/index.ts | 1 + .../backend-core/src/queue/inMemoryQueue.ts | 2 +- packages/backend-core/src/queue/queue.ts | 20 ++++++-- packages/backend-core/src/utils/Duration.ts | 49 +++++++++++++++++++ packages/backend-core/src/utils/index.ts | 1 + .../src/utils/tests/Duration.spec.ts | 19 +++++++ 6 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 packages/backend-core/src/utils/Duration.ts create mode 100644 packages/backend-core/src/utils/tests/Duration.spec.ts diff --git a/packages/backend-core/src/index.ts b/packages/backend-core/src/index.ts index ffffd8240a..c7cf9f56cc 100644 --- a/packages/backend-core/src/index.ts +++ b/packages/backend-core/src/index.ts @@ -30,6 +30,7 @@ export * as timers from "./timers" export { default as env } from "./environment" export * as blacklist from "./blacklist" export * as docUpdates from "./docUpdates" +export * from "./utils/Duration" export { SearchParams } from "./db" // Add context to tenancy for backwards compatibility // only do this for external usages to prevent internal diff --git a/packages/backend-core/src/queue/inMemoryQueue.ts b/packages/backend-core/src/queue/inMemoryQueue.ts index af2ec6dbaa..a8add7ecb6 100644 --- a/packages/backend-core/src/queue/inMemoryQueue.ts +++ b/packages/backend-core/src/queue/inMemoryQueue.ts @@ -36,7 +36,7 @@ class InMemoryQueue { * @param opts This is not used by the in memory queue as there is no real use * case when in memory, but is the same API as Bull */ - constructor(name: string, opts = null) { + constructor(name: string, opts?: any) { this._name = name this._opts = opts this._messages = [] diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index 0658147709..c0d1861de3 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -2,11 +2,18 @@ import env from "../environment" import { getRedisOptions } from "../redis/utils" import { JobQueue } from "./constants" import InMemoryQueue from "./inMemoryQueue" -import BullQueue from "bull" +import BullQueue, { QueueOptions } from "bull" import { addListeners, StalledFn } from "./listeners" +import { Duration } from "../utils" import * as timers from "../timers" +import * as Redis from "ioredis" -const CLEANUP_PERIOD_MS = 60 * 1000 +// the queue lock is held for 5 minutes +const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs() +// queue lock is refreshed every 30 seconds +const QUEUE_LOCK_RENEW_INTERNAL_MS = Duration.fromSeconds(30).toMs() +// cleanup the queue every 60 seconds +const CLEANUP_PERIOD_MS = Duration.fromSeconds(60).toMs() let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = [] let cleanupInterval: NodeJS.Timeout @@ -21,7 +28,14 @@ export function createQueue( opts: { removeStalledCb?: StalledFn } = {} ): BullQueue.Queue { const { opts: redisOpts, redisProtocolUrl } = getRedisOptions() - const queueConfig: any = redisProtocolUrl || { redis: redisOpts } + const queueConfig: QueueOptions = { + redis: redisProtocolUrl! || (redisOpts as Redis.RedisOptions), + settings: { + maxStalledCount: 0, + lockDuration: QUEUE_LOCK_MS, + lockRenewTime: QUEUE_LOCK_RENEW_INTERNAL_MS, + }, + } let queue: any if (!env.isTest()) { queue = new BullQueue(jobQueue, queueConfig) diff --git a/packages/backend-core/src/utils/Duration.ts b/packages/backend-core/src/utils/Duration.ts new file mode 100644 index 0000000000..f376c2f7c7 --- /dev/null +++ b/packages/backend-core/src/utils/Duration.ts @@ -0,0 +1,49 @@ +export enum DurationType { + MILLISECONDS = "milliseconds", + SECONDS = "seconds", + MINUTES = "minutes", + HOURS = "hours", + DAYS = "days", +} + +const conversion: Record = { + milliseconds: 1, + seconds: 1000, + minutes: 60 * 1000, + hours: 60 * 60 * 1000, + days: 24 * 60 * 60 * 1000, +} + +export class Duration { + static convert(from: DurationType, to: DurationType, duration: number) { + const milliseconds = duration * conversion[from] + return milliseconds / conversion[to] + } + + static from(from: DurationType, duration: number) { + return { + to: (to: DurationType) => { + return Duration.convert(from, to, duration) + }, + toMs: () => { + return Duration.convert(from, DurationType.MILLISECONDS, duration) + }, + } + } + + static fromSeconds(duration: number) { + return Duration.from(DurationType.SECONDS, duration) + } + + static fromMinutes(duration: number) { + return Duration.from(DurationType.MINUTES, duration) + } + + static fromHours(duration: number) { + return Duration.from(DurationType.HOURS, duration) + } + + static fromDays(duration: number) { + return Duration.from(DurationType.DAYS, duration) + } +} diff --git a/packages/backend-core/src/utils/index.ts b/packages/backend-core/src/utils/index.ts index 318a7f13ba..ac17227459 100644 --- a/packages/backend-core/src/utils/index.ts +++ b/packages/backend-core/src/utils/index.ts @@ -1,3 +1,4 @@ export * from "./hashing" export * from "./utils" export * from "./stringUtils" +export * from "./Duration" diff --git a/packages/backend-core/src/utils/tests/Duration.spec.ts b/packages/backend-core/src/utils/tests/Duration.spec.ts new file mode 100644 index 0000000000..46b996f788 --- /dev/null +++ b/packages/backend-core/src/utils/tests/Duration.spec.ts @@ -0,0 +1,19 @@ +import { Duration, DurationType } from "../Duration" + +describe("duration", () => { + it("should convert minutes to milliseconds", () => { + expect(Duration.fromMinutes(5).toMs()).toBe(300000) + }) + + it("should convert seconds to milliseconds", () => { + expect(Duration.fromSeconds(30).toMs()).toBe(30000) + }) + + it("should convert days to milliseconds", () => { + expect(Duration.fromDays(1).toMs()).toBe(86400000) + }) + + it("should convert minutes to days", () => { + expect(Duration.fromMinutes(1440).to(DurationType.DAYS)).toBe(1) + }) +}) From a03a00c4af0e3deffb55c16c24c0a37748fae594 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Fri, 3 Nov 2023 14:08:46 +0000 Subject: [PATCH 307/427] Toggle preview --- packages/bbui/src/Markdown/MarkdownEditor.svelte | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/bbui/src/Markdown/MarkdownEditor.svelte b/packages/bbui/src/Markdown/MarkdownEditor.svelte index 6c711c9d28..888187c8da 100644 --- a/packages/bbui/src/Markdown/MarkdownEditor.svelte +++ b/packages/bbui/src/Markdown/MarkdownEditor.svelte @@ -20,7 +20,9 @@ // control $: checkValue(value) $: mde?.codemirror.on("change", debouncedUpdate) - $: mde?.codemirror.setOption("readOnly", readonly) + $: if (readonly || disabled) { + mde?.togglePreview() + } const checkValue = val => { if (mde && val !== latestValue) { From a27a9dc2afb2246f64ee1b1333f300290e9b3b5a Mon Sep 17 00:00:00 2001 From: Sam Rose Date: Fri, 3 Nov 2023 14:29:54 +0000 Subject: [PATCH 308/427] Eliminate TOCTOU problem in creating bbTmp. --- packages/backend-core/src/objectStore/utils.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/backend-core/src/objectStore/utils.ts b/packages/backend-core/src/objectStore/utils.ts index dba5f3d1c2..4c3a84ba91 100644 --- a/packages/backend-core/src/objectStore/utils.ts +++ b/packages/backend-core/src/objectStore/utils.ts @@ -18,8 +18,12 @@ export const ObjectStoreBuckets = { } const bbTmp = join(tmpdir(), ".budibase") -if (!fs.existsSync(bbTmp)) { +try { fs.mkdirSync(bbTmp) +} catch (e: any) { + if (e.code !== "EEXIST") { + throw e + } } export function budibaseTempDir() { From 86f7bd192fb4c6bbe12636e94cca69dc28957d23 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 3 Nov 2023 14:55:56 +0000 Subject: [PATCH 309/427] Moving audit log init to be part of the server startup. --- packages/worker/src/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/worker/src/index.ts b/packages/worker/src/index.ts index 3905a21c73..4b1d11ecf7 100644 --- a/packages/worker/src/index.ts +++ b/packages/worker/src/index.ts @@ -31,10 +31,6 @@ import destroyable from "server-destroy" import { initPro } from "./initPro" import { handleScimBody } from "./middleware/handleScimBody" -// configure events to use the pro audit log write -// can't integrate directly into backend-core due to cyclic issues -events.processors.init(proSdk.auditLogs.write) - if (coreEnv.ENABLE_SSO_MAINTENANCE_MODE) { console.warn( "Warning: ENABLE_SSO_MAINTENANCE_MODE is set. It is recommended this flag is disabled if maintenance is not in progress" @@ -93,6 +89,9 @@ export default server.listen(parseInt(env.PORT || "4002"), async () => { console.log(`Worker running on ${JSON.stringify(server.address())}`) await initPro() await redis.init() + // configure events to use the pro audit log write + // can't integrate directly into backend-core due to cyclic issues + await events.processors.init(proSdk.auditLogs.write) }) process.on("uncaughtException", err => { From b2e42c837c8052e011f2797bbf4d721070677272 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Fri, 3 Nov 2023 15:13:59 +0000 Subject: [PATCH 310/427] Remove spacing --- pull_request_template.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pull_request_template.md b/pull_request_template.md index fa0f7f2143..ca01390557 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -2,7 +2,7 @@ _Describe the problem or feature in addition to a link to the relevant github issues._ -Addresses: +### Addresses: - `` - ...more if required From 927cdae8dfc6d6502d62e0b314666cbb19ed3874 Mon Sep 17 00:00:00 2001 From: Mel O'Hagan Date: Fri, 3 Nov 2023 15:36:33 +0000 Subject: [PATCH 311/427] Remove whitespace --- pull_request_template.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pull_request_template.md b/pull_request_template.md index ca01390557..1dd1a1d45d 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -1,16 +1,12 @@ ## Description - _Describe the problem or feature in addition to a link to the relevant github issues._ ### Addresses: - - `` - ...more if required ## App Export - - If possible, attach an app export file along with your request template to make QA testing easier, with minimal setup. ## Screenshots - _If a UI facing feature, a short video of the happy path, and some screenshots of the new functionality._ From 8fed47766aa514936aec4e41c1a64a9d0d3e4fdc Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Fri, 3 Nov 2023 15:42:25 +0000 Subject: [PATCH 312/427] Bump version to 2.12.10 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 2deb06656d..9257a36444 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.9", + "version": "2.12.10", "npmClient": "yarn", "packages": [ "packages/*" From f8f1ec4ce930f93e57a74fa57679b9cc582168db Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 3 Nov 2023 17:17:20 +0000 Subject: [PATCH 313/427] Removing old redisProtocol string - it is causing confusion and should not be necessary. --- packages/backend-core/src/queue/queue.ts | 5 ++-- packages/backend-core/src/redis/redis.ts | 4 +-- packages/backend-core/src/redis/utils.ts | 38 +++++++++++++----------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index c0d1861de3..b460a7312b 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -6,7 +6,6 @@ import BullQueue, { QueueOptions } from "bull" import { addListeners, StalledFn } from "./listeners" import { Duration } from "../utils" import * as timers from "../timers" -import * as Redis from "ioredis" // the queue lock is held for 5 minutes const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs() @@ -27,9 +26,9 @@ export function createQueue( jobQueue: JobQueue, opts: { removeStalledCb?: StalledFn } = {} ): BullQueue.Queue { - const { opts: redisOpts, redisProtocolUrl } = getRedisOptions() + const { opts: redisOpts } = getRedisOptions() const queueConfig: QueueOptions = { - redis: redisProtocolUrl! || (redisOpts as Redis.RedisOptions), + redis: redisOpts, settings: { maxStalledCount: 0, lockDuration: QUEUE_LOCK_MS, diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index d1e2d8989e..1ae2bd7794 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -91,12 +91,10 @@ function init(selectDb = DEFAULT_SELECT_DB) { if (client) { client.disconnect() } - const { redisProtocolUrl, opts, host, port } = getRedisOptions() + const { opts, host, port } = getRedisOptions() if (CLUSTERED) { client = new RedisCore.Cluster([{ host, port }], opts) - } else if (redisProtocolUrl) { - client = new RedisCore(redisProtocolUrl) } else { client = new RedisCore(opts) } diff --git a/packages/backend-core/src/redis/utils.ts b/packages/backend-core/src/redis/utils.ts index 34b7275a2b..6cac7b2633 100644 --- a/packages/backend-core/src/redis/utils.ts +++ b/packages/backend-core/src/redis/utils.ts @@ -1,4 +1,5 @@ import env from "../environment" +import * as Redis from "ioredis" const SLOT_REFRESH_MS = 2000 const CONNECT_TIMEOUT_MS = 10000 @@ -74,28 +75,29 @@ export function getRedisOptions() { } const [host, port] = url.split(":") - let redisProtocolUrl - - // fully qualified redis URL - if (/rediss?:\/\//.test(env.REDIS_URL)) { - redisProtocolUrl = env.REDIS_URL - } - - const opts: any = { + let redisOpts: Redis.RedisOptions = { connectTimeout: CONNECT_TIMEOUT_MS, + port: parseInt(port), + host, + password, } + let opts: Redis.ClusterOptions | Redis.RedisOptions = redisOpts if (env.REDIS_CLUSTERED) { - opts.redisOptions = {} - opts.redisOptions.tls = {} - opts.redisOptions.password = password - opts.slotsRefreshTimeout = SLOT_REFRESH_MS - opts.dnsLookup = (address: string, callback: any) => callback(null, address) - } else { - opts.host = host - opts.port = port - opts.password = password + opts = { + connectTimeout: CONNECT_TIMEOUT_MS, + redisOptions: { + ...redisOpts, + tls: {}, + }, + slotsRefreshTimeout: SLOT_REFRESH_MS, + dnsLookup: (address: string, callback: any) => callback(null, address), + } as Redis.ClusterOptions + } + return { + opts, + host, + port: parseInt(port), } - return { opts, host, port: parseInt(port), redisProtocolUrl } } export function addDbPrefix(db: string, key: string) { From 001cf0130360ea5874fc4979bad65c8c557a1e74 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 3 Nov 2023 18:00:13 +0000 Subject: [PATCH 314/427] Updating public API rate limiting functionality to be better typed as well. --- packages/backend-core/src/redis/utils.ts | 3 +- .../server/src/api/routes/public/index.ts | 47 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/backend-core/src/redis/utils.ts b/packages/backend-core/src/redis/utils.ts index 6cac7b2633..e0bdcfcd20 100644 --- a/packages/backend-core/src/redis/utils.ts +++ b/packages/backend-core/src/redis/utils.ts @@ -43,7 +43,7 @@ export enum Databases { export enum SelectableDatabase { DEFAULT = 0, SOCKET_IO = 1, - UNUSED_1 = 2, + RATE_LIMITING = 2, UNUSED_2 = 3, UNUSED_3 = 4, UNUSED_4 = 5, @@ -96,6 +96,7 @@ export function getRedisOptions() { return { opts, host, + password, port: parseInt(port), } } diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts index 4cc1eff8a4..ab10b2ed74 100644 --- a/packages/server/src/api/routes/public/index.ts +++ b/packages/server/src/api/routes/public/index.ts @@ -15,6 +15,16 @@ import env from "../../../environment" const Router = require("@koa/router") const { RateLimit, Stores } = require("koa2-ratelimit") import { middleware, redis } from "@budibase/backend-core" +import { SelectableDatabase } from "@budibase/backend-core/src/redis/utils" + +interface KoaRateLimitOptions { + socket: { + host: string + port: number + } + password?: string + database?: number +} const PREFIX = "/api/public/v1" // allow a lot more requests when in test @@ -29,32 +39,21 @@ function getApiLimitPerSecond(): number { let rateLimitStore: any = null if (!env.isTest()) { - const REDIS_OPTS = redis.utils.getRedisOptions() - let options - if (REDIS_OPTS.redisProtocolUrl) { - // fully qualified redis URL - options = { - url: REDIS_OPTS.redisProtocolUrl, - } - } else { - options = { - socket: { - host: REDIS_OPTS.host, - port: REDIS_OPTS.port, - }, - } + const { password, host, port } = redis.utils.getRedisOptions() + let options: KoaRateLimitOptions = { + socket: { + host: host, + port: port, + }, + } - if (REDIS_OPTS.opts?.password || REDIS_OPTS.opts.redisOptions?.password) { - // @ts-ignore - options.password = - REDIS_OPTS.opts.password || REDIS_OPTS.opts.redisOptions.password - } + if (password) { + options.password = password + } - if (!env.REDIS_CLUSTERED) { - // @ts-ignore - // Can't set direct redis db in clustered env - options.database = 1 - } + if (!env.REDIS_CLUSTERED) { + // Can't set direct redis db in clustered env + options.database = SelectableDatabase.RATE_LIMITING } rateLimitStore = new Stores.Redis(options) RateLimit.defaultOptions({ From 7bf307b0c2e86284b93741e94e0e106261d2e638 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 3 Nov 2023 18:03:11 +0000 Subject: [PATCH 315/427] Further updates to typing. --- packages/backend-core/src/redis/utils.ts | 20 +++++++++++-------- .../server/src/api/routes/public/index.ts | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/backend-core/src/redis/utils.ts b/packages/backend-core/src/redis/utils.ts index e0bdcfcd20..5187fe13f8 100644 --- a/packages/backend-core/src/redis/utils.ts +++ b/packages/backend-core/src/redis/utils.ts @@ -59,7 +59,7 @@ export enum SelectableDatabase { UNUSED_14 = 15, } -export function getRedisOptions() { +export function getRedisConnectionDetails() { let password = env.REDIS_PASSWORD let url: string[] | string = env.REDIS_URL.split("//") // get rid of the protocol @@ -75,9 +75,18 @@ export function getRedisOptions() { } const [host, port] = url.split(":") + return { + host, + password, + port: parseInt(port), + } +} + +export function getRedisOptions() { + const { host, password, port } = getRedisConnectionDetails() let redisOpts: Redis.RedisOptions = { connectTimeout: CONNECT_TIMEOUT_MS, - port: parseInt(port), + port: port, host, password, } @@ -93,12 +102,7 @@ export function getRedisOptions() { dnsLookup: (address: string, callback: any) => callback(null, address), } as Redis.ClusterOptions } - return { - opts, - host, - password, - port: parseInt(port), - } + return opts } export function addDbPrefix(db: string, key: string) { diff --git a/packages/server/src/api/routes/public/index.ts b/packages/server/src/api/routes/public/index.ts index ab10b2ed74..b37ed931fc 100644 --- a/packages/server/src/api/routes/public/index.ts +++ b/packages/server/src/api/routes/public/index.ts @@ -39,7 +39,7 @@ function getApiLimitPerSecond(): number { let rateLimitStore: any = null if (!env.isTest()) { - const { password, host, port } = redis.utils.getRedisOptions() + const { password, host, port } = redis.utils.getRedisConnectionDetails() let options: KoaRateLimitOptions = { socket: { host: host, From 08c4ba00975d074c10f01cabc79aa92089f8022b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Fri, 3 Nov 2023 18:06:12 +0000 Subject: [PATCH 316/427] Updating redis option functions usage, as it is no longer returned as a part of the getRedisOptions response. --- packages/backend-core/src/queue/queue.ts | 2 +- packages/backend-core/src/redis/redis.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/backend-core/src/queue/queue.ts b/packages/backend-core/src/queue/queue.ts index b460a7312b..0657437a3b 100644 --- a/packages/backend-core/src/queue/queue.ts +++ b/packages/backend-core/src/queue/queue.ts @@ -26,7 +26,7 @@ export function createQueue( jobQueue: JobQueue, opts: { removeStalledCb?: StalledFn } = {} ): BullQueue.Queue { - const { opts: redisOpts } = getRedisOptions() + const redisOpts = getRedisOptions() const queueConfig: QueueOptions = { redis: redisOpts, settings: { diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index 1ae2bd7794..6f1b573718 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -16,6 +16,7 @@ import { getRedisOptions, SEPARATOR, SelectableDatabase, + getRedisConnectionDetails, } from "./utils" import * as timers from "../timers" @@ -91,7 +92,8 @@ function init(selectDb = DEFAULT_SELECT_DB) { if (client) { client.disconnect() } - const { opts, host, port } = getRedisOptions() + const { host, port } = getRedisConnectionDetails() + const opts = getRedisOptions() if (CLUSTERED) { client = new RedisCore.Cluster([{ host, port }], opts) From b07382fa2e0efb0009bf98f14f3140ebd07da591 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Sat, 4 Nov 2023 11:04:09 +0100 Subject: [PATCH 317/427] WIP - Migrating worker --- packages/types/src/sdk/licensing/plan.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/types/src/sdk/licensing/plan.ts b/packages/types/src/sdk/licensing/plan.ts index 1604dfb8af..5ac8b1c9f6 100644 --- a/packages/types/src/sdk/licensing/plan.ts +++ b/packages/types/src/sdk/licensing/plan.ts @@ -7,7 +7,9 @@ export enum PlanType { /** @deprecated */ PREMIUM = "premium", PREMIUM_PLUS = "premium_plus", + /** @deprecated */ BUSINESS = "business", + ENTERPRISE_BASIC = "enterprise_basic", ENTERPRISE = "enterprise", } From cfc0258f3fdaa0d1d93d87772bd602b22e463d94 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Sat, 4 Nov 2023 12:46:50 +0100 Subject: [PATCH 318/427] Change Posthog's feature flags Ids --- packages/types/src/sdk/featureFlag.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/types/src/sdk/featureFlag.ts b/packages/types/src/sdk/featureFlag.ts index e3935bc7ee..ca0046696a 100644 --- a/packages/types/src/sdk/featureFlag.ts +++ b/packages/types/src/sdk/featureFlag.ts @@ -1,8 +1,7 @@ export enum FeatureFlag { LICENSING = "LICENSING", - // Feature IDs in Posthog - PER_CREATOR_PER_USER_PRICE = "18873", - PER_CREATOR_PER_USER_PRICE_ALERT = "18530", + PER_CREATOR_PER_USER_PRICE = "PER_CREATOR_PER_USER_PRICE", + PER_CREATOR_PER_USER_PRICE_ALERT = "PER_CREATOR_PER_USER_PRICE_ALERT", } export interface TenantFeatureFlags { From 5e3cb99dbb253e869dce73f18d256e7c71c69a23 Mon Sep 17 00:00:00 2001 From: Michael Drury Date: Sat, 4 Nov 2023 11:53:14 +0000 Subject: [PATCH 319/427] Switching back to old MinIO install method. --- hosting/single/runner.sh | 2 +- scripts/install-minio.sh | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hosting/single/runner.sh b/hosting/single/runner.sh index 770b23eec1..9dc7aa25d8 100644 --- a/hosting/single/runner.sh +++ b/hosting/single/runner.sh @@ -77,7 +77,7 @@ mkdir -p ${DATA_DIR}/minio chown -R couchdb:couchdb ${DATA_DIR}/couch redis-server --requirepass $REDIS_PASSWORD > /dev/stdout 2>&1 & /bbcouch-runner.sh & -minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & +/minio/minio server --console-address ":9001" ${DATA_DIR}/minio > /dev/stdout 2>&1 & /etc/init.d/nginx restart if [[ ! -z "${CUSTOM_DOMAIN}" ]]; then # Add monthly cron job to renew certbot certificate diff --git a/scripts/install-minio.sh b/scripts/install-minio.sh index b1e0d9ee80..fede984377 100755 --- a/scripts/install-minio.sh +++ b/scripts/install-minio.sh @@ -2,9 +2,9 @@ if [[ $TARGETARCH == arm* ]] ; then echo "INSTALLING ARM64 MINIO" - wget wget https://dl.min.io/server/minio/release/linux-arm64/archive/minio.deb -O minio.deb + wget https://dl.min.io/server/minio/release/linux-arm64/minio else echo "INSTALLING AMD64 MINIO" - wget wget https://dl.min.io/server/minio/release/linux-amd64/archive/minio.deb -O minio.deb + wget https://dl.min.io/server/minio/release/linux-amd64/minio fi -dpkg -i minio.deb +chmod +x minio \ No newline at end of file From c539985088cf8bf98336df7b34f1a3266a85200e Mon Sep 17 00:00:00 2001 From: Budibase Staging Release Bot <> Date: Sat, 4 Nov 2023 12:07:22 +0000 Subject: [PATCH 320/427] Bump version to 2.12.11 --- lerna.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lerna.json b/lerna.json index 9257a36444..d102c3f41f 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.12.10", + "version": "2.12.11", "npmClient": "yarn", "packages": [ "packages/*" From 5088caf0f944c82cad3b292df3c06896a0583893 Mon Sep 17 00:00:00 2001 From: jvcalderon Date: Mon, 6 Nov 2023 08:37:53 +0100 Subject: [PATCH 321/427] Update pro submodule --- packages/pro | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/pro b/packages/pro index 3820c0c93a..34cce46d9c 160000 --- a/packages/pro +++ b/packages/pro @@ -1 +1 @@ -Subproject commit 3820c0c93a3e448e10a60a9feb5396844b537ca8 +Subproject commit 34cce46d9c67f03aa66272e079e1d460a731225e From 727a9a7154c924db928e625a2649b4b9716fa2c7 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Mon, 6 Nov 2023 09:41:59 +0000 Subject: [PATCH 322/427] Remove beta button --- .../src/pages/builder/app/[application]/data/_layout.svelte | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte b/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte index 4f64edb34d..d89f09fc08 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/_layout.svelte @@ -3,7 +3,6 @@ import DatasourceNavigator from "components/backend/DatasourceNavigator/DatasourceNavigator.svelte" import Panel from "components/design/Panel.svelte" import { isActive, redirect, goto, params } from "@roxi/routify" - import BetaButton from "./_components/BetaButton.svelte" import { datasources } from "stores/backend" $: { @@ -30,7 +29,6 @@
-
diff --git a/packages/bbui/src/Form/Core/CheckboxGroup.svelte b/packages/bbui/src/Form/Core/CheckboxGroup.svelte index faf37f3ad8..66ac55561b 100644 --- a/packages/bbui/src/Form/Core/CheckboxGroup.svelte +++ b/packages/bbui/src/Form/Core/CheckboxGroup.svelte @@ -35,17 +35,13 @@ title={getOptionLabel(option)} class="spectrum-Checkbox spectrum-FieldGroup-item" class:is-invalid={!!error} + class:readonly >

( getTableParams(null, { include_docs: true, }) @@ -124,7 +124,7 @@ export async function getTables(tableIds: string[]): Promise { } if (internalTableIds.length) { const db = context.getAppDB() - const internalTableDocs = await db.allDocs( + const internalTableDocs = await db.allDocs
( getMultiIDParams(internalTableIds) ) tables = tables.concat(internalTableDocs.rows.map(row => row.doc!)) diff --git a/packages/server/src/sdk/users/utils.ts b/packages/server/src/sdk/users/utils.ts index 03ddc954e9..9632ac29d8 100644 --- a/packages/server/src/sdk/users/utils.ts +++ b/packages/server/src/sdk/users/utils.ts @@ -7,12 +7,17 @@ import { InternalTables, } from "../../db/utils" import isEqual from "lodash/isEqual" -import { ContextUser, UserMetadata, User, Database } from "@budibase/types" +import { + ContextUser, + UserMetadata, + Database, + ContextUserMetadata, +} from "@budibase/types" export function combineMetadataAndUser( user: ContextUser, metadata: UserMetadata | UserMetadata[] -) { +): ContextUserMetadata | null { const metadataId = generateUserMetadataID(user._id!) const found = Array.isArray(metadata) ? metadata.find(doc => doc._id === metadataId) @@ -51,33 +56,33 @@ export function combineMetadataAndUser( return null } -export async function rawUserMetadata(db?: Database) { +export async function rawUserMetadata(db?: Database): Promise { if (!db) { db = context.getAppDB() } return ( - await db.allDocs( + await db.allDocs( getUserMetadataParams(null, { include_docs: true, }) ) - ).rows.map(row => row.doc) + ).rows.map(row => row.doc!) } -export async function fetchMetadata() { +export async function fetchMetadata(): Promise { const global = await getGlobalUsers() const metadata = await rawUserMetadata() - const users = [] + const users: ContextUserMetadata[] = [] for (let user of global) { // find the metadata that matches up to the global ID - const info = metadata.find(meta => meta._id.includes(user._id)) + const info = metadata.find(meta => meta._id!.includes(user._id!)) // remove these props, not for the correct DB users.push({ ...user, ...info, tableId: InternalTables.USER_METADATA, // make sure the ID is always a local ID, not a global one - _id: generateUserMetadataID(user._id), + _id: generateUserMetadataID(user._id!), }) } return users @@ -90,9 +95,10 @@ export async function syncGlobalUsers() { if (!(await db.exists())) { continue } - const resp = await Promise.all([getGlobalUsers(), rawUserMetadata(db)]) - const users = resp[0] as User[] - const metadata = resp[1] as UserMetadata[] + const [users, metadata] = await Promise.all([ + getGlobalUsers(), + rawUserMetadata(db), + ]) const toWrite = [] for (let user of users) { const combined = combineMetadataAndUser(user, metadata) diff --git a/packages/server/src/utilities/global.ts b/packages/server/src/utilities/global.ts index 5aa201990c..cdc2d84513 100644 --- a/packages/server/src/utilities/global.ts +++ b/packages/server/src/utilities/global.ts @@ -71,69 +71,67 @@ export async function processUser( return user } -export async function getCachedSelf(ctx: UserCtx, appId: string) { +export async function getCachedSelf( + ctx: UserCtx, + appId: string +): Promise { // this has to be tenant aware, can't depend on the context to find it out // running some middlewares before the tenancy causes context to break const user = await cache.user.getUser(ctx.user?._id!) return processUser(user, { appId }) } -export async function getRawGlobalUser(userId: string) { +export async function getRawGlobalUser(userId: string): Promise { const db = tenancy.getGlobalDB() return db.get(getGlobalIDFromUserMetadataID(userId)) } -export async function getGlobalUser(userId: string) { +export async function getGlobalUser(userId: string): Promise { const appId = context.getAppId() let user = await getRawGlobalUser(userId) return processUser(user, { appId }) } -export async function getGlobalUsers( - userIds?: string[], - opts?: { noProcessing?: boolean } -) { - const appId = context.getAppId() +export async function getRawGlobalUsers(userIds?: string[]): Promise { const db = tenancy.getGlobalDB() - let globalUsers + let globalUsers: User[] if (userIds) { - globalUsers = (await db.allDocs(getMultiIDParams(userIds))).rows.map( - row => row.doc + globalUsers = (await db.allDocs(getMultiIDParams(userIds))).rows.map( + row => row.doc! ) } else { globalUsers = ( - await db.allDocs( + await db.allDocs( dbCore.getGlobalUserParams(null, { include_docs: true, }) ) - ).rows.map(row => row.doc) + ).rows.map(row => row.doc!) } - globalUsers = globalUsers + return globalUsers .filter(user => user != null) .map(user => { delete user.password delete user.forceResetPassword return user }) +} - if (opts?.noProcessing || !appId) { - return globalUsers - } else { - // pass in the groups, meaning we don't actually need to retrieve them for - // each user individually - const allGroups = await groups.fetch() - return Promise.all( - globalUsers.map(user => processUser(user, { groups: allGroups })) - ) - } +export async function getGlobalUsers( + userIds?: string[] +): Promise { + const users = await getRawGlobalUsers(userIds) + const allGroups = await groups.fetch() + return Promise.all( + users.map(user => processUser(user, { groups: allGroups })) + ) } export async function getGlobalUsersFromMetadata(users: ContextUser[]) { const globalUsers = await getGlobalUsers(users.map(user => user._id!)) return users.map(user => { const globalUser = globalUsers.find( - globalUser => globalUser && user._id?.includes(globalUser._id) + globalUser => globalUser && user._id?.includes(globalUser._id!) ) return { ...globalUser, diff --git a/packages/server/src/utilities/routing/index.ts b/packages/server/src/utilities/routing/index.ts index de966a946b..82d45743ce 100644 --- a/packages/server/src/utilities/routing/index.ts +++ b/packages/server/src/utilities/routing/index.ts @@ -1,9 +1,9 @@ import { createRoutingView } from "../../db/views/staticViews" import { ViewName, getQueryIndex, UNICODE_MAX } from "../../db/utils" import { context } from "@budibase/backend-core" -import { ScreenRouting } from "@budibase/types" +import { ScreenRouting, Document } from "@budibase/types" -type ScreenRoutesView = { +interface ScreenRoutesView extends Document { id: string routing: ScreenRouting } diff --git a/packages/types/src/documents/app/user.ts b/packages/types/src/documents/app/user.ts index 4defd4a414..207997245e 100644 --- a/packages/types/src/documents/app/user.ts +++ b/packages/types/src/documents/app/user.ts @@ -1,6 +1,6 @@ -import { Document } from "../document" +import { User } from "../global" +import { Row } from "./row" +import { ContextUser } from "../../sdk" -export interface UserMetadata extends Document { - roleId: string - email?: string -} +export type UserMetadata = User & Row +export type ContextUserMetadata = ContextUser & Row diff --git a/packages/types/src/documents/pouch.ts b/packages/types/src/documents/pouch.ts index d484f4700d..11efc502be 100644 --- a/packages/types/src/documents/pouch.ts +++ b/packages/types/src/documents/pouch.ts @@ -1,17 +1,19 @@ +import { Document } from "../" + export interface RowValue { rev: string deleted: boolean } -export interface RowResponse { +export interface RowResponse { id: string key: string error: string value: T | RowValue - doc?: T | any + doc?: T } -export interface AllDocsResponse { +export interface AllDocsResponse { offset: number total_rows: number rows: RowResponse[] diff --git a/packages/types/src/sdk/db.ts b/packages/types/src/sdk/db.ts index 05f72f5524..12c53a9561 100644 --- a/packages/types/src/sdk/db.ts +++ b/packages/types/src/sdk/db.ts @@ -101,8 +101,10 @@ export interface Database { opts?: DatabasePutOpts ): Promise bulkDocs(documents: AnyDocument[]): Promise - allDocs(params: DatabaseQueryOpts): Promise> - query( + allDocs( + params: DatabaseQueryOpts + ): Promise> + query( viewName: string, params: DatabaseQueryOpts ): Promise> diff --git a/packages/worker/src/constants/templates/index.ts b/packages/worker/src/constants/templates/index.ts index 1feac62040..6dd3f556a6 100644 --- a/packages/worker/src/constants/templates/index.ts +++ b/packages/worker/src/constants/templates/index.ts @@ -56,12 +56,12 @@ export async function getTemplates({ id, }: { ownerId?: string; type?: string; id?: string } = {}) { const db = tenancy.getGlobalDB() - const response = await db.allDocs( + const response = await db.allDocs