Tests complete + backwards compatibility for deployment
This commit is contained in:
parent
715d42d3e6
commit
0a4b1eb552
|
@ -98,10 +98,6 @@ spec:
|
||||||
value: http://worker-service:{{ .Values.services.worker.port }}
|
value: http://worker-service:{{ .Values.services.worker.port }}
|
||||||
- name: PLATFORM_URL
|
- name: PLATFORM_URL
|
||||||
value: {{ .Values.globals.platformUrl | quote }}
|
value: {{ .Values.globals.platformUrl | quote }}
|
||||||
- name: USE_QUOTAS
|
|
||||||
value: {{ .Values.globals.useQuotas | quote }}
|
|
||||||
- name: EXCLUDE_QUOTAS_TENANTS
|
|
||||||
value: {{ .Values.globals.excludeQuotasTenants | quote }}
|
|
||||||
- name: ACCOUNT_PORTAL_URL
|
- name: ACCOUNT_PORTAL_URL
|
||||||
value: {{ .Values.globals.accountPortalUrl | quote }}
|
value: {{ .Values.globals.accountPortalUrl | quote }}
|
||||||
- name: ACCOUNT_PORTAL_API_KEY
|
- name: ACCOUNT_PORTAL_API_KEY
|
||||||
|
|
|
@ -93,15 +93,13 @@ globals:
|
||||||
logLevel: info
|
logLevel: info
|
||||||
selfHosted: "1" # set to 0 for budibase cloud environment, set to 1 for self-hosted setup
|
selfHosted: "1" # set to 0 for budibase cloud environment, set to 1 for self-hosted setup
|
||||||
multiTenancy: "0" # set to 0 to disable multiple orgs, set to 1 to enable multiple orgs
|
multiTenancy: "0" # set to 0 to disable multiple orgs, set to 1 to enable multiple orgs
|
||||||
useQuotas: "0"
|
|
||||||
excludeQuotasTenants: "" # comma seperated list of tenants to exclude from quotas
|
|
||||||
accountPortalUrl: ""
|
accountPortalUrl: ""
|
||||||
accountPortalApiKey: ""
|
accountPortalApiKey: ""
|
||||||
cookieDomain: ""
|
cookieDomain: ""
|
||||||
platformUrl: ""
|
platformUrl: ""
|
||||||
httpMigrations: "0"
|
httpMigrations: "0"
|
||||||
google:
|
google:
|
||||||
clientId: ""
|
clientId: ""
|
||||||
secret: ""
|
secret: ""
|
||||||
|
|
||||||
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
|
||||||
|
|
|
@ -56,7 +56,8 @@ exports.createApiKeyView = async () => {
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createUserBuildersView = async db => {
|
exports.createUserBuildersView = async () => {
|
||||||
|
const db = getGlobalDB()
|
||||||
let designDoc
|
let designDoc
|
||||||
try {
|
try {
|
||||||
designDoc = await db.get("_design/database")
|
designDoc = await db.get("_design/database")
|
||||||
|
@ -82,6 +83,7 @@ exports.queryGlobalView = async (viewName, params, db = null) => {
|
||||||
const CreateFuncByName = {
|
const CreateFuncByName = {
|
||||||
[ViewNames.USER_BY_EMAIL]: exports.createUserEmailView,
|
[ViewNames.USER_BY_EMAIL]: exports.createUserEmailView,
|
||||||
[ViewNames.BY_API_KEY]: exports.createApiKeyView,
|
[ViewNames.BY_API_KEY]: exports.createApiKeyView,
|
||||||
|
[ViewNames.USER_BY_BUILDERS]: exports.createUserBuildersView,
|
||||||
}
|
}
|
||||||
// can pass DB in if working with something specific
|
// can pass DB in if working with something specific
|
||||||
if (!db) {
|
if (!db) {
|
||||||
|
|
|
@ -28,8 +28,7 @@ module.exports = {
|
||||||
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED),
|
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED),
|
||||||
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
||||||
PLATFORM_URL: process.env.PLATFORM_URL,
|
PLATFORM_URL: process.env.PLATFORM_URL,
|
||||||
USE_QUOTAS: process.env.USE_QUOTAS,
|
TENANT_FEATURE_FLAGS: process.env.TENANT_FEATURE_FLAGS,
|
||||||
EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
|
|
||||||
isTest,
|
isTest,
|
||||||
_set(key, value) {
|
_set(key, value) {
|
||||||
process.env[key] = value
|
process.env[key] = value
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
const env = require("../environment")
|
||||||
|
const tenancy = require("../tenancy")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the TENANT_FEATURE_FLAGS env var and return an array of features flags for each tenant.
|
||||||
|
* The env var is formatted as:
|
||||||
|
* tenant1:feature1:feature2,tenant2:feature1
|
||||||
|
*/
|
||||||
|
const getFeatureFlags = () => {
|
||||||
|
if (!env.TENANT_FEATURE_FLAGS) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const tenantFeatureFlags = {}
|
||||||
|
|
||||||
|
env.TENANT_FEATURE_FLAGS.split(",").forEach(tenantToFeatures => {
|
||||||
|
const [tenantId, ...features] = tenantToFeatures.split(":")
|
||||||
|
|
||||||
|
features.forEach(feature => {
|
||||||
|
if (!tenantFeatureFlags[tenantId]) {
|
||||||
|
tenantFeatureFlags[tenantId] = []
|
||||||
|
}
|
||||||
|
tenantFeatureFlags[tenantId].push(feature)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
return tenantFeatureFlags
|
||||||
|
}
|
||||||
|
|
||||||
|
const TENANT_FEATURE_FLAGS = getFeatureFlags()
|
||||||
|
|
||||||
|
exports.isEnabled = featureFlag => {
|
||||||
|
const tenantId = tenancy.getTenantId()
|
||||||
|
|
||||||
|
return (
|
||||||
|
TENANT_FEATURE_FLAGS &&
|
||||||
|
TENANT_FEATURE_FLAGS[tenantId] &&
|
||||||
|
TENANT_FEATURE_FLAGS[tenantId].includes(featureFlag)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.getTenantFeatureFlags = tenantId => {
|
||||||
|
if (TENANT_FEATURE_FLAGS && TENANT_FEATURE_FLAGS[tenantId]) {
|
||||||
|
return TENANT_FEATURE_FLAGS[tenantId]
|
||||||
|
}
|
||||||
|
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.FeatureFlag = {
|
||||||
|
LICENSING: "LICENSING",
|
||||||
|
}
|
|
@ -19,4 +19,5 @@ module.exports = {
|
||||||
env: require("./environment"),
|
env: require("./environment"),
|
||||||
accounts: require("./cloud/accounts"),
|
accounts: require("./cloud/accounts"),
|
||||||
tenancy: require("./tenancy"),
|
tenancy: require("./tenancy"),
|
||||||
|
featureFlags: require("./featureFlags"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,4 +55,4 @@ exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// expose for testing
|
// expose for testing
|
||||||
exports.authenticate = buildVerifyFn
|
exports.buildVerifyFn = buildVerifyFn
|
||||||
|
|
|
@ -129,4 +129,4 @@ exports.strategyFactory = async function (config, callbackUrl, saveUserFn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// expose for testing
|
// expose for testing
|
||||||
exports.authenticate = buildVerifyFn
|
exports.buildVerifyFn = buildVerifyFn
|
||||||
|
|
|
@ -58,8 +58,10 @@ describe("google", () => {
|
||||||
|
|
||||||
it("delegates authentication to third party common", async () => {
|
it("delegates authentication to third party common", async () => {
|
||||||
const google = require("../google")
|
const google = require("../google")
|
||||||
|
const mockSaveUserFn = jest.fn()
|
||||||
|
const authenticate = await google.buildVerifyFn(mockSaveUserFn)
|
||||||
|
|
||||||
await google.authenticate(
|
await authenticate(
|
||||||
data.accessToken,
|
data.accessToken,
|
||||||
data.refreshToken,
|
data.refreshToken,
|
||||||
profile,
|
profile,
|
||||||
|
@ -69,7 +71,8 @@ describe("google", () => {
|
||||||
expect(authenticateThirdParty).toHaveBeenCalledWith(
|
expect(authenticateThirdParty).toHaveBeenCalledWith(
|
||||||
user,
|
user,
|
||||||
true,
|
true,
|
||||||
mockDone)
|
mockDone,
|
||||||
|
mockSaveUserFn)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -83,8 +83,10 @@ describe("oidc", () => {
|
||||||
|
|
||||||
async function doAuthenticate() {
|
async function doAuthenticate() {
|
||||||
const oidc = require("../oidc")
|
const oidc = require("../oidc")
|
||||||
|
const mockSaveUserFn = jest.fn()
|
||||||
|
const authenticate = await oidc.buildVerifyFn(mockSaveUserFn)
|
||||||
|
|
||||||
await oidc.authenticate(
|
await authenticate(
|
||||||
issuer,
|
issuer,
|
||||||
sub,
|
sub,
|
||||||
profile,
|
profile,
|
||||||
|
|
|
@ -147,7 +147,9 @@ exports.getGlobalUserByEmail = async email => {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getBuildersCount = async () => {
|
exports.getBuildersCount = async () => {
|
||||||
const builders = await queryGlobalView(ViewNames.BUILDERS)
|
const builders = await queryGlobalView(ViewNames.USER_BY_BUILDERS, {
|
||||||
|
include_docs: false,
|
||||||
|
})
|
||||||
return builders.total_rows
|
return builders.total_rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -9,7 +9,7 @@
|
||||||
"url": "https://github.com/Budibase/budibase.git"
|
"url": "https://github.com/Budibase/budibase.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
"build": "rimraf dist/ && tsc -p tsconfig.build.json && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild",
|
||||||
"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": "jest --coverage --maxWorkers=2",
|
"test": "jest --coverage --maxWorkers=2",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
|
@ -81,6 +81,7 @@
|
||||||
"@koa/router": "8.0.0",
|
"@koa/router": "8.0.0",
|
||||||
"@sendgrid/mail": "7.1.1",
|
"@sendgrid/mail": "7.1.1",
|
||||||
"@sentry/node": "6.17.7",
|
"@sentry/node": "6.17.7",
|
||||||
|
"@types/koa__router": "^8.0.11",
|
||||||
"airtable": "0.10.1",
|
"airtable": "0.10.1",
|
||||||
"arangojs": "7.2.0",
|
"arangojs": "7.2.0",
|
||||||
"aws-sdk": "^2.767.0",
|
"aws-sdk": "^2.767.0",
|
||||||
|
@ -146,7 +147,7 @@
|
||||||
"@types/apidoc": "^0.50.0",
|
"@types/apidoc": "^0.50.0",
|
||||||
"@types/bull": "^3.15.1",
|
"@types/bull": "^3.15.1",
|
||||||
"@types/google-spreadsheet": "^3.1.5",
|
"@types/google-spreadsheet": "^3.1.5",
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^27.4.1",
|
||||||
"@types/koa": "^2.13.3",
|
"@types/koa": "^2.13.3",
|
||||||
"@types/koa-router": "^7.4.2",
|
"@types/koa-router": "^7.4.2",
|
||||||
"@types/lodash": "^4.14.179",
|
"@types/lodash": "^4.14.179",
|
||||||
|
|
|
@ -389,7 +389,7 @@ const destroyApp = async (ctx: any) => {
|
||||||
const db = getAppDB()
|
const db = getAppDB()
|
||||||
|
|
||||||
const result = await db.destroy()
|
const result = await db.destroy()
|
||||||
if (ctx.query.unpublish) {
|
if (ctx.query?.unpublish) {
|
||||||
await quotas.removePublishedApp()
|
await quotas.removePublishedApp()
|
||||||
} else {
|
} else {
|
||||||
await quotas.removeApp()
|
await quotas.removeApp()
|
||||||
|
@ -408,7 +408,7 @@ const destroyApp = async (ctx: any) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const preDestroyApp = async (ctx: any) => {
|
const preDestroyApp = async (ctx: any) => {
|
||||||
const rows = await getUniqueRows([ctx.appId])
|
const rows = await getUniqueRows([ctx.params.appId])
|
||||||
ctx.rowCount = rows.length
|
ctx.rowCount = rows.length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/application"
|
import * as controller from "../controllers/application"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
const { BUILDER } = require("@budibase/backend-core/permissions")
|
import { BUILDER } from "@budibase/backend-core/permissions"
|
||||||
|
|
||||||
const router = Router()
|
const router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
.post("/api/applications/:appId/sync", authorized(BUILDER), controller.sync)
|
||||||
|
|
|
@ -127,4 +127,4 @@ applyRoutes(queryEndpoints, PermissionTypes.QUERY, "queryId")
|
||||||
// needs to be applied last for routing purposes, don't override other endpoints
|
// needs to be applied last for routing purposes, don't override other endpoints
|
||||||
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
|
applyRoutes(rowEndpoints, PermissionTypes.TABLE, "tableId", "rowId")
|
||||||
|
|
||||||
module.exports = publicRouter
|
export default publicRouter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
import * as rowController from "../controllers/row"
|
import * as rowController from "../controllers/row"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
import { paramResource, paramSubResource } from "../../middleware/resourceId"
|
import { paramResource, paramSubResource } from "../../middleware/resourceId"
|
||||||
|
@ -8,7 +8,7 @@ const {
|
||||||
} = require("@budibase/backend-core/permissions")
|
} = require("@budibase/backend-core/permissions")
|
||||||
const { internalSearchValidator } = require("./utils/validators")
|
const { internalSearchValidator } = require("./utils/validators")
|
||||||
|
|
||||||
const router = Router()
|
const router = new Router()
|
||||||
|
|
||||||
router
|
router
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const Router = require("@koa/router")
|
import Router from "@koa/router"
|
||||||
import * as controller from "../controllers/static"
|
import * as controller from "../controllers/static"
|
||||||
import { budibaseTempDir } from "../../utilities/budibaseDir"
|
import { budibaseTempDir } from "../../utilities/budibaseDir"
|
||||||
import authorized from "../../middleware/authorized"
|
import authorized from "../../middleware/authorized"
|
||||||
|
@ -10,10 +10,10 @@ import {
|
||||||
import * as env from "../../environment"
|
import * as env from "../../environment"
|
||||||
import { paramResource } from "../../middleware/resourceId"
|
import { paramResource } from "../../middleware/resourceId"
|
||||||
|
|
||||||
const router = Router()
|
const router = new Router()
|
||||||
|
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
router.param("file", async (file, ctx, next) => {
|
router.param("file", async (file: any, ctx: any, next: any) => {
|
||||||
ctx.file = file && file.includes(".") ? file : "index.html"
|
ctx.file = file && file.includes(".") ? file : "index.html"
|
||||||
if (!ctx.file.startsWith("budibase-client")) {
|
if (!ctx.file.startsWith("budibase-client")) {
|
||||||
return next()
|
return next()
|
|
@ -1,31 +1,38 @@
|
||||||
const rowController = require("../../../controllers/row")
|
import * as rowController from "../../../controllers/row"
|
||||||
const appController = require("../../../controllers/application")
|
import * as appController from "../../../controllers/application"
|
||||||
const { AppStatus } = require("../../../../db/utils")
|
import { AppStatus } from "../../../../db/utils"
|
||||||
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
|
import { BUILTIN_ROLE_IDS } from "@budibase/backend-core/roles"
|
||||||
const { TENANT_ID } = require("../../../../tests/utilities/structures")
|
import { TENANT_ID } from "../../../../tests/utilities/structures"
|
||||||
const { getAppDB, doInAppContext } = require("@budibase/backend-core/context")
|
import { getAppDB, doInAppContext } from "@budibase/backend-core/context"
|
||||||
const env = require("../../../../environment")
|
import * as env from "../../../../environment"
|
||||||
|
|
||||||
function Request(appId, params) {
|
class Request {
|
||||||
this.appId = appId
|
appId: any
|
||||||
this.params = params
|
params: any
|
||||||
this.request = {}
|
request: any
|
||||||
|
body: any
|
||||||
|
|
||||||
|
constructor(appId: any, params: any) {
|
||||||
|
this.appId = appId
|
||||||
|
this.params = params
|
||||||
|
this.request = {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function runRequest(appId, controlFunc, request) {
|
function runRequest(appId: any, controlFunc: any, request?: any) {
|
||||||
return doInAppContext(appId, async () => {
|
return doInAppContext(appId, async () => {
|
||||||
return controlFunc(request)
|
return controlFunc(request)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getAllTableRows = async config => {
|
export const getAllTableRows = async (config: any) => {
|
||||||
const req = new Request(config.appId, { tableId: config.table._id })
|
const req = new Request(config.appId, { tableId: config.table._id })
|
||||||
await runRequest(config.appId, rowController.fetch, req)
|
await runRequest(config.appId, rowController.fetch, req)
|
||||||
return req.body
|
return req.body
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.clearAllApps = async (tenantId = TENANT_ID) => {
|
export const clearAllApps = async (tenantId = TENANT_ID) => {
|
||||||
const req = { query: { status: AppStatus.DEV }, user: { tenantId } }
|
const req: any = { query: { status: AppStatus.DEV }, user: { tenantId } }
|
||||||
await appController.fetch(req)
|
await appController.fetch(req)
|
||||||
const apps = req.body
|
const apps = req.body
|
||||||
if (!apps || apps.length <= 0) {
|
if (!apps || apps.length <= 0) {
|
||||||
|
@ -34,11 +41,11 @@ exports.clearAllApps = async (tenantId = TENANT_ID) => {
|
||||||
for (let app of apps) {
|
for (let app of apps) {
|
||||||
const { appId } = app
|
const { appId } = app
|
||||||
const req = new Request(null, { appId })
|
const req = new Request(null, { appId })
|
||||||
await runRequest(appId, appController.delete, req)
|
await runRequest(appId, appController.destroy, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.clearAllAutomations = async config => {
|
export const clearAllAutomations = async (config: any) => {
|
||||||
const automations = await config.getAllAutomations()
|
const automations = await config.getAllAutomations()
|
||||||
for (let auto of automations) {
|
for (let auto of automations) {
|
||||||
await doInAppContext(config.appId, async () => {
|
await doInAppContext(config.appId, async () => {
|
||||||
|
@ -47,7 +54,12 @@ exports.clearAllAutomations = async config => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createRequest = (request, method, url, body) => {
|
export const createRequest = (
|
||||||
|
request: any,
|
||||||
|
method: any,
|
||||||
|
url: any,
|
||||||
|
body: any
|
||||||
|
) => {
|
||||||
let req
|
let req
|
||||||
|
|
||||||
if (method === "POST") req = request.post(url).send(body)
|
if (method === "POST") req = request.post(url).send(body)
|
||||||
|
@ -59,7 +71,12 @@ exports.createRequest = (request, method, url, body) => {
|
||||||
return req
|
return req
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
|
export const checkBuilderEndpoint = async ({
|
||||||
|
config,
|
||||||
|
method,
|
||||||
|
url,
|
||||||
|
body,
|
||||||
|
}: any) => {
|
||||||
const headers = await config.login({
|
const headers = await config.login({
|
||||||
userId: "us_fail",
|
userId: "us_fail",
|
||||||
builder: false,
|
builder: false,
|
||||||
|
@ -71,14 +88,14 @@ exports.checkBuilderEndpoint = async ({ config, method, url, body }) => {
|
||||||
.expect(403)
|
.expect(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.checkPermissionsEndpoint = async ({
|
export const checkPermissionsEndpoint = async ({
|
||||||
config,
|
config,
|
||||||
method,
|
method,
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
passRole,
|
passRole,
|
||||||
failRole,
|
failRole,
|
||||||
}) => {
|
}: any) => {
|
||||||
const passHeader = await config.login({
|
const passHeader = await config.login({
|
||||||
roleId: passRole,
|
roleId: passRole,
|
||||||
prodApp: true,
|
prodApp: true,
|
||||||
|
@ -106,11 +123,11 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
.expect(403)
|
.expect(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.getDB = () => {
|
export const getDB = () => {
|
||||||
return getAppDB()
|
return getAppDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.testAutomation = async (config, automation) => {
|
export const testAutomation = async (config: any, automation: any) => {
|
||||||
return runRequest(automation.appId, async () => {
|
return runRequest(automation.appId, async () => {
|
||||||
return await config.request
|
return await config.request
|
||||||
.post(`/api/automations/${automation._id}/test`)
|
.post(`/api/automations/${automation._id}/test`)
|
||||||
|
@ -126,7 +143,7 @@ exports.testAutomation = async (config, automation) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.runInProd = async func => {
|
export const runInProd = async (func: any) => {
|
||||||
const nodeEnv = env.NODE_ENV
|
const nodeEnv = env.NODE_ENV
|
||||||
const workerId = env.JEST_WORKER_ID
|
const workerId = env.JEST_WORKER_ID
|
||||||
env._set("NODE_ENV", "PRODUCTION")
|
env._set("NODE_ENV", "PRODUCTION")
|
|
@ -1,4 +1,3 @@
|
||||||
import { quotas } from "@budibase/pro"
|
|
||||||
import { save } from "../../api/controllers/row"
|
import { save } from "../../api/controllers/row"
|
||||||
import { cleanUpRow, getError } from "../automationUtils"
|
import { cleanUpRow, getError } from "../automationUtils"
|
||||||
import { buildCtx } from "./utils"
|
import { buildCtx } from "./utils"
|
||||||
|
@ -78,7 +77,7 @@ export async function run({ inputs, appId, emitter }: any) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row)
|
inputs.row = await cleanUpRow(inputs.row.tableId, inputs.row)
|
||||||
await quotas.addRow(() => save(ctx))
|
await save(ctx)
|
||||||
return {
|
return {
|
||||||
row: inputs.row,
|
row: inputs.row,
|
||||||
response: ctx.body,
|
response: ctx.body,
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { destroy } from "../../api/controllers/row"
|
import { destroy } from "../../api/controllers/row"
|
||||||
import { buildCtx } from "./utils"
|
import { buildCtx } from "./utils"
|
||||||
import { getError } from "../automationUtils"
|
import { getError } from "../automationUtils"
|
||||||
import { quotas } from "@budibase/pro"
|
|
||||||
|
|
||||||
export const definition = {
|
export const definition = {
|
||||||
description: "Delete a row from your database",
|
description: "Delete a row from your database",
|
||||||
|
@ -74,7 +73,6 @@ export async function run({ inputs, appId, emitter }: any) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await destroy(ctx)
|
await destroy(ctx)
|
||||||
await quotas.removeRow()
|
|
||||||
return {
|
return {
|
||||||
response: ctx.body,
|
response: ctx.body,
|
||||||
row: ctx.row,
|
row: ctx.row,
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
jest.mock("../../utilities/usageQuota")
|
|
||||||
jest.mock("../../threads/automation")
|
jest.mock("../../threads/automation")
|
||||||
jest.mock("../../utilities/redis", () => ({
|
jest.mock("../../utilities/redis", () => ({
|
||||||
init: jest.fn(),
|
init: jest.fn(),
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
jest.mock("../../utilities/usageQuota")
|
import * as setup from "./utilities"
|
||||||
|
|
||||||
const usageQuota = require("../../utilities/usageQuota")
|
|
||||||
const setup = require("./utilities")
|
|
||||||
|
|
||||||
describe("test the create row action", () => {
|
describe("test the create row action", () => {
|
||||||
let table, row
|
let table: any
|
||||||
|
let row: any
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -36,20 +34,11 @@ describe("test the create row action", () => {
|
||||||
row: {
|
row: {
|
||||||
tableId: "invalid",
|
tableId: "invalid",
|
||||||
invalid: "invalid",
|
invalid: "invalid",
|
||||||
}
|
},
|
||||||
})
|
})
|
||||||
expect(res.success).toEqual(false)
|
expect(res.success).toEqual(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("check usage quota attempts", async () => {
|
|
||||||
await setup.runInProd(async () => {
|
|
||||||
await setup.runStep(setup.actions.CREATE_ROW.stepId, {
|
|
||||||
row
|
|
||||||
})
|
|
||||||
expect(usageQuota.update).toHaveBeenCalledWith("rows", 1)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should check invalid inputs return an error", async () => {
|
it("should check invalid inputs return an error", async () => {
|
||||||
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {})
|
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {})
|
||||||
expect(res.success).toEqual(false)
|
expect(res.success).toEqual(false)
|
|
@ -1,10 +1,9 @@
|
||||||
jest.mock("../../utilities/usageQuota")
|
|
||||||
|
|
||||||
const usageQuota = require("../../utilities/usageQuota")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
|
||||||
describe("test the delete row action", () => {
|
describe("test the delete row action", () => {
|
||||||
let table, row, inputs
|
let table: any
|
||||||
|
let row: any
|
||||||
|
let inputs: any
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
@ -37,7 +36,6 @@ describe("test the delete row action", () => {
|
||||||
it("check usage quota attempts", async () => {
|
it("check usage quota attempts", async () => {
|
||||||
await setup.runInProd(async () => {
|
await setup.runInProd(async () => {
|
||||||
await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs)
|
await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs)
|
||||||
expect(usageQuota.update).toHaveBeenCalledWith("rows", -1)
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,6 @@ exports.afterAll = () => {
|
||||||
|
|
||||||
exports.runInProd = async fn => {
|
exports.runInProd = async fn => {
|
||||||
env._set("NODE_ENV", "production")
|
env._set("NODE_ENV", "production")
|
||||||
env._set("USE_QUOTAS", 1)
|
|
||||||
let error
|
let error
|
||||||
try {
|
try {
|
||||||
await fn()
|
await fn()
|
||||||
|
@ -26,7 +25,6 @@ exports.runInProd = async fn => {
|
||||||
error = err
|
error = err
|
||||||
}
|
}
|
||||||
env._set("NODE_ENV", "jest")
|
env._set("NODE_ENV", "jest")
|
||||||
env._set("USE_QUOTAS", null)
|
|
||||||
if (error) {
|
if (error) {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,8 +38,6 @@ module.exports = {
|
||||||
ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS,
|
ENABLE_ANALYTICS: process.env.ENABLE_ANALYTICS,
|
||||||
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
|
||||||
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
|
||||||
USE_QUOTAS: process.env.USE_QUOTAS,
|
|
||||||
EXCLUDE_QUOTAS_TENANTS: process.env.EXCLUDE_QUOTAS_TENANTS,
|
|
||||||
REDIS_URL: process.env.REDIS_URL,
|
REDIS_URL: process.env.REDIS_URL,
|
||||||
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
|
||||||
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
|
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
const {
|
import {
|
||||||
getUserRoleHierarchy,
|
getUserRoleHierarchy,
|
||||||
getRequiredResourceRole,
|
getRequiredResourceRole,
|
||||||
BUILTIN_ROLE_IDS,
|
BUILTIN_ROLE_IDS,
|
||||||
} = require("@budibase/backend-core/roles")
|
} from "@budibase/backend-core/roles"
|
||||||
const {
|
const {
|
||||||
PermissionTypes,
|
PermissionTypes,
|
||||||
doesHaveBasePermission,
|
doesHaveBasePermission,
|
||||||
|
@ -12,7 +12,7 @@ const { isWebhookEndpoint } = require("./utils")
|
||||||
const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
|
const { buildCsrfMiddleware } = require("@budibase/backend-core/auth")
|
||||||
const { getAppId } = require("@budibase/backend-core/context")
|
const { getAppId } = require("@budibase/backend-core/context")
|
||||||
|
|
||||||
function hasResource(ctx) {
|
function hasResource(ctx: any) {
|
||||||
return ctx.resourceId != null
|
return ctx.resourceId != null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,12 @@ const csrf = buildCsrfMiddleware()
|
||||||
* - Builders can access all resources.
|
* - Builders can access all resources.
|
||||||
* - Otherwise the user must have the required role.
|
* - Otherwise the user must have the required role.
|
||||||
*/
|
*/
|
||||||
const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
|
const checkAuthorized = async (
|
||||||
|
ctx: any,
|
||||||
|
resourceRoles: any,
|
||||||
|
permType: any,
|
||||||
|
permLevel: any
|
||||||
|
) => {
|
||||||
// check if this is a builder api and the user is not a builder
|
// check if this is a builder api and the user is not a builder
|
||||||
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
const isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
|
||||||
const isBuilderApi = permType === PermissionTypes.BUILDER
|
const isBuilderApi = permType === PermissionTypes.BUILDER
|
||||||
|
@ -39,10 +44,10 @@ const checkAuthorized = async (ctx, resourceRoles, permType, permLevel) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const checkAuthorizedResource = async (
|
const checkAuthorizedResource = async (
|
||||||
ctx,
|
ctx: any,
|
||||||
resourceRoles,
|
resourceRoles: any,
|
||||||
permType,
|
permType: any,
|
||||||
permLevel
|
permLevel: any
|
||||||
) => {
|
) => {
|
||||||
// get the user's roles
|
// get the user's roles
|
||||||
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
const roleId = ctx.roleId || BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
@ -53,7 +58,9 @@ const checkAuthorizedResource = async (
|
||||||
// check if the user has the required role
|
// check if the user has the required role
|
||||||
if (resourceRoles.length > 0) {
|
if (resourceRoles.length > 0) {
|
||||||
// deny access if the user doesn't have the required resource role
|
// deny access if the user doesn't have the required resource role
|
||||||
const found = userRoles.find(role => resourceRoles.indexOf(role._id) !== -1)
|
const found = userRoles.find(
|
||||||
|
(role: any) => resourceRoles.indexOf(role._id) !== -1
|
||||||
|
)
|
||||||
if (!found) {
|
if (!found) {
|
||||||
ctx.throw(403, permError)
|
ctx.throw(403, permError)
|
||||||
}
|
}
|
||||||
|
@ -63,9 +70,8 @@ const checkAuthorizedResource = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports =
|
export = (permType: any, permLevel: any = null) =>
|
||||||
(permType, permLevel = null) =>
|
async (ctx: any, next: any) => {
|
||||||
async (ctx, next) => {
|
|
||||||
// webhooks don't need authentication, each webhook unique
|
// webhooks don't need authentication, each webhook unique
|
||||||
// also internal requests (between services) don't need authorized
|
// also internal requests (between services) don't need authorized
|
||||||
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
if (isWebhookEndpoint(ctx) || ctx.internal) {
|
||||||
|
@ -81,7 +87,7 @@ module.exports =
|
||||||
await builderMiddleware(ctx, permType)
|
await builderMiddleware(ctx, permType)
|
||||||
|
|
||||||
// get the resource roles
|
// get the resource roles
|
||||||
let resourceRoles = []
|
let resourceRoles: any = []
|
||||||
const appId = getAppId()
|
const appId = getAppId()
|
||||||
if (appId && hasResource(ctx)) {
|
if (appId && hasResource(ctx)) {
|
||||||
resourceRoles = await getRequiredResourceRole(permLevel, ctx)
|
resourceRoles = await getRequiredResourceRole(permLevel, ctx)
|
|
@ -1,134 +0,0 @@
|
||||||
jest.mock("../../db")
|
|
||||||
jest.mock("../../utilities/usageQuota")
|
|
||||||
jest.mock("@budibase/backend-core/tenancy", () => ({
|
|
||||||
getTenantId: () => "testing123"
|
|
||||||
}))
|
|
||||||
|
|
||||||
const usageQuotaMiddleware = require("../usageQuota")
|
|
||||||
const usageQuota = require("../../utilities/usageQuota")
|
|
||||||
const CouchDB = require("../../db")
|
|
||||||
const env = require("../../environment")
|
|
||||||
|
|
||||||
class TestConfiguration {
|
|
||||||
constructor() {
|
|
||||||
this.throw = jest.fn()
|
|
||||||
this.next = jest.fn()
|
|
||||||
this.middleware = usageQuotaMiddleware
|
|
||||||
this.ctx = {
|
|
||||||
throw: this.throw,
|
|
||||||
next: this.next,
|
|
||||||
appId: "test",
|
|
||||||
request: {
|
|
||||||
body: {}
|
|
||||||
},
|
|
||||||
req: {
|
|
||||||
method: "POST",
|
|
||||||
url: "/applications"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
usageQuota.useQuotas = () => true
|
|
||||||
}
|
|
||||||
|
|
||||||
executeMiddleware() {
|
|
||||||
return this.middleware(this.ctx, this.next)
|
|
||||||
}
|
|
||||||
|
|
||||||
setProd(bool) {
|
|
||||||
if (bool) {
|
|
||||||
env.isDev = () => false
|
|
||||||
env.isProd = () => true
|
|
||||||
this.ctx.user = { tenantId: "test" }
|
|
||||||
} else {
|
|
||||||
env.isDev = () => true
|
|
||||||
env.isProd = () => false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setMethod(method) {
|
|
||||||
this.ctx.req.method = method
|
|
||||||
}
|
|
||||||
|
|
||||||
setUrl(url) {
|
|
||||||
this.ctx.req.url = url
|
|
||||||
}
|
|
||||||
|
|
||||||
setBody(body) {
|
|
||||||
this.ctx.request.body = body
|
|
||||||
}
|
|
||||||
|
|
||||||
setFiles(files) {
|
|
||||||
this.ctx.request.files = { file: files }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("usageQuota middleware", () => {
|
|
||||||
let config
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
config = new TestConfiguration()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("skips the middleware if there is no usage property or method", async () => {
|
|
||||||
await config.executeMiddleware()
|
|
||||||
expect(config.next).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("passes through to next middleware if document already exists", async () => {
|
|
||||||
config.setProd(true)
|
|
||||||
config.setBody({
|
|
||||||
_id: "test",
|
|
||||||
_rev: "test",
|
|
||||||
})
|
|
||||||
|
|
||||||
CouchDB.mockImplementationOnce(() => ({
|
|
||||||
get: async () => true
|
|
||||||
}))
|
|
||||||
|
|
||||||
await config.executeMiddleware()
|
|
||||||
|
|
||||||
expect(config.next).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it("throws if request has _id, but the document no longer exists", async () => {
|
|
||||||
config.setBody({
|
|
||||||
_id: "123",
|
|
||||||
_rev: "test",
|
|
||||||
})
|
|
||||||
config.setProd(true)
|
|
||||||
|
|
||||||
CouchDB.mockImplementationOnce(() => ({
|
|
||||||
get: async () => {
|
|
||||||
throw new Error()
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
|
|
||||||
await config.executeMiddleware()
|
|
||||||
expect(config.throw).toHaveBeenCalledWith(404, `${config.ctx.request.body._id} does not exist`)
|
|
||||||
})
|
|
||||||
|
|
||||||
it("calculates and persists the correct usage quota for the relevant action", async () => {
|
|
||||||
config.setUrl("/rows")
|
|
||||||
|
|
||||||
await config.executeMiddleware()
|
|
||||||
|
|
||||||
expect(usageQuota.update).toHaveBeenCalledWith("rows", 1)
|
|
||||||
expect(config.next).toHaveBeenCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
// it("calculates the correct file size from a file upload call and adds it to quota", async () => {
|
|
||||||
// config.setUrl("/upload")
|
|
||||||
// config.setProd(true)
|
|
||||||
// config.setFiles([
|
|
||||||
// {
|
|
||||||
// size: 100
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// size: 10000
|
|
||||||
// },
|
|
||||||
// ])
|
|
||||||
// await config.executeMiddleware()
|
|
||||||
|
|
||||||
// expect(usageQuota.update).toHaveBeenCalledWith("storage", 10100)
|
|
||||||
// expect(config.next).toHaveBeenCalled()
|
|
||||||
// })
|
|
||||||
})
|
|
|
@ -1,4 +1,3 @@
|
||||||
const env = require("../../../environment")
|
|
||||||
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
||||||
|
|
||||||
const syncApps = jest.fn()
|
const syncApps = jest.fn()
|
||||||
|
@ -14,7 +13,6 @@ describe("run", () => {
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
env._set("USE_QUOTAS", 1)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(config.end)
|
afterAll(config.end)
|
||||||
|
|
|
@ -1,37 +0,0 @@
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
|
||||||
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
|
||||||
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
|
|
||||||
const syncApps = require("../syncApps")
|
|
||||||
const env = require("../../../../environment")
|
|
||||||
|
|
||||||
describe("syncApps", () => {
|
|
||||||
let config = new TestConfig(false)
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await config.init()
|
|
||||||
env._set("USE_QUOTAS", 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(config.end)
|
|
||||||
|
|
||||||
it("runs successfully", async () => {
|
|
||||||
// create the usage quota doc and mock usages
|
|
||||||
const db = getGlobalDB()
|
|
||||||
await getUsageQuotaDoc(db)
|
|
||||||
await update(Properties.APPS, 3)
|
|
||||||
|
|
||||||
let usageDoc = await getUsageQuotaDoc(db)
|
|
||||||
expect(usageDoc.usageQuota.apps).toEqual(3)
|
|
||||||
|
|
||||||
// create an extra app to test the migration
|
|
||||||
await config.createApp("quota-test")
|
|
||||||
|
|
||||||
// migrate
|
|
||||||
await syncApps.run()
|
|
||||||
|
|
||||||
// assert the migration worked
|
|
||||||
usageDoc = await getUsageQuotaDoc(db)
|
|
||||||
expect(usageDoc.usageQuota.apps).toEqual(2)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
import TestConfig from "../../../../tests/utilities/TestConfiguration"
|
||||||
|
import * as syncApps from "../syncApps"
|
||||||
|
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
|
||||||
|
|
||||||
|
describe("syncApps", () => {
|
||||||
|
let config = new TestConfig(false)
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(config.end)
|
||||||
|
|
||||||
|
it("runs successfully", async () => {
|
||||||
|
// create the usage quota doc and mock usages
|
||||||
|
await quotas.getQuotaUsage()
|
||||||
|
await quotas.setUsage(3, StaticQuotaName.APPS, QuotaUsageType.STATIC)
|
||||||
|
|
||||||
|
let usageDoc = await quotas.getQuotaUsage()
|
||||||
|
expect(usageDoc.usageQuota.apps).toEqual(3)
|
||||||
|
|
||||||
|
// create an extra app to test the migration
|
||||||
|
await config.createApp("quota-test")
|
||||||
|
|
||||||
|
// migrate
|
||||||
|
await syncApps.run()
|
||||||
|
|
||||||
|
// assert the migration worked
|
||||||
|
usageDoc = await quotas.getQuotaUsage()
|
||||||
|
expect(usageDoc.usageQuota.apps).toEqual(2)
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,43 +0,0 @@
|
||||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
|
||||||
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
|
||||||
const { getUsageQuotaDoc, update, Properties } = require("../../../../utilities/usageQuota")
|
|
||||||
const syncRows = require("../syncRows")
|
|
||||||
const env = require("../../../../environment")
|
|
||||||
|
|
||||||
describe("syncRows", () => {
|
|
||||||
let config = new TestConfig(false)
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
await config.init()
|
|
||||||
env._set("USE_QUOTAS", 1)
|
|
||||||
})
|
|
||||||
|
|
||||||
afterAll(config.end)
|
|
||||||
|
|
||||||
it("runs successfully", async () => {
|
|
||||||
// create the usage quota doc and mock usages
|
|
||||||
const db = getGlobalDB()
|
|
||||||
await getUsageQuotaDoc(db)
|
|
||||||
await update(Properties.ROW, 300)
|
|
||||||
|
|
||||||
let usageDoc = await getUsageQuotaDoc(db)
|
|
||||||
expect(usageDoc.usageQuota.rows).toEqual(300)
|
|
||||||
|
|
||||||
// app 1
|
|
||||||
await config.createTable()
|
|
||||||
await config.createRow()
|
|
||||||
// app 2
|
|
||||||
await config.createApp("second-app")
|
|
||||||
await config.createTable()
|
|
||||||
await config.createRow()
|
|
||||||
await config.createRow()
|
|
||||||
|
|
||||||
// migrate
|
|
||||||
await syncRows.run()
|
|
||||||
|
|
||||||
// assert the migration worked
|
|
||||||
usageDoc = await getUsageQuotaDoc(db)
|
|
||||||
expect(usageDoc.usageQuota.rows).toEqual(3)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import TestConfig from "../../../../tests/utilities/TestConfiguration"
|
||||||
|
import * as syncRows from "../syncRows"
|
||||||
|
import { quotas, QuotaUsageType, StaticQuotaName } from "@budibase/pro"
|
||||||
|
|
||||||
|
describe("syncRows", () => {
|
||||||
|
let config = new TestConfig(false)
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(config.end)
|
||||||
|
|
||||||
|
it("runs successfully", async () => {
|
||||||
|
// create the usage quota doc and mock usages
|
||||||
|
await quotas.getQuotaUsage()
|
||||||
|
await quotas.setUsage(300, StaticQuotaName.ROWS, QuotaUsageType.STATIC)
|
||||||
|
|
||||||
|
let usageDoc = await quotas.getQuotaUsage()
|
||||||
|
expect(usageDoc.usageQuota.rows).toEqual(300)
|
||||||
|
|
||||||
|
// app 1
|
||||||
|
await config.createTable()
|
||||||
|
await config.createRow()
|
||||||
|
// app 2
|
||||||
|
await config.createApp("second-app")
|
||||||
|
await config.createTable()
|
||||||
|
await config.createRow()
|
||||||
|
await config.createRow()
|
||||||
|
|
||||||
|
// migrate
|
||||||
|
await syncRows.run()
|
||||||
|
|
||||||
|
// assert the migration worked
|
||||||
|
usageDoc = await quotas.getQuotaUsage()
|
||||||
|
expect(usageDoc.usageQuota.rows).toEqual(3)
|
||||||
|
})
|
||||||
|
})
|
|
@ -3,3 +3,5 @@ declare module "@budibase/backend-core/tenancy"
|
||||||
declare module "@budibase/backend-core/db"
|
declare module "@budibase/backend-core/db"
|
||||||
declare module "@budibase/backend-core/context"
|
declare module "@budibase/backend-core/context"
|
||||||
declare module "@budibase/backend-core/cache"
|
declare module "@budibase/backend-core/cache"
|
||||||
|
declare module "@budibase/backend-core/permissions"
|
||||||
|
declare module "@budibase/backend-core/roles"
|
||||||
|
|
|
@ -1,72 +0,0 @@
|
||||||
const getTenantId = jest.fn()
|
|
||||||
jest.mock("@budibase/backend-core/tenancy", () => ({
|
|
||||||
getTenantId
|
|
||||||
}))
|
|
||||||
const usageQuota = require("../../usageQuota")
|
|
||||||
const env = require("../../../environment")
|
|
||||||
|
|
||||||
class TestConfiguration {
|
|
||||||
constructor() {
|
|
||||||
this.enableQuotas()
|
|
||||||
}
|
|
||||||
|
|
||||||
enableQuotas = () => {
|
|
||||||
env.USE_QUOTAS = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
disableQuotas = () => {
|
|
||||||
env.USE_QUOTAS = null
|
|
||||||
}
|
|
||||||
|
|
||||||
setTenantId = (tenantId) => {
|
|
||||||
getTenantId.mockReturnValue(tenantId)
|
|
||||||
}
|
|
||||||
|
|
||||||
setExcludedTenants = (tenants) => {
|
|
||||||
env.EXCLUDE_QUOTAS_TENANTS = tenants
|
|
||||||
}
|
|
||||||
|
|
||||||
reset = () => {
|
|
||||||
this.disableQuotas()
|
|
||||||
this.setExcludedTenants(null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("usageQuota", () => {
|
|
||||||
let config
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
config = new TestConfiguration()
|
|
||||||
})
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
config.reset()
|
|
||||||
})
|
|
||||||
|
|
||||||
describe("useQuotas", () => {
|
|
||||||
it("works when no settings have been provided", () => {
|
|
||||||
config.reset()
|
|
||||||
expect(usageQuota.useQuotas()).toBe(false)
|
|
||||||
})
|
|
||||||
it("honours USE_QUOTAS setting", () => {
|
|
||||||
config.disableQuotas()
|
|
||||||
expect(usageQuota.useQuotas()).toBe(false)
|
|
||||||
|
|
||||||
config.enableQuotas()
|
|
||||||
expect(usageQuota.useQuotas()).toBe(true)
|
|
||||||
})
|
|
||||||
it("honours EXCLUDE_QUOTAS_TENANTS setting", () => {
|
|
||||||
config.setTenantId("test")
|
|
||||||
|
|
||||||
// tenantId is in the list
|
|
||||||
config.setExcludedTenants("test, test2, test2")
|
|
||||||
expect(usageQuota.useQuotas()).toBe(false)
|
|
||||||
config.setExcludedTenants("test,test2,test2")
|
|
||||||
expect(usageQuota.useQuotas()).toBe(false)
|
|
||||||
|
|
||||||
// tenantId is not in the list
|
|
||||||
config.setTenantId("other")
|
|
||||||
expect(usageQuota.useQuotas()).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
// Used for building with tsc
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/*.json",
|
||||||
|
"**/*.spec.js",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
]
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"**/*.json",
|
"**/*.json",
|
||||||
"**/*.spec.ts",
|
"**/*.spec.js",
|
||||||
"**/*.spec.js"
|
// "**/*.spec.ts" // don't exclude spec.ts files for editor support
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -2531,13 +2531,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/istanbul-lib-report" "*"
|
"@types/istanbul-lib-report" "*"
|
||||||
|
|
||||||
"@types/jest@^26.0.23":
|
"@types/jest@^27.4.1":
|
||||||
version "26.0.24"
|
version "27.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-26.0.24.tgz#943d11976b16739185913a1936e0de0c4a7d595a"
|
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-27.4.1.tgz#185cbe2926eaaf9662d340cc02e548ce9e11ab6d"
|
||||||
integrity sha512-E/X5Vib8BWqZNRlDxj9vYXhsDwPYbPINqKF9BsnSoon4RQ0D9moEuLD8txgyypFLH7J4+Lho9Nr/c8H0Fi+17w==
|
integrity sha512-23iPJADSmicDVrWk+HT58LMJtzLAnB2AgIzplQuq/bSrGaxCrlvRFjGbXmamnnk/mAmCdLStiGqggu28ocUyiw==
|
||||||
dependencies:
|
dependencies:
|
||||||
jest-diff "^26.0.0"
|
jest-matcher-utils "^27.0.0"
|
||||||
pretty-format "^26.0.0"
|
pretty-format "^27.0.0"
|
||||||
|
|
||||||
"@types/json-schema@*", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.8":
|
"@types/json-schema@*", "@types/json-schema@^7.0.6", "@types/json-schema@^7.0.8":
|
||||||
version "7.0.9"
|
version "7.0.9"
|
||||||
|
@ -2577,6 +2577,13 @@
|
||||||
"@types/koa-compose" "*"
|
"@types/koa-compose" "*"
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/koa__router@^8.0.11":
|
||||||
|
version "8.0.11"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/koa__router/-/koa__router-8.0.11.tgz#d7b37e6db934fc072ea1baa2ab92bc8ac4564f3e"
|
||||||
|
integrity sha512-WXgKWpBsbS14kzmzD9LeFapOIa678h7zvUHxDwXwSx4ETKXhXLVUAToX6jZ/U7EihM7qwyD9W/BZvB0MRu7MTQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/koa" "*"
|
||||||
|
|
||||||
"@types/lodash@^4.14.179":
|
"@types/lodash@^4.14.179":
|
||||||
version "4.14.180"
|
version "4.14.180"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.180.tgz#4ab7c9ddfc92ec4a887886483bc14c79fb380670"
|
||||||
|
@ -7375,7 +7382,7 @@ jest-diff@^24.9.0:
|
||||||
jest-get-type "^24.9.0"
|
jest-get-type "^24.9.0"
|
||||||
pretty-format "^24.9.0"
|
pretty-format "^24.9.0"
|
||||||
|
|
||||||
jest-diff@^26.0.0, jest-diff@^26.6.2:
|
jest-diff@^26.6.2:
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
|
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-26.6.2.tgz#1aa7468b52c3a68d7d5c5fdcdfcd5e49bd164394"
|
||||||
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
|
integrity sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==
|
||||||
|
@ -7614,7 +7621,7 @@ jest-matcher-utils@^26.6.2:
|
||||||
jest-get-type "^26.3.0"
|
jest-get-type "^26.3.0"
|
||||||
pretty-format "^26.6.2"
|
pretty-format "^26.6.2"
|
||||||
|
|
||||||
jest-matcher-utils@^27.5.1:
|
jest-matcher-utils@^27.0.0, jest-matcher-utils@^27.5.1:
|
||||||
version "27.5.1"
|
version "27.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
|
resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz#9c0cdbda8245bc22d2331729d1091308b40cf8ab"
|
||||||
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
|
integrity sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==
|
||||||
|
@ -10413,7 +10420,7 @@ pretty-format@^24.9.0:
|
||||||
ansi-styles "^3.2.0"
|
ansi-styles "^3.2.0"
|
||||||
react-is "^16.8.4"
|
react-is "^16.8.4"
|
||||||
|
|
||||||
pretty-format@^26.0.0, pretty-format@^26.6.2:
|
pretty-format@^26.6.2:
|
||||||
version "26.6.2"
|
version "26.6.2"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
|
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-26.6.2.tgz#e35c2705f14cb7fe2fe94fa078345b444120fc93"
|
||||||
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
|
integrity sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==
|
||||||
|
@ -10423,7 +10430,7 @@ pretty-format@^26.0.0, pretty-format@^26.6.2:
|
||||||
ansi-styles "^4.0.0"
|
ansi-styles "^4.0.0"
|
||||||
react-is "^17.0.1"
|
react-is "^17.0.1"
|
||||||
|
|
||||||
pretty-format@^27.5.1:
|
pretty-format@^27.0.0, pretty-format@^27.5.1:
|
||||||
version "27.5.1"
|
version "27.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e"
|
||||||
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==
|
||||||
|
|
|
@ -75,7 +75,8 @@ describe("/api/global/auth", () => {
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
expect(strategyFactory).toBeCalledWith(
|
expect(strategyFactory).toBeCalledWith(
|
||||||
chosenConfig,
|
chosenConfig,
|
||||||
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`
|
`http://localhost:10000/api/global/auth/${TENANT_ID}/oidc/callback`,
|
||||||
|
expect.any(Function)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue