diff --git a/.github/workflows/budibase_ci.yml b/.github/workflows/budibase_ci.yml index fc35575ec6..9d1131ed7f 100644 --- a/.github/workflows/budibase_ci.yml +++ b/.github/workflows/budibase_ci.yml @@ -30,8 +30,8 @@ jobs: with: root-reserve-mb: 35000 swap-size-mb: 1024 - remove-android: 'true' - remove-dotnet: 'true' + remove-android: "true" + remove-dotnet: "true" - name: Checkout repo and submodules uses: actions/checkout@v3 if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' @@ -121,7 +121,7 @@ jobs: name: codecov-umbrella verbose: true - test-services: + test-worker: runs-on: ubuntu-latest steps: - name: Checkout repo and submodules @@ -143,12 +143,48 @@ jobs: node-version: 18.x cache: "yarn" - run: yarn --frozen-lockfile - - name: Test worker and server + - name: Test worker run: | if ${{ env.USE_NX_AFFECTED }}; then - yarn test --scope=@budibase/worker --scope=@budibase/server --since=${{ env.NX_BASE_BRANCH }} + yarn test --scope=@budibase/worker --since=${{ env.NX_BASE_BRANCH }} else - yarn test --scope=@budibase/worker --scope=@budibase/server + yarn test --scope=@budibase/worker + fi + + - uses: codecov/codecov-action@v3 + with: + token: ${{ secrets.CODECOV_TOKEN || github.token }} # not required for public repos + name: codecov-umbrella + verbose: true + + test-server: + runs-on: ubuntu-latest + steps: + - name: Checkout repo and submodules + uses: actions/checkout@v3 + if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == 'Budibase/budibase' + with: + submodules: true + token: ${{ secrets.PERSONAL_ACCESS_TOKEN || github.token }} + fetch-depth: 0 + - name: Checkout repo only + uses: actions/checkout@v3 + if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != 'Budibase/budibase' + with: + fetch-depth: 0 + + - name: Use Node.js 18.x + uses: actions/setup-node@v3 + with: + node-version: 18.x + cache: "yarn" + - run: yarn --frozen-lockfile + - name: Test server + run: | + if ${{ env.USE_NX_AFFECTED }}; then + yarn test --scope=@budibase/server --since=${{ env.NX_BASE_BRANCH }} + else + yarn test --scope=@budibase/server fi - uses: codecov/codecov-action@v3 @@ -259,4 +295,4 @@ jobs: process.exit(1); } else { console.log('All good, the submodule had been merged and setup correctly!') - } \ No newline at end of file + } diff --git a/.github/workflows/close-featurebranch.yml b/.github/workflows/close-featurebranch.yml new file mode 100644 index 0000000000..0ec3b43598 --- /dev/null +++ b/.github/workflows/close-featurebranch.yml @@ -0,0 +1,21 @@ +name: close-featurebranch + +on: + pull_request: + types: [closed] + branches: + - develop + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: passeidireto/trigger-external-workflow-action@main + env: + PAYLOAD_BRANCH: ${{ github.head_ref }} + PAYLOAD_PR_NUMBER: ${{ github.ref }} + with: + repository: budibase/budibase-deploys + event: featurebranch-qa-close + github_pat: ${{ secrets.GH_ACCESS_TOKEN }} diff --git a/.github/workflows/deploy-featurebranch.yml b/.github/workflows/deploy-featurebranch.yml index 9057d32c4c..f06707ab2b 100644 --- a/.github/workflows/deploy-featurebranch.yml +++ b/.github/workflows/deploy-featurebranch.yml @@ -12,7 +12,8 @@ jobs: - uses: actions/checkout@v3 - uses: passeidireto/trigger-external-workflow-action@main env: - BRANCH: ${{ github.head_ref }} + PAYLOAD_BRANCH: ${{ github.head_ref }} + PAYLOAD_PR_NUMBER: ${{ github.ref }} with: repository: budibase/budibase-deploys event: featurebranch-qa-deploy diff --git a/hosting/proxy/nginx.prod.conf b/hosting/proxy/nginx.prod.conf index 634bce18ac..365765ccbb 100644 --- a/hosting/proxy/nginx.prod.conf +++ b/hosting/proxy/nginx.prod.conf @@ -55,7 +55,7 @@ http { 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'"; - set $csp_connect "connect-src 'self' https://*.budibase.app https://*.budibase.qa https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.s3.us-east-2.amazonaws.com https://*.s3.us-east-1.amazonaws.com https://*.s3.us-west-1.amazonaws.com https://*.s3.us-west-2.amazonaws.com https://*.s3.af-south-1.amazonaws.com https://*.s3.ap-east-1.amazonaws.com https://*.s3.ap-southeast-3.amazonaws.com https://*.s3.ap-south-1.amazonaws.com https://*.s3.ap-northeast-3.amazonaws.com https://*.s3.ap-northeast-2.amazonaws.com https://*.s3.ap-southeast-1.amazonaws.com https://*.s3.ap-southeast-2.amazonaws.com https://*.s3.ap-northeast-1.amazonaws.com https://*.s3.ca-central-1.amazonaws.com https://*.s3.cn-north-1.amazonaws.com https://*.s3.cn-northwest-1.amazonaws.com https://*.s3.eu-central-1.amazonaws.com https://*.s3.eu-west-1.amazonaws.com https://*.s3.eu-west-2.amazonaws.com https://*.s3.eu-south-1.amazonaws.com https://*.s3.eu-west-3.amazonaws.com https://*.s3.eu-north-1.amazonaws.com https://*.s3.sa-east-1.amazonaws.com https://*.s3.me-south-1.amazonaws.com https://*.s3.us-gov-east-1.amazonaws.com https://*.s3.us-gov-west-1.amazonaws.com https://api.github.com"; + set $csp_connect "connect-src 'self' https://*.budibase.app https://*.budibaseqa.app https://*.budibase.net https://api-iam.intercom.io https://api-iam.intercom.io https://api-ping.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io wss://nexus-websocket-b.intercom.io https://nexus-websocket-a.intercom.io https://nexus-websocket-b.intercom.io https://uploads.intercomcdn.com https://uploads.intercomusercontent.com https://*.amazonaws.com https://*.s3.amazonaws.com https://*.s3.us-east-2.amazonaws.com https://*.s3.us-east-1.amazonaws.com https://*.s3.us-west-1.amazonaws.com https://*.s3.us-west-2.amazonaws.com https://*.s3.af-south-1.amazonaws.com https://*.s3.ap-east-1.amazonaws.com https://*.s3.ap-southeast-3.amazonaws.com https://*.s3.ap-south-1.amazonaws.com https://*.s3.ap-northeast-3.amazonaws.com https://*.s3.ap-northeast-2.amazonaws.com https://*.s3.ap-southeast-1.amazonaws.com https://*.s3.ap-southeast-2.amazonaws.com https://*.s3.ap-northeast-1.amazonaws.com https://*.s3.ca-central-1.amazonaws.com https://*.s3.cn-north-1.amazonaws.com https://*.s3.cn-northwest-1.amazonaws.com https://*.s3.eu-central-1.amazonaws.com https://*.s3.eu-west-1.amazonaws.com https://*.s3.eu-west-2.amazonaws.com https://*.s3.eu-south-1.amazonaws.com https://*.s3.eu-west-3.amazonaws.com https://*.s3.eu-north-1.amazonaws.com https://*.s3.sa-east-1.amazonaws.com https://*.s3.me-south-1.amazonaws.com https://*.s3.us-gov-east-1.amazonaws.com https://*.s3.us-gov-west-1.amazonaws.com https://api.github.com"; set $csp_font "font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com https://js.intercomcdn.com https://fonts.intercomcdn.com"; set $csp_frame "frame-src 'self' https:"; set $csp_img "img-src http: https: data: blob:"; diff --git a/lerna.json b/lerna.json index 124446321f..c4d2219e21 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.10.12-alpha.5", + "version": "2.10.12-alpha.23", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/package.json b/package.json index 6df4105e25..d758888eab 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "@esbuild-plugins/tsconfig-paths": "^0.1.2", "@nx/js": "16.4.3", "@rollup/plugin-json": "^4.0.2", - "@typescript-eslint/parser": "5.45.0", + "@typescript-eslint/parser": "6.7.2", "esbuild": "^0.18.17", "esbuild-node-externals": "^1.8.0", "eslint": "^8.44.0", @@ -22,7 +22,7 @@ "rimraf": "^3.0.2", "rollup-plugin-replace": "^2.2.0", "svelte": "^3.38.2", - "typescript": "4.7.3", + "typescript": "5.2.2", "@babel/core": "^7.22.5", "@babel/eslint-parser": "^7.22.5", "@babel/preset-env": "^7.22.5", diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index 739469b49a..1c94163d93 100644 --- a/packages/backend-core/package.json +++ b/packages/backend-core/package.json @@ -87,7 +87,7 @@ "timekeeper": "2.2.0", "ts-node": "10.8.1", "tsconfig-paths": "4.0.0", - "typescript": "4.7.3" + "typescript": "5.2.2" }, "nx": { "targets": { diff --git a/packages/backend-core/src/cache/user.ts b/packages/backend-core/src/cache/user.ts index b3fd7c08cd..481d3691e4 100644 --- a/packages/backend-core/src/cache/user.ts +++ b/packages/backend-core/src/cache/user.ts @@ -120,7 +120,7 @@ export async function getUsers( ): Promise<{ users: User[]; notFoundIds?: string[] }> { const client = await redis.getUserClient() // try cache - let usersFromCache = await client.bulkGet(userIds) + let usersFromCache = await client.bulkGet(userIds) const missingUsersFromCache = userIds.filter(uid => !usersFromCache[uid]) const users = Object.values(usersFromCache) let notFoundIds diff --git a/packages/backend-core/src/redis/redis.ts b/packages/backend-core/src/redis/redis.ts index 78817d0aa0..e7755f275d 100644 --- a/packages/backend-core/src/redis/redis.ts +++ b/packages/backend-core/src/redis/redis.ts @@ -242,7 +242,7 @@ class RedisWrapper { } } - async bulkGet(keys: string[]) { + async bulkGet(keys: string[]) { const db = this._db if (keys.length === 0) { return {} @@ -250,7 +250,7 @@ class RedisWrapper { const prefixedKeys = keys.map(key => addDbPrefix(db, key)) let response = await this.getClient().mget(prefixedKeys) if (Array.isArray(response)) { - let final: Record = {} + let final: Record = {} let count = 0 for (let result of response) { if (result) { diff --git a/packages/backend-core/tests/core/utilities/mocks/licenses.ts b/packages/backend-core/tests/core/utilities/mocks/licenses.ts index 309f0fd159..758fd6bf9a 100644 --- a/packages/backend-core/tests/core/utilities/mocks/licenses.ts +++ b/packages/backend-core/tests/core/utilities/mocks/licenses.ts @@ -86,8 +86,8 @@ export const useAuditLogs = () => { return useFeature(Feature.AUDIT_LOGS) } -export const usePublicApiUserRoles = () => { - return useFeature(Feature.USER_ROLE_PUBLIC_API) +export const useExpandedPublicApi = () => { + return useFeature(Feature.EXPANDED_PUBLIC_API) } export const useScimIntegration = () => { diff --git a/packages/backend-core/tests/core/utilities/structures/users.ts b/packages/backend-core/tests/core/utilities/structures/users.ts index 420a9fde0e..66d23696e0 100644 --- a/packages/backend-core/tests/core/utilities/structures/users.ts +++ b/packages/backend-core/tests/core/utilities/structures/users.ts @@ -10,14 +10,13 @@ import { authDetails } from "./sso" import { uuid } from "./common" import { generator } from "./generator" import { tenant } from "." -import { generateGlobalUserID } from "../../../../src/docIds" export const newEmail = () => { return `${uuid()}@test.com` } export const user = (userProps?: Partial>): User => { - const userId = userProps?._id || generateGlobalUserID() + const userId = userProps?._id return { _id: userId, userId, @@ -53,7 +52,7 @@ export const adminOnlyUser = (userProps?: any): AdminOnlyUser => { } } -export const builderUser = (userProps?: any): BuilderUser => { +export const builderUser = (userProps?: Partial): BuilderUser => { return { ...user(userProps), builder: { diff --git a/packages/backend-core/tests/extra/DBTestConfiguration.ts b/packages/backend-core/tests/extra/DBTestConfiguration.ts index a2550a6e24..99a5bcba46 100644 --- a/packages/backend-core/tests/extra/DBTestConfiguration.ts +++ b/packages/backend-core/tests/extra/DBTestConfiguration.ts @@ -18,7 +18,7 @@ class DBTestConfiguration { // TENANCY - doInTenant(task: any) { + doInTenant(task: () => Promise) { return context.doInTenant(this.tenantId, () => { return task() }) diff --git a/packages/backend-core/tests/index.ts b/packages/backend-core/tests/index.ts index 50fc1dc431..cdbacc12d8 100644 --- a/packages/backend-core/tests/index.ts +++ b/packages/backend-core/tests/index.ts @@ -1 +1,2 @@ export * from "./core/utilities" +export * from "./extra" diff --git a/packages/bbui/src/Form/Core/Multiselect.svelte b/packages/bbui/src/Form/Core/Multiselect.svelte index 8816da33c4..8d72dd0652 100644 --- a/packages/bbui/src/Form/Core/Multiselect.svelte +++ b/packages/bbui/src/Form/Core/Multiselect.svelte @@ -14,12 +14,12 @@ export let autocomplete = false export let sort = false export let autoWidth = false - export let fetchTerm = null - export let useFetch = false + export let searchTerm = null export let customPopoverHeight export let customPopoverOffsetBelow export let customPopoverMaxHeight export let open = false + export let loading const dispatch = createEventDispatcher() @@ -82,6 +82,7 @@ diff --git a/packages/bbui/src/Form/Core/Picker.svelte b/packages/bbui/src/Form/Core/Picker.svelte index 9b90c1a865..aa06d5f748 100644 --- a/packages/bbui/src/Form/Core/Picker.svelte +++ b/packages/bbui/src/Form/Core/Picker.svelte @@ -2,7 +2,7 @@ import "@spectrum-css/picker/dist/index-vars.css" import "@spectrum-css/popover/dist/index-vars.css" import "@spectrum-css/menu/dist/index-vars.css" - import { createEventDispatcher } from "svelte" + import { createEventDispatcher, onDestroy } from "svelte" import clickOutside from "../../Actions/click_outside" import Search from "./Search.svelte" import Icon from "../../Icon/Icon.svelte" @@ -10,6 +10,7 @@ import Popover from "../../Popover/Popover.svelte" import Tags from "../../Tags/Tags.svelte" import Tag from "../../Tags/Tag.svelte" + import ProgressCircle from "../../ProgressCircle/ProgressCircle.svelte" export let id = null export let disabled = false @@ -35,19 +36,20 @@ export let autoWidth = false export let autocomplete = false export let sort = false - export let fetchTerm = null - export let useFetch = false + export let searchTerm = null export let customPopoverHeight export let customPopoverOffsetBelow export let customPopoverMaxHeight export let align = "left" export let footer = null export let customAnchor = null + export let loading + const dispatch = createEventDispatcher() - let searchTerm = null let button let popover + let component $: sortedOptions = getSortedOptions(options, getOptionLabel, sort) $: filteredOptions = getFilteredOptions( @@ -82,7 +84,7 @@ } const getFilteredOptions = (options, term, getLabel) => { - if (autocomplete && term && !fetchTerm) { + if (autocomplete && term) { const lowerCaseTerm = term.toLowerCase() return options.filter(option => { return `${getLabel(option)}`.toLowerCase().includes(lowerCaseTerm) @@ -90,6 +92,20 @@ } return options } + + const onScroll = e => { + const scrollPxThreshold = 100 + const scrollPositionFromBottom = + e.target.scrollHeight - e.target.clientHeight - e.target.scrollTop + if (scrollPositionFromBottom < scrollPxThreshold) { + dispatch("loadMore") + } + } + + $: component?.addEventListener("scroll", onScroll) + onDestroy(() => { + component?.removeEventListener("scroll", null) + })