Merge branch 'master' of github.com:Budibase/budibase into frontend-core-ts-2

This commit is contained in:
Andrew Kingston 2024-12-10 10:33:53 +00:00
commit 65f37e046e
No known key found for this signature in database
152 changed files with 1203 additions and 1488 deletions

View File

@ -1,13 +0,0 @@
node_modules
public
dist
packages/server/builder
packages/server/coverage
packages/worker/coverage
packages/backend-core/coverage
packages/server/client
packages/server/coverage
packages/builder/.routify
packages/sdk/sdk
**/*.ivm.bundle.js
packages/server/build/oldClientVersions/**/**

View File

@ -1,129 +0,0 @@
{
"root": true,
"env": {
"browser": true,
"es6": true,
"jest": true,
"node": true
},
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 2019,
"sourceType": "module",
"allowImportExportEverywhere": true
},
"ignorePatterns": [
"node_modules",
"dist",
"public",
"*.spec.js",
"bundle.js"
],
"extends": ["eslint:recommended"],
"plugins": ["import", "eslint-plugin-local-rules"],
"overrides": [
{
"files": ["**/*.svelte"],
"extends": "plugin:svelte/recommended",
"parser": "svelte-eslint-parser",
"parserOptions": {
"parser": "@typescript-eslint/parser",
"ecmaVersion": 2019,
"allowImportExportEverywhere": true
}
},
{
"files": ["**/*.ts"],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended"],
"globals": {
"NodeJS": true
},
"rules": {
"no-unused-vars": "off",
"local-rules/no-barrel-imports": "error",
"local-rules/no-budibase-imports": "error",
"local-rules/no-console-error": "error",
"@typescript-eslint/no-unused-vars": [
"error",
{
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": "error",
// have to turn this off to allow function overloading in typescript
"no-dupe-class-members": "off"
}
},
{
"files": ["**/*.spec.ts"],
"parser": "@typescript-eslint/parser",
"plugins": ["jest", "@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:jest/recommended"],
"env": {
"jest/globals": true
},
"globals": {
"NodeJS": true
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"local-rules/no-test-com": "error",
"local-rules/email-domain-example-com": "error",
"no-console": "warn",
// We have a lot of tests that don't have assertions, they use our test
// API client that does the assertions for them
"jest/expect-expect": "off",
// We do this in some tests where the behaviour of internal tables
// differs to external, but the API is broadly the same
"jest/no-conditional-expect": "off",
// have to turn this off to allow function overloading in typescript
"no-dupe-class-members": "off",
"no-redeclare": "off"
}
},
{
"files": [
"packages/builder/**/*",
"packages/client/**/*",
"packages/frontend-core/**/*"
],
"rules": {
"no-console": ["error", { "allow": ["warn", "error", "debug"] }]
}
}
],
"rules": {
"no-self-assign": "off",
"no-unused-vars": [
"error",
{
"varsIgnorePattern": "^_",
"argsIgnorePattern": "^_",
"destructuredArrayIgnorePattern": "^_",
"ignoreRestSiblings": true
}
],
"import/no-relative-packages": "error",
"import/export": "error",
"import/no-duplicates": "error",
"import/newline-after-import": "error"
},
"globals": {
"GeolocationPositionError": true
}
}

197
eslint.config.mjs Normal file
View File

@ -0,0 +1,197 @@
import globals from "globals"
import babelParser from "@babel/eslint-parser"
import svelteParser from "svelte-eslint-parser"
import tsParser from "@typescript-eslint/parser"
import eslintPluginJest from "eslint-plugin-jest"
import eslintPluginSvelte from "eslint-plugin-svelte"
import eslintPluginLocalRules from "eslint-plugin-local-rules"
import eslintPluginVitest from "@vitest/eslint-plugin"
import eslint from "@eslint/js"
import tseslint from "typescript-eslint"
export default [
eslint.configs.recommended,
{
ignores: [
"**/node_modules",
"**/dist",
"**/public",
"**/bundle.js",
"**/coverage",
"packages/server/builder",
"packages/server/client",
"packages/builder/.routify",
"packages/sdk/sdk",
"**/*.ivm.bundle.js",
"packages/server/build/oldClientVersions/**/**/*",
],
},
{
plugins: {
"local-rules": eslintPluginLocalRules,
},
languageOptions: {
globals: {
...globals.browser,
...globals.jest,
...globals.node,
GeolocationPositionError: true,
},
parser: babelParser,
ecmaVersion: 2019,
sourceType: "module",
parserOptions: {
allowImportExportEverywhere: true,
},
},
rules: {
"no-self-compare": "error",
"no-template-curly-in-string": "error",
"no-unmodified-loop-condition": "error",
"no-unreachable-loop": "error",
"no-implied-eval": "error",
"no-extend-native": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-return-assign": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-rename": "error",
"no-var": "error",
"no-void": "error",
"no-unused-vars": [
"error",
{
varsIgnorePattern: "^_",
argsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
ignoreRestSiblings: true,
caughtErrors: "none",
},
],
},
},
...eslintPluginSvelte.configs["flat/recommended"].map(config => ({
...config,
files: ["**/*.svelte"],
languageOptions: {
parser: svelteParser,
ecmaVersion: 2019,
sourceType: "script",
parserOptions: {
parser: "@typescript-eslint/parser",
allowImportExportEverywhere: true,
},
},
})),
...tseslint.configs.strict.map(config => ({
...config,
files: ["**/*.ts"],
languageOptions: {
globals: {
NodeJS: true,
},
parser: tsParser,
},
rules: {
"local-rules/no-barrel-imports": "error",
"local-rules/no-budibase-imports": "error",
"local-rules/no-console-error": "error",
"@typescript-eslint/no-inferrable-types": "error",
"@typescript-eslint/adjacent-overload-signatures": "error",
"@typescript-eslint/class-literal-property-style": "error",
"@typescript-eslint/no-confusing-non-null-assertion": "error",
"@typescript-eslint/no-unnecessary-parameter-property-assignment":
"error",
"@typescript-eslint/no-useless-empty-export": "error",
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
varsIgnorePattern: "^_",
argsIgnorePattern: "^_",
destructuredArrayIgnorePattern: "^_",
ignoreRestSiblings: true,
caughtErrors: "none",
},
],
"no-redeclare": "off",
"@typescript-eslint/no-redeclare": "error",
// @typescript-eslint/no-dupe-class-members supersedes no-dupe-class-members
"no-dupe-class-members": "off",
"@typescript-eslint/no-dupe-class-members": "error",
"no-useless-constructor": "off",
"@typescript-eslint/no-useless-constructor": "error",
},
})),
{
files: ["**/*.spec.ts", "**/*.spec.js"],
plugins: {
jest: eslintPluginJest,
vitest: eslintPluginVitest,
},
languageOptions: {
globals: {
...eslintPluginJest.environments.globals.globals,
...eslintPluginVitest.environments.env.globals,
NodeJS: true,
},
parser: tsParser,
},
rules: {
...eslintPluginVitest.configs.recommended.rules,
...eslintPluginJest.configs.recommended.rules,
"no-console": "warn",
"vitest/expect-expect": "off",
"jest/expect-expect": "off",
"jest/no-conditional-expect": "off",
"jest/no-disabled-tests": "off",
"jest/no-standalone-expect": "off",
"local-rules/no-test-com": "error",
"local-rules/email-domain-example-com": "error",
},
},
{
files: [
"packages/builder/**/*",
"packages/client/**/*",
"packages/frontend-core/**/*",
],
rules: {
"no-console": [
"error",
{
allow: ["warn", "error", "debug"],
},
],
},
},
]

View File

@ -18,7 +18,7 @@
"react-notifications-component": "^3.4.1" "react-notifications-component": "^3.4.1"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "17.0.21", "@types/node": "^20.17.9",
"@types/react": "17.0.39", "@types/react": "17.0.39",
"eslint": "8.10.0", "eslint": "8.10.0",
"eslint-config-next": "12.1.0", "eslint-config-next": "12.1.0",

View File

@ -147,10 +147,12 @@
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
"@types/node@17.0.21": "@types/node@^20.17.9":
version "17.0.21" version "20.17.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.17.9.tgz#5f141d4b7ee125cdee5faefe28de095398865bab"
integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== integrity sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==
dependencies:
undici-types "~6.19.2"
"@types/prop-types@*": "@types/prop-types@*":
version "15.7.4" version "15.7.4"
@ -1746,10 +1748,10 @@ type-fest@^0.20.2:
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
typescript@5.5.2: typescript@5.7.2:
version "5.5.2" version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.2.tgz#c26f023cb0054e657ce04f72583ea2d85f8d0507" resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-NcRtPEOsPFFWjobJEtfihkLCZCXZt/os3zf8nTxjVH3RvTSxjrCamJpbExGvYOF+tFHc3pA65qpdwPbzjohhew== integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==
unbox-primitive@^1.0.1: unbox-primitive@^1.0.1:
version "1.0.1" version "1.0.1"
@ -1761,6 +1763,11 @@ unbox-primitive@^1.0.1:
has-symbols "^1.0.2" has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2" which-boxed-primitive "^1.0.2"
undici-types@~6.19.2:
version "6.19.8"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.19.8.tgz#35111c9d1437ab83a7cdc0abae2f26d88eda0a02"
integrity sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==
uri-js@^4.2.2: uri-js@^4.2.2:
version "4.4.1" version "4.4.1"
resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"

View File

@ -2,7 +2,6 @@
const os = require("os") const os = require("os")
const exec = require("child_process").exec const exec = require("child_process").exec
const fs = require("fs")
const platform = os.platform() const platform = os.platform()
const windows = platform === "win32" const windows = platform === "win32"
@ -17,10 +16,11 @@ function execute(command) {
async function commandExistsUnix(command) { async function commandExistsUnix(command) {
const unixCmd = `command -v ${command} 2>/dev/null && { echo >&1 ${command}; exit 0; }` const unixCmd = `command -v ${command} 2>/dev/null && { echo >&1 ${command}; exit 0; }`
return execute(command) return execute(unixCmd)
} }
async function commandExistsWindows(command) { async function commandExistsWindows(command) {
// eslint-disable-next-line no-control-regex
if (/[\x00-\x1f<>:"|?*]/.test(command)) { if (/[\x00-\x1f<>:"|?*]/.test(command)) {
return false return false
} }

View File

@ -1,6 +1,6 @@
{ {
"$schema": "node_modules/lerna/schemas/lerna-schema.json", "$schema": "node_modules/lerna/schemas/lerna-schema.json",
"version": "3.2.25", "version": "3.2.26",
"npmClient": "yarn", "npmClient": "yarn",
"concurrency": 20, "concurrency": 20,
"command": { "command": {

View File

@ -12,7 +12,8 @@
"inputs": [ "inputs": [
"{workspaceRoot}/scripts/*", "{workspaceRoot}/scripts/*",
"{workspaceRoot}/lerna.json", "{workspaceRoot}/lerna.json",
"{workspaceRoot}/.github/workflows/*" "{workspaceRoot}/.github/workflows/*",
"{workspaceRoot}/tsconfig.build.json"
] ]
}, },
"test": { "test": {

View File

@ -3,33 +3,34 @@
"private": true, "private": true,
"devDependencies": { "devDependencies": {
"@babel/core": "^7.22.5", "@babel/core": "^7.22.5",
"@babel/eslint-parser": "^7.22.5", "@babel/eslint-parser": "7.25.9",
"@babel/preset-env": "^7.22.5", "@babel/preset-env": "^7.22.5",
"@esbuild-plugins/tsconfig-paths": "^0.1.2", "@esbuild-plugins/tsconfig-paths": "^0.1.2",
"@types/node": "20.10.0", "@types/node": "^20.17.9",
"@types/proper-lockfile": "^4.1.4", "@types/proper-lockfile": "^4.1.4",
"@typescript-eslint/parser": "6.9.0", "@typescript-eslint/parser": "8.17.0",
"@vitest/eslint-plugin": "^1.1.14",
"cross-spawn": "7.0.6", "cross-spawn": "7.0.6",
"depcheck": "^1.4.7", "depcheck": "^1.4.7",
"esbuild": "^0.18.17", "esbuild": "^0.18.17",
"esbuild-node-externals": "^1.14.0", "esbuild-node-externals": "^1.14.0",
"eslint": "^8.52.0", "eslint": "9.16.0",
"eslint-plugin-import": "^2.29.0", "eslint-plugin-jest": "28.9.0",
"eslint-plugin-jest": "^27.9.0", "eslint-plugin-local-rules": "3.0.2",
"eslint-plugin-local-rules": "^2.0.0", "eslint-plugin-svelte": "2.46.1",
"eslint-plugin-svelte": "^2.34.0",
"husky": "^8.0.3", "husky": "^8.0.3",
"kill-port": "^1.6.1", "kill-port": "^1.6.1",
"lerna": "7.4.2", "lerna": "7.4.2",
"load-tsconfig": "^0.2.5",
"madge": "^6.0.0", "madge": "^6.0.0",
"nx-cloud": "16.0.5", "nx-cloud": "16.0.5",
"prettier": "2.8.8", "prettier": "2.8.8",
"prettier-plugin-svelte": "^2.3.0", "prettier-plugin-svelte": "^2.3.0",
"proper-lockfile": "^4.1.2", "proper-lockfile": "^4.1.2",
"svelte": "4.2.19", "svelte": "4.2.19",
"svelte-eslint-parser": "^0.33.1", "svelte-eslint-parser": "0.43.0",
"typescript": "5.7.2", "typescript": "5.7.2",
"typescript-eslint": "^7.3.1", "typescript-eslint": "8.17.0",
"yargs": "^17.7.2" "yargs": "^17.7.2"
}, },
"scripts": { "scripts": {
@ -99,10 +100,10 @@
] ]
}, },
"resolutions": { "resolutions": {
"@budibase/backend-core": "0.0.0", "@budibase/backend-core": "*",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"@budibase/pro": "npm:@budibase/pro@latest", "@budibase/pro": "npm:@budibase/pro@latest",
"tough-cookie": "4.1.3", "tough-cookie": "4.1.3",
"node-fetch": "2.6.7", "node-fetch": "2.6.7",
@ -115,7 +116,8 @@
"passport": "0.6.0", "passport": "0.6.0",
"fast-xml-parser": "4.4.1", "fast-xml-parser": "4.4.1",
"@azure/identity": "4.2.1", "@azure/identity": "4.2.1",
"kind-of": "6.0.3" "kind-of": "6.0.3",
"globals": "15.13.0"
}, },
"engines": { "engines": {
"node": ">=20.0.0 <21.0.0" "node": ">=20.0.0 <21.0.0"

View File

@ -21,9 +21,10 @@
"scripts": { "scripts": {
"prebuild": "rimraf dist/", "prebuild": "rimraf dist/",
"prepack": "cp package.json dist", "prepack": "cp package.json dist",
"build": "tsc -p tsconfig.build.json --paths null && node ./scripts/build.js", "build": "node ./scripts/build.js && tsc -p tsconfig.build.json --emitDeclarationOnly --paths null",
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput", "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
"check:types": "tsc -p tsconfig.json --noEmit --paths null --target es2020", "build:oss": "node ./scripts/build.js",
"check:types": "tsc -p tsconfig.json --noEmit --paths null",
"check:dependencies": "node ../../scripts/depcheck.js", "check:dependencies": "node ../../scripts/depcheck.js",
"test": "bash scripts/test.sh", "test": "bash scripts/test.sh",
"test:watch": "jest --watchAll" "test:watch": "jest --watchAll"
@ -31,8 +32,8 @@
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.5", "@budibase/nano": "10.1.5",
"@budibase/pouchdb-replication-stream": "1.2.11", "@budibase/pouchdb-replication-stream": "1.2.11",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"@techpass/passport-openidconnect": "0.3.3", "@techpass/passport-openidconnect": "0.3.3",
"aws-cloudfront-sign": "3.0.2", "aws-cloudfront-sign": "3.0.2",
"aws-sdk": "2.1692.0", "aws-sdk": "2.1692.0",
@ -76,7 +77,6 @@
"@types/cookies": "0.7.8", "@types/cookies": "0.7.8",
"@types/jest": "29.5.5", "@types/jest": "29.5.5",
"@types/lodash": "4.14.200", "@types/lodash": "4.14.200",
"@types/node": "^22.9.0",
"@types/node-fetch": "2.6.4", "@types/node-fetch": "2.6.4",
"@types/pouchdb": "6.4.2", "@types/pouchdb": "6.4.2",
"@types/redlock": "4.0.7", "@types/redlock": "4.0.7",

View File

@ -289,7 +289,7 @@ export class DatabaseImpl implements Database {
return return
} }
let errorFound = false let errorFound = false
let errorMessage: string = "Unable to bulk remove documents: " let errorMessage = "Unable to bulk remove documents: "
for (let res of response) { for (let res of response) {
if (res.error) { if (res.error) {
errorFound = true errorFound = true

View File

@ -4,7 +4,7 @@ import { checkSlashesInUrl } from "../../helpers"
export async function directCouchCall( export async function directCouchCall(
path: string, path: string,
method: string = "GET", method = "GET",
body?: any body?: any
) { ) {
let { url, cookie } = getCouchInfo() let { url, cookie } = getCouchInfo()
@ -43,7 +43,7 @@ export async function directCouchUrlCall({
export async function directCouchQuery( export async function directCouchQuery(
path: string, path: string,
method: string = "GET", method = "GET",
body?: any body?: any
) { ) {
const response = await directCouchCall(path, method, body) const response = await directCouchCall(path, method, body)

View File

@ -279,7 +279,7 @@ export class QueryBuilder<T> {
let query = allOr ? "" : "*:*" let query = allOr ? "" : "*:*"
let allFiltersEmpty = true let allFiltersEmpty = true
const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true } const allPreProcessingOpts = { escape: true, lowercase: true, wrap: true }
let tableId: string = "" let tableId = ""
if (this.#query.equal!.tableId) { if (this.#query.equal!.tableId) {
tableId = this.#query.equal!.tableId tableId = this.#query.equal!.tableId
delete this.#query.equal!.tableId delete this.#query.equal!.tableId

View File

@ -134,10 +134,10 @@ const environment = {
BUDIBASE_ENVIRONMENT: process.env.BUDIBASE_ENVIRONMENT, BUDIBASE_ENVIRONMENT: process.env.BUDIBASE_ENVIRONMENT,
JS_BCRYPT: process.env.JS_BCRYPT, JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET JWT_SECRET: process.env.JWT_SECRET
? createSecretKey(Buffer.from(process.env.JWT_SECRET)) ? createSecretKey(process.env.JWT_SECRET, "utf8")
: undefined, : undefined,
JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK
? createSecretKey(Buffer.from(process.env.JWT_SECRET_FALLBACK)) ? createSecretKey(process.env.JWT_SECRET_FALLBACK, "utf8")
: undefined, : undefined,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY, ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
API_ENCRYPTION_KEY: getAPIEncryptionKey(), API_ENCRYPTION_KEY: getAPIEncryptionKey(),

View File

@ -2,7 +2,7 @@ import { Event, Identity, Group } from "@budibase/types"
import { EventProcessor } from "./types" import { EventProcessor } from "./types"
export default class Processor implements EventProcessor { export default class Processor implements EventProcessor {
initialised: boolean = false initialised = false
processors: EventProcessor[] = [] processors: EventProcessor[] = []
constructor(processors: EventProcessor[]) { constructor(processors: EventProcessor[]) {

View File

@ -13,9 +13,7 @@ const EXCLUDED_EVENTS: Event[] = [
Event.ROLE_UPDATED, Event.ROLE_UPDATED,
Event.DATASOURCE_UPDATED, Event.DATASOURCE_UPDATED,
Event.QUERY_UPDATED, Event.QUERY_UPDATED,
Event.TABLE_UPDATED,
Event.VIEW_UPDATED, Event.VIEW_UPDATED,
Event.VIEW_FILTER_UPDATED,
Event.VIEW_CALCULATION_UPDATED, Event.VIEW_CALCULATION_UPDATED,
Event.AUTOMATION_TRIGGER_UPDATED, Event.AUTOMATION_TRIGGER_UPDATED,
Event.USER_GROUP_UPDATED, Event.USER_GROUP_UPDATED,

View File

@ -1 +1 @@
export { EventProcessor } from "@budibase/types" export type { EventProcessor } from "@budibase/types"

View File

@ -23,3 +23,4 @@ export { default as plugin } from "./plugin"
export { default as backup } from "./backup" export { default as backup } from "./backup"
export { default as environmentVariable } from "./environmentVariable" export { default as environmentVariable } from "./environmentVariable"
export { default as auditLog } from "./auditLog" export { default as auditLog } from "./auditLog"
export { default as rowAction } from "./rowAction"

View File

@ -12,8 +12,6 @@ import {
QueriesRunEvent, QueriesRunEvent,
} from "@budibase/types" } from "@budibase/types"
/* eslint-disable */
const created = async ( const created = async (
datasource: Datasource, datasource: Datasource,
query: Query, query: Query,

View File

@ -0,0 +1,13 @@
import { publishEvent } from "../events"
import { Event, RowActionCreatedEvent } from "@budibase/types"
async function created(
rowAction: RowActionCreatedEvent,
timestamp?: string | number
) {
await publishEvent(Event.ROW_ACTION_CREATED, rowAction, timestamp)
}
export default {
created,
}

View File

@ -6,8 +6,6 @@ import {
Table, Table,
} from "@budibase/types" } from "@budibase/types"
/* eslint-disable */
const created = async (count: number, timestamp?: string | number) => { const created = async (count: number, timestamp?: string | number) => {
const properties: RowsCreatedEvent = { const properties: RowsCreatedEvent = {
count, count,

View File

@ -1,13 +1,14 @@
import { publishEvent } from "../events" import { publishEvent } from "../events"
import { import {
Event, Event,
TableExportFormat, FieldType,
Table, Table,
TableCreatedEvent, TableCreatedEvent,
TableUpdatedEvent,
TableDeletedEvent, TableDeletedEvent,
TableExportedEvent, TableExportedEvent,
TableExportFormat,
TableImportedEvent, TableImportedEvent,
TableUpdatedEvent,
} from "@budibase/types" } from "@budibase/types"
async function created(table: Table, timestamp?: string | number) { async function created(table: Table, timestamp?: string | number) {
@ -20,15 +21,35 @@ async function created(table: Table, timestamp?: string | number) {
await publishEvent(Event.TABLE_CREATED, properties, timestamp) await publishEvent(Event.TABLE_CREATED, properties, timestamp)
} }
async function updated(table: Table) { async function updated(oldTable: Table, newTable: Table) {
// only publish the event if it has fields we are interested in
let defaultValues, aiColumn
// check that new fields have been added
for (const key in newTable.schema) {
if (!oldTable.schema[key]) {
const newColumn = newTable.schema[key]
if ("default" in newColumn && newColumn.default != null) {
defaultValues = true
}
if (newColumn.type === FieldType.AI) {
aiColumn = newColumn.operation
}
}
}
const properties: TableUpdatedEvent = { const properties: TableUpdatedEvent = {
tableId: table._id as string, tableId: newTable._id as string,
defaultValues,
aiColumn,
audited: { audited: {
name: table.name, name: newTable.name,
}, },
} }
if (defaultValues || aiColumn) {
await publishEvent(Event.TABLE_UPDATED, properties) await publishEvent(Event.TABLE_UPDATED, properties)
} }
}
async function deleted(table: Table) { async function deleted(table: Table) {
const properties: TableDeletedEvent = { const properties: TableDeletedEvent = {

View File

@ -1,6 +1,11 @@
import { publishEvent } from "../events" import { publishEvent } from "../events"
import { import {
CalculationType,
Event, Event,
Table,
TableExportFormat,
View,
ViewCalculation,
ViewCalculationCreatedEvent, ViewCalculationCreatedEvent,
ViewCalculationDeletedEvent, ViewCalculationDeletedEvent,
ViewCalculationUpdatedEvent, ViewCalculationUpdatedEvent,
@ -11,22 +16,20 @@ import {
ViewFilterDeletedEvent, ViewFilterDeletedEvent,
ViewFilterUpdatedEvent, ViewFilterUpdatedEvent,
ViewUpdatedEvent, ViewUpdatedEvent,
View, ViewV2,
ViewCalculation, ViewJoinCreatedEvent,
Table,
TableExportFormat,
} from "@budibase/types" } from "@budibase/types"
/* eslint-disable */ async function created(view: ViewV2, timestamp?: string | number) {
async function created(view: View, timestamp?: string | number) {
const properties: ViewCreatedEvent = { const properties: ViewCreatedEvent = {
name: view.name,
type: view.type,
tableId: view.tableId, tableId: view.tableId,
} }
await publishEvent(Event.VIEW_CREATED, properties, timestamp) await publishEvent(Event.VIEW_CREATED, properties, timestamp)
} }
async function updated(view: View) { async function updated(view: ViewV2) {
const properties: ViewUpdatedEvent = { const properties: ViewUpdatedEvent = {
tableId: view.tableId, tableId: view.tableId,
} }
@ -48,16 +51,27 @@ async function exported(table: Table, format: TableExportFormat) {
await publishEvent(Event.VIEW_EXPORTED, properties) await publishEvent(Event.VIEW_EXPORTED, properties)
} }
async function filterCreated(view: View, timestamp?: string | number) { async function filterCreated(
{ tableId, filterGroups }: { tableId: string; filterGroups: number },
timestamp?: string | number
) {
const properties: ViewFilterCreatedEvent = { const properties: ViewFilterCreatedEvent = {
tableId: view.tableId, tableId,
filterGroups,
} }
await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp) await publishEvent(Event.VIEW_FILTER_CREATED, properties, timestamp)
} }
async function filterUpdated(view: View) { async function filterUpdated({
tableId,
filterGroups,
}: {
tableId: string
filterGroups: number
}) {
const properties: ViewFilterUpdatedEvent = { const properties: ViewFilterUpdatedEvent = {
tableId: view.tableId, tableId: tableId,
filterGroups,
} }
await publishEvent(Event.VIEW_FILTER_UPDATED, properties) await publishEvent(Event.VIEW_FILTER_UPDATED, properties)
} }
@ -69,10 +83,16 @@ async function filterDeleted(view: View) {
await publishEvent(Event.VIEW_FILTER_DELETED, properties) await publishEvent(Event.VIEW_FILTER_DELETED, properties)
} }
async function calculationCreated(view: View, timestamp?: string | number) { async function calculationCreated(
{
tableId,
calculationType,
}: { tableId: string; calculationType: CalculationType },
timestamp?: string | number
) {
const properties: ViewCalculationCreatedEvent = { const properties: ViewCalculationCreatedEvent = {
tableId: view.tableId, tableId,
calculation: view.calculation as ViewCalculation, calculation: calculationType,
} }
await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp) await publishEvent(Event.VIEW_CALCULATION_CREATED, properties, timestamp)
} }
@ -93,6 +113,13 @@ async function calculationDeleted(existingView: View) {
await publishEvent(Event.VIEW_CALCULATION_DELETED, properties) await publishEvent(Event.VIEW_CALCULATION_DELETED, properties)
} }
async function viewJoinCreated(tableId: any, timestamp?: string | number) {
const properties: ViewJoinCreatedEvent = {
tableId,
}
await publishEvent(Event.VIEW_JOIN_CREATED, properties, timestamp)
}
export default { export default {
created, created,
updated, updated,
@ -104,4 +131,5 @@ export default {
calculationCreated, calculationCreated,
calculationUpdated, calculationUpdated,
calculationDeleted, calculationDeleted,
viewJoinCreated,
} }

View File

@ -137,9 +137,9 @@ export default function (
} }
const tenantId = getHeader(ctx, Header.TENANT_ID) const tenantId = getHeader(ctx, Header.TENANT_ID)
let authenticated: boolean = false, let authenticated = false,
user: User | { tenantId: string } | undefined = undefined, user: User | { tenantId: string } | undefined = undefined,
internal: boolean = false, internal = false,
loginMethod: LoginMethod | undefined = undefined loginMethod: LoginMethod | undefined = undefined
if (authCookie && !apiKey) { if (authCookie && !apiKey) {
const sessionId = authCookie.sessionId const sessionId = authCookie.sessionId

View File

@ -20,7 +20,7 @@ export const ssoSaveUserNoOp: SaveSSOUserFunction = (user: SSOUser) =>
*/ */
export async function authenticate( export async function authenticate(
details: SSOAuthDetails, details: SSOAuthDetails,
requireLocalAccount: boolean = true, requireLocalAccount = true,
done: any, done: any,
saveUserFn: SaveSSOUserFunction saveUserFn: SaveSSOUserFunction
) { ) {

View File

@ -35,7 +35,7 @@ export const backPopulateMigrations = async (opts: MigrationNoOpOptions) => {
// filter migrations to the type and populate a no-op migration // filter migrations to the type and populate a no-op migration
const migrations: Migration[] = DEFINITIONS.filter( const migrations: Migration[] = DEFINITIONS.filter(
def => def.type === opts.type def => def.type === opts.type
).map(d => ({ ...d, fn: () => {} })) ).map(d => ({ ...d, fn: async () => {} }))
await runMigrations(migrations, { noOp: opts }) await runMigrations(migrations, { noOp: opts })
} }

View File

@ -334,7 +334,7 @@ export async function listAllObjects(bucketName: string, path: string) {
export function getPresignedUrl( export function getPresignedUrl(
bucketName: string, bucketName: string,
key: string, key: string,
durationSeconds: number = 3600 durationSeconds = 3600
) { ) {
const objectStore = ObjectStore(bucketName, { presigning: true }) const objectStore = ObjectStore(bucketName, { presigning: true })
const params = { const params = {

View File

@ -7,7 +7,7 @@ import { addListeners, StalledFn } from "./listeners"
import { Duration } from "../utils" import { Duration } from "../utils"
import * as timers from "../timers" import * as timers from "../timers"
export { QueueOptions, Queue, JobOptions } from "bull" export type { QueueOptions, Queue, JobOptions } from "bull"
// the queue lock is held for 5 minutes // the queue lock is held for 5 minutes
const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs() const QUEUE_LOCK_MS = Duration.fromMinutes(5).toMs()

View File

@ -92,7 +92,7 @@ function getLockName(opts: LockOptions) {
// determine lock name // determine lock name
// by default use the tenantId for uniqueness, unless using a system lock // by default use the tenantId for uniqueness, unless using a system lock
const prefix = opts.systemLock ? "system" : context.getTenantId() const prefix = opts.systemLock ? "system" : context.getTenantId()
let name: string = `lock:${prefix}_${opts.name}` let name = `lock:${prefix}_${opts.name}`
// add additional unique name if required // add additional unique name if required
if (opts.resource) { if (opts.resource) {
name = name + `_${opts.resource}` name = name + `_${opts.resource}`

View File

@ -11,15 +11,15 @@ const { getCreatorCount } = require("../../../src/users/users")
describe("Users", () => { describe("Users", () => {
let getGlobalDBMock let getGlobalDBMock
let getGlobalUserParamsMock
let paginationMock let paginationMock
beforeEach(() => { beforeEach(() => {
jest.resetAllMocks() jest.resetAllMocks()
getGlobalDBMock = jest.spyOn(context, "getGlobalDB") getGlobalDBMock = jest.spyOn(context, "getGlobalDB")
getGlobalUserParamsMock = jest.spyOn(db, "getGlobalUserParams")
paginationMock = jest.spyOn(db, "pagination") paginationMock = jest.spyOn(db, "pagination")
jest.spyOn(db, "getGlobalUserParams")
}) })
it("Retrieves the number of creators", async () => { it("Retrieves the number of creators", async () => {

View File

@ -117,6 +117,7 @@ beforeAll(async () => {
jest.spyOn(events.view, "calculationCreated") jest.spyOn(events.view, "calculationCreated")
jest.spyOn(events.view, "calculationUpdated") jest.spyOn(events.view, "calculationUpdated")
jest.spyOn(events.view, "calculationDeleted") jest.spyOn(events.view, "calculationDeleted")
jest.spyOn(events.view, "viewJoinCreated")
jest.spyOn(events.plugin, "init") jest.spyOn(events.plugin, "init")
jest.spyOn(events.plugin, "imported") jest.spyOn(events.plugin, "imported")

View File

@ -1,6 +1,6 @@
import { MonthlyQuotaName, QuotaUsage } from "@budibase/types" import { MonthlyQuotaName, QuotaUsage } from "@budibase/types"
export const usage = (users: number = 0, creators: number = 0): QuotaUsage => { export const usage = (users = 0, creators = 0): QuotaUsage => {
return { return {
_id: "usage_quota", _id: "usage_quota",
quotaReset: new Date().toISOString(), quotaReset: new Date().toISOString(),

View File

@ -1,24 +1,8 @@
{ {
"extends": "../../tsconfig.build.json",
"compilerOptions": { "compilerOptions": {
"target": "es6", "outDir": "dist"
"module": "commonjs",
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"sourceMap": true,
"declaration": true,
"types": ["node", "jest"],
"outDir": "dist",
"skipLibCheck": true,
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/shared-core": ["../shared-core/src"]
}
}, },
"include": ["**/*.js", "**/*.ts"],
"exclude": [ "exclude": [
"node_modules", "node_modules",
"dist", "dist",

View File

@ -29,8 +29,8 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1", "@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@spectrum-css/accordion": "3.0.24", "@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1", "@spectrum-css/actiongroup": "1.0.1",

View File

@ -85,7 +85,7 @@
} }
const getPos = e => { const getPos = e => {
var rect = canvasRef.getBoundingClientRect() let rect = canvasRef.getBoundingClientRect()
const canvasX = e.offsetX || e.targetTouches?.[0].pageX - rect.left const canvasX = e.offsetX || e.targetTouches?.[0].pageX - rect.left
const canvasY = e.offsetY || e.targetTouches?.[0].pageY - rect.top const canvasY = e.offsetY || e.targetTouches?.[0].pageY - rect.top

View File

@ -48,11 +48,11 @@
] ]
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "0.0.0", "@budibase/bbui": "*",
"@budibase/frontend-core": "0.0.0", "@budibase/frontend-core": "*",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"@codemirror/autocomplete": "^6.7.1", "@codemirror/autocomplete": "^6.7.1",
"@codemirror/commands": "^6.2.4", "@codemirror/commands": "^6.2.4",
"@codemirror/lang-javascript": "^6.1.8", "@codemirror/lang-javascript": "^6.1.8",

View File

@ -46,7 +46,7 @@
} }
} else { } else {
// Leave the core data as it is // Leave the core data as it is
return testData return cloneDeep(testData)
} }
} }
@ -63,7 +63,10 @@
return true return true
} }
$: testData = testData || parseTestData($selectedAutomation.data.testData) $: currentTestData = $selectedAutomation.data.testData
// Can be updated locally to avoid race condition when testing
$: testData = parseTestData(currentTestData)
$: { $: {
// clone the trigger so we're not mutating the reference // clone the trigger so we're not mutating the reference
@ -85,7 +88,7 @@
required => testData?.[required] || required !== "row" required => testData?.[required] || required !== "row"
) )
function parseTestJSON(e) { async function parseTestJSON(e) {
let jsonUpdate let jsonUpdate
try { try {
@ -105,7 +108,9 @@
} }
} }
const updatedAuto =
automationStore.actions.addTestDataToAutomation(jsonUpdate) automationStore.actions.addTestDataToAutomation(jsonUpdate)
await automationStore.actions.save(updatedAuto)
} }
const testAutomation = async () => { const testAutomation = async () => {
@ -150,10 +155,14 @@
{#if selectedValues} {#if selectedValues}
<div class="tab-content-padding"> <div class="tab-content-padding">
<AutomationBlockSetup <AutomationBlockSetup
bind:testData
{schemaProperties} {schemaProperties}
isTestModal isTestModal
{testData}
block={trigger} block={trigger}
on:update={e => {
const { testData: updatedTestData } = e.detail
testData = updatedTestData
}}
/> />
</div> </div>
{/if} {/if}
@ -162,7 +171,7 @@
<TextArea <TextArea
value={JSON.stringify($selectedAutomation.data.testData, null, 2)} value={JSON.stringify($selectedAutomation.data.testData, null, 2)}
error={failedParse} error={failedParse}
on:change={e => parseTestJSON(e)} on:change={async e => await parseTestJSON(e)}
/> />
</div> </div>
{/if} {/if}

View File

@ -48,7 +48,7 @@
import { QueryUtils, Utils, search, memo } from "@budibase/frontend-core" import { QueryUtils, Utils, search, memo } from "@budibase/frontend-core"
import { getSchemaForDatasourcePlus } from "dataBinding" import { getSchemaForDatasourcePlus } from "dataBinding"
import { TriggerStepID, ActionStepID } from "constants/backend/automations" import { TriggerStepID, ActionStepID } from "constants/backend/automations"
import { onMount } from "svelte" import { onMount, createEventDispatcher } from "svelte"
import { writable } from "svelte/store" import { writable } from "svelte/store"
import { cloneDeep } from "lodash/fp" import { cloneDeep } from "lodash/fp"
import { import {
@ -67,6 +67,8 @@
export let isTestModal = false export let isTestModal = false
export let bindings = [] export let bindings = []
const dispatch = createEventDispatcher()
// Stop unnecessary rendering // Stop unnecessary rendering
const memoBlock = memo(block) const memoBlock = memo(block)
@ -503,15 +505,7 @@
row: { "Active": true, "Order Id" : 14, ... } row: { "Active": true, "Order Id" : 14, ... }
}) })
*/ */
const onChange = async update => { const onChange = Utils.sequential(async update => {
if (isTestModal) {
testData = update
}
updateAutomation(update)
}
const updateAutomation = Utils.sequential(async update => {
const request = cloneDeep(update) const request = cloneDeep(update)
// Process app trigger updates // Process app trigger updates
if (isTrigger && !isTestModal) { if (isTrigger && !isTestModal) {
@ -540,7 +534,9 @@
} }
try { try {
if (isTestModal) { if (isTestModal) {
let newTestData = { schema } // Be sure to merge in the testData prop data, as it can contain custom
// default data
let newTestData = { schema, ...testData }
// Special case for webhook, as it requires a body, but the schema already brings back the body's contents // Special case for webhook, as it requires a body, but the schema already brings back the body's contents
if (stepId === TriggerStepID.WEBHOOK) { if (stepId === TriggerStepID.WEBHOOK) {
@ -557,7 +553,13 @@
...request, ...request,
} }
await automationStore.actions.addTestDataToAutomation(newTestData) const updatedAuto =
automationStore.actions.addTestDataToAutomation(newTestData)
// Ensure the test request has the latest info.
dispatch("update", updatedAuto)
await automationStore.actions.save(updatedAuto)
} else { } else {
const data = { schema, ...request } const data = { schema, ...request }
await automationStore.actions.updateBlockInputs(block, data) await automationStore.actions.updateBlockInputs(block, data)

View File

@ -24,9 +24,9 @@
const j = 32 // Outer radius strength (higher is stronger) const j = 32 // Outer radius strength (higher is stronger)
// Calculate unit vector // Calculate unit vector
var dx = x1 - x2 let dx = x1 - x2
var dy = y1 - y2 let dy = y1 - y2
var len = Math.sqrt(dx * dx + dy * dy) let len = Math.sqrt(dx * dx + dy * dy)
dx = dx / len dx = dx / len
dy = dy / len dy = dy / len

View File

@ -12,7 +12,6 @@ vi.mock("stores/selectors", () => ({
describe("datasource creation store", () => { describe("datasource creation store", () => {
beforeEach(ctx => { beforeEach(ctx => {
vi.clearAllMocks() vi.clearAllMocks()
// eslint-disable-next-line no-import-assign
ctx.store = createDatasourceCreationStore() ctx.store = createDatasourceCreationStore()
ctx.integration = { data: "integration" } ctx.integration = { data: "integration" }

View File

@ -871,13 +871,13 @@ const automationActions = store => ({
clearLogErrors: async ({ automationId, appId } = {}) => { clearLogErrors: async ({ automationId, appId } = {}) => {
return await API.clearAutomationLogErrors(automationId, appId) return await API.clearAutomationLogErrors(automationId, appId)
}, },
addTestDataToAutomation: async data => { addTestDataToAutomation: data => {
let newAutomation = cloneDeep(get(selectedAutomation).data) let newAutomation = cloneDeep(get(selectedAutomation).data)
newAutomation.testData = { newAutomation.testData = {
...newAutomation.testData, ...newAutomation.testData,
...data, ...data,
} }
await store.actions.save(newAutomation) return newAutomation
}, },
constructBlock(type, stepId, blockDefinition) { constructBlock(type, stepId, blockDefinition) {
let newName let newName

View File

@ -1,16 +1,7 @@
{ {
"extends": "../../tsconfig.build.json",
"compilerOptions": { "compilerOptions": {
"target": "es6", "allowJs": true
"module": "commonjs",
"lib": ["es2019"],
"allowJs": true,
"outDir": "dist",
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"skipLibCheck": true
}, },
"include": ["./src/**/*"], "include": ["./src/**/*"],
"exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"] "exclude": ["node_modules", "**/*.json", "**/*.spec.ts", "**/*.spec.js"]

View File

@ -11,13 +11,13 @@
"scripts": { "scripts": {
"tsc": "node ../../scripts/build.js", "tsc": "node ../../scripts/build.js",
"build": "yarn tsc", "build": "yarn tsc",
"check:types": "tsc -p tsconfig.json --noEmit --paths null --target es2020", "check:types": "tsc -p tsconfig.json --noEmit --paths null",
"start": "ts-node ./src/index.ts" "start": "ts-node ./src/index.ts"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "0.0.0", "@budibase/backend-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",
"commander": "7.1.0", "commander": "7.1.0",

View File

@ -1,25 +1,5 @@
{ {
"compilerOptions": { "extends": "../../tsconfig.build.json",
"target": "es6",
"module": "commonjs",
"lib": ["es2020"],
"strict": true,
"noImplicitAny": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"incremental": true,
"types": ["node", "jest"],
"outDir": "dist",
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@budibase/types": ["../types/src"],
"@budibase/backend-core": ["../backend-core/src"],
"@budibase/backend-core/*": ["../backend-core/*"],
"@budibase/shared-core": ["../shared-core/src"],
"@budibase/string-templates": ["../string-templates/src"]
}
},
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"] "exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.spec.js"]
} }

View File

@ -19,11 +19,11 @@
"dev": "vite build --watch" "dev": "vite build --watch"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "0.0.0", "@budibase/bbui": "*",
"@budibase/frontend-core": "0.0.0", "@budibase/frontend-core": "*",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"@spectrum-css/card": "3.0.3", "@spectrum-css/card": "3.0.3",
"apexcharts": "^3.48.0", "apexcharts": "^3.48.0",
"dayjs": "^1.10.8", "dayjs": "^1.10.8",
@ -61,9 +61,7 @@
"dependsOn": [ "dependsOn": [
{ {
"projects": [ "projects": [
"@budibase/shared-core", "@budibase/string-templates"
"@budibase/string-templates",
"@budibase/types"
], ],
"target": "build" "target": "build"
} }

View File

@ -30,7 +30,7 @@ const FullScreenControl = L.Control.extend({
fullScreenTitle: "Enter Fullscreen", fullScreenTitle: "Enter Fullscreen",
}, },
onAdd: function () { onAdd: function () {
var fullScreenClassName = "leaflet-control-fullscreen", let fullScreenClassName = "leaflet-control-fullscreen",
container = L.DomUtil.create("div", fullScreenClassName + " leaflet-bar"), container = L.DomUtil.create("div", fullScreenClassName + " leaflet-bar"),
options = this.options options = this.options
@ -45,7 +45,7 @@ const FullScreenControl = L.Control.extend({
return container return container
}, },
_fullScreen: function () { _fullScreen: function () {
var map = this._map let map = this._map
if (screenfull.isEnabled) { if (screenfull.isEnabled) {
screenfull.toggle(map.getContainer()) screenfull.toggle(map.getContainer())
} }
@ -80,7 +80,7 @@ const LocationControl = L.Control.extend({
locationTitle: "Show Your Location", locationTitle: "Show Your Location",
}, },
onAdd: function () { onAdd: function () {
var locationClassName = "leaflet-control-location", let locationClassName = "leaflet-control-location",
container = L.DomUtil.create("div", locationClassName + " leaflet-bar"), container = L.DomUtil.create("div", locationClassName + " leaflet-bar"),
options = this.options options = this.options
@ -136,7 +136,7 @@ const LocationControl = L.Control.extend({
}) })
}, },
_getPosition: function () { _getPosition: function () {
var options = { let options = {
enableHighAccuracy: false, enableHighAccuracy: false,
timeout: 5000, timeout: 5000,
maximumAge: 30000, maximumAge: 30000,

View File

@ -1,3 +1,4 @@
/* eslint-disable no-useless-concat */
import { derived } from "svelte/store" import { derived } from "svelte/store"
import { appStore } from "./app" import { appStore } from "./app"
import { builderStore } from "./builder" import { builderStore } from "./builder"

View File

@ -6,9 +6,9 @@
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "./src/index.ts", "svelte": "./src/index.ts",
"dependencies": { "dependencies": {
"@budibase/bbui": "0.0.0", "@budibase/bbui": "*",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"dayjs": "^1.10.8", "dayjs": "^1.10.8",
"lodash": "4.17.21", "lodash": "4.17.21",
"shortid": "2.2.15", "shortid": "2.2.15",

View File

@ -7,7 +7,6 @@
async function checkMigrationsFinished() { async function checkMigrationsFinished() {
let totalWaitMs = 0 let totalWaitMs = 0
// eslint-disable-next-line no-constant-condition
while (true) { while (true) {
const waitForMs = 5000 + Math.random() * 5000 const waitForMs = 5000 + Math.random() * 5000
await new Promise(resolve => setTimeout(resolve, waitForMs)) await new Promise(resolve => setTimeout(resolve, waitForMs))

@ -1 +1 @@
Subproject commit 5321c7589257711cf153600597ef4e6a5f6b7162 Subproject commit 977baca179fef1192f8fe051122288a4128f7a63

View File

@ -12,7 +12,7 @@
"prebuild": "rimraf dist/", "prebuild": "rimraf dist/",
"build": "node ./scripts/build.js", "build": "node ./scripts/build.js",
"postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && copyfiles -f ../../yarn.lock ./dist/", "postbuild": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client && copyfiles -f ../../yarn.lock ./dist/",
"check:types": "tsc -p tsconfig.json --noEmit --paths null --target es2020", "check:types": "tsc -p tsconfig.json --noEmit --paths null",
"check:dependencies": "node ../../scripts/depcheck.js", "check:dependencies": "node ../../scripts/depcheck.js",
"build:isolated-vm-lib:snippets": "esbuild --minify --bundle src/jsRunner/bundles/snippets.ts --outfile=src/jsRunner/bundles/snippets.ivm.bundle.js --platform=node --format=iife --global-name=snippets", "build:isolated-vm-lib:snippets": "esbuild --minify --bundle src/jsRunner/bundles/snippets.ts --outfile=src/jsRunner/bundles/snippets.ivm.bundle.js --platform=node --format=iife --global-name=snippets",
"build:isolated-vm-lib:string-templates": "esbuild --minify --bundle src/jsRunner/bundles/index-helpers.ts --outfile=src/jsRunner/bundles/index-helpers.ivm.bundle.js --platform=node --format=iife --external:handlebars --global-name=helpers", "build:isolated-vm-lib:string-templates": "esbuild --minify --bundle src/jsRunner/bundles/index-helpers.ts --outfile=src/jsRunner/bundles/index-helpers.ivm.bundle.js --platform=node --format=iife --external:handlebars --global-name=helpers",
@ -51,14 +51,14 @@
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@azure/msal-node": "^2.5.1", "@azure/msal-node": "^2.5.1",
"@budibase/backend-core": "0.0.0", "@budibase/backend-core": "*",
"@budibase/client": "0.0.0", "@budibase/client": "*",
"@budibase/frontend-core": "0.0.0", "@budibase/frontend-core": "*",
"@budibase/nano": "10.1.5", "@budibase/nano": "10.1.5",
"@budibase/pro": "0.0.0", "@budibase/pro": "*",
"@budibase/shared-core": "0.0.0", "@budibase/shared-core": "*",
"@budibase/string-templates": "0.0.0", "@budibase/string-templates": "*",
"@budibase/types": "0.0.0", "@budibase/types": "*",
"@bull-board/api": "5.10.2", "@bull-board/api": "5.10.2",
"@bull-board/koa": "5.10.2", "@bull-board/koa": "5.10.2",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
@ -147,7 +147,6 @@
"@types/koa__router": "12.0.4", "@types/koa__router": "12.0.4",
"@types/lodash": "4.14.200", "@types/lodash": "4.14.200",
"@types/mssql": "9.1.5", "@types/mssql": "9.1.5",
"@types/node": "^22.9.0",
"@types/node-fetch": "2.6.4", "@types/node-fetch": "2.6.4",
"@types/oracledb": "6.5.1", "@types/oracledb": "6.5.1",
"@types/pg": "8.6.6", "@types/pg": "8.6.6",
@ -188,7 +187,7 @@
"projects": [ "projects": [
"@budibase/backend-core" "@budibase/backend-core"
], ],
"target": "build" "target": "build:oss"
} }
] ]
}, },

View File

@ -22,7 +22,7 @@ import {
async function redirect( async function redirect(
ctx: any, ctx: any,
method: "GET" | "POST" | "DELETE", method: "GET" | "POST" | "DELETE",
path: string = "global" path = "global"
) { ) {
const { devPath } = ctx.params const { devPath } = ctx.params
const queryString = ctx.originalUrl.split("?")[1] || "" const queryString = ctx.originalUrl.split("?")[1] || ""

View File

@ -111,8 +111,8 @@ describe("Rest Importer", () => {
const importResult = await restImporter.importQueries(datasource._id) const importResult = await restImporter.importQueries(datasource._id)
expect(importResult.errorQueries.length).toBe(0) expect(importResult.errorQueries.length).toBe(0)
expect(importResult.queries.length).toBe(assertions[key].count) expect(importResult.queries.length).toBe(assertions[key].count)
expect(events.query.imported).toBeCalledTimes(1) expect(events.query.imported).toHaveBeenCalledTimes(1)
expect(events.query.imported).toBeCalledWith( expect(events.query.imported).toHaveBeenCalledWith(
datasource, datasource,
assertions[key].source, assertions[key].source,
assertions[key].count assertions[key].count

View File

@ -378,7 +378,7 @@ export class ExternalRequest<T extends Operation> {
} }
// many to one // many to one
else { else {
const thisKey: string = "id" const thisKey = "id"
// @ts-ignore // @ts-ignore
const otherKey: string = field.fieldName const otherKey: string = field.fieldName
for (const relationship of row[key]) { for (const relationship of row[key]) {

View File

@ -45,7 +45,7 @@ export function getInternalRowId(row: Row, table: Table): string {
export function generateIdForRow( export function generateIdForRow(
row: Row | undefined, row: Row | undefined,
table: Table, table: Table,
isLinked: boolean = false isLinked = false
): string { ): string {
const primary = table.primary const primary = table.primary
if (!row || !primary) { if (!row || !primary) {

View File

@ -6,6 +6,7 @@ import {
RowActionResponse, RowActionResponse,
RowActionsResponse, RowActionsResponse,
} from "@budibase/types" } from "@budibase/types"
import { events } from "@budibase/backend-core"
import sdk from "../../../sdk" import sdk from "../../../sdk"
async function getTable(ctx: Ctx) { async function getTable(ctx: Ctx) {
@ -59,6 +60,8 @@ export async function create(
name: ctx.request.body.name, name: ctx.request.body.name,
}) })
await events.rowAction.created(createdAction)
ctx.body = { ctx.body = {
tableId, tableId,
id: createdAction.id, id: createdAction.id,

View File

@ -45,13 +45,13 @@ export async function updateTable(
inputs.created = true inputs.created = true
} }
try { try {
const { datasource, table } = await sdk.tables.external.save( const { datasource, oldTable, table } = await sdk.tables.external.save(
datasourceId!, datasourceId!,
inputs, inputs,
{ tableId, renaming } { tableId, renaming }
) )
builderSocket?.emitDatasourceUpdate(ctx, datasource) builderSocket?.emitDatasourceUpdate(ctx, datasource)
return table return { table, oldTable }
} catch (err: any) { } catch (err: any) {
if (err instanceof Error) { if (err instanceof Error) {
ctx.throw(400, err.message) ctx.throw(400, err.message)

View File

@ -120,8 +120,15 @@ export async function save(ctx: UserCtx<SaveTableRequest, SaveTableResponse>) {
await events.table.created(savedTable) await events.table.created(savedTable)
} else { } else {
const api = pickApi({ table }) const api = pickApi({ table })
savedTable = await api.updateTable(ctx, renaming) const { table: updatedTable, oldTable } = await api.updateTable(
await events.table.updated(savedTable) ctx,
renaming
)
savedTable = updatedTable
if (oldTable) {
await events.table.updated(oldTable, savedTable)
}
} }
if (renaming) { if (renaming) {
await sdk.views.renameLinkedViews(savedTable, renaming) await sdk.views.renameLinkedViews(savedTable, renaming)

View File

@ -30,14 +30,14 @@ export async function updateTable(
} }
try { try {
const { table } = await sdk.tables.internal.save(tableToSave, { const { table, oldTable } = await sdk.tables.internal.save(tableToSave, {
userId: ctx.user._id, userId: ctx.user._id,
rowsToImport: rows, rowsToImport: rows,
tableId: ctx.request.body._id, tableId: ctx.request.body._id,
renaming, renaming,
}) })
return table return { table, oldTable }
} catch (err: any) { } catch (err: any) {
if (err instanceof Error) { if (err instanceof Error) {
ctx.throw(400, err.message) ctx.throw(400, err.message)

View File

@ -16,7 +16,7 @@ function escapeCsvString(str: string) {
export function csv( export function csv(
headers: string[], headers: string[],
rows: Row[], rows: Row[],
delimiter: string = ",", delimiter = ",",
customHeaders: { [key: string]: string } = {} customHeaders: { [key: string]: string } = {}
) { ) {
let csvRows = [getHeaders(headers, customHeaders)] let csvRows = [getHeaders(headers, customHeaders)]

View File

@ -19,8 +19,6 @@ import { builderSocket } from "../../../websockets"
const cloneDeep = require("lodash/cloneDeep") const cloneDeep = require("lodash/cloneDeep")
import isEqual from "lodash/isEqual"
export async function fetch(ctx: Ctx) { export async function fetch(ctx: Ctx) {
ctx.body = await getViews() ctx.body = await getViews()
} }
@ -60,71 +58,11 @@ export async function save(ctx: Ctx) {
existingTable.views[viewName] = existingTable.views[originalName] existingTable.views[viewName] = existingTable.views[originalName]
} }
await db.put(table) await db.put(table)
await handleViewEvents(
existingTable.views[viewName] as View,
table.views[viewName]
)
ctx.body = table.views[viewName] ctx.body = table.views[viewName]
builderSocket?.emitTableUpdate(ctx, table) builderSocket?.emitTableUpdate(ctx, table)
} }
export async function calculationEvents(existingView: View, newView: View) {
const existingCalculation = existingView && existingView.calculation
const newCalculation = newView && newView.calculation
if (existingCalculation && !newCalculation) {
await events.view.calculationDeleted(existingView)
}
if (!existingCalculation && newCalculation) {
await events.view.calculationCreated(newView)
}
if (
existingCalculation &&
newCalculation &&
existingCalculation !== newCalculation
) {
await events.view.calculationUpdated(newView)
}
}
export async function filterEvents(existingView: View, newView: View) {
const hasExistingFilters = !!(
existingView &&
existingView.filters &&
existingView.filters.length
)
const hasNewFilters = !!(newView && newView.filters && newView.filters.length)
if (hasExistingFilters && !hasNewFilters) {
await events.view.filterDeleted(newView)
}
if (!hasExistingFilters && hasNewFilters) {
await events.view.filterCreated(newView)
}
if (
hasExistingFilters &&
hasNewFilters &&
!isEqual(existingView.filters, newView.filters)
) {
await events.view.filterUpdated(newView)
}
}
async function handleViewEvents(existingView: View, newView: View) {
if (!existingView) {
await events.view.created(newView)
} else {
await events.view.updated(newView)
}
await calculationEvents(existingView, newView)
await filterEvents(existingView, newView)
}
export async function destroy(ctx: Ctx) { export async function destroy(ctx: Ctx) {
const db = context.getAppDB() const db = context.getAppDB()
const viewName = decodeURIComponent(ctx.params.viewName) const viewName = decodeURIComponent(ctx.params.viewName)

View File

@ -17,6 +17,7 @@ import {
CreateViewResponse, CreateViewResponse,
UpdateViewResponse, UpdateViewResponse,
} from "@budibase/types" } from "@budibase/types"
import { events } from "@budibase/backend-core"
import { builderSocket, gridSocket } from "../../../websockets" import { builderSocket, gridSocket } from "../../../websockets"
import { helpers } from "@budibase/shared-core" import { helpers } from "@budibase/shared-core"
@ -150,6 +151,9 @@ export async function create(ctx: Ctx<CreateViewRequest, CreateViewResponse>) {
primaryDisplay: view.primaryDisplay, primaryDisplay: view.primaryDisplay,
} }
const result = await sdk.views.create(tableId, parsedView) const result = await sdk.views.create(tableId, parsedView)
await events.view.created(result)
ctx.status = 201 ctx.status = 201
ctx.body = { ctx.body = {
data: result, data: result,
@ -160,6 +164,46 @@ export async function create(ctx: Ctx<CreateViewRequest, CreateViewResponse>) {
gridSocket?.emitViewUpdate(ctx, result) gridSocket?.emitViewUpdate(ctx, result)
} }
async function handleViewFilterEvents(existingView: ViewV2, view: ViewV2) {
const filterGroups = view.queryUI?.groups?.length || 0
const properties = { filterGroups, tableId: view.tableId }
if (
filterGroups >= 2 &&
filterGroups > (existingView?.queryUI?.groups?.length || 0)
) {
await events.view.filterUpdated(properties)
}
}
async function handleViewEvents(existingView: ViewV2, view: ViewV2) {
// Grouped filters
if (view.queryUI?.groups) {
await handleViewFilterEvents(existingView, view)
}
// if new columns in the view
for (const key in view.schema) {
if ("calculationType" in view.schema[key] && !existingView?.schema?.[key]) {
await events.view.calculationCreated({
calculationType: view.schema[key].calculationType,
tableId: view.tableId,
})
}
// view joins
for (const column in view.schema[key]?.columns ?? []) {
// if the new column is visible and it wasn't before
if (
!existingView?.schema?.[key].columns?.[column].visible &&
view.schema?.[key].columns?.[column].visible
) {
// new view join exposing a column
await events.view.viewJoinCreated({ tableId: view.tableId })
}
}
}
}
export async function update(ctx: Ctx<UpdateViewRequest, UpdateViewResponse>) { export async function update(ctx: Ctx<UpdateViewRequest, UpdateViewResponse>) {
const view = ctx.request.body const view = ctx.request.body
@ -187,10 +231,15 @@ export async function update(ctx: Ctx<UpdateViewRequest, UpdateViewResponse>) {
primaryDisplay: view.primaryDisplay, primaryDisplay: view.primaryDisplay,
} }
const result = await sdk.views.update(tableId, parsedView) const { view: result, existingView } = await sdk.views.update(
ctx.body = { tableId,
data: result, parsedView
} )
await handleViewEvents(existingView, result)
await events.view.updated(result)
ctx.body = { data: result }
const table = await sdk.tables.getTable(tableId) const table = await sdk.tables.getTable(tableId)
builderSocket?.emitTableUpdate(ctx, table) builderSocket?.emitTableUpdate(ctx, table)

View File

@ -4,14 +4,13 @@ const { events, constants } = require("@budibase/backend-core")
describe("/static", () => { describe("/static", () => {
let request = setup.getRequest() let request = setup.getRequest()
let config = setup.getConfig() let config = setup.getConfig()
let app
const timezone = "Europe/London" const timezone = "Europe/London"
afterAll(setup.afterAll) afterAll(setup.afterAll)
beforeAll(async () => { beforeAll(async () => {
app = await config.init() await config.init()
}) })
beforeEach(() => { beforeEach(() => {
@ -26,10 +25,10 @@ describe("/static", () => {
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect(200) .expect(200)
expect(events.serve.servedBuilder).toBeCalledTimes(1) expect(events.serve.servedBuilder).toHaveBeenCalledTimes(1)
expect(events.serve.servedBuilder).toBeCalledWith(timezone) expect(events.serve.servedBuilder).toHaveBeenCalledWith(timezone)
expect(events.serve.servedApp).not.toBeCalled() expect(events.serve.servedApp).not.toHaveBeenCalled()
expect(events.serve.servedAppPreview).not.toBeCalled() expect(events.serve.servedAppPreview).not.toHaveBeenCalled()
}) })
it("should ping from app preview", async () => { it("should ping from app preview", async () => {
@ -39,12 +38,12 @@ describe("/static", () => {
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect(200) .expect(200)
expect(events.serve.servedAppPreview).toBeCalledTimes(1) expect(events.serve.servedAppPreview).toHaveBeenCalledTimes(1)
expect(events.serve.servedAppPreview).toBeCalledWith( expect(events.serve.servedAppPreview).toHaveBeenCalledWith(
config.getApp(), config.getApp(),
timezone timezone
) )
expect(events.serve.servedApp).not.toBeCalled() expect(events.serve.servedApp).not.toHaveBeenCalled()
}) })
it("should ping from app", async () => { it("should ping from app", async () => {
@ -57,13 +56,13 @@ describe("/static", () => {
.set(headers) .set(headers)
.expect(200) .expect(200)
expect(events.serve.servedApp).toBeCalledTimes(1) expect(events.serve.servedApp).toHaveBeenCalledTimes(1)
expect(events.serve.servedApp).toBeCalledWith( expect(events.serve.servedApp).toHaveBeenCalledWith(
config.getProdApp(), config.getProdApp(),
timezone, timezone,
undefined undefined
) )
expect(events.serve.servedAppPreview).not.toBeCalled() expect(events.serve.servedAppPreview).not.toHaveBeenCalled()
}) })
it("should ping from an embedded app", async () => { it("should ping from an embedded app", async () => {
@ -76,13 +75,13 @@ describe("/static", () => {
.set(headers) .set(headers)
.expect(200) .expect(200)
expect(events.serve.servedApp).toBeCalledTimes(1) expect(events.serve.servedApp).toHaveBeenCalledTimes(1)
expect(events.serve.servedApp).toBeCalledWith( expect(events.serve.servedApp).toHaveBeenCalledWith(
config.getProdApp(), config.getProdApp(),
timezone, timezone,
true true
) )
expect(events.serve.servedAppPreview).not.toBeCalled() expect(events.serve.servedAppPreview).not.toHaveBeenCalled()
}) })
}) })
}) })

View File

@ -208,7 +208,7 @@ describe("/applications", () => {
it("should reject with a known url", async () => { it("should reject with a known url", async () => {
await config.api.application.create( await config.api.application.create(
{ name: "made up", url: app?.url! }, { name: "made up", url: app!.url! },
{ body: { message: "App URL is already in use." }, status: 400 } { body: { message: "App URL is already in use." }, status: 400 }
) )
}) })

View File

@ -19,7 +19,7 @@ describe("/dev", () => {
.set(config.defaultHeaders()) .set(config.defaultHeaders())
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(events.app.reverted).toBeCalledTimes(1) expect(events.app.reverted).toHaveBeenCalledTimes(1)
}) })
}) })
@ -32,8 +32,10 @@ describe("/dev", () => {
.expect(200) .expect(200)
expect(res.body.version).toBe("0.0.0+jest") expect(res.body.version).toBe("0.0.0+jest")
expect(events.installation.versionChecked).toBeCalledTimes(1) expect(events.installation.versionChecked).toHaveBeenCalledTimes(1)
expect(events.installation.versionChecked).toBeCalledWith("0.0.0+jest") expect(events.installation.versionChecked).toHaveBeenCalledWith(
"0.0.0+jest"
)
}) })
}) })
}) })

View File

@ -25,7 +25,7 @@ describe("/layouts", () => {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body._rev).toBeDefined() expect(res.body._rev).toBeDefined()
expect(events.layout.created).toBeCalledTimes(1) expect(events.layout.created).toHaveBeenCalledTimes(1)
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {
@ -45,7 +45,7 @@ describe("/layouts", () => {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.message).toBeDefined() expect(res.body.message).toBeDefined()
expect(events.layout.deleted).toBeCalledTimes(1) expect(events.layout.deleted).toHaveBeenCalledTimes(1)
}) })
it("should apply authorization to endpoint", async () => { it("should apply authorization to endpoint", async () => {

View File

@ -3,7 +3,7 @@ import {
DatabaseName, DatabaseName,
datasourceDescribe, datasourceDescribe,
} from "../../../../integrations/tests/utils" } from "../../../../integrations/tests/utils"
import { Expectations } from "src/tests/utilities/api/base" import { Expectations } from "../../../../tests/utilities/api/base"
import { events } from "@budibase/backend-core" import { events } from "@budibase/backend-core"
import { Knex } from "knex" import { Knex } from "knex"
import { generator } from "@budibase/backend-core/tests" import { generator } from "@budibase/backend-core/tests"

View File

@ -314,9 +314,7 @@ if (descriptions.length) {
const cloned = cloneDeep(response) const cloned = cloneDeep(response)
const foundRows = response.rows const foundRows = response.rows
// eslint-disable-next-line jest/no-standalone-expect
expect(foundRows).toHaveLength(expectedRows.length) expect(foundRows).toHaveLength(expectedRows.length)
// eslint-disable-next-line jest/no-standalone-expect
expect([...foundRows]).toEqual( expect([...foundRows]).toEqual(
expectedRows.map((expectedRow: any) => expectedRows.map((expectedRow: any) =>
expect.objectContaining(this.popRow(expectedRow, foundRows)) expect.objectContaining(this.popRow(expectedRow, foundRows))
@ -333,9 +331,7 @@ if (descriptions.length) {
const cloned = cloneDeep(response) const cloned = cloneDeep(response)
const foundRows = response.rows const foundRows = response.rows
// eslint-disable-next-line jest/no-standalone-expect
expect(foundRows).toHaveLength(expectedRows.length) expect(foundRows).toHaveLength(expectedRows.length)
// eslint-disable-next-line jest/no-standalone-expect
expect([...foundRows]).toEqual( expect([...foundRows]).toEqual(
expect.arrayContaining( expect.arrayContaining(
expectedRows.map((expectedRow: any) => expectedRows.map((expectedRow: any) =>
@ -358,10 +354,8 @@ if (descriptions.length) {
keyof SearchResponse<Row> keyof SearchResponse<Row>
> >
for (let key of keys) { for (let key of keys) {
// eslint-disable-next-line jest/no-standalone-expect
expect(response[key]).toBeDefined() expect(response[key]).toBeDefined()
if (properties[key]) { if (properties[key]) {
// eslint-disable-next-line jest/no-standalone-expect
expect(response[key]).toEqual(properties[key]) expect(response[key]).toEqual(properties[key])
} }
} }
@ -375,7 +369,6 @@ if (descriptions.length) {
const response = await this.performSearch() const response = await this.performSearch()
const cloned = cloneDeep(response) const cloned = cloneDeep(response)
for (let property of properties) { for (let property of properties) {
// eslint-disable-next-line jest/no-standalone-expect
expect(response[property]).toBeUndefined() expect(response[property]).toBeUndefined()
} }
return cloned return cloned
@ -389,7 +382,6 @@ if (descriptions.length) {
const cloned = cloneDeep(response) const cloned = cloneDeep(response)
const foundRows = response.rows const foundRows = response.rows
// eslint-disable-next-line jest/no-standalone-expect
expect([...foundRows]).toEqual( expect([...foundRows]).toEqual(
expect.arrayContaining( expect.arrayContaining(
expectedRows.map((expectedRow: any) => expectedRows.map((expectedRow: any) =>
@ -409,7 +401,6 @@ if (descriptions.length) {
async toHaveLength(length: number) { async toHaveLength(length: number) {
const { rows: foundRows } = await this.performSearch() const { rows: foundRows } = await this.performSearch()
// eslint-disable-next-line jest/no-standalone-expect
expect(foundRows).toHaveLength(length) expect(foundRows).toHaveLength(length)
} }
} }
@ -2142,7 +2133,7 @@ if (descriptions.length) {
// repeat the search many times to check the first row is always the same // repeat the search many times to check the first row is always the same
let bookmark: string | number | undefined, let bookmark: string | number | undefined,
hasNextPage: boolean | undefined = true, hasNextPage: boolean | undefined = true,
rowCount: number = 0 rowCount = 0
do { do {
const response = await config.api.row.search( const response = await config.api.row.search(
tableOrViewId, tableOrViewId,
@ -2169,7 +2160,6 @@ if (descriptions.length) {
let bookmark: string | number = undefined let bookmark: string | number = undefined
let rows: Row[] = [] let rows: Row[] = []
// eslint-disable-next-line no-constant-condition
while (true) { while (true) {
const response = await config.api.row.search( const response = await config.api.row.search(
tableOrViewId, tableOrViewId,

View File

@ -247,6 +247,9 @@ if (descriptions.length) {
}, },
}, },
}, },
primary: ["_id"],
views: {},
sql: true,
}) })
) )
@ -254,9 +257,8 @@ if (descriptions.length) {
...table, ...table,
name: generator.guid(), name: generator.guid(),
}) })
expect(events.table.updated).toHaveBeenCalledTimes(1) expect(events.table.updated).toHaveBeenCalledTimes(1)
expect(events.table.updated).toHaveBeenCalledWith(updatedTable) expect(events.table.updated).toHaveBeenCalledWith(table, updatedTable)
}) })
it("updates all the row fields for a table when a schema key is renamed", async () => { it("updates all the row fields for a table when a schema key is renamed", async () => {

View File

@ -4,7 +4,7 @@ import { AppStatus } from "../../../../db/utils"
import { roles, tenancy, context, db } from "@budibase/backend-core" import { roles, tenancy, context, db } from "@budibase/backend-core"
import env from "../../../../environment" import env from "../../../../environment"
import Nano from "@budibase/nano" import Nano from "@budibase/nano"
import TestConfiguration from "src/tests/utilities/TestConfiguration" import TestConfiguration from "../../../../tests/utilities/TestConfiguration"
class Request { class Request {
appId: any appId: any

View File

@ -73,25 +73,12 @@ describe("/views", () => {
} }
describe("create", () => { describe("create", () => {
it("returns a success message when the view is successfully created", async () => {
await saveView()
expect(events.view.created).toHaveBeenCalledTimes(1)
})
it("creates a view with a calculation", async () => { it("creates a view with a calculation", async () => {
jest.clearAllMocks() jest.clearAllMocks()
const view = await saveView({ calculation: ViewCalculation.COUNT }) const view = await saveView({ calculation: ViewCalculation.COUNT })
expect(view.tableId).toBe(table._id) expect(view.tableId).toBe(table._id)
expect(events.view.created).toHaveBeenCalledTimes(1)
expect(events.view.updated).not.toHaveBeenCalled()
expect(events.view.calculationCreated).toHaveBeenCalledTimes(1)
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("creates a view with a filter", async () => { it("creates a view with a filter", async () => {
@ -109,14 +96,6 @@ describe("/views", () => {
}) })
expect(view.tableId).toBe(table._id) expect(view.tableId).toBe(table._id)
expect(events.view.created).toHaveBeenCalledTimes(1)
expect(events.view.updated).not.toHaveBeenCalled()
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).toHaveBeenCalledTimes(1)
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("updates the table row with the new view metadata", async () => { it("updates the table row with the new view metadata", async () => {
@ -166,13 +145,6 @@ describe("/views", () => {
await saveView() await saveView()
expect(events.view.created).not.toHaveBeenCalled() expect(events.view.created).not.toHaveBeenCalled()
expect(events.view.updated).toHaveBeenCalledTimes(1)
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("updates a view calculation", async () => { it("updates a view calculation", async () => {
@ -182,13 +154,6 @@ describe("/views", () => {
await saveView({ calculation: ViewCalculation.COUNT }) await saveView({ calculation: ViewCalculation.COUNT })
expect(events.view.created).not.toHaveBeenCalled() expect(events.view.created).not.toHaveBeenCalled()
expect(events.view.updated).toHaveBeenCalledTimes(1)
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).toHaveBeenCalledTimes(1)
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("deletes a view calculation", async () => { it("deletes a view calculation", async () => {
@ -198,13 +163,6 @@ describe("/views", () => {
await saveView({ calculation: undefined }) await saveView({ calculation: undefined })
expect(events.view.created).not.toHaveBeenCalled() expect(events.view.created).not.toHaveBeenCalled()
expect(events.view.updated).toHaveBeenCalledTimes(1)
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).toHaveBeenCalledTimes(1)
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("updates a view filter", async () => { it("updates a view filter", async () => {
@ -230,13 +188,6 @@ describe("/views", () => {
}) })
expect(events.view.created).not.toHaveBeenCalled() expect(events.view.created).not.toHaveBeenCalled()
expect(events.view.updated).toHaveBeenCalledTimes(1)
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).toHaveBeenCalledTimes(1)
expect(events.view.filterDeleted).not.toHaveBeenCalled()
}) })
it("deletes a view filter", async () => { it("deletes a view filter", async () => {
@ -254,13 +205,6 @@ describe("/views", () => {
await saveView({ filters: [] }) await saveView({ filters: [] })
expect(events.view.created).not.toHaveBeenCalled() expect(events.view.created).not.toHaveBeenCalled()
expect(events.view.updated).toHaveBeenCalledTimes(1)
expect(events.view.calculationCreated).not.toHaveBeenCalled()
expect(events.view.calculationUpdated).not.toHaveBeenCalled()
expect(events.view.calculationDeleted).not.toHaveBeenCalled()
expect(events.view.filterCreated).not.toHaveBeenCalled()
expect(events.view.filterUpdated).not.toHaveBeenCalled()
expect(events.view.filterDeleted).toHaveBeenCalledTimes(1)
}) })
}) })

View File

@ -1,39 +1,39 @@
import { import {
ArrayOperator,
BasicOperator,
BBReferenceFieldSubType,
CalculationType,
CreateViewRequest, CreateViewRequest,
Datasource, Datasource,
EmptyFilterOption,
FieldSchema, FieldSchema,
FieldType, FieldType,
INTERNAL_TABLE_SOURCE_ID, INTERNAL_TABLE_SOURCE_ID,
JsonFieldSubType,
JsonTypes,
LegacyFilter,
NumericCalculationFieldMetadata,
PermissionLevel, PermissionLevel,
QuotaUsageType, QuotaUsageType,
RelationshipType,
RenameColumn,
Row, Row,
SaveTableRequest, SaveTableRequest,
SearchFilters,
SearchResponse,
SearchViewRowRequest,
SortOrder, SortOrder,
SortType, SortType,
StaticQuotaName, StaticQuotaName,
Table, Table,
TableSchema,
TableSourceType, TableSourceType,
UILogicalOperator,
UISearchFilter,
UpdateViewRequest, UpdateViewRequest,
ViewV2, ViewV2,
SearchResponse,
BasicOperator,
CalculationType,
RelationshipType,
TableSchema,
RenameColumn,
BBReferenceFieldSubType,
NumericCalculationFieldMetadata,
ViewV2Schema, ViewV2Schema,
ViewV2Type, ViewV2Type,
JsonTypes,
EmptyFilterOption,
JsonFieldSubType,
UISearchFilter,
LegacyFilter,
SearchViewRowRequest,
ArrayOperator,
UILogicalOperator,
SearchFilters,
} from "@budibase/types" } from "@budibase/types"
import { generator, mocks } from "@budibase/backend-core/tests" import { generator, mocks } from "@budibase/backend-core/tests"
import { import {
@ -42,7 +42,7 @@ import {
} from "../../../integrations/tests/utils" } from "../../../integrations/tests/utils"
import merge from "lodash/merge" import merge from "lodash/merge"
import { quotas } from "@budibase/pro" import { quotas } from "@budibase/pro"
import { db, roles, context } from "@budibase/backend-core" import { context, db, events, roles } from "@budibase/backend-core"
const descriptions = datasourceDescribe({ exclude: [DatabaseName.MONGODB] }) const descriptions = datasourceDescribe({ exclude: [DatabaseName.MONGODB] })
@ -129,6 +129,7 @@ if (descriptions.length) {
id: expect.stringMatching(new RegExp(`${table._id!}_`)), id: expect.stringMatching(new RegExp(`${table._id!}_`)),
version: 2, version: 2,
}) })
expect(events.view.created).toHaveBeenCalledTimes(1)
}) })
it("can persist views with all fields", async () => { it("can persist views with all fields", async () => {
@ -195,6 +196,7 @@ if (descriptions.length) {
} }
expect(res).toEqual(expected) expect(res).toEqual(expected)
expect(events.view.created).toHaveBeenCalledTimes(1)
}) })
it("can create a view with just a query field, no queryUI, for backwards compatibility", async () => { it("can create a view with just a query field, no queryUI, for backwards compatibility", async () => {
@ -224,6 +226,7 @@ if (descriptions.length) {
}, },
} }
const res = await config.api.viewV2.create(newView) const res = await config.api.viewV2.create(newView)
expect(events.view.created).toHaveBeenCalledTimes(1)
const expected: ViewV2 = { const expected: ViewV2 = {
...newView, ...newView,
@ -283,6 +286,7 @@ if (descriptions.length) {
} }
const createdView = await config.api.viewV2.create(newView) const createdView = await config.api.viewV2.create(newView)
expect(events.view.created).toHaveBeenCalledTimes(1)
expect(createdView).toEqual({ expect(createdView).toEqual({
...newView, ...newView,
@ -990,6 +994,46 @@ if (descriptions.length) {
expect((await config.api.table.get(tableId)).views).toEqual({ expect((await config.api.table.get(tableId)).views).toEqual({
[view.name]: expected, [view.name]: expected,
}) })
expect(events.view.updated).toHaveBeenCalledTimes(1)
})
it("handles view grouped filter events", async () => {
view.queryUI = {
logicalOperator: UILogicalOperator.ALL,
onEmptyFilter: EmptyFilterOption.RETURN_ALL,
groups: [
{
logicalOperator: UILogicalOperator.ALL,
filters: [
{
operator: BasicOperator.EQUAL,
field: "newField",
value: "newValue",
},
],
},
],
}
await config.api.viewV2.update(view)
expect(events.view.filterUpdated).not.toHaveBeenCalled()
// @ts-ignore
view.queryUI.groups.push({
logicalOperator: UILogicalOperator.ALL,
filters: [
{
operator: BasicOperator.EQUAL,
field: "otherField",
value: "otherValue",
},
],
})
await config.api.viewV2.update(view)
expect(events.view.filterUpdated).toHaveBeenCalledWith({
filterGroups: 2,
tableId: view.tableId,
})
}) })
it("can update all fields", async () => { it("can update all fields", async () => {
@ -1621,6 +1665,7 @@ if (descriptions.length) {
field: "age", field: "age",
} }
await config.api.viewV2.update(view) await config.api.viewV2.update(view)
expect(events.view.calculationCreated).toHaveBeenCalledTimes(1)
const { rows } = await config.api.row.search(view.id) const { rows } = await config.api.row.search(view.id)
expect(rows).toHaveLength(2) expect(rows).toHaveLength(2)
@ -2154,6 +2199,7 @@ if (descriptions.length) {
}), }),
}) })
) )
expect(events.view.viewJoinCreated).not.toHaveBeenCalled()
}) })
it("does not rename columns with the same name but from other tables", async () => { it("does not rename columns with the same name but from other tables", async () => {
@ -2226,6 +2272,36 @@ if (descriptions.length) {
) )
}) })
it("handles events for changing column visibility from default false", async () => {
let auxTable = await createAuxTable()
let aux2Table = await createAuxTable()
const table = await createMainTable([
{ name: "aux", tableId: auxTable._id!, fk: "fk_aux" },
{ name: "aux2", tableId: aux2Table._id!, fk: "fk_aux2" },
])
const view = await createView(table._id!, {
aux: {
visible: true,
columns: {
name: { visible: false, readonly: true },
},
},
aux2: {
visible: true,
columns: {
name: { visible: false, readonly: true },
},
},
})
// @ts-expect-error column exists above
view.schema.aux2.columns.name.visible = true
await config.api.viewV2.update(view)
expect(events.view.viewJoinCreated).toHaveBeenCalledTimes(1)
})
it("updates all views references", async () => { it("updates all views references", async () => {
let auxTable = await createAuxTable() let auxTable = await createAuxTable()

View File

@ -263,7 +263,7 @@ class AutomationBuilder extends BaseStepBuilder {
private automationConfig: Automation private automationConfig: Automation
private config: TestConfiguration private config: TestConfiguration
private triggerOutputs: any private triggerOutputs: any
private triggerSet: boolean = false private triggerSet = false
constructor( constructor(
options: { name?: string; appId?: string; config?: TestConfiguration } = {} options: { name?: string; appId?: string; config?: TestConfiguration } = {}

View File

@ -107,7 +107,7 @@ if (mainDescriptions.length) {
const table = response.datasource.entities?.["binaryTable"] const table = response.datasource.entities?.["binaryTable"]
expect(table).toBeDefined() expect(table).toBeDefined()
expect(table?.schema.id.externalType).toBe("bytea") expect(table?.schema.id.externalType).toBe("bytea")
const row = await config.api.row.save(table?._id!, { const row = await config.api.row.save(table!._id!, {
id: "1111", id: "1111",
column1: "hello", column1: "hello",
column2: 222, column2: 222,

View File

@ -223,7 +223,7 @@ const COLUMN_DEFINITION_METADATA: Record<string, ColumnDefinitionMetadata> = {
class SqlServerIntegration extends Sql implements DatasourcePlus { class SqlServerIntegration extends Sql implements DatasourcePlus {
private readonly config: MSSQLConfig private readonly config: MSSQLConfig
private index: number = 0 private index = 0
private client?: sqlServer.ConnectionPool private client?: sqlServer.ConnectionPool
MASTER_TABLES = [ MASTER_TABLES = [

View File

@ -116,7 +116,7 @@ const OracleContraintTypes = {
class OracleIntegration extends Sql implements DatasourcePlus { class OracleIntegration extends Sql implements DatasourcePlus {
private readonly config: OracleConfig private readonly config: OracleConfig
private index: number = 1 private index = 1
private static readonly COLUMNS_SQL = ` private static readonly COLUMNS_SQL = `
SELECT SELECT

View File

@ -149,7 +149,7 @@ const SCHEMA: Integration = {
class PostgresIntegration extends Sql implements DatasourcePlus { class PostgresIntegration extends Sql implements DatasourcePlus {
private readonly client: Client private readonly client: Client
private readonly config: PostgresConfig private readonly config: PostgresConfig
private index: number = 1 private index = 1
private open: boolean private open: boolean
PRIMARY_KEYS_SQL = () => ` PRIMARY_KEYS_SQL = () => `
@ -252,7 +252,7 @@ class PostgresIntegration extends Sql implements DatasourcePlus {
}) })
} }
async internalQuery(query: SqlQuery, close: boolean = true) { async internalQuery(query: SqlQuery, close = true) {
if (!this.open) { if (!this.open) {
await this.openConnection() await this.openConnection()
} }

View File

@ -129,7 +129,7 @@ class RedisIntegration {
return this.client.quit() return this.client.quit()
} }
async redisContext(query: Function) { async redisContext<T>(query: () => Promise<T>) {
try { try {
return await query() return await query()
} catch (err) { } catch (err) {

View File

@ -171,7 +171,7 @@ export class RestIntegration implements IntegrationBase {
path.basename(parse(contentDisposition).parameters?.filename) || "" path.basename(parse(contentDisposition).parameters?.filename) || ""
} }
let triedParsing: boolean = false, let triedParsing = false,
responseTxt: string | undefined responseTxt: string | undefined
try { try {
if (filename) { if (filename) {
@ -313,7 +313,9 @@ export class RestIntegration implements IntegrationBase {
} }
// Util to add pagination values to a certain body type // Util to add pagination values to a certain body type
const addPaginationToBody = (insertFn: Function) => { const addPaginationToBody = (
insertFn: (pageParam: string, page?: string | number) => void
) => {
if (pagination?.location === "body") { if (pagination?.location === "body") {
if (pagination?.pageParam && paginationValues?.page != null) { if (pagination?.pageParam && paginationValues?.page != null) {
insertFn(pagination.pageParam, paginationValues.page) insertFn(pagination.pageParam, paginationValues.page)

View File

@ -389,25 +389,24 @@ describe("Google Sheets Integration", () => {
}) })
// TODO: this gets the error "Sheet is not large enough to fit 27 columns. Resize the sheet first." // TODO: this gets the error "Sheet is not large enough to fit 27 columns. Resize the sheet first."
// eslint-disable-next-line jest/no-commented-out-tests it.skip("should be able to add a new column", async () => {
// it("should be able to add a new column", async () => { const updatedTable = await config.api.table.save({
// const updatedTable = await config.api.table.save({ ...table,
// ...table, schema: {
// schema: { ...table.schema,
// ...table.schema, newColumn: {
// newColumn: { name: "newColumn",
// name: "newColumn", type: FieldType.STRING,
// type: FieldType.STRING, },
// }, },
// }, })
// })
// expect(updatedTable.schema.newColumn).toBeDefined() expect(updatedTable.schema.newColumn).toBeDefined()
// expect(mock.cell("A1")).toEqual("name") expect(mock.cell("A1")).toEqual("name")
// expect(mock.cell("B1")).toEqual("description") expect(mock.cell("B1")).toEqual("description")
// expect(mock.cell("C1")).toEqual("newColumn") expect(mock.cell("C1")).toEqual("newColumn")
// }) })
it("should be able to delete a column", async () => { it("should be able to delete a column", async () => {
const row = await config.api.row.save(table._id!, { const row = await config.api.row.save(table._id!, {

View File

@ -3,6 +3,8 @@ import { RestIntegration } from "../rest"
import { BodyType, RestAuthType } from "@budibase/types" import { BodyType, RestAuthType } from "@budibase/types"
import { Response } from "node-fetch" import { Response } from "node-fetch"
import TestConfiguration from "../../../src/tests/utilities/TestConfiguration" import TestConfiguration from "../../../src/tests/utilities/TestConfiguration"
import { createServer } from "http"
import { AddressInfo } from "net"
const UUID_REGEX = const UUID_REGEX =
"[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}" "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}"
@ -455,29 +457,27 @@ describe("REST Integration", () => {
// NOTE(samwho): it seems like this code doesn't actually work because it requires // NOTE(samwho): it seems like this code doesn't actually work because it requires
// node-fetch >=3, and we're not on that because upgrading to it produces errors to // node-fetch >=3, and we're not on that because upgrading to it produces errors to
// do with ESM that are above my pay grade. // do with ESM that are above my pay grade.
it.skip("doesn't fail when legacyHttpParser is set", async () => {
const server = createServer((req, res) => {
res.writeHead(200, {
"Transfer-Encoding": "chunked",
"Content-Length": "10",
})
res.end(JSON.stringify({ foo: "bar" }))
})
// eslint-disable-next-line jest/no-commented-out-tests server.listen()
// it("doesn't fail when legacyHttpParser is set", async () => { await new Promise(resolve => server.once("listening", resolve))
// const server = createServer((req, res) => {
// res.writeHead(200, {
// "Transfer-Encoding": "chunked",
// "Content-Length": "10",
// })
// res.end(JSON.stringify({ foo: "bar" }))
// })
// server.listen() const address = server.address() as AddressInfo
// await new Promise(resolve => server.once("listening", resolve))
// const address = server.address() as AddressInfo const integration = new RestIntegration({
url: `http://localhost:${address.port}`,
// const integration = new RestIntegration({ legacyHttpParser: true,
// url: `http://localhost:${address.port}`, })
// legacyHttpParser: true, const { data } = await integration.read({})
// }) expect(data).toEqual({ foo: "bar" })
// const { data } = await integration.read({}) })
// expect(data).toEqual({ foo: "bar" })
// })
it("doesn't fail when legacyHttpParser is true", async () => { it("doesn't fail when legacyHttpParser is true", async () => {
nock("https://example.com").get("/").reply(200, { foo: "bar" }) nock("https://example.com").get("/").reply(200, { foo: "bar" })

View File

@ -144,10 +144,10 @@ describe("Captures of real examples", () => {
queryJson queryJson
) )
const filters = queryJson.filters const filters = queryJson.filters
const notEqualsValue = Object.values(filters?.notEqual!)[0] const notEqualsValue = Object.values(filters!.notEqual!)[0]
const rangeValue: { high?: string | number; low?: string | number } = const rangeValue: { high?: string | number; low?: string | number } =
Object.values(filters?.range!)[0] Object.values(filters!.range!)[0]
const equalValue = Object.values(filters?.equal!)[0] const equalValue = Object.values(filters!.equal!)[0]
expect(query).toEqual({ expect(query).toEqual({
bindings: [ bindings: [
@ -245,7 +245,7 @@ describe("Captures of real examples", () => {
tableNames.push(generator.guid()) tableNames.push(generator.guid())
} }
const aliasing = new AliasTables(tableNames) const aliasing = new AliasTables(tableNames)
let alias: string = "" let alias = ""
for (let table of tableNames) { for (let table of tableNames) {
alias = aliasing.getAlias(table) alias = aliasing.getAlias(table)
} }

View File

@ -72,7 +72,7 @@ export class IsolatedVM implements VM {
this.addToContext({ this.addToContext({
helpersStripProtocol: new ivm.Callback((str: string) => { helpersStripProtocol: new ivm.Callback((str: string) => {
var parsed = url.parse(str) as any let parsed = url.parse(str) as any
parsed.protocol = "" parsed.protocol = ""
return parsed.format() return parsed.format()
}), }),

View File

@ -50,7 +50,7 @@ async function updateAppUpdatedAt(ctx: UserCtx) {
const metadata = await db.get<any>(DocumentType.APP_METADATA) const metadata = await db.get<any>(DocumentType.APP_METADATA)
metadata.updatedAt = new Date().toISOString() metadata.updatedAt = new Date().toISOString()
metadata.updatedBy = getGlobalIDFromUserMetadataID(ctx.user?.userId!) metadata.updatedBy = getGlobalIDFromUserMetadataID(ctx.user!.userId!)
const response = await db.put(metadata) const response = await db.put(metadata)
metadata._rev = response.rev metadata._rev = response.rev
@ -59,9 +59,7 @@ async function updateAppUpdatedAt(ctx: UserCtx) {
await setDebounce(appId, DEBOUNCE_TIME_SEC) await setDebounce(appId, DEBOUNCE_TIME_SEC)
} catch (err: any) { } catch (err: any) {
// if a 409 occurs, then multiple clients connected at the same time - ignore // if a 409 occurs, then multiple clients connected at the same time - ignore
if (err?.status === 409) { if (err && err.status !== 409) {
return
} else {
throw err throw err
} }
} }

View File

@ -7,24 +7,6 @@ export const backfill = async (appDb: Database, timestamp: string | number) => {
for (const table of tables) { for (const table of tables) {
await events.table.created(table, timestamp) await events.table.created(table, timestamp)
if (table.views) {
for (const view of Object.values(table.views)) {
if (sdk.views.isV2(view)) {
continue
}
await events.view.created(view, timestamp)
if (view.calculation) {
await events.view.calculationCreated(view, timestamp)
}
if (view.filters?.length) {
await events.view.filterCreated(view, timestamp)
}
}
}
} }
return tables.length return tables.length

View File

@ -1,3 +1,3 @@
export const runQuotaMigration = async (migration: Function) => { export const runQuotaMigration = async (migration: () => Promise<void>) => {
await migration() await migration()
} }

View File

@ -73,16 +73,12 @@ describe("migrations", () => {
expect(events.query.created).toHaveBeenCalledTimes(2) expect(events.query.created).toHaveBeenCalledTimes(2)
expect(events.role.created).toHaveBeenCalledTimes(3) // created roles + admin (created on table creation) expect(events.role.created).toHaveBeenCalledTimes(3) // created roles + admin (created on table creation)
expect(events.table.created).toHaveBeenCalledTimes(3) expect(events.table.created).toHaveBeenCalledTimes(3)
expect(events.view.created).toHaveBeenCalledTimes(2)
expect(events.view.calculationCreated).toHaveBeenCalledTimes(1)
expect(events.view.filterCreated).toHaveBeenCalledTimes(1)
expect(events.screen.created).toHaveBeenCalledTimes(2)
expect(events.backfill.appSucceeded).toHaveBeenCalledTimes(2) expect(events.backfill.appSucceeded).toHaveBeenCalledTimes(2)
// to make sure caching is working as expected // to make sure caching is working as expected
expect( expect(
events.processors.analyticsProcessor.processEvent events.processors.analyticsProcessor.processEvent
).toHaveBeenCalledTimes(24) // Addtion of of the events above ).toHaveBeenCalledTimes(20) // Addition of of the events above
}) })
}) })
}) })

View File

@ -104,8 +104,6 @@ export async function getDependantResources(
return p return p
}, {} as Record<string, number>) }, {} as Record<string, number>)
} }
return
} }
export async function updatePermissionOnRole( export async function updatePermissionOnRole(

View File

@ -437,7 +437,7 @@ export async function search(
) )
// check for pagination final row // check for pagination final row
let nextRow: boolean = false let nextRow = false
if (paginate && params.limit && rows.length > params.limit) { if (paginate && params.limit && rows.length > params.limit) {
// remove the extra row that confirmed if there is another row to move to // remove the extra row that confirmed if there is another row to move to
nextRow = true nextRow = true

View File

@ -281,7 +281,7 @@ export async function save(
tableToSave.sql = true tableToSave.sql = true
} }
return { datasource: updatedDatasource, table: tableToSave } return { datasource: updatedDatasource, table: tableToSave, oldTable }
} }
export async function destroy(datasourceId: string, table: Table) { export async function destroy(datasourceId: string, table: Table) {

View File

@ -171,7 +171,7 @@ export async function save(
} }
// has to run after, make sure it has _id // has to run after, make sure it has _id
await runStaticFormulaChecks(table, { oldTable, deletion: false }) await runStaticFormulaChecks(table, { oldTable, deletion: false })
return { table } return { table, oldTable }
} }
export async function destroy(table: Table) { export async function destroy(table: Table) {

View File

@ -63,7 +63,7 @@ export async function create(
export async function update( export async function update(
tableId: string, tableId: string,
view: Readonly<ViewV2> view: Readonly<ViewV2>
): Promise<ViewV2> { ): Promise<{ view: Readonly<ViewV2>; existingView: ViewV2 }> {
const db = context.getAppDB() const db = context.getAppDB()
const { datasourceId, tableName } = breakExternalTableId(tableId) const { datasourceId, tableName } = breakExternalTableId(tableId)
@ -87,7 +87,7 @@ export async function update(
delete views[existingView.name] delete views[existingView.name]
views[view.name] = view views[view.name] = view
await db.put(ds) await db.put(ds)
return view return { view, existingView } as { view: ViewV2; existingView: ViewV2 }
} }
export async function remove(viewId: string): Promise<ViewV2> { export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -315,7 +315,10 @@ export async function create(
return view return view
} }
export async function update(tableId: string, view: ViewV2): Promise<ViewV2> { export async function update(
tableId: string,
view: ViewV2
): Promise<{ view: ViewV2; existingView: ViewV2 }> {
await guardViewSchema(tableId, view) await guardViewSchema(tableId, view)
return pickApi(tableId).update(tableId, view) return pickApi(tableId).update(tableId, view)

View File

@ -54,7 +54,7 @@ export async function create(
export async function update( export async function update(
tableId: string, tableId: string,
view: Readonly<ViewV2> view: Readonly<ViewV2>
): Promise<ViewV2> { ): Promise<{ view: ViewV2; existingView: ViewV2 }> {
const db = context.getAppDB() const db = context.getAppDB()
const table = await sdk.tables.getTable(tableId) const table = await sdk.tables.getTable(tableId)
table.views ??= {} table.views ??= {}
@ -76,7 +76,7 @@ export async function update(
delete table.views[existingView.name] delete table.views[existingView.name]
table.views[view.name] = view table.views[view.name] = view
await db.put(table) await db.put(table)
return view return { view, existingView } as { view: ViewV2; existingView: ViewV2 }
} }
export async function remove(viewId: string): Promise<ViewV2> { export async function remove(viewId: string): Promise<ViewV2> {

View File

@ -47,7 +47,7 @@ describe("check BB_ADMIN environment variables", () => {
}) })
expect(user).toBeDefined() expect(user).toBeDefined()
expect(user?.password).toBeDefined() expect(user?.password).toBeDefined()
expect(await utils.compare(PASSWORD, user?.password!)).toEqual( expect(await utils.compare(PASSWORD, user!.password!)).toEqual(
true true
) )
} }

View File

@ -535,7 +535,7 @@ export function basicRow(tableId: string) {
export function basicLinkedRow( export function basicLinkedRow(
tableId: string, tableId: string,
linkedRowId: string, linkedRowId: string,
linkField: string = "link" linkField = "link"
) { ) {
// this is based on the basic linked tables you get from the test configuration // this is based on the basic linked tables you get from the test configuration
return { return {
@ -586,14 +586,14 @@ export function basicUser(role: string) {
} }
} }
export function basicScreen(route: string = "/") { export function basicScreen(route = "/") {
return createHomeScreen({ return createHomeScreen({
roleId: BUILTIN_ROLE_IDS.BASIC, roleId: BUILTIN_ROLE_IDS.BASIC,
route, route,
}) })
} }
export function powerScreen(route: string = "/") { export function powerScreen(route = "/") {
return createHomeScreen({ return createHomeScreen({
roleId: BUILTIN_ROLE_IDS.POWER, roleId: BUILTIN_ROLE_IDS.POWER,
route, route,

View File

@ -78,7 +78,7 @@ export async function getCachedSelf(
// this has to be tenant aware, can't depend on the context to find it out // 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 // running some middlewares before the tenancy causes context to break
const user = await cache.user.getUser({ const user = await cache.user.getUser({
userId: ctx.user?._id!, userId: ctx.user!._id!,
}) })
return processUser(user, { appId }) return processUser(user, { appId })
} }

View File

@ -1,6 +1,6 @@
export async function retry<T extends (...arg0: any[]) => any>( export async function retry<T extends (...arg0: any[]) => any>(
fn: T, fn: T,
maxTry: number = 5, maxTry = 5,
retryCount = 1 retryCount = 1
): Promise<Awaited<ReturnType<T>>> { ): Promise<Awaited<ReturnType<T>>> {
const currRetry = typeof retryCount === "number" ? retryCount : 1 const currRetry = typeof retryCount === "number" ? retryCount : 1

View File

@ -92,7 +92,7 @@ export default class BuilderSocket extends BaseSocket {
} }
} }
async updateUser(socket: Socket, patch: Object) { async updateUser(socket: Socket, patch: object) {
await super.updateUser(socket, { await super.updateUser(socket, {
builderMetadata: { builderMetadata: {
...socket.data.builderMetadata, ...socket.data.builderMetadata,

View File

@ -70,7 +70,7 @@ export default class GridSocket extends BaseSocket {
}) })
} }
async updateUser(socket: Socket, patch: Object) { async updateUser(socket: Socket, patch: object) {
await super.updateUser(socket, { await super.updateUser(socket, {
gridMetadata: { gridMetadata: {
...socket.data.gridMetadata, ...socket.data.gridMetadata,

Some files were not shown because too many files have changed in this diff Show More