Merge pull request #10120 from Budibase/fix/budi-6657
Fix for user groups adding users to app user metadata tables
This commit is contained in:
commit
2295440370
|
@ -56,11 +56,11 @@ jobs:
|
||||||
run: yarn install:pro $BRANCH $BASE_BRANCH
|
run: yarn install:pro $BRANCH $BASE_BRANCH
|
||||||
- run: yarn
|
- run: yarn
|
||||||
- run: yarn bootstrap
|
- run: yarn bootstrap
|
||||||
|
- run: yarn build:client
|
||||||
- run: yarn test
|
- run: yarn test
|
||||||
- uses: codecov/codecov-action@v1
|
- uses: codecov/codecov-action@v3
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
|
||||||
files: ./packages/server/coverage/clover.xml,./packages/worker/coverage/clover.xml,./packages/backend-core/coverage/clover.xml
|
|
||||||
name: codecov-umbrella
|
name: codecov-umbrella
|
||||||
verbose: true
|
verbose: true
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
"setup": "node ./hosting/scripts/setup.js && yarn && yarn bootstrap && yarn build && yarn dev",
|
||||||
"bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh",
|
"bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh",
|
||||||
"build": "lerna run build",
|
"build": "lerna run build",
|
||||||
|
"build:client": "lerna run build --ignore @budibase/backend-core --ignore @budibase/worker --ignore @budibase/server --ignore @budibase/builder --ignore @budibase/cli --ignore @budibase/sdk",
|
||||||
"build:dev": "lerna run prebuild && tsc --build --watch --preserveWatchOutput",
|
"build:dev": "lerna run prebuild && tsc --build --watch --preserveWatchOutput",
|
||||||
"build:backend": "lerna run build --ignore @budibase/client --ignore @budibase/bbui --ignore @budibase/builder --ignore @budibase/cli",
|
"build:backend": "lerna run build --ignore @budibase/client --ignore @budibase/bbui --ignore @budibase/builder --ignore @budibase/cli",
|
||||||
"build:sdk": "lerna run build:sdk",
|
"build:sdk": "lerna run build:sdk",
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
if [[ -n $CI ]]
|
if [[ -n $CI ]]
|
||||||
then
|
then
|
||||||
# --runInBand performs better in ci where resources are limited
|
# --runInBand performs better in ci where resources are limited
|
||||||
echo "jest --coverage --runInBand"
|
echo "jest --coverage --runInBand --forceExit"
|
||||||
jest --coverage --runInBand
|
jest --coverage --runInBand --forceExit
|
||||||
else
|
else
|
||||||
# --maxWorkers performs better in development
|
# --maxWorkers performs better in development
|
||||||
echo "jest --coverage"
|
echo "jest --coverage"
|
||||||
jest --coverage
|
jest --coverage
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -24,6 +24,7 @@ export * as redis from "./redis"
|
||||||
export * as locks from "./redis/redlockImpl"
|
export * as locks from "./redis/redlockImpl"
|
||||||
export * as utils from "./utils"
|
export * as utils from "./utils"
|
||||||
export * as errors from "./errors"
|
export * as errors from "./errors"
|
||||||
|
export * as timers from "./timers"
|
||||||
export { default as env } from "./environment"
|
export { default as env } from "./environment"
|
||||||
export { SearchParams } from "./db"
|
export { SearchParams } from "./db"
|
||||||
// Add context to tenancy for backwards compatibility
|
// Add context to tenancy for backwards compatibility
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { JobQueue } from "./constants"
|
||||||
import InMemoryQueue from "./inMemoryQueue"
|
import InMemoryQueue from "./inMemoryQueue"
|
||||||
import BullQueue from "bull"
|
import BullQueue from "bull"
|
||||||
import { addListeners, StalledFn } from "./listeners"
|
import { addListeners, StalledFn } from "./listeners"
|
||||||
|
import * as timers from "../timers"
|
||||||
|
|
||||||
const CLEANUP_PERIOD_MS = 60 * 1000
|
const CLEANUP_PERIOD_MS = 60 * 1000
|
||||||
let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = []
|
let QUEUES: BullQueue.Queue[] | InMemoryQueue[] = []
|
||||||
|
@ -29,8 +30,8 @@ export function createQueue<T>(
|
||||||
}
|
}
|
||||||
addListeners(queue, jobQueue, opts?.removeStalledCb)
|
addListeners(queue, jobQueue, opts?.removeStalledCb)
|
||||||
QUEUES.push(queue)
|
QUEUES.push(queue)
|
||||||
if (!cleanupInterval) {
|
if (!cleanupInterval && !env.isTest()) {
|
||||||
cleanupInterval = setInterval(cleanup, CLEANUP_PERIOD_MS)
|
cleanupInterval = timers.set(cleanup, CLEANUP_PERIOD_MS)
|
||||||
// fire off an initial cleanup
|
// fire off an initial cleanup
|
||||||
cleanup().catch(err => {
|
cleanup().catch(err => {
|
||||||
console.error(`Unable to cleanup automation queue initially - ${err}`)
|
console.error(`Unable to cleanup automation queue initially - ${err}`)
|
||||||
|
@ -41,7 +42,7 @@ export function createQueue<T>(
|
||||||
|
|
||||||
export async function shutdown() {
|
export async function shutdown() {
|
||||||
if (cleanupInterval) {
|
if (cleanupInterval) {
|
||||||
clearInterval(cleanupInterval)
|
timers.clear(cleanupInterval)
|
||||||
}
|
}
|
||||||
if (QUEUES.length) {
|
if (QUEUES.length) {
|
||||||
for (let queue of QUEUES) {
|
for (let queue of QUEUES) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
SEPARATOR,
|
SEPARATOR,
|
||||||
SelectableDatabase,
|
SelectableDatabase,
|
||||||
} from "./utils"
|
} from "./utils"
|
||||||
|
import * as timers from "../timers"
|
||||||
|
|
||||||
const RETRY_PERIOD_MS = 2000
|
const RETRY_PERIOD_MS = 2000
|
||||||
const STARTUP_TIMEOUT_MS = 5000
|
const STARTUP_TIMEOUT_MS = 5000
|
||||||
|
@ -117,9 +118,9 @@ function waitForConnection(selectDb: number = DEFAULT_SELECT_DB) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// check if the connection is ready
|
// check if the connection is ready
|
||||||
const interval = setInterval(() => {
|
const interval = timers.set(() => {
|
||||||
if (CONNECTED) {
|
if (CONNECTED) {
|
||||||
clearInterval(interval)
|
timers.clear(interval)
|
||||||
resolve("")
|
resolve("")
|
||||||
}
|
}
|
||||||
}, 500)
|
}, 500)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./timers"
|
|
@ -0,0 +1,22 @@
|
||||||
|
let intervals: NodeJS.Timeout[] = []
|
||||||
|
|
||||||
|
export function set(callback: () => any, period: number) {
|
||||||
|
const interval = setInterval(callback, period)
|
||||||
|
intervals.push(interval)
|
||||||
|
return interval
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clear(interval: NodeJS.Timeout) {
|
||||||
|
const idx = intervals.indexOf(interval)
|
||||||
|
if (idx !== -1) {
|
||||||
|
intervals.splice(idx, 1)
|
||||||
|
}
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cleanup() {
|
||||||
|
for (let interval of intervals) {
|
||||||
|
clearInterval(interval)
|
||||||
|
}
|
||||||
|
intervals = []
|
||||||
|
}
|
|
@ -4,3 +4,4 @@ process.env.NODE_ENV = "jest"
|
||||||
process.env.MOCK_REDIS = "1"
|
process.env.MOCK_REDIS = "1"
|
||||||
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
||||||
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
||||||
|
process.env.REDIS_PASSWORD = "budibase"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import "./logging"
|
import "./logging"
|
||||||
import env from "../src/environment"
|
import env from "../src/environment"
|
||||||
|
import { cleanup } from "../src/timers"
|
||||||
import { mocks, testContainerUtils } from "./utilities"
|
import { mocks, testContainerUtils } from "./utilities"
|
||||||
|
|
||||||
// must explicitly enable fetch mock
|
// must explicitly enable fetch mock
|
||||||
|
@ -21,3 +22,7 @@ if (!process.env.CI) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testContainerUtils.setupEnv(env)
|
testContainerUtils.setupEnv(env)
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
cleanup()
|
||||||
|
})
|
||||||
|
|
|
@ -4,6 +4,7 @@ module FetchMock {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const fetch = jest.requireActual("node-fetch")
|
const fetch = jest.requireActual("node-fetch")
|
||||||
let failCount = 0
|
let failCount = 0
|
||||||
|
let mockSearch = false
|
||||||
|
|
||||||
const func = async (url: any, opts: any) => {
|
const func = async (url: any, opts: any) => {
|
||||||
function json(body: any, status = 200) {
|
function json(body: any, status = 200) {
|
||||||
|
@ -69,7 +70,7 @@ module FetchMock {
|
||||||
},
|
},
|
||||||
404
|
404
|
||||||
)
|
)
|
||||||
} else if (url.includes("_search")) {
|
} else if (mockSearch && url.includes("_search")) {
|
||||||
const body = opts.body
|
const body = opts.body
|
||||||
const parts = body.split("tableId:")
|
const parts = body.split("tableId:")
|
||||||
let tableId
|
let tableId
|
||||||
|
@ -192,5 +193,9 @@ module FetchMock {
|
||||||
|
|
||||||
func.Headers = fetch.Headers
|
func.Headers = fetch.Headers
|
||||||
|
|
||||||
|
func.mockSearch = () => {
|
||||||
|
mockSearch = true
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = func
|
module.exports = func
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ const config: Config.InitialOptions = {
|
||||||
"../backend-core/src/**/*.{js,ts}",
|
"../backend-core/src/**/*.{js,ts}",
|
||||||
// The use of coverage with couchdb view functions breaks tests
|
// The use of coverage with couchdb view functions breaks tests
|
||||||
"!src/db/views/staticViews.*",
|
"!src/db/views/staticViews.*",
|
||||||
|
"!src/**/*.spec.{js,ts}",
|
||||||
],
|
],
|
||||||
coverageReporters: ["lcov", "json", "clover"],
|
coverageReporters: ["lcov", "json", "clover"],
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
"build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
|
||||||
"debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js",
|
"debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js",
|
||||||
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
|
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
|
||||||
"test": "bash scripts/test.sh",
|
"test": "NODE_OPTIONS=\"--max-old-space-size=4096\" bash scripts/test.sh",
|
||||||
|
"test:memory": "jest --maxWorkers=2 --logHeapUsage --forceExit",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client",
|
"predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client",
|
||||||
"build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
|
"build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
|
||||||
|
@ -125,7 +126,7 @@
|
||||||
"@babel/core": "7.17.4",
|
"@babel/core": "7.17.4",
|
||||||
"@babel/preset-env": "7.16.11",
|
"@babel/preset-env": "7.16.11",
|
||||||
"@budibase/standard-components": "^0.9.139",
|
"@budibase/standard-components": "^0.9.139",
|
||||||
"@jest/test-sequencer": "24.9.0",
|
"@jest/test-sequencer": "29.5.0",
|
||||||
"@swc/core": "^1.3.25",
|
"@swc/core": "^1.3.25",
|
||||||
"@swc/jest": "^0.2.24",
|
"@swc/jest": "^0.2.24",
|
||||||
"@trendyol/jest-testcontainers": "^2.1.1",
|
"@trendyol/jest-testcontainers": "^2.1.1",
|
||||||
|
@ -134,7 +135,7 @@
|
||||||
"@types/global-agent": "2.1.1",
|
"@types/global-agent": "2.1.1",
|
||||||
"@types/google-spreadsheet": "3.1.5",
|
"@types/google-spreadsheet": "3.1.5",
|
||||||
"@types/ioredis": "4.28.10",
|
"@types/ioredis": "4.28.10",
|
||||||
"@types/jest": "27.5.1",
|
"@types/jest": "29.5.0",
|
||||||
"@types/koa": "2.13.4",
|
"@types/koa": "2.13.4",
|
||||||
"@types/koa__router": "8.0.8",
|
"@types/koa__router": "8.0.8",
|
||||||
"@types/lodash": "4.14.180",
|
"@types/lodash": "4.14.180",
|
||||||
|
@ -154,7 +155,7 @@
|
||||||
"eslint": "6.8.0",
|
"eslint": "6.8.0",
|
||||||
"ioredis-mock": "7.2.0",
|
"ioredis-mock": "7.2.0",
|
||||||
"is-wsl": "2.2.0",
|
"is-wsl": "2.2.0",
|
||||||
"jest": "28.1.1",
|
"jest": "29.5.0",
|
||||||
"jest-openapi": "0.14.2",
|
"jest-openapi": "0.14.2",
|
||||||
"jest-serial-runner": "^1.2.1",
|
"jest-serial-runner": "^1.2.1",
|
||||||
"nodemon": "2.0.15",
|
"nodemon": "2.0.15",
|
||||||
|
@ -166,7 +167,7 @@
|
||||||
"supertest": "6.2.2",
|
"supertest": "6.2.2",
|
||||||
"swagger-jsdoc": "6.1.0",
|
"swagger-jsdoc": "6.1.0",
|
||||||
"timekeeper": "2.2.0",
|
"timekeeper": "2.2.0",
|
||||||
"ts-jest": "28.0.4",
|
"ts-jest": "29.0.5",
|
||||||
"ts-node": "10.8.1",
|
"ts-node": "10.8.1",
|
||||||
"tsconfig-paths": "4.0.0",
|
"tsconfig-paths": "4.0.0",
|
||||||
"typescript": "4.7.3",
|
"typescript": "4.7.3",
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
if [[ -n $CI ]]
|
if [[ -n $CI ]]
|
||||||
then
|
then
|
||||||
# --runInBand performs better in ci where resources are limited
|
# --runInBand performs better in ci where resources are limited
|
||||||
echo "jest --coverage --runInBand"
|
echo "jest --coverage --runInBand --forceExit"
|
||||||
jest --coverage --runInBand
|
jest --coverage --runInBand --forceExit
|
||||||
else
|
else
|
||||||
# --maxWorkers performs better in development
|
# --maxWorkers performs better in development
|
||||||
echo "jest --coverage --maxWorkers=2"
|
echo "jest --coverage --maxWorkers=2"
|
||||||
jest --coverage --maxWorkers=2
|
jest --coverage --maxWorkers=2
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -44,7 +44,6 @@ import {
|
||||||
Layout,
|
Layout,
|
||||||
Screen,
|
Screen,
|
||||||
MigrationType,
|
MigrationType,
|
||||||
BBContext,
|
|
||||||
Database,
|
Database,
|
||||||
UserCtx,
|
UserCtx,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
@ -74,14 +73,14 @@ async function getScreens() {
|
||||||
).rows.map((row: any) => row.doc)
|
).rows.map((row: any) => row.doc)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUserRoleId(ctx: BBContext) {
|
function getUserRoleId(ctx: UserCtx) {
|
||||||
return !ctx.user?.role || !ctx.user.role._id
|
return !ctx.user?.role || !ctx.user.role._id
|
||||||
? roles.BUILTIN_ROLE_IDS.PUBLIC
|
? roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
: ctx.user.role._id
|
: ctx.user.role._id
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAppUrl(
|
function checkAppUrl(
|
||||||
ctx: BBContext,
|
ctx: UserCtx,
|
||||||
apps: App[],
|
apps: App[],
|
||||||
url: string,
|
url: string,
|
||||||
currentAppId?: string
|
currentAppId?: string
|
||||||
|
@ -95,7 +94,7 @@ function checkAppUrl(
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkAppName(
|
function checkAppName(
|
||||||
ctx: BBContext,
|
ctx: UserCtx,
|
||||||
apps: App[],
|
apps: App[],
|
||||||
name: string,
|
name: string,
|
||||||
currentAppId?: string
|
currentAppId?: string
|
||||||
|
@ -160,7 +159,7 @@ async function addDefaultTables(db: Database) {
|
||||||
await db.bulkDocs([...defaultDbDocs])
|
await db.bulkDocs([...defaultDbDocs])
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetch(ctx: BBContext) {
|
export async function fetch(ctx: UserCtx) {
|
||||||
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
const dev = ctx.query && ctx.query.status === AppStatus.DEV
|
||||||
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
const all = ctx.query && ctx.query.status === AppStatus.ALL
|
||||||
const apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
const apps = (await dbCore.getAllApps({ dev, all })) as App[]
|
||||||
|
@ -185,7 +184,7 @@ export async function fetch(ctx: BBContext) {
|
||||||
ctx.body = await checkAppMetadata(apps)
|
ctx.body = await checkAppMetadata(apps)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAppDefinition(ctx: BBContext) {
|
export async function fetchAppDefinition(ctx: UserCtx) {
|
||||||
const layouts = await getLayouts()
|
const layouts = await getLayouts()
|
||||||
const userRoleId = getUserRoleId(ctx)
|
const userRoleId = getUserRoleId(ctx)
|
||||||
const accessController = new roles.AccessController()
|
const accessController = new roles.AccessController()
|
||||||
|
@ -231,7 +230,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function performAppCreate(ctx: BBContext) {
|
async function performAppCreate(ctx: UserCtx) {
|
||||||
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
||||||
const name = ctx.request.body.name,
|
const name = ctx.request.body.name,
|
||||||
possibleUrl = ctx.request.body.url
|
possibleUrl = ctx.request.body.url
|
||||||
|
@ -360,7 +359,7 @@ async function creationEvents(request: any, app: App) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function appPostCreate(ctx: BBContext, app: App) {
|
async function appPostCreate(ctx: UserCtx, app: App) {
|
||||||
const tenantId = tenancy.getTenantId()
|
const tenantId = tenancy.getTenantId()
|
||||||
await migrations.backPopulateMigrations({
|
await migrations.backPopulateMigrations({
|
||||||
type: MigrationType.APP,
|
type: MigrationType.APP,
|
||||||
|
@ -391,7 +390,7 @@ async function appPostCreate(ctx: BBContext, app: App) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function create(ctx: BBContext) {
|
export async function create(ctx: UserCtx) {
|
||||||
const newApplication = await quotas.addApp(() => performAppCreate(ctx))
|
const newApplication = await quotas.addApp(() => performAppCreate(ctx))
|
||||||
await appPostCreate(ctx, newApplication)
|
await appPostCreate(ctx, newApplication)
|
||||||
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
await cache.bustCache(cache.CacheKey.CHECKLIST)
|
||||||
|
@ -401,7 +400,7 @@ export async function create(ctx: BBContext) {
|
||||||
|
|
||||||
// This endpoint currently operates as a PATCH rather than a PUT
|
// This endpoint currently operates as a PATCH rather than a PUT
|
||||||
// Thus name and url fields are handled only if present
|
// Thus name and url fields are handled only if present
|
||||||
export async function update(ctx: BBContext) {
|
export async function update(ctx: UserCtx) {
|
||||||
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
const apps = (await dbCore.getAllApps({ dev: true })) as App[]
|
||||||
// validation
|
// validation
|
||||||
const name = ctx.request.body.name,
|
const name = ctx.request.body.name,
|
||||||
|
@ -421,7 +420,7 @@ export async function update(ctx: BBContext) {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function updateClient(ctx: BBContext) {
|
export async function updateClient(ctx: UserCtx) {
|
||||||
// Get current app version
|
// Get current app version
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentType.APP_METADATA)
|
const application = await db.get(DocumentType.APP_METADATA)
|
||||||
|
@ -445,7 +444,7 @@ export async function updateClient(ctx: BBContext) {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function revertClient(ctx: BBContext) {
|
export async function revertClient(ctx: UserCtx) {
|
||||||
// Check app can be reverted
|
// Check app can be reverted
|
||||||
const db = context.getAppDB()
|
const db = context.getAppDB()
|
||||||
const application = await db.get(DocumentType.APP_METADATA)
|
const application = await db.get(DocumentType.APP_METADATA)
|
||||||
|
@ -471,7 +470,7 @@ export async function revertClient(ctx: BBContext) {
|
||||||
ctx.body = app
|
ctx.body = app
|
||||||
}
|
}
|
||||||
|
|
||||||
const unpublishApp = async (ctx: any) => {
|
async function unpublishApp(ctx: UserCtx) {
|
||||||
let appId = ctx.params.appId
|
let appId = ctx.params.appId
|
||||||
appId = dbCore.getProdAppID(appId)
|
appId = dbCore.getProdAppID(appId)
|
||||||
|
|
||||||
|
@ -487,7 +486,7 @@ const unpublishApp = async (ctx: any) => {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function destroyApp(ctx: BBContext) {
|
async function destroyApp(ctx: UserCtx) {
|
||||||
let appId = ctx.params.appId
|
let appId = ctx.params.appId
|
||||||
appId = dbCore.getProdAppID(appId)
|
appId = dbCore.getProdAppID(appId)
|
||||||
const devAppId = dbCore.getDevAppID(appId)
|
const devAppId = dbCore.getDevAppID(appId)
|
||||||
|
@ -515,12 +514,12 @@ async function destroyApp(ctx: BBContext) {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async function preDestroyApp(ctx: BBContext) {
|
async function preDestroyApp(ctx: UserCtx) {
|
||||||
const { rows } = await getUniqueRows([ctx.params.appId])
|
const { rows } = await getUniqueRows([ctx.params.appId])
|
||||||
ctx.rowCount = rows.length
|
ctx.rowCount = rows.length
|
||||||
}
|
}
|
||||||
|
|
||||||
async function postDestroyApp(ctx: BBContext) {
|
async function postDestroyApp(ctx: UserCtx) {
|
||||||
const rowCount = ctx.rowCount
|
const rowCount = ctx.rowCount
|
||||||
await groups.cleanupApp(ctx.params.appId)
|
await groups.cleanupApp(ctx.params.appId)
|
||||||
if (rowCount) {
|
if (rowCount) {
|
||||||
|
@ -528,7 +527,7 @@ async function postDestroyApp(ctx: BBContext) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function destroy(ctx: BBContext) {
|
export async function destroy(ctx: UserCtx) {
|
||||||
await preDestroyApp(ctx)
|
await preDestroyApp(ctx)
|
||||||
const result = await destroyApp(ctx)
|
const result = await destroyApp(ctx)
|
||||||
await postDestroyApp(ctx)
|
await postDestroyApp(ctx)
|
||||||
|
@ -536,7 +535,7 @@ export async function destroy(ctx: BBContext) {
|
||||||
ctx.body = result
|
ctx.body = result
|
||||||
}
|
}
|
||||||
|
|
||||||
export const unpublish = async (ctx: BBContext) => {
|
export async function unpublish(ctx: UserCtx) {
|
||||||
const prodAppId = dbCore.getProdAppID(ctx.params.appId)
|
const prodAppId = dbCore.getProdAppID(ctx.params.appId)
|
||||||
const dbExists = await dbCore.dbExists(prodAppId)
|
const dbExists = await dbCore.dbExists(prodAppId)
|
||||||
|
|
||||||
|
@ -551,7 +550,7 @@ export const unpublish = async (ctx: BBContext) => {
|
||||||
ctx.status = 204
|
ctx.status = 204
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sync(ctx: BBContext) {
|
export async function sync(ctx: UserCtx) {
|
||||||
const appId = ctx.params.appId
|
const appId = ctx.params.appId
|
||||||
try {
|
try {
|
||||||
ctx.body = await sdk.applications.syncApp(appId)
|
ctx.body = await sdk.applications.syncApp(appId)
|
||||||
|
|
|
@ -62,10 +62,11 @@ export async function validate({
|
||||||
}
|
}
|
||||||
const errors: any = {}
|
const errors: any = {}
|
||||||
for (let fieldName of Object.keys(fetchedTable.schema)) {
|
for (let fieldName of Object.keys(fetchedTable.schema)) {
|
||||||
const constraints = cloneDeep(fetchedTable.schema[fieldName].constraints)
|
const column = fetchedTable.schema[fieldName]
|
||||||
const type = fetchedTable.schema[fieldName].type
|
const constraints = cloneDeep(column.constraints)
|
||||||
|
const type = column.type
|
||||||
// formulas shouldn't validated, data will be deleted anyway
|
// formulas shouldn't validated, data will be deleted anyway
|
||||||
if (type === FieldTypes.FORMULA) {
|
if (type === FieldTypes.FORMULA || column.autocolumn) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
// special case for options, need to always allow unselected (null)
|
// special case for options, need to always allow unselected (null)
|
||||||
|
|
|
@ -1,48 +1,48 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
|
exports[`viewBuilder Calculate and filter creates a view with the calculation statistics and filter schema 1`] = `
|
||||||
Object {
|
{
|
||||||
"map": "function (doc) {
|
"map": "function (doc) {
|
||||||
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
|
||||||
doc[\\"myField\\"] === undefined ||
|
doc["myField"] === undefined ||
|
||||||
doc[\\"myField\\"] === null ||
|
doc["myField"] === null ||
|
||||||
doc[\\"myField\\"] === \\"\\" ||
|
doc["myField"] === "" ||
|
||||||
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
(Array.isArray(doc["myField"]) && doc["myField"].length === 0)
|
||||||
)) && (doc[\\"age\\"] > 17)) {
|
)) && (doc["age"] > 17)) {
|
||||||
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
emit(doc["_id"], doc["myField"]);
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
"meta": Object {
|
"meta": {
|
||||||
"calculation": "stats",
|
"calculation": "stats",
|
||||||
"field": "myField",
|
"field": "myField",
|
||||||
"filters": Array [
|
"filters": [
|
||||||
Object {
|
{
|
||||||
"condition": "MT",
|
"condition": "MT",
|
||||||
"key": "age",
|
"key": "age",
|
||||||
"value": 17,
|
"value": 17,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"groupBy": undefined,
|
"groupBy": undefined,
|
||||||
"schema": Object {
|
"schema": {
|
||||||
"avg": Object {
|
"avg": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"count": Object {
|
"count": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"field": Object {
|
"field": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"max": Object {
|
"max": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"min": Object {
|
"min": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"sum": Object {
|
"sum": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"sumsqr": Object {
|
"sumsqr": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -53,42 +53,42 @@ Object {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
exports[`viewBuilder Calculate creates a view with the calculation statistics schema 1`] = `
|
||||||
Object {
|
{
|
||||||
"map": "function (doc) {
|
"map": "function (doc) {
|
||||||
if ((doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && !(
|
if ((doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && !(
|
||||||
doc[\\"myField\\"] === undefined ||
|
doc["myField"] === undefined ||
|
||||||
doc[\\"myField\\"] === null ||
|
doc["myField"] === null ||
|
||||||
doc[\\"myField\\"] === \\"\\" ||
|
doc["myField"] === "" ||
|
||||||
(Array.isArray(doc[\\"myField\\"]) && doc[\\"myField\\"].length === 0)
|
(Array.isArray(doc["myField"]) && doc["myField"].length === 0)
|
||||||
)) ) {
|
)) ) {
|
||||||
emit(doc[\\"_id\\"], doc[\\"myField\\"]);
|
emit(doc["_id"], doc["myField"]);
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
"meta": Object {
|
"meta": {
|
||||||
"calculation": "stats",
|
"calculation": "stats",
|
||||||
"field": "myField",
|
"field": "myField",
|
||||||
"filters": Array [],
|
"filters": [],
|
||||||
"groupBy": undefined,
|
"groupBy": undefined,
|
||||||
"schema": Object {
|
"schema": {
|
||||||
"avg": Object {
|
"avg": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"count": Object {
|
"count": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"field": Object {
|
"field": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"max": Object {
|
"max": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"min": Object {
|
"min": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"sum": Object {
|
"sum": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
"sumsqr": Object {
|
"sumsqr": {
|
||||||
"type": "number",
|
"type": "number",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -99,22 +99,22 @@ Object {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`viewBuilder Filter creates a view with multiple filters and conjunctions 1`] = `
|
exports[`viewBuilder Filter creates a view with multiple filters and conjunctions 1`] = `
|
||||||
Object {
|
{
|
||||||
"map": "function (doc) {
|
"map": "function (doc) {
|
||||||
if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" && (doc[\\"Name\\"] === \\"Test\\" || doc[\\"Yes\\"] > \\"Value\\")) {
|
if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" && (doc["Name"] === "Test" || doc["Yes"] > "Value")) {
|
||||||
emit(doc[\\"_id\\"], doc[\\"undefined\\"]);
|
emit(doc["_id"], doc["undefined"]);
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
"meta": Object {
|
"meta": {
|
||||||
"calculation": undefined,
|
"calculation": undefined,
|
||||||
"field": undefined,
|
"field": undefined,
|
||||||
"filters": Array [
|
"filters": [
|
||||||
Object {
|
{
|
||||||
"condition": "EQUALS",
|
"condition": "EQUALS",
|
||||||
"key": "Name",
|
"key": "Name",
|
||||||
"value": "Test",
|
"value": "Test",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"condition": "MT",
|
"condition": "MT",
|
||||||
"conjunction": "OR",
|
"conjunction": "OR",
|
||||||
"key": "Yes",
|
"key": "Yes",
|
||||||
|
@ -129,16 +129,16 @@ Object {
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`viewBuilder Group By creates a view emitting the group by field 1`] = `
|
exports[`viewBuilder Group By creates a view emitting the group by field 1`] = `
|
||||||
Object {
|
{
|
||||||
"map": "function (doc) {
|
"map": "function (doc) {
|
||||||
if (doc.tableId === \\"14f1c4e94d6a47b682ce89d35d4c78b0\\" ) {
|
if (doc.tableId === "14f1c4e94d6a47b682ce89d35d4c78b0" ) {
|
||||||
emit(doc[\\"age\\"], doc[\\"score\\"]);
|
emit(doc["age"], doc["score"]);
|
||||||
}
|
}
|
||||||
}",
|
}",
|
||||||
"meta": Object {
|
"meta": {
|
||||||
"calculation": undefined,
|
"calculation": undefined,
|
||||||
"field": "score",
|
"field": "score",
|
||||||
"filters": Array [],
|
"filters": [],
|
||||||
"groupBy": "age",
|
"groupBy": "age",
|
||||||
"schema": null,
|
"schema": null,
|
||||||
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
"tableId": "14f1c4e94d6a47b682ce89d35d4c78b0",
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`/datasources fetch returns all the datasources from the server 1`] = `
|
exports[`/datasources fetch returns all the datasources from the server 1`] = `
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"config": Object {},
|
"config": {},
|
||||||
"entities": Array [
|
"entities": [
|
||||||
Object {
|
{
|
||||||
"_id": "ta_users",
|
"_id": "ta_users",
|
||||||
"_rev": "1-2375e1bc58aeec664dc1b1f04ad43e44",
|
"_rev": "1-2375e1bc58aeec664dc1b1f04ad43e44",
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
"name": "Users",
|
"name": "Users",
|
||||||
"primaryDisplay": "email",
|
"primaryDisplay": "email",
|
||||||
"schema": Object {
|
"schema": {
|
||||||
"email": Object {
|
"email": {
|
||||||
"constraints": Object {
|
"constraints": {
|
||||||
"email": true,
|
"email": true,
|
||||||
"length": Object {
|
"length": {
|
||||||
"maximum": "",
|
"maximum": "",
|
||||||
},
|
},
|
||||||
"presence": true,
|
"presence": true,
|
||||||
|
@ -25,8 +25,8 @@ Array [
|
||||||
"name": "email",
|
"name": "email",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"firstName": Object {
|
"firstName": {
|
||||||
"constraints": Object {
|
"constraints": {
|
||||||
"presence": false,
|
"presence": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
@ -34,8 +34,8 @@ Array [
|
||||||
"name": "firstName",
|
"name": "firstName",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"lastName": Object {
|
"lastName": {
|
||||||
"constraints": Object {
|
"constraints": {
|
||||||
"presence": false,
|
"presence": false,
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
|
@ -43,9 +43,9 @@ Array [
|
||||||
"name": "lastName",
|
"name": "lastName",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
},
|
},
|
||||||
"roleId": Object {
|
"roleId": {
|
||||||
"constraints": Object {
|
"constraints": {
|
||||||
"inclusion": Array [
|
"inclusion": [
|
||||||
"ADMIN",
|
"ADMIN",
|
||||||
"POWER",
|
"POWER",
|
||||||
"BASIC",
|
"BASIC",
|
||||||
|
@ -58,9 +58,9 @@ Array [
|
||||||
"name": "roleId",
|
"name": "roleId",
|
||||||
"type": "options",
|
"type": "options",
|
||||||
},
|
},
|
||||||
"status": Object {
|
"status": {
|
||||||
"constraints": Object {
|
"constraints": {
|
||||||
"inclusion": Array [
|
"inclusion": [
|
||||||
"active",
|
"active",
|
||||||
"inactive",
|
"inactive",
|
||||||
],
|
],
|
||||||
|
@ -74,15 +74,15 @@ Array [
|
||||||
},
|
},
|
||||||
"type": "table",
|
"type": "table",
|
||||||
"updatedAt": "2020-01-01T00:00:00.000Z",
|
"updatedAt": "2020-01-01T00:00:00.000Z",
|
||||||
"views": Object {},
|
"views": {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
"name": "Budibase DB",
|
"name": "Budibase DB",
|
||||||
"source": "BUDIBASE",
|
"source": "BUDIBASE",
|
||||||
"type": "budibase",
|
"type": "budibase",
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"config": Object {},
|
"config": {},
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
"name": "Test",
|
"name": "Test",
|
||||||
"source": "POSTGRES",
|
"source": "POSTGRES",
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`/views query returns data for the created view 1`] = `
|
exports[`/views query returns data for the created view 1`] = `
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"avg": 2333.3333333333335,
|
"avg": 2333.3333333333335,
|
||||||
"count": 3,
|
"count": 3,
|
||||||
"group": null,
|
"group": null,
|
||||||
|
@ -15,8 +15,8 @@ Array [
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`/views query returns data for the created view using a group by 1`] = `
|
exports[`/views query returns data for the created view using a group by 1`] = `
|
||||||
Array [
|
[
|
||||||
Object {
|
{
|
||||||
"avg": 1500,
|
"avg": 1500,
|
||||||
"count": 2,
|
"count": 2,
|
||||||
"group": "One",
|
"group": "One",
|
||||||
|
@ -25,7 +25,7 @@ Array [
|
||||||
"sum": 3000,
|
"sum": 3000,
|
||||||
"sumsqr": 5000000,
|
"sumsqr": 5000000,
|
||||||
},
|
},
|
||||||
Object {
|
{
|
||||||
"avg": 4000,
|
"avg": 4000,
|
||||||
"count": 1,
|
"count": 1,
|
||||||
"group": "Two",
|
"group": "Two",
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
import * as setup from "./utilities"
|
||||||
|
import { roles, db as dbCore } from "@budibase/backend-core"
|
||||||
|
|
||||||
|
describe("/api/applications/:appId/sync", () => {
|
||||||
|
let request = setup.getRequest()
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let app
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
app = await config.init()
|
||||||
|
// create some users which we will use throughout the tests
|
||||||
|
await config.createUser({
|
||||||
|
email: "sync1@test.com",
|
||||||
|
roles: {
|
||||||
|
[app._id!]: roles.BUILTIN_ROLE_IDS.BASIC,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
async function getUserMetadata() {
|
||||||
|
const { rows } = await config.searchRows(dbCore.InternalTable.USER_METADATA)
|
||||||
|
return rows
|
||||||
|
}
|
||||||
|
|
||||||
|
it("make sure that user metadata is correctly sync'd", async () => {
|
||||||
|
const rows = await getUserMetadata()
|
||||||
|
expect(rows.length).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,6 +1,7 @@
|
||||||
|
const fetch = require("node-fetch")
|
||||||
|
fetch.mockSearch()
|
||||||
const search = require("../../controllers/row/internalSearch")
|
const search = require("../../controllers/row/internalSearch")
|
||||||
// this will be mocked out for _search endpoint
|
// this will be mocked out for _search endpoint
|
||||||
const fetch = require("node-fetch")
|
|
||||||
const PARAMS = {
|
const PARAMS = {
|
||||||
tableId: "ta_12345679abcdef",
|
tableId: "ta_12345679abcdef",
|
||||||
version: "1",
|
version: "1",
|
||||||
|
@ -20,7 +21,7 @@ function checkLucene(resp, expected, params = PARAMS) {
|
||||||
expect(json.bookmark).toBe(PARAMS.bookmark)
|
expect(json.bookmark).toBe(PARAMS.bookmark)
|
||||||
}
|
}
|
||||||
expect(json.include_docs).toBe(true)
|
expect(json.include_docs).toBe(true)
|
||||||
expect(json.q).toBe(`(${expected}) AND tableId:"${params.tableId}"`)
|
expect(json.q).toBe(`${expected} AND tableId:"${params.tableId}"`)
|
||||||
expect(json.limit).toBe(params.limit || 50)
|
expect(json.limit).toBe(params.limit || 50)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ describe("internal search", () => {
|
||||||
"column": "1",
|
"column": "1",
|
||||||
}
|
}
|
||||||
}, PARAMS)
|
}, PARAMS)
|
||||||
checkLucene(response, `column:"2" OR !column:"1"`)
|
checkLucene(response, `(column:"2" OR !column:"1")`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test AND query", async () => {
|
it("test AND query", async () => {
|
||||||
|
@ -71,7 +72,7 @@ describe("internal search", () => {
|
||||||
"column": "1",
|
"column": "1",
|
||||||
}
|
}
|
||||||
}, PARAMS)
|
}, PARAMS)
|
||||||
checkLucene(response, `*:* AND column:"2" AND !column:"1"`)
|
checkLucene(response, `(*:* AND column:"2" AND !column:"1")`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test pagination query", async () => {
|
it("test pagination query", async () => {
|
||||||
|
@ -132,7 +133,7 @@ describe("internal search", () => {
|
||||||
"colArr": [1, 2, 3],
|
"colArr": [1, 2, 3],
|
||||||
},
|
},
|
||||||
}, PARAMS)
|
}, PARAMS)
|
||||||
checkLucene(response, `*:* AND column:a AND colArr:(1 AND 2 AND 3)`, PARAMS)
|
checkLucene(response, `(*:* AND column:a AND colArr:(1 AND 2 AND 3))`, PARAMS)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("test multiple of same column", async () => {
|
it("test multiple of same column", async () => {
|
||||||
|
@ -144,7 +145,7 @@ describe("internal search", () => {
|
||||||
"3:column": "c",
|
"3:column": "c",
|
||||||
},
|
},
|
||||||
}, PARAMS)
|
}, PARAMS)
|
||||||
checkLucene(response, `column:"a" OR column:"b" OR column:"c"`, PARAMS)
|
checkLucene(response, `(column:"a" OR column:"b" OR column:"c")`, PARAMS)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("check a weird case for lucene building", async () => {
|
it("check a weird case for lucene building", async () => {
|
||||||
|
@ -191,6 +192,6 @@ describe("internal search", () => {
|
||||||
expect(json.bookmark).toBe(PARAMS.bookmark)
|
expect(json.bookmark).toBe(PARAMS.bookmark)
|
||||||
}
|
}
|
||||||
expect(json.include_docs).toBe(true)
|
expect(json.include_docs).toBe(true)
|
||||||
expect(json.q).toBe(`(*:* AND column:"1") AND tableId:${PARAMS.tableId}`)
|
expect(json.q).toBe(`*:* AND column:"1" AND tableId:${PARAMS.tableId}`)
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -27,7 +27,7 @@ import * as api from "./api"
|
||||||
import * as automations from "./automations"
|
import * as automations from "./automations"
|
||||||
import { Thread } from "./threads"
|
import { Thread } from "./threads"
|
||||||
import * as redis from "./utilities/redis"
|
import * as redis from "./utilities/redis"
|
||||||
import { events, logging, middleware } from "@budibase/backend-core"
|
import { events, logging, middleware, timers } from "@budibase/backend-core"
|
||||||
import { initialise as initialiseWebsockets } from "./websocket"
|
import { initialise as initialiseWebsockets } from "./websocket"
|
||||||
import { startup } from "./startup"
|
import { startup } from "./startup"
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
|
@ -84,6 +84,7 @@ server.on("close", async () => {
|
||||||
}
|
}
|
||||||
shuttingDown = true
|
shuttingDown = true
|
||||||
console.log("Server Closed")
|
console.log("Server Closed")
|
||||||
|
timers.cleanup()
|
||||||
await automations.shutdown()
|
await automations.shutdown()
|
||||||
await redis.shutdown()
|
await redis.shutdown()
|
||||||
events.shutdown()
|
events.shutdown()
|
||||||
|
|
|
@ -23,7 +23,7 @@ import { LoopStep, LoopStepType, LoopInput } from "../definitions/automations"
|
||||||
* @returns {object} The inputs object which has had all the various types supported by this function converted to their
|
* @returns {object} The inputs object which has had all the various types supported by this function converted to their
|
||||||
* primitive types.
|
* primitive types.
|
||||||
*/
|
*/
|
||||||
export function cleanInputValues(inputs: Record<string, any>, schema: any) {
|
export function cleanInputValues(inputs: Record<string, any>, schema?: any) {
|
||||||
if (schema == null) {
|
if (schema == null) {
|
||||||
return inputs
|
return inputs
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
jest.mock("../../threads/automation")
|
|
||||||
jest.mock("../../utilities/redis", () => ({
|
|
||||||
init: jest.fn(),
|
|
||||||
checkTestFlag: () => {
|
|
||||||
return false
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
|
|
||||||
jest.spyOn(global.console, "error")
|
|
||||||
|
|
||||||
require("../../environment")
|
|
||||||
const automation = require("../index")
|
|
||||||
const thread = require("../../threads/automation")
|
|
||||||
const triggers = require("../triggers")
|
|
||||||
const { basicAutomation } = require("../../tests/utilities/structures")
|
|
||||||
const { wait } = require("../../utilities")
|
|
||||||
const { makePartial } = require("../../tests/utilities")
|
|
||||||
const { cleanInputValues } = require("../automationUtils")
|
|
||||||
const setup = require("./utilities")
|
|
||||||
|
|
||||||
describe("Run through some parts of the automations system", () => {
|
|
||||||
let config = setup.getConfig()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
await automation.init()
|
|
||||||
await config.init()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
|
||||||
|
|
||||||
it("should be able to init in builder", async () => {
|
|
||||||
await triggers.externalTrigger(basicAutomation(), { a: 1, appId: config.appId })
|
|
||||||
await wait(100)
|
|
||||||
expect(thread.execute).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should check coercion", async () => {
|
|
||||||
const table = await config.createTable()
|
|
||||||
const automation = basicAutomation()
|
|
||||||
automation.definition.trigger.inputs.tableId = table._id
|
|
||||||
automation.definition.trigger.stepId = "APP"
|
|
||||||
automation.definition.trigger.inputs.fields = { a: "number" }
|
|
||||||
await triggers.externalTrigger(automation, {
|
|
||||||
appId: config.getAppId(),
|
|
||||||
fields: {
|
|
||||||
a: "1"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
await wait(100)
|
|
||||||
expect(thread.execute).toHaveBeenCalledWith(makePartial({
|
|
||||||
data: {
|
|
||||||
event: {
|
|
||||||
fields: {
|
|
||||||
a: 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}), expect.any(Function))
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should be able to clean inputs with the utilities", () => {
|
|
||||||
// can't clean without a schema
|
|
||||||
let output = cleanInputValues({a: "1"})
|
|
||||||
expect(output.a).toBe("1")
|
|
||||||
output = cleanInputValues({a: "1", b: "true", c: "false", d: 1, e: "help"}, {
|
|
||||||
properties: {
|
|
||||||
a: {
|
|
||||||
type: "number",
|
|
||||||
},
|
|
||||||
b: {
|
|
||||||
type: "boolean",
|
|
||||||
},
|
|
||||||
c: {
|
|
||||||
type: "boolean",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
expect(output.a).toBe(1)
|
|
||||||
expect(output.b).toBe(true)
|
|
||||||
expect(output.c).toBe(false)
|
|
||||||
expect(output.d).toBe(1)
|
|
||||||
expect(output.e).toBe("help")
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
jest.mock("../../threads/automation")
|
||||||
|
jest.mock("../../utilities/redis", () => ({
|
||||||
|
init: jest.fn(),
|
||||||
|
checkTestFlag: () => {
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
jest.spyOn(global.console, "error")
|
||||||
|
|
||||||
|
import "../../environment"
|
||||||
|
import * as automation from "../index"
|
||||||
|
import * as thread from "../../threads/automation"
|
||||||
|
import * as triggers from "../triggers"
|
||||||
|
import { basicAutomation } from "../../tests/utilities/structures"
|
||||||
|
import { wait } from "../../utilities"
|
||||||
|
import { makePartial } from "../../tests/utilities"
|
||||||
|
import { cleanInputValues } from "../automationUtils"
|
||||||
|
import * as setup from "./utilities"
|
||||||
|
import { Automation } from "@budibase/types"
|
||||||
|
|
||||||
|
describe("Run through some parts of the automations system", () => {
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
await automation.init()
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await automation.shutdown()
|
||||||
|
setup.afterAll()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to init in builder", async () => {
|
||||||
|
const automation: Automation = {
|
||||||
|
...basicAutomation(),
|
||||||
|
appId: config.appId,
|
||||||
|
}
|
||||||
|
const fields: any = { a: 1, appId: config.appId }
|
||||||
|
await triggers.externalTrigger(automation, fields)
|
||||||
|
await wait(100)
|
||||||
|
expect(thread.execute).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check coercion", async () => {
|
||||||
|
const table = await config.createTable()
|
||||||
|
const automation: any = basicAutomation()
|
||||||
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
automation.definition.trigger.stepId = "APP"
|
||||||
|
automation.definition.trigger.inputs.fields = { a: "number" }
|
||||||
|
const fields: any = {
|
||||||
|
appId: config.getAppId(),
|
||||||
|
fields: {
|
||||||
|
a: "1",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
await triggers.externalTrigger(automation, fields)
|
||||||
|
await wait(100)
|
||||||
|
expect(thread.execute).toHaveBeenCalledWith(
|
||||||
|
makePartial({
|
||||||
|
data: {
|
||||||
|
event: {
|
||||||
|
fields: {
|
||||||
|
a: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
expect.any(Function)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to clean inputs with the utilities", () => {
|
||||||
|
// can't clean without a schema
|
||||||
|
let output = cleanInputValues({ a: "1" })
|
||||||
|
expect(output.a).toBe("1")
|
||||||
|
output = cleanInputValues(
|
||||||
|
{ a: "1", b: "true", c: "false", d: 1, e: "help" },
|
||||||
|
{
|
||||||
|
properties: {
|
||||||
|
a: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
expect(output.a).toBe(1)
|
||||||
|
expect(output.b).toBe(true)
|
||||||
|
expect(output.c).toBe(false)
|
||||||
|
expect(output.d).toBe(1)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,3 +1,6 @@
|
||||||
|
import fetch from "node-fetch"
|
||||||
|
// @ts-ignore
|
||||||
|
fetch.mockSearch()
|
||||||
import {
|
import {
|
||||||
generateMakeRequest,
|
generateMakeRequest,
|
||||||
MakeRequestResponse,
|
MakeRequestResponse,
|
||||||
|
@ -16,6 +19,7 @@ import _ from "lodash"
|
||||||
import { generator } from "@budibase/backend-core/tests"
|
import { generator } from "@budibase/backend-core/tests"
|
||||||
import { utils } from "@budibase/backend-core"
|
import { utils } from "@budibase/backend-core"
|
||||||
import { GenericContainer } from "testcontainers"
|
import { GenericContainer } from "testcontainers"
|
||||||
|
import { generateRowIdField } from "../integrations/utils"
|
||||||
|
|
||||||
const config = setup.getConfig()!
|
const config = setup.getConfig()!
|
||||||
|
|
||||||
|
@ -80,16 +84,10 @@ describe("row api - postgres", () => {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
|
||||||
presence: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
name: "title",
|
name: "title",
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
|
||||||
presence: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourceId: postgresDatasource._id,
|
sourceId: postgresDatasource._id,
|
||||||
|
@ -121,16 +119,10 @@ describe("row api - postgres", () => {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
autocolumn: true,
|
autocolumn: true,
|
||||||
constraints: {
|
|
||||||
presence: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
name: "name",
|
name: "name",
|
||||||
type: FieldType.STRING,
|
type: FieldType.STRING,
|
||||||
constraints: {
|
|
||||||
presence: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
name: "description",
|
name: "description",
|
||||||
|
@ -144,7 +136,6 @@ describe("row api - postgres", () => {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
|
||||||
},
|
},
|
||||||
fieldName: oneToManyRelationshipInfo.fieldName,
|
fieldName: oneToManyRelationshipInfo.fieldName,
|
||||||
name: "oneToManyRelation",
|
name: "oneToManyRelation",
|
||||||
|
@ -156,7 +147,6 @@ describe("row api - postgres", () => {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
|
||||||
},
|
},
|
||||||
fieldName: manyToOneRelationshipInfo.fieldName,
|
fieldName: manyToOneRelationshipInfo.fieldName,
|
||||||
name: "manyToOneRelation",
|
name: "manyToOneRelation",
|
||||||
|
@ -168,7 +158,6 @@ describe("row api - postgres", () => {
|
||||||
type: FieldType.LINK,
|
type: FieldType.LINK,
|
||||||
constraints: {
|
constraints: {
|
||||||
type: "array",
|
type: "array",
|
||||||
presence: false,
|
|
||||||
},
|
},
|
||||||
fieldName: manyToManyRelationshipInfo.fieldName,
|
fieldName: manyToManyRelationshipInfo.fieldName,
|
||||||
name: "manyToManyRelation",
|
name: "manyToManyRelation",
|
||||||
|
@ -309,9 +298,6 @@ describe("row api - postgres", () => {
|
||||||
id: {
|
id: {
|
||||||
name: "id",
|
name: "id",
|
||||||
type: FieldType.AUTO,
|
type: FieldType.AUTO,
|
||||||
constraints: {
|
|
||||||
presence: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
sourceId: postgresDatasource._id,
|
sourceId: postgresDatasource._id,
|
||||||
|
@ -921,47 +907,55 @@ describe("row api - postgres", () => {
|
||||||
foreignRows,
|
foreignRows,
|
||||||
x => x.relationshipType
|
x => x.relationshipType
|
||||||
)
|
)
|
||||||
expect(res.body).toEqual({
|
const m2mFieldName = manyToManyRelationshipInfo.fieldName,
|
||||||
...rowData,
|
o2mFieldName = oneToManyRelationshipInfo.fieldName,
|
||||||
[`fk_${oneToManyRelationshipInfo.table.name}_${oneToManyRelationshipInfo.fieldName}`]:
|
m2oFieldName = manyToOneRelationshipInfo.fieldName
|
||||||
foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row.id,
|
const m2mRow1 = res.body[m2mFieldName].find(
|
||||||
[oneToManyRelationshipInfo.fieldName]: [
|
(row: Row) => row.id === 1
|
||||||
|
)
|
||||||
|
const m2mRow2 = res.body[m2mFieldName].find(
|
||||||
|
(row: Row) => row.id === 2
|
||||||
|
)
|
||||||
|
expect(m2mRow1).toEqual({
|
||||||
|
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row,
|
||||||
|
[m2mFieldName]: [
|
||||||
{
|
{
|
||||||
...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row,
|
_id: row._id,
|
||||||
_id: expect.any(String),
|
|
||||||
_rev: expect.any(String),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[manyToOneRelationshipInfo.fieldName]: [
|
|
||||||
{
|
|
||||||
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row,
|
|
||||||
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
|
||||||
row.id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row,
|
|
||||||
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
|
||||||
row.id,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row,
|
|
||||||
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
|
||||||
row.id,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[manyToManyRelationshipInfo.fieldName]: [
|
|
||||||
{
|
|
||||||
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][0].row,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
id: row.id,
|
|
||||||
tableId: row.tableId,
|
|
||||||
_id: expect.any(String),
|
|
||||||
_rev: expect.any(String),
|
|
||||||
})
|
})
|
||||||
|
expect(m2mRow2).toEqual({
|
||||||
|
...foreignRowsByType[RelationshipTypes.MANY_TO_MANY][1].row,
|
||||||
|
[m2mFieldName]: [
|
||||||
|
{
|
||||||
|
_id: row._id,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
expect(res.body[m2oFieldName]).toEqual([
|
||||||
|
{
|
||||||
|
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][0].row,
|
||||||
|
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
||||||
|
row.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][1].row,
|
||||||
|
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
||||||
|
row.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
...foreignRowsByType[RelationshipTypes.MANY_TO_ONE][2].row,
|
||||||
|
[`fk_${manyToOneRelationshipInfo.table.name}_${manyToOneRelationshipInfo.fieldName}`]:
|
||||||
|
row.id,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
expect(res.body[o2mFieldName]).toEqual([
|
||||||
|
{
|
||||||
|
...foreignRowsByType[RelationshipTypes.ONE_TO_MANY][0].row,
|
||||||
|
_id: expect.any(String),
|
||||||
|
_rev: expect.any(String),
|
||||||
|
},
|
||||||
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -92,7 +92,7 @@ class RedisIntegration {
|
||||||
}
|
}
|
||||||
|
|
||||||
async disconnect() {
|
async disconnect() {
|
||||||
return this.client.disconnect()
|
return this.client.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
async redisContext(query: Function) {
|
async redisContext(query: Function) {
|
||||||
|
|
|
@ -39,6 +39,10 @@ describe("Google Sheets Integration", () => {
|
||||||
config.setGoogleAuth("test")
|
config.setGoogleAuth("test")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await config.end()
|
||||||
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
integration = new GoogleSheetsIntegration.integration({
|
integration = new GoogleSheetsIntegration.integration({
|
||||||
spreadsheetId: "randomId",
|
spreadsheetId: "randomId",
|
||||||
|
@ -99,8 +103,8 @@ describe("Google Sheets Integration", () => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
test("removing an existing field will not remove the data from the spreadsheet", async () => {
|
test("removing an existing field will remove the header from the google sheet", async () => {
|
||||||
await config.doInContext(structures.uuid(), async () => {
|
const sheet = await config.doInContext(structures.uuid(), async () => {
|
||||||
const tableColumns = ["name"]
|
const tableColumns = ["name"]
|
||||||
const table = createBasicTable(structures.uuid(), tableColumns)
|
const table = createBasicTable(structures.uuid(), tableColumns)
|
||||||
|
|
||||||
|
@ -109,18 +113,14 @@ describe("Google Sheets Integration", () => {
|
||||||
})
|
})
|
||||||
sheetsByTitle[table.name] = sheet
|
sheetsByTitle[table.name] = sheet
|
||||||
await integration.updateTable(table)
|
await integration.updateTable(table)
|
||||||
|
return sheet
|
||||||
expect(sheet.loadHeaderRow).toBeCalledTimes(1)
|
|
||||||
expect(sheet.setHeaderRow).toBeCalledTimes(1)
|
|
||||||
expect(sheet.setHeaderRow).toBeCalledWith([
|
|
||||||
"name",
|
|
||||||
"description",
|
|
||||||
"location",
|
|
||||||
])
|
|
||||||
|
|
||||||
// No undefineds are sent
|
|
||||||
expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(3)
|
|
||||||
})
|
})
|
||||||
|
expect(sheet.loadHeaderRow).toBeCalledTimes(1)
|
||||||
|
expect(sheet.setHeaderRow).toBeCalledTimes(1)
|
||||||
|
expect(sheet.setHeaderRow).toBeCalledWith(["name"])
|
||||||
|
|
||||||
|
// No undefined are sent
|
||||||
|
expect((sheet.setHeaderRow as any).mock.calls[0][0]).toHaveLength(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,17 +3,17 @@ import { default as RedisIntegration } from "../redis"
|
||||||
|
|
||||||
class TestConfiguration {
|
class TestConfiguration {
|
||||||
integration: any
|
integration: any
|
||||||
redis: any
|
|
||||||
|
|
||||||
constructor(config: any = {}) {
|
constructor(config: any = {}) {
|
||||||
this.integration = new RedisIntegration.integration(config)
|
this.integration = new RedisIntegration.integration(config)
|
||||||
this.redis = new Redis({
|
// have to kill the basic integration before replacing it
|
||||||
|
this.integration.client.quit()
|
||||||
|
this.integration.client = new Redis({
|
||||||
data: {
|
data: {
|
||||||
test: "test",
|
test: "test",
|
||||||
result: "1",
|
result: "1",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
this.integration.client = this.redis
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,13 +24,17 @@ describe("Redis Integration", () => {
|
||||||
config = new TestConfiguration()
|
config = new TestConfiguration()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
config.integration.disconnect()
|
||||||
|
})
|
||||||
|
|
||||||
it("calls the create method with the correct params", async () => {
|
it("calls the create method with the correct params", async () => {
|
||||||
const body = {
|
const body = {
|
||||||
key: "key",
|
key: "key",
|
||||||
value: "value",
|
value: "value",
|
||||||
}
|
}
|
||||||
await config.integration.create(body)
|
await config.integration.create(body)
|
||||||
expect(await config.redis.get("key")).toEqual("value")
|
expect(await config.integration.client.get("key")).toEqual("value")
|
||||||
})
|
})
|
||||||
|
|
||||||
it("calls the read method with the correct params", async () => {
|
it("calls the read method with the correct params", async () => {
|
||||||
|
@ -46,7 +50,7 @@ describe("Redis Integration", () => {
|
||||||
key: "test",
|
key: "test",
|
||||||
}
|
}
|
||||||
await config.integration.delete(body)
|
await config.integration.delete(body)
|
||||||
expect(await config.redis.get(body.key)).toEqual(null)
|
expect(await config.integration.client.get(body.key)).toEqual(null)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("calls the pipeline method with the correct params", async () => {
|
it("calls the pipeline method with the correct params", async () => {
|
||||||
|
|
|
@ -10,9 +10,9 @@ import { generateUserMetadataID, isDevAppID } from "../db/utils"
|
||||||
import { getCachedSelf } from "../utilities/global"
|
import { getCachedSelf } from "../utilities/global"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { isWebhookEndpoint } from "./utils"
|
import { isWebhookEndpoint } from "./utils"
|
||||||
import { BBContext } from "@budibase/types"
|
import { UserCtx } from "@budibase/types"
|
||||||
|
|
||||||
export default async (ctx: BBContext, next: any) => {
|
export default async (ctx: UserCtx, next: any) => {
|
||||||
// try to get the appID from the request
|
// try to get the appID from the request
|
||||||
let requestAppId = await utils.getAppIdFromCtx(ctx)
|
let requestAppId = await utils.getAppIdFromCtx(ctx)
|
||||||
// get app cookie if it exists
|
// get app cookie if it exists
|
||||||
|
|
|
@ -9,7 +9,10 @@ import { isEqual } from "lodash"
|
||||||
|
|
||||||
export function combineMetadataAndUser(user: any, metadata: any) {
|
export function combineMetadataAndUser(user: any, metadata: any) {
|
||||||
// skip users with no access
|
// skip users with no access
|
||||||
if (user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC) {
|
if (
|
||||||
|
user.roleId == null ||
|
||||||
|
user.roleId === rolesCore.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
delete user._rev
|
delete user._rev
|
||||||
|
|
|
@ -9,3 +9,4 @@ process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
|
||||||
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
process.env.ENABLE_4XX_HTTP_LOGGING = "0"
|
||||||
process.env.MOCK_REDIS = "1"
|
process.env.MOCK_REDIS = "1"
|
||||||
process.env.PLATFORM_URL = "http://localhost:10000"
|
process.env.PLATFORM_URL = "http://localhost:10000"
|
||||||
|
process.env.REDIS_PASSWORD = "budibase"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import "./logging"
|
import "./logging"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { env as coreEnv } from "@budibase/backend-core"
|
import { env as coreEnv, timers } from "@budibase/backend-core"
|
||||||
import { testContainerUtils } from "@budibase/backend-core/tests"
|
import { testContainerUtils } from "@budibase/backend-core/tests"
|
||||||
|
|
||||||
if (!process.env.DEBUG) {
|
if (!process.env.DEBUG) {
|
||||||
|
@ -17,3 +17,7 @@ if (!process.env.CI) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testContainerUtils.setupEnv(env, coreEnv)
|
testContainerUtils.setupEnv(env, coreEnv)
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
timers.cleanup()
|
||||||
|
})
|
||||||
|
|
|
@ -46,6 +46,7 @@ import {
|
||||||
Row,
|
Row,
|
||||||
SourceName,
|
SourceName,
|
||||||
Table,
|
Table,
|
||||||
|
SearchFilters,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
|
|
||||||
type DefaultUserValues = {
|
type DefaultUserValues = {
|
||||||
|
@ -164,6 +165,8 @@ class TestConfiguration {
|
||||||
}
|
}
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
this.server.close()
|
this.server.close()
|
||||||
|
} else {
|
||||||
|
require("../../app").default.close()
|
||||||
}
|
}
|
||||||
if (this.allApps) {
|
if (this.allApps) {
|
||||||
cleanup(this.allApps.map(app => app.appId))
|
cleanup(this.allApps.map(app => app.appId))
|
||||||
|
@ -568,6 +571,16 @@ class TestConfiguration {
|
||||||
return this._req(null, { tableId }, controllers.row.fetch)
|
return this._req(null, { tableId }, controllers.row.fetch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async searchRows(tableId: string, searchParams: SearchFilters = {}) {
|
||||||
|
if (!tableId && this.table) {
|
||||||
|
tableId = this.table._id
|
||||||
|
}
|
||||||
|
const body = {
|
||||||
|
query: searchParams,
|
||||||
|
}
|
||||||
|
return this._req(body, { tableId }, controllers.row.search)
|
||||||
|
}
|
||||||
|
|
||||||
// ROLE
|
// ROLE
|
||||||
|
|
||||||
async createRole(config?: any) {
|
async createRole(config?: any) {
|
||||||
|
|
|
@ -106,7 +106,7 @@ export function newAutomation({ steps, trigger }: any = {}) {
|
||||||
return automation
|
return automation
|
||||||
}
|
}
|
||||||
|
|
||||||
export function basicAutomation() {
|
export function basicAutomation(appId?: string) {
|
||||||
return {
|
return {
|
||||||
name: "My Automation",
|
name: "My Automation",
|
||||||
screenId: "kasdkfldsafkl",
|
screenId: "kasdkfldsafkl",
|
||||||
|
@ -114,11 +114,23 @@ export function basicAutomation() {
|
||||||
uiTree: {},
|
uiTree: {},
|
||||||
definition: {
|
definition: {
|
||||||
trigger: {
|
trigger: {
|
||||||
|
stepId: AutomationTriggerStepId.APP,
|
||||||
|
name: "test",
|
||||||
|
tagline: "test",
|
||||||
|
icon: "test",
|
||||||
|
description: "test",
|
||||||
|
type: "trigger",
|
||||||
|
id: "test",
|
||||||
inputs: {},
|
inputs: {},
|
||||||
|
schema: {
|
||||||
|
inputs: {},
|
||||||
|
outputs: {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
steps: [],
|
steps: [],
|
||||||
},
|
},
|
||||||
type: "automation",
|
type: "automation",
|
||||||
|
appId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { groups } from "@budibase/pro"
|
import { groups } from "@budibase/pro"
|
||||||
import { BBContext, ContextUser, User } from "@budibase/types"
|
import { UserCtx, ContextUser, User, UserGroup } from "@budibase/types"
|
||||||
|
|
||||||
export function updateAppRole(
|
export function updateAppRole(
|
||||||
user: ContextUser,
|
user: ContextUser,
|
||||||
|
@ -43,33 +43,40 @@ export function updateAppRole(
|
||||||
|
|
||||||
async function checkGroupRoles(
|
async function checkGroupRoles(
|
||||||
user: ContextUser,
|
user: ContextUser,
|
||||||
{ appId }: { appId?: string } = {}
|
opts: { appId?: string; groups?: UserGroup[] } = {}
|
||||||
) {
|
) {
|
||||||
if (user.roleId && user.roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
if (user.roleId && user.roleId !== roles.BUILTIN_ROLE_IDS.PUBLIC) {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
if (appId) {
|
if (opts.appId) {
|
||||||
user.roleId = await groups.getGroupRoleId(user as User, appId)
|
user.roleId = await groups.getGroupRoleId(user as User, opts.appId, {
|
||||||
|
groups: opts.groups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// final fallback, simply couldn't find a role - user must be public
|
||||||
|
if (!user.roleId) {
|
||||||
|
user.roleId = roles.BUILTIN_ROLE_IDS.PUBLIC
|
||||||
}
|
}
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processUser(
|
async function processUser(
|
||||||
user: ContextUser,
|
user: ContextUser,
|
||||||
{ appId }: { appId?: string } = {}
|
opts: { appId?: string; groups?: UserGroup[] } = {}
|
||||||
) {
|
) {
|
||||||
if (user) {
|
if (user) {
|
||||||
delete user.password
|
delete user.password
|
||||||
}
|
}
|
||||||
user = await updateAppRole(user, { appId })
|
const appId = opts.appId || context.getAppId()
|
||||||
|
user = updateAppRole(user, { appId })
|
||||||
if (!user.roleId && user?.userGroups?.length) {
|
if (!user.roleId && user?.userGroups?.length) {
|
||||||
user = await checkGroupRoles(user, { appId })
|
user = await checkGroupRoles(user, { appId, groups: opts?.groups })
|
||||||
}
|
}
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCachedSelf(ctx: BBContext, appId: string) {
|
export async function getCachedSelf(ctx: UserCtx, appId: string) {
|
||||||
// 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(ctx.user?._id!)
|
const user = await cache.user.getUser(ctx.user?._id!)
|
||||||
|
@ -90,6 +97,7 @@ export async function getGlobalUser(userId: string) {
|
||||||
export async function getGlobalUsers(users?: ContextUser[]) {
|
export async function getGlobalUsers(users?: ContextUser[]) {
|
||||||
const appId = context.getAppId()
|
const appId = context.getAppId()
|
||||||
const db = tenancy.getGlobalDB()
|
const db = tenancy.getGlobalDB()
|
||||||
|
const allGroups = await groups.fetch()
|
||||||
let globalUsers
|
let globalUsers
|
||||||
if (users) {
|
if (users) {
|
||||||
const globalIds = users.map(user =>
|
const globalIds = users.map(user =>
|
||||||
|
@ -118,7 +126,11 @@ export async function getGlobalUsers(users?: ContextUser[]) {
|
||||||
return globalUsers
|
return globalUsers
|
||||||
}
|
}
|
||||||
|
|
||||||
return globalUsers.map(user => updateAppRole(user))
|
// pass in the groups, meaning we don't actually need to retrieve them for
|
||||||
|
// each user individually
|
||||||
|
return Promise.all(
|
||||||
|
globalUsers.map(user => processUser(user, { groups: allGroups }))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getGlobalUsersFromMetadata(users: ContextUser[]) {
|
export async function getGlobalUsersFromMetadata(users: ContextUser[]) {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -3,10 +3,10 @@
|
||||||
if [[ -n $CI ]]
|
if [[ -n $CI ]]
|
||||||
then
|
then
|
||||||
# --runInBand performs better in ci where resources are limited
|
# --runInBand performs better in ci where resources are limited
|
||||||
echo "jest --coverage --runInBand"
|
echo "jest --coverage --runInBand --forceExit"
|
||||||
jest --coverage --runInBand
|
jest --coverage --runInBand --forceExit
|
||||||
else
|
else
|
||||||
# --maxWorkers performs better in development
|
# --maxWorkers performs better in development
|
||||||
echo "jest --coverage --maxWorkers=2"
|
echo "jest --coverage --maxWorkers=2"
|
||||||
jest --coverage --maxWorkers=2
|
jest --coverage --maxWorkers=2
|
||||||
fi
|
fi
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {
|
||||||
middleware,
|
middleware,
|
||||||
queue,
|
queue,
|
||||||
env as coreEnv,
|
env as coreEnv,
|
||||||
|
timers,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
db.init()
|
db.init()
|
||||||
import Koa from "koa"
|
import Koa from "koa"
|
||||||
|
@ -91,6 +92,7 @@ server.on("close", async () => {
|
||||||
}
|
}
|
||||||
shuttingDown = true
|
shuttingDown = true
|
||||||
console.log("Server Closed")
|
console.log("Server Closed")
|
||||||
|
timers.cleanup()
|
||||||
await redis.shutdown()
|
await redis.shutdown()
|
||||||
await events.shutdown()
|
await events.shutdown()
|
||||||
await queue.shutdown()
|
await queue.shutdown()
|
||||||
|
|
|
@ -106,6 +106,8 @@ class TestConfiguration {
|
||||||
async afterAll() {
|
async afterAll() {
|
||||||
if (this.server) {
|
if (this.server) {
|
||||||
await this.server.close()
|
await this.server.close()
|
||||||
|
} else {
|
||||||
|
await require("../index").default.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,4 @@ process.env.MINIO_SECRET_KEY = "test"
|
||||||
process.env.PLATFORM_URL = "http://localhost:10000"
|
process.env.PLATFORM_URL = "http://localhost:10000"
|
||||||
process.env.INTERNAL_API_KEY = "tet"
|
process.env.INTERNAL_API_KEY = "tet"
|
||||||
process.env.DISABLE_ACCOUNT_PORTAL = "0"
|
process.env.DISABLE_ACCOUNT_PORTAL = "0"
|
||||||
|
process.env.REDIS_PASSWORD = "budibase"
|
||||||
|
|
|
@ -2,7 +2,7 @@ import "./logging"
|
||||||
|
|
||||||
import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
|
import { mocks, testContainerUtils } from "@budibase/backend-core/tests"
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import { env as coreEnv } from "@budibase/backend-core"
|
import { env as coreEnv, timers } from "@budibase/backend-core"
|
||||||
|
|
||||||
// must explicitly enable fetch mock
|
// must explicitly enable fetch mock
|
||||||
mocks.fetch.enable()
|
mocks.fetch.enable()
|
||||||
|
@ -21,3 +21,7 @@ if (!process.env.CI) {
|
||||||
}
|
}
|
||||||
|
|
||||||
testContainerUtils.setupEnv(env, coreEnv)
|
testContainerUtils.setupEnv(env, coreEnv)
|
||||||
|
|
||||||
|
afterAll(() => {
|
||||||
|
timers.cleanup()
|
||||||
|
})
|
||||||
|
|
Loading…
Reference in New Issue