Merge branch 'develop' of github.com:Budibase/budibase into labday/automation-plugins
This commit is contained in:
commit
a9fb09424b
95 changed files with 1400 additions and 996 deletions
2
.github/workflows/deploy-cloud.yaml
vendored
2
.github/workflows/deploy-cloud.yaml
vendored
|
@ -55,7 +55,7 @@ jobs:
|
||||||
config-files: values.production.yaml
|
config-files: values.production.yaml
|
||||||
chart-path: charts/budibase
|
chart-path: charts/budibase
|
||||||
namespace: budibase
|
namespace: budibase
|
||||||
values: globals.appVersion=v${{ env.RELEASE_VERSION }},services.couchdb.url=${{ secrets.PRODUCTION_COUCHDB_URL }},services.couchdb.password=${{ secrets.PRODUCTION_COUCHDB_PASSWORD }}
|
values: globals.appVersion=${{ env.RELEASE_VERSION }},services.couchdb.url=${{ secrets.PRODUCTION_COUCHDB_URL }},services.couchdb.password=${{ secrets.PRODUCTION_COUCHDB_PASSWORD }}
|
||||||
name: budibase-prod
|
name: budibase-prod
|
||||||
|
|
||||||
- name: Discord Webhook Action
|
- name: Discord Webhook Action
|
||||||
|
|
|
@ -199,7 +199,11 @@ spec:
|
||||||
value: {{ .Values.services.tlsRejectUnauthorized }}
|
value: {{ .Values.services.tlsRejectUnauthorized }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/apps:{{ .Values.globals.appVersion }}
|
{{ if .Values.globals.appVersion }}
|
||||||
|
image: budibase/apps:v{{ .Values.globals.appVersion }}
|
||||||
|
{{ else }}
|
||||||
|
image: budibase/apps:v{{ .Chart.AppVersion }}
|
||||||
|
{{ end }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -37,7 +37,11 @@ spec:
|
||||||
{{ end }}
|
{{ end }}
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- image: budibase/proxy:{{ .Values.globals.appVersion }}
|
{{ if .Values.globals.appVersion }}
|
||||||
|
image: budibase/proxy:v{{ .Values.globals.appVersion }}
|
||||||
|
{{ else }}
|
||||||
|
image: budibase/proxy:v{{ .Chart.AppVersion }}
|
||||||
|
{{ end }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
name: proxy-service
|
name: proxy-service
|
||||||
ports:
|
ports:
|
||||||
|
|
|
@ -189,7 +189,11 @@ spec:
|
||||||
value: {{ .Values.services.tlsRejectUnauthorized }}
|
value: {{ .Values.services.tlsRejectUnauthorized }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
image: budibase/worker:{{ .Values.globals.appVersion }}
|
{{ if .Values.globals.appVersion }}
|
||||||
|
image: budibase/worker:v{{ .Values.globals.appVersion }}
|
||||||
|
{{ else }}
|
||||||
|
image: budibase/worker:v{{ .Chart.AppVersion }}
|
||||||
|
{{ end }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
livenessProbe:
|
livenessProbe:
|
||||||
httpGet:
|
httpGet:
|
||||||
|
|
|
@ -74,7 +74,7 @@ tolerations: []
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
appVersion: "latest"
|
appVersion: "" # Use as an override to .Chart.AppVersion
|
||||||
budibaseEnv: PRODUCTION
|
budibaseEnv: PRODUCTION
|
||||||
tenantFeatureFlags: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"
|
tenantFeatureFlags: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR"
|
||||||
enableAnalytics: "1"
|
enableAnalytics: "1"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": ["packages/*"],
|
"packages": ["packages/*"],
|
||||||
"command": {
|
"command": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/backend-core",
|
"name": "@budibase/backend-core",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase backend core libraries used in server and worker",
|
"description": "Budibase backend core libraries used in server and worker",
|
||||||
"main": "dist/src/index.js",
|
"main": "dist/src/index.js",
|
||||||
"types": "dist/src/index.d.ts",
|
"types": "dist/src/index.d.ts",
|
||||||
|
@ -24,7 +24,7 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/nano": "10.1.2",
|
"@budibase/nano": "10.1.2",
|
||||||
"@budibase/pouchdb-replication-stream": "1.2.10",
|
"@budibase/pouchdb-replication-stream": "1.2.10",
|
||||||
"@budibase/types": "2.4.44-alpha.11",
|
"@budibase/types": "2.4.44-alpha.16",
|
||||||
"@shopify/jest-koa-mocks": "5.0.1",
|
"@shopify/jest-koa-mocks": "5.0.1",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
"aws-cloudfront-sign": "2.2.0",
|
"aws-cloudfront-sign": "2.2.0",
|
||||||
|
@ -58,6 +58,7 @@
|
||||||
"zlib": "1.0.5"
|
"zlib": "1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@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",
|
||||||
|
@ -75,14 +76,14 @@
|
||||||
"@types/uuid": "8.3.4",
|
"@types/uuid": "8.3.4",
|
||||||
"chance": "1.1.8",
|
"chance": "1.1.8",
|
||||||
"ioredis-mock": "5.8.0",
|
"ioredis-mock": "5.8.0",
|
||||||
"jest": "28.1.1",
|
"jest": "29.5.0",
|
||||||
"jest-serial-runner": "^1.2.1",
|
"jest-serial-runner": "^1.2.1",
|
||||||
"koa": "2.13.4",
|
"koa": "2.13.4",
|
||||||
"nodemon": "2.0.16",
|
"nodemon": "2.0.16",
|
||||||
"pino-pretty": "10.0.0",
|
"pino-pretty": "10.0.0",
|
||||||
"pouchdb-adapter-memory": "7.2.2",
|
"pouchdb-adapter-memory": "7.2.2",
|
||||||
"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"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { structures, testEnv } from "../../../tests"
|
import { structures } from "../../../tests"
|
||||||
|
import { testEnv } from "../../../tests/extra"
|
||||||
import * as auth from "../auth"
|
import * as auth from "../auth"
|
||||||
import * as events from "../../events"
|
import * as events from "../../events"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import { DBTestConfiguration } from "../../../tests/extra"
|
||||||
import {
|
import {
|
||||||
structures,
|
structures,
|
||||||
DBTestConfiguration,
|
|
||||||
expectFunctionWasCalledTimesWith,
|
expectFunctionWasCalledTimesWith,
|
||||||
mocks,
|
mocks,
|
||||||
} from "../../../tests"
|
} from "../../../tests"
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
import {
|
import { generator, structures } from "../../../tests"
|
||||||
DBTestConfiguration,
|
import { DBTestConfiguration, testEnv } from "../../../tests/extra"
|
||||||
generator,
|
|
||||||
testEnv,
|
|
||||||
structures,
|
|
||||||
} from "../../../tests"
|
|
||||||
import { ConfigType } from "@budibase/types"
|
import { ConfigType } from "@budibase/types"
|
||||||
import env from "../../environment"
|
import env from "../../environment"
|
||||||
import * as configs from "../configs"
|
import * as configs from "../configs"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// store an app ID to pretend there is a context
|
// store an app ID to pretend there is a context
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import Context from "./Context"
|
import Context from "./Context"
|
||||||
import * as conversions from "../db/conversions"
|
import * as conversions from "../docIds/conversions"
|
||||||
import { getDB } from "../db/db"
|
import { getDB } from "../db/db"
|
||||||
import {
|
import {
|
||||||
DocumentType,
|
DocumentType,
|
||||||
|
@ -43,8 +43,12 @@ export function baseGlobalDBName(tenantId: string | undefined | null) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getPlatformURL() {
|
||||||
|
return env.PLATFORM_URL
|
||||||
|
}
|
||||||
|
|
||||||
export function isMultiTenant() {
|
export function isMultiTenant() {
|
||||||
return env.MULTI_TENANCY
|
return !!env.MULTI_TENANCY
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isTenantIdSet() {
|
export function isTenantIdSet() {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { testEnv } from "../../../tests"
|
import { testEnv } from "../../../tests/extra"
|
||||||
import * as context from "../"
|
import * as context from "../"
|
||||||
import { DEFAULT_TENANT_ID } from "../../constants"
|
import { DEFAULT_TENANT_ID } from "../../constants"
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ import { getCouchInfo } from "./connections"
|
||||||
import { directCouchCall } from "./utils"
|
import { directCouchCall } from "./utils"
|
||||||
import { getPouchDB } from "./pouchDB"
|
import { getPouchDB } from "./pouchDB"
|
||||||
import { WriteStream, ReadStream } from "fs"
|
import { WriteStream, ReadStream } from "fs"
|
||||||
import { newid } from "../../newid"
|
import { newid } from "../../docIds/newid"
|
||||||
|
|
||||||
function buildNano(couchInfo: { url: string; cookie: string }) {
|
function buildNano(couchInfo: { url: string; cookie: string }) {
|
||||||
return Nano({
|
return Nano({
|
||||||
|
|
|
@ -2,7 +2,7 @@ export * from "./couch"
|
||||||
export * from "./db"
|
export * from "./db"
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
export * from "./views"
|
export * from "./views"
|
||||||
export * from "./conversions"
|
export * from "../docIds/conversions"
|
||||||
export { default as Replication } from "./Replication"
|
export { default as Replication } from "./Replication"
|
||||||
// exports to support old export structure
|
// exports to support old export structure
|
||||||
export * from "../constants/db"
|
export * from "../constants/db"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { newid } from "../../newid"
|
import { newid } from "../../docIds/newid"
|
||||||
import { getDB } from "../db"
|
import { getDB } from "../db"
|
||||||
import { Database } from "@budibase/types"
|
import { Database } from "@budibase/types"
|
||||||
import { QueryBuilder, paginatedSearch, fullSearch } from "../lucene"
|
import { QueryBuilder, paginatedSearch, fullSearch } from "../lucene"
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {
|
||||||
getProdAppID,
|
getProdAppID,
|
||||||
isDevAppID,
|
isDevAppID,
|
||||||
isProdAppID,
|
isProdAppID,
|
||||||
} from "../conversions"
|
} from "../../docIds/conversions"
|
||||||
import { generateAppID } from "../utils"
|
import { generateAppID } from "../utils"
|
||||||
|
|
||||||
describe("utils", () => {
|
describe("utils", () => {
|
||||||
|
|
|
@ -1,257 +1,12 @@
|
||||||
import { newid } from "../newid"
|
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
import {
|
import { DEFAULT_TENANT_ID, SEPARATOR, DocumentType } from "../constants"
|
||||||
DEFAULT_TENANT_ID,
|
|
||||||
SEPARATOR,
|
|
||||||
DocumentType,
|
|
||||||
UNICODE_MAX,
|
|
||||||
ViewName,
|
|
||||||
InternalTable,
|
|
||||||
APP_PREFIX,
|
|
||||||
} from "../constants"
|
|
||||||
import { getTenantId, getGlobalDBName } from "../context"
|
import { getTenantId, getGlobalDBName } from "../context"
|
||||||
import { doWithDB, directCouchAllDbs } from "./db"
|
import { doWithDB, directCouchAllDbs } from "./db"
|
||||||
import { getAppMetadata } from "../cache/appMetadata"
|
import { getAppMetadata } from "../cache/appMetadata"
|
||||||
import { isDevApp, isDevAppID, getProdAppID } from "./conversions"
|
import { isDevApp, isDevAppID, getProdAppID } from "../docIds/conversions"
|
||||||
import { App, Database } from "@budibase/types"
|
import { App, Database } from "@budibase/types"
|
||||||
|
import { getStartEndKeyURL } from "../docIds"
|
||||||
/**
|
export * from "../docIds"
|
||||||
* Generates a new app ID.
|
|
||||||
* @returns {string} The new app ID which the app doc can be stored under.
|
|
||||||
*/
|
|
||||||
export const generateAppID = (tenantId?: string | null) => {
|
|
||||||
let id = APP_PREFIX
|
|
||||||
if (tenantId) {
|
|
||||||
id += `${tenantId}${SEPARATOR}`
|
|
||||||
}
|
|
||||||
return `${id}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
|
||||||
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
|
||||||
* More complex cases such as link docs and rows which have multiple levels of IDs that their
|
|
||||||
* ID consists of need their own functions to build the allDocs parameters.
|
|
||||||
* @param {string} docType The type of document which input params are being built for, e.g. user,
|
|
||||||
* link, app, table and so on.
|
|
||||||
* @param {string|null} docId The ID of the document minus its type - this is only needed if looking
|
|
||||||
* for a singular document.
|
|
||||||
* @param {object} otherProps Add any other properties onto the request, e.g. include_docs.
|
|
||||||
* @returns {object} Parameters which can then be used with an allDocs request.
|
|
||||||
*/
|
|
||||||
export function getDocParams(
|
|
||||||
docType: string,
|
|
||||||
docId?: string | null,
|
|
||||||
otherProps: any = {}
|
|
||||||
) {
|
|
||||||
if (docId == null) {
|
|
||||||
docId = ""
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
startkey: `${docType}${SEPARATOR}${docId}`,
|
|
||||||
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the DB allDocs/query params for retrieving a row.
|
|
||||||
* @param {string|null} tableId The table in which the rows have been stored.
|
|
||||||
* @param {string|null} rowId The ID of the row which is being specifically queried for. This can be
|
|
||||||
* left null to get all the rows in the table.
|
|
||||||
* @param {object} otherProps Any other properties to add to the request.
|
|
||||||
* @returns {object} Parameters which can then be used with an allDocs request.
|
|
||||||
*/
|
|
||||||
export function getRowParams(
|
|
||||||
tableId?: string | null,
|
|
||||||
rowId?: string | null,
|
|
||||||
otherProps = {}
|
|
||||||
) {
|
|
||||||
if (tableId == null) {
|
|
||||||
return getDocParams(DocumentType.ROW, null, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
const endOfKey = rowId == null ? `${tableId}${SEPARATOR}` : rowId
|
|
||||||
|
|
||||||
return getDocParams(DocumentType.ROW, endOfKey, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the correct index for a view based on default design DB.
|
|
||||||
*/
|
|
||||||
export function getQueryIndex(viewName: ViewName) {
|
|
||||||
return `database/${viewName}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a new row ID for the specified table.
|
|
||||||
* @param {string} tableId The table which the row is being created for.
|
|
||||||
* @param {string|null} id If an ID is to be used then the UUID can be substituted for this.
|
|
||||||
* @returns {string} The new ID which a row doc can be stored under.
|
|
||||||
*/
|
|
||||||
export function generateRowID(tableId: string, id?: string) {
|
|
||||||
id = id || newid()
|
|
||||||
return `${DocumentType.ROW}${SEPARATOR}${tableId}${SEPARATOR}${id}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a given ID is that of a table.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export const isTableId = (id: string) => {
|
|
||||||
// this includes datasource plus tables
|
|
||||||
return (
|
|
||||||
id &&
|
|
||||||
(id.startsWith(`${DocumentType.TABLE}${SEPARATOR}`) ||
|
|
||||||
id.startsWith(`${DocumentType.DATASOURCE_PLUS}${SEPARATOR}`))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a given ID is that of a datasource or datasource plus.
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
export const isDatasourceId = (id: string) => {
|
|
||||||
// this covers both datasources and datasource plus
|
|
||||||
return id && id.startsWith(`${DocumentType.DATASOURCE}${SEPARATOR}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new workspace ID.
|
|
||||||
* @returns {string} The new workspace ID which the workspace doc can be stored under.
|
|
||||||
*/
|
|
||||||
export function generateWorkspaceID() {
|
|
||||||
return `${DocumentType.WORKSPACE}${SEPARATOR}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving workspaces.
|
|
||||||
*/
|
|
||||||
export function getWorkspaceParams(id = "", otherProps = {}) {
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
startkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}`,
|
|
||||||
endkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new global user ID.
|
|
||||||
* @returns {string} The new user ID which the user doc can be stored under.
|
|
||||||
*/
|
|
||||||
export function generateGlobalUserID(id?: any) {
|
|
||||||
return `${DocumentType.USER}${SEPARATOR}${id || newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving users.
|
|
||||||
*/
|
|
||||||
export function getGlobalUserParams(globalId: any, otherProps: any = {}) {
|
|
||||||
if (!globalId) {
|
|
||||||
globalId = ""
|
|
||||||
}
|
|
||||||
const startkey = otherProps?.startkey
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
// need to include this incase pagination
|
|
||||||
startkey: startkey
|
|
||||||
? startkey
|
|
||||||
: `${DocumentType.USER}${SEPARATOR}${globalId}`,
|
|
||||||
endkey: `${DocumentType.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
|
||||||
*/
|
|
||||||
export function getUserMetadataParams(userId?: string | null, otherProps = {}) {
|
|
||||||
return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new user ID based on the passed in global ID.
|
|
||||||
* @param {string} globalId The ID of the global user.
|
|
||||||
* @returns {string} The new user ID which the user doc can be stored under.
|
|
||||||
*/
|
|
||||||
export function generateUserMetadataID(globalId: string) {
|
|
||||||
return generateRowID(InternalTable.USER_METADATA, globalId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Breaks up the ID to get the global ID.
|
|
||||||
*/
|
|
||||||
export function getGlobalIDFromUserMetadataID(id: string) {
|
|
||||||
const prefix = `${DocumentType.ROW}${SEPARATOR}${InternalTable.USER_METADATA}${SEPARATOR}`
|
|
||||||
if (!id || !id.includes(prefix)) {
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
return id.split(prefix)[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getUsersByAppParams(appId: any, otherProps: any = {}) {
|
|
||||||
const prodAppId = getProdAppID(appId)
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
startkey: prodAppId,
|
|
||||||
endkey: `${prodAppId}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a template ID.
|
|
||||||
* @param ownerId The owner/user of the template, this could be global or a workspace level.
|
|
||||||
*/
|
|
||||||
export function generateTemplateID(ownerId: any) {
|
|
||||||
return `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
export function generateAppUserID(prodAppId: string, userId: string) {
|
|
||||||
return `${prodAppId}${SEPARATOR}${userId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving templates. Owner ID must be specified, either global or a workspace level.
|
|
||||||
*/
|
|
||||||
export function getTemplateParams(
|
|
||||||
ownerId: any,
|
|
||||||
templateId: any,
|
|
||||||
otherProps = {}
|
|
||||||
) {
|
|
||||||
if (!templateId) {
|
|
||||||
templateId = ""
|
|
||||||
}
|
|
||||||
let final
|
|
||||||
if (templateId) {
|
|
||||||
final = templateId
|
|
||||||
} else {
|
|
||||||
final = `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}`
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
...otherProps,
|
|
||||||
startkey: final,
|
|
||||||
endkey: `${final}${UNICODE_MAX}`,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new role ID.
|
|
||||||
* @returns {string} The new role ID which the role doc can be stored under.
|
|
||||||
*/
|
|
||||||
export function generateRoleID(id?: any) {
|
|
||||||
return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving a role, this is a utility function for the getDocParams function.
|
|
||||||
*/
|
|
||||||
export function getRoleParams(roleId?: string | null, otherProps = {}) {
|
|
||||||
return getDocParams(DocumentType.ROLE, roleId, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStartEndKeyURL(baseKey: any, tenantId?: string) {
|
|
||||||
const tenancy = tenantId ? `${SEPARATOR}${tenantId}` : ""
|
|
||||||
return `startkey="${baseKey}${tenancy}"&endkey="${baseKey}${tenancy}${UNICODE_MAX}"`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing
|
* if in production this will use the CouchDB _all_dbs call to retrieve a list of databases. If testing
|
||||||
|
@ -411,29 +166,6 @@ export async function dbExists(dbName: any) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new dev info document ID - this is scoped to a user.
|
|
||||||
* @returns {string} The new dev info ID which info for dev (like api key) can be stored under.
|
|
||||||
*/
|
|
||||||
export const generateDevInfoID = (userId: any) => {
|
|
||||||
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a new plugin ID - to be used in the global DB.
|
|
||||||
* @returns {string} The new plugin ID which a plugin metadata document can be stored under.
|
|
||||||
*/
|
|
||||||
export const generatePluginID = (name: string) => {
|
|
||||||
return `${DocumentType.PLUGIN}${SEPARATOR}${name}`
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
|
|
||||||
*/
|
|
||||||
export const getPluginParams = (pluginId?: string | null, otherProps = {}) => {
|
|
||||||
return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pagination<T>(
|
export function pagination<T>(
|
||||||
data: T[],
|
data: T[],
|
||||||
pageSize: number,
|
pageSize: number,
|
||||||
|
|
102
packages/backend-core/src/docIds/ids.ts
Normal file
102
packages/backend-core/src/docIds/ids.ts
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
import {
|
||||||
|
APP_PREFIX,
|
||||||
|
DocumentType,
|
||||||
|
InternalTable,
|
||||||
|
SEPARATOR,
|
||||||
|
} from "../constants"
|
||||||
|
import { newid } from "./newid"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new app ID.
|
||||||
|
* @returns {string} The new app ID which the app doc can be stored under.
|
||||||
|
*/
|
||||||
|
export const generateAppID = (tenantId?: string | null) => {
|
||||||
|
let id = APP_PREFIX
|
||||||
|
if (tenantId) {
|
||||||
|
id += `${tenantId}${SEPARATOR}`
|
||||||
|
}
|
||||||
|
return `${id}${newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a new row ID for the specified table.
|
||||||
|
* @param {string} tableId The table which the row is being created for.
|
||||||
|
* @param {string|null} id If an ID is to be used then the UUID can be substituted for this.
|
||||||
|
* @returns {string} The new ID which a row doc can be stored under.
|
||||||
|
*/
|
||||||
|
export function generateRowID(tableId: string, id?: string) {
|
||||||
|
id = id || newid()
|
||||||
|
return `${DocumentType.ROW}${SEPARATOR}${tableId}${SEPARATOR}${id}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new workspace ID.
|
||||||
|
* @returns {string} The new workspace ID which the workspace doc can be stored under.
|
||||||
|
*/
|
||||||
|
export function generateWorkspaceID() {
|
||||||
|
return `${DocumentType.WORKSPACE}${SEPARATOR}${newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new global user ID.
|
||||||
|
* @returns {string} The new user ID which the user doc can be stored under.
|
||||||
|
*/
|
||||||
|
export function generateGlobalUserID(id?: any) {
|
||||||
|
return `${DocumentType.USER}${SEPARATOR}${id || newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new user ID based on the passed in global ID.
|
||||||
|
* @param {string} globalId The ID of the global user.
|
||||||
|
* @returns {string} The new user ID which the user doc can be stored under.
|
||||||
|
*/
|
||||||
|
export function generateUserMetadataID(globalId: string) {
|
||||||
|
return generateRowID(InternalTable.USER_METADATA, globalId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks up the ID to get the global ID.
|
||||||
|
*/
|
||||||
|
export function getGlobalIDFromUserMetadataID(id: string) {
|
||||||
|
const prefix = `${DocumentType.ROW}${SEPARATOR}${InternalTable.USER_METADATA}${SEPARATOR}`
|
||||||
|
if (!id || !id.includes(prefix)) {
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
return id.split(prefix)[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a template ID.
|
||||||
|
* @param ownerId The owner/user of the template, this could be global or a workspace level.
|
||||||
|
*/
|
||||||
|
export function generateTemplateID(ownerId: any) {
|
||||||
|
return `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}${newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateAppUserID(prodAppId: string, userId: string) {
|
||||||
|
return `${prodAppId}${SEPARATOR}${userId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new role ID.
|
||||||
|
* @returns {string} The new role ID which the role doc can be stored under.
|
||||||
|
*/
|
||||||
|
export function generateRoleID(id?: any) {
|
||||||
|
return `${DocumentType.ROLE}${SEPARATOR}${id || newid()}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new dev info document ID - this is scoped to a user.
|
||||||
|
* @returns {string} The new dev info ID which info for dev (like api key) can be stored under.
|
||||||
|
*/
|
||||||
|
export const generateDevInfoID = (userId: any) => {
|
||||||
|
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a new plugin ID - to be used in the global DB.
|
||||||
|
* @returns {string} The new plugin ID which a plugin metadata document can be stored under.
|
||||||
|
*/
|
||||||
|
export const generatePluginID = (name: string) => {
|
||||||
|
return `${DocumentType.PLUGIN}${SEPARATOR}${name}`
|
||||||
|
}
|
2
packages/backend-core/src/docIds/index.ts
Normal file
2
packages/backend-core/src/docIds/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * from "./ids"
|
||||||
|
export * from "./params"
|
174
packages/backend-core/src/docIds/params.ts
Normal file
174
packages/backend-core/src/docIds/params.ts
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
import {
|
||||||
|
DocumentType,
|
||||||
|
InternalTable,
|
||||||
|
SEPARATOR,
|
||||||
|
UNICODE_MAX,
|
||||||
|
ViewName,
|
||||||
|
} from "../constants"
|
||||||
|
import { getProdAppID } from "./conversions"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If creating DB allDocs/query params with only a single top level ID this can be used, this
|
||||||
|
* is usually the case as most of our docs are top level e.g. tables, automations, users and so on.
|
||||||
|
* More complex cases such as link docs and rows which have multiple levels of IDs that their
|
||||||
|
* ID consists of need their own functions to build the allDocs parameters.
|
||||||
|
* @param {string} docType The type of document which input params are being built for, e.g. user,
|
||||||
|
* link, app, table and so on.
|
||||||
|
* @param {string|null} docId The ID of the document minus its type - this is only needed if looking
|
||||||
|
* for a singular document.
|
||||||
|
* @param {object} otherProps Add any other properties onto the request, e.g. include_docs.
|
||||||
|
* @returns {object} Parameters which can then be used with an allDocs request.
|
||||||
|
*/
|
||||||
|
export function getDocParams(
|
||||||
|
docType: string,
|
||||||
|
docId?: string | null,
|
||||||
|
otherProps: any = {}
|
||||||
|
) {
|
||||||
|
if (docId == null) {
|
||||||
|
docId = ""
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
startkey: `${docType}${SEPARATOR}${docId}`,
|
||||||
|
endkey: `${docType}${SEPARATOR}${docId}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the DB allDocs/query params for retrieving a row.
|
||||||
|
* @param {string|null} tableId The table in which the rows have been stored.
|
||||||
|
* @param {string|null} rowId The ID of the row which is being specifically queried for. This can be
|
||||||
|
* left null to get all the rows in the table.
|
||||||
|
* @param {object} otherProps Any other properties to add to the request.
|
||||||
|
* @returns {object} Parameters which can then be used with an allDocs request.
|
||||||
|
*/
|
||||||
|
export function getRowParams(
|
||||||
|
tableId?: string | null,
|
||||||
|
rowId?: string | null,
|
||||||
|
otherProps = {}
|
||||||
|
) {
|
||||||
|
if (tableId == null) {
|
||||||
|
return getDocParams(DocumentType.ROW, null, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
const endOfKey = rowId == null ? `${tableId}${SEPARATOR}` : rowId
|
||||||
|
|
||||||
|
return getDocParams(DocumentType.ROW, endOfKey, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the correct index for a view based on default design DB.
|
||||||
|
*/
|
||||||
|
export function getQueryIndex(viewName: ViewName) {
|
||||||
|
return `database/${viewName}`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given ID is that of a table.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const isTableId = (id: string) => {
|
||||||
|
// this includes datasource plus tables
|
||||||
|
return (
|
||||||
|
id &&
|
||||||
|
(id.startsWith(`${DocumentType.TABLE}${SEPARATOR}`) ||
|
||||||
|
id.startsWith(`${DocumentType.DATASOURCE_PLUS}${SEPARATOR}`))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given ID is that of a datasource or datasource plus.
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export const isDatasourceId = (id: string) => {
|
||||||
|
// this covers both datasources and datasource plus
|
||||||
|
return id && id.startsWith(`${DocumentType.DATASOURCE}${SEPARATOR}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving workspaces.
|
||||||
|
*/
|
||||||
|
export function getWorkspaceParams(id = "", otherProps = {}) {
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
startkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}`,
|
||||||
|
endkey: `${DocumentType.WORKSPACE}${SEPARATOR}${id}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving users.
|
||||||
|
*/
|
||||||
|
export function getGlobalUserParams(globalId: any, otherProps: any = {}) {
|
||||||
|
if (!globalId) {
|
||||||
|
globalId = ""
|
||||||
|
}
|
||||||
|
const startkey = otherProps?.startkey
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
// need to include this incase pagination
|
||||||
|
startkey: startkey
|
||||||
|
? startkey
|
||||||
|
: `${DocumentType.USER}${SEPARATOR}${globalId}`,
|
||||||
|
endkey: `${DocumentType.USER}${SEPARATOR}${globalId}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving users, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
export function getUserMetadataParams(userId?: string | null, otherProps = {}) {
|
||||||
|
return getRowParams(InternalTable.USER_METADATA, userId, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getUsersByAppParams(appId: any, otherProps: any = {}) {
|
||||||
|
const prodAppId = getProdAppID(appId)
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
startkey: prodAppId,
|
||||||
|
endkey: `${prodAppId}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving templates. Owner ID must be specified, either global or a workspace level.
|
||||||
|
*/
|
||||||
|
export function getTemplateParams(
|
||||||
|
ownerId: any,
|
||||||
|
templateId: any,
|
||||||
|
otherProps = {}
|
||||||
|
) {
|
||||||
|
if (!templateId) {
|
||||||
|
templateId = ""
|
||||||
|
}
|
||||||
|
let final
|
||||||
|
if (templateId) {
|
||||||
|
final = templateId
|
||||||
|
} else {
|
||||||
|
final = `${DocumentType.TEMPLATE}${SEPARATOR}${ownerId}${SEPARATOR}`
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...otherProps,
|
||||||
|
startkey: final,
|
||||||
|
endkey: `${final}${UNICODE_MAX}`,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving a role, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
export function getRoleParams(roleId?: string | null, otherProps = {}) {
|
||||||
|
return getDocParams(DocumentType.ROLE, roleId, otherProps)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getStartEndKeyURL(baseKey: any, tenantId?: string) {
|
||||||
|
const tenancy = tenantId ? `${SEPARATOR}${tenantId}` : ""
|
||||||
|
return `startkey="${baseKey}${tenancy}"&endkey="${baseKey}${tenancy}${UNICODE_MAX}"`
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
|
||||||
|
*/
|
||||||
|
export const getPluginParams = (pluginId?: string | null, otherProps = {}) => {
|
||||||
|
return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
|
||||||
|
}
|
|
@ -97,3 +97,11 @@ export class InvalidAPIKeyError extends BudibaseError {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// USERS
|
||||||
|
|
||||||
|
export class EmailUnavailableError extends Error {
|
||||||
|
constructor(email: string) {
|
||||||
|
super(`Email already in use: '${email}'`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { testEnv } from "../../../../../tests"
|
import { testEnv } from "../../../../../tests/extra"
|
||||||
import PosthogProcessor from "../PosthogProcessor"
|
import PosthogProcessor from "../PosthogProcessor"
|
||||||
import { Event, IdentityType, Hosting } from "@budibase/types"
|
import { Event, IdentityType, Hosting } from "@budibase/types"
|
||||||
const tk = require("timekeeper")
|
const tk = require("timekeeper")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { structures, testEnv, mocks } from "../../../../../tests"
|
import { structures, mocks } from "../../../../../tests"
|
||||||
|
import { testEnv } from "../../../../../tests/extra"
|
||||||
import { SSOAuthDetails, User } from "@budibase/types"
|
import { SSOAuthDetails, User } from "@budibase/types"
|
||||||
|
|
||||||
import { HTTPError } from "../../../../errors"
|
import { HTTPError } from "../../../../errors"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
exports[`migrations should match snapshot 1`] = `
|
exports[`migrations should match snapshot 1`] = `
|
||||||
Object {
|
{
|
||||||
"_id": "migrations",
|
"_id": "migrations",
|
||||||
"_rev": "1-2f64479842a0513aa8b97f356b0b9127",
|
"_rev": "1-2f64479842a0513aa8b97f356b0b9127",
|
||||||
"createdAt": "2020-01-01T00:00:00.000Z",
|
"createdAt": "2020-01-01T00:00:00.000Z",
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { testEnv, DBTestConfiguration } from "../../../tests"
|
import { testEnv, DBTestConfiguration } from "../../../tests/extra"
|
||||||
import * as migrations from "../index"
|
import * as migrations from "../index"
|
||||||
import * as context from "../../context"
|
import * as context from "../../context"
|
||||||
import { MigrationType } from "@budibase/types"
|
import { MigrationType } from "@budibase/types"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import * as app from "../app"
|
import * as app from "../app"
|
||||||
import { getAppFileUrl } from "../app"
|
import { getAppFileUrl } from "../app"
|
||||||
import { testEnv } from "../../../../tests"
|
import { testEnv } from "../../../../tests/extra"
|
||||||
|
|
||||||
describe("app", () => {
|
describe("app", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import * as global from "../global"
|
import * as global from "../global"
|
||||||
import { testEnv } from "../../../../tests"
|
import { testEnv } from "../../../../tests/extra"
|
||||||
|
|
||||||
describe("global", () => {
|
describe("global", () => {
|
||||||
describe("getGlobalFileUrl", () => {
|
describe("getGlobalFileUrl", () => {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import * as plugins from "../plugins"
|
import * as plugins from "../plugins"
|
||||||
import { structures, testEnv } from "../../../../tests"
|
import { structures } from "../../../../tests"
|
||||||
|
import { testEnv } from "../../../../tests/extra"
|
||||||
|
|
||||||
describe("plugins", () => {
|
describe("plugins", () => {
|
||||||
describe("enrichPluginURLs", () => {
|
describe("enrichPluginURLs", () => {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { DBTestConfiguration, structures } from "../../../tests"
|
import { structures } from "../../../tests"
|
||||||
|
import { DBTestConfiguration } from "../../../tests/extra"
|
||||||
import * as tenants from "../tenants"
|
import * as tenants from "../tenants"
|
||||||
|
|
||||||
describe("tenants", () => {
|
describe("tenants", () => {
|
||||||
|
|
|
@ -12,7 +12,7 @@ export enum SecretOption {
|
||||||
ENCRYPTION = "encryption",
|
ENCRYPTION = "encryption",
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSecret(secretOption: SecretOption): string {
|
export function getSecret(secretOption: SecretOption): string {
|
||||||
let secret, secretName
|
let secret, secretName
|
||||||
switch (secretOption) {
|
switch (secretOption) {
|
||||||
case SecretOption.ENCRYPTION:
|
case SecretOption.ENCRYPTION:
|
||||||
|
|
31
packages/backend-core/src/security/tests/encryption.spec.ts
Normal file
31
packages/backend-core/src/security/tests/encryption.spec.ts
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { encrypt, decrypt, SecretOption, getSecret } from "../encryption"
|
||||||
|
import env from "../../environment"
|
||||||
|
|
||||||
|
describe("encryption", () => {
|
||||||
|
it("should throw an error if API encryption key is not set", () => {
|
||||||
|
const jwt = getSecret(SecretOption.API)
|
||||||
|
expect(jwt).toBe(env.JWT_SECRET)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should throw an error if encryption key is not set", () => {
|
||||||
|
expect(() => getSecret(SecretOption.ENCRYPTION)).toThrow(
|
||||||
|
'Secret "ENCRYPTION_KEY" has not been set in environment.'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should encrypt and decrypt a string using API encryption key", () => {
|
||||||
|
env._set("API_ENCRYPTION_KEY", "api_secret")
|
||||||
|
const plaintext = "budibase"
|
||||||
|
const apiEncrypted = encrypt(plaintext, SecretOption.API)
|
||||||
|
const decrypted = decrypt(apiEncrypted, SecretOption.API)
|
||||||
|
expect(decrypted).toEqual(plaintext)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should encrypt and decrypt a string using encryption key", () => {
|
||||||
|
env._set("ENCRYPTION_KEY", "normal_secret")
|
||||||
|
const plaintext = "budibase"
|
||||||
|
const encryptionEncrypted = encrypt(plaintext, SecretOption.ENCRYPTION)
|
||||||
|
const decrypted = decrypt(encryptionEncrypted, SecretOption.ENCRYPTION)
|
||||||
|
expect(decrypted).toEqual(plaintext)
|
||||||
|
})
|
||||||
|
})
|
|
@ -3,8 +3,8 @@ import {
|
||||||
getTenantId,
|
getTenantId,
|
||||||
getTenantIDFromAppID,
|
getTenantIDFromAppID,
|
||||||
isMultiTenant,
|
isMultiTenant,
|
||||||
|
getPlatformURL,
|
||||||
} from "../context"
|
} from "../context"
|
||||||
import env from "../environment"
|
|
||||||
import {
|
import {
|
||||||
BBContext,
|
BBContext,
|
||||||
TenantResolutionStrategy,
|
TenantResolutionStrategy,
|
||||||
|
@ -93,7 +93,7 @@ export const getTenantIDFromCtx = (
|
||||||
// subdomain
|
// subdomain
|
||||||
if (isAllowed(TenantResolutionStrategy.SUBDOMAIN)) {
|
if (isAllowed(TenantResolutionStrategy.SUBDOMAIN)) {
|
||||||
// e.g. budibase.app or local.com:10000
|
// e.g. budibase.app or local.com:10000
|
||||||
const platformHost = new URL(env.PLATFORM_URL).host.split(":")[0]
|
const platformHost = new URL(getPlatformURL()).host.split(":")[0]
|
||||||
// e.g. tenant.budibase.app or tenant.local.com
|
// e.g. tenant.budibase.app or tenant.local.com
|
||||||
const requestHost = ctx.host
|
const requestHost = ctx.host
|
||||||
// parse the tenant id from the difference
|
// parse the tenant id from the difference
|
||||||
|
|
184
packages/backend-core/src/tenancy/tests/tenancy.spec.ts
Normal file
184
packages/backend-core/src/tenancy/tests/tenancy.spec.ts
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
import { TenantResolutionStrategy } from "@budibase/types"
|
||||||
|
import { addTenantToUrl, isUserInAppTenant, getTenantIDFromCtx } from "../"
|
||||||
|
import { isMultiTenant, getTenantIDFromAppID } from "../../context"
|
||||||
|
|
||||||
|
jest.mock("../../context", () => ({
|
||||||
|
getTenantId: jest.fn(() => "budibase"),
|
||||||
|
isMultiTenant: jest.fn(() => true),
|
||||||
|
getTenantIDFromAppID: jest.fn(),
|
||||||
|
getPlatformURL: jest.fn(() => "https://app.com"),
|
||||||
|
DEFAULT_TENANT_ID: "default",
|
||||||
|
}))
|
||||||
|
|
||||||
|
const mockedIsMultiTenant = isMultiTenant as jest.MockedFunction<
|
||||||
|
typeof isMultiTenant
|
||||||
|
>
|
||||||
|
const mockedGetTenantIDFromAppID = getTenantIDFromAppID as jest.MockedFunction<
|
||||||
|
typeof getTenantIDFromAppID
|
||||||
|
>
|
||||||
|
|
||||||
|
describe("addTenantToUrl", () => {
|
||||||
|
it("should append tenantId parameter to the URL", () => {
|
||||||
|
const url = "https://budibase.com"
|
||||||
|
const expectedUrl = "https://budibase.com?tenantId=budibase"
|
||||||
|
expect(addTenantToUrl(url)).toEqual(expectedUrl)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should append tenantId parameter to the URL query string", () => {
|
||||||
|
const url = "https://budibase.com?var=test"
|
||||||
|
const expectedUrl = "https://budibase.com?var=test&tenantId=budibase"
|
||||||
|
expect(addTenantToUrl(url)).toEqual(expectedUrl)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should not append tenantId parameter to the URL if isMultiTenant is false", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => false)
|
||||||
|
|
||||||
|
const url = "https://budibase.com"
|
||||||
|
const expectedUrl = "https://budibase.com"
|
||||||
|
expect(addTenantToUrl(url)).toEqual(expectedUrl)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("isUserInAppTenant", () => {
|
||||||
|
mockedGetTenantIDFromAppID.mockImplementation(() => "budibase")
|
||||||
|
const mockUser = { tenantId: "budibase" }
|
||||||
|
|
||||||
|
it("returns true if user tenant ID matches app tenant ID", () => {
|
||||||
|
const appId = "app-budibase"
|
||||||
|
const result = isUserInAppTenant(appId, mockUser)
|
||||||
|
expect(result).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("uses default tenant ID if user is not provided", () => {
|
||||||
|
const appId = "app-budibase"
|
||||||
|
const result = isUserInAppTenant(appId)
|
||||||
|
expect(result).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("uses default tenant ID if app tenant ID is not found", () => {
|
||||||
|
const appId = "not-budibase-app"
|
||||||
|
const result = isUserInAppTenant(appId, mockUser)
|
||||||
|
expect(result).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns false if user tenant ID does not match app tenant ID", () => {
|
||||||
|
const appId = "app-budibase"
|
||||||
|
mockedGetTenantIDFromAppID.mockImplementation(() => "not-budibase")
|
||||||
|
const result = isUserInAppTenant(appId, mockUser)
|
||||||
|
expect(result).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let mockOpts: any = {}
|
||||||
|
function createCtx(opts: {
|
||||||
|
originalUrl?: string
|
||||||
|
headers?: Record<string, string>
|
||||||
|
qsTenantId?: string
|
||||||
|
userTenantId?: string
|
||||||
|
host?: string
|
||||||
|
path?: string
|
||||||
|
}) {
|
||||||
|
const createdCtx: any = {
|
||||||
|
originalUrl: opts.originalUrl || "budibase.com",
|
||||||
|
matched: [{ name: "name" }],
|
||||||
|
throw: jest.fn(),
|
||||||
|
request: { headers: {} },
|
||||||
|
}
|
||||||
|
if (opts.headers) {
|
||||||
|
createdCtx.request.headers = opts.headers
|
||||||
|
}
|
||||||
|
if (opts.qsTenantId) {
|
||||||
|
createdCtx.request.query = { tenantId: opts.qsTenantId }
|
||||||
|
}
|
||||||
|
if (opts.userTenantId) {
|
||||||
|
createdCtx.user = { tenantId: opts.userTenantId }
|
||||||
|
}
|
||||||
|
if (opts.host) {
|
||||||
|
createdCtx.host = opts.host
|
||||||
|
}
|
||||||
|
if (opts.path) {
|
||||||
|
createdCtx.matched = [
|
||||||
|
{
|
||||||
|
paramNames: [{ name: "tenantId" }],
|
||||||
|
params: () => ({ tenantId: opts.path }),
|
||||||
|
captures: jest.fn(),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
return createdCtx as any
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("getTenantIDFromCtx", () => {
|
||||||
|
describe("when tenant can be found", () => {
|
||||||
|
it("returns the tenant ID from the user object", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => true)
|
||||||
|
const ctx = createCtx({ userTenantId: "budibase" })
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("budibase")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the tenant ID from the header", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => true)
|
||||||
|
const ctx = createCtx({ headers: { "x-budibase-tenant-id": "budibase" } })
|
||||||
|
mockOpts = { includeStrategies: [TenantResolutionStrategy.HEADER] }
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("budibase")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the tenant ID from the query param", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => true)
|
||||||
|
mockOpts = { includeStrategies: [TenantResolutionStrategy.QUERY] }
|
||||||
|
const ctx = createCtx({ qsTenantId: "budibase" })
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("budibase")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the tenant ID from the subdomain", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => true)
|
||||||
|
const ctx = createCtx({ host: "bb.app.com" })
|
||||||
|
mockOpts = { includeStrategies: [TenantResolutionStrategy.SUBDOMAIN] }
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("bb")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the tenant ID from the path", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => true)
|
||||||
|
const ctx = createCtx({ path: "bb" })
|
||||||
|
mockOpts = { includeStrategies: [TenantResolutionStrategy.PATH] }
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("bb")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("when tenant cannot be found", () => {
|
||||||
|
it("throws a 403 error if allowNoTenant is false", () => {
|
||||||
|
const ctx = createCtx({})
|
||||||
|
mockOpts = {
|
||||||
|
allowNoTenant: false,
|
||||||
|
excludeStrategies: [
|
||||||
|
TenantResolutionStrategy.QUERY,
|
||||||
|
TenantResolutionStrategy.SUBDOMAIN,
|
||||||
|
TenantResolutionStrategy.PATH,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toBeNull()
|
||||||
|
expect(ctx.throw).toBeCalledTimes(1)
|
||||||
|
expect(ctx.throw).toBeCalledWith(403, "Tenant id not set")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns null if allowNoTenant is true", () => {
|
||||||
|
const ctx = createCtx({})
|
||||||
|
mockOpts = {
|
||||||
|
allowNoTenant: true,
|
||||||
|
excludeStrategies: [
|
||||||
|
TenantResolutionStrategy.QUERY,
|
||||||
|
TenantResolutionStrategy.SUBDOMAIN,
|
||||||
|
TenantResolutionStrategy.PATH,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toBeNull()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("returns the default tenant ID when isMultiTenant() returns false", () => {
|
||||||
|
mockedIsMultiTenant.mockImplementation(() => false)
|
||||||
|
const ctx = createCtx({})
|
||||||
|
expect(getTenantIDFromCtx(ctx, mockOpts)).toEqual("default")
|
||||||
|
})
|
||||||
|
})
|
|
@ -1,5 +1,5 @@
|
||||||
import env from "../environment"
|
import env from "../environment"
|
||||||
export * from "../newid"
|
export * from "../docIds/newid"
|
||||||
const bcrypt = env.JS_BCRYPT ? require("bcryptjs") : require("bcrypt")
|
const bcrypt = env.JS_BCRYPT ? require("bcryptjs") : require("bcrypt")
|
||||||
|
|
||||||
const SALT_ROUNDS = env.SALT_ROUNDS || 10
|
const SALT_ROUNDS = env.SALT_ROUNDS || 10
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { structures, DBTestConfiguration } from "../../../tests"
|
import { structures } from "../../../tests"
|
||||||
|
import { DBTestConfiguration } from "../../../tests/extra"
|
||||||
import * as utils from "../../utils"
|
import * as utils from "../../utils"
|
||||||
import * as db from "../../db"
|
import * as db from "../../db"
|
||||||
import { Header } from "../../constants"
|
import { Header } from "../../constants"
|
||||||
|
|
34
packages/backend-core/tests/core/logging.ts
Normal file
34
packages/backend-core/tests/core/logging.ts
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
export enum LogLevel {
|
||||||
|
TRACE = "trace",
|
||||||
|
DEBUG = "debug",
|
||||||
|
INFO = "info",
|
||||||
|
WARN = "warn",
|
||||||
|
ERROR = "error",
|
||||||
|
}
|
||||||
|
|
||||||
|
const LOG_INDEX: { [key in LogLevel]: number } = {
|
||||||
|
[LogLevel.TRACE]: 1,
|
||||||
|
[LogLevel.DEBUG]: 2,
|
||||||
|
[LogLevel.INFO]: 3,
|
||||||
|
[LogLevel.WARN]: 4,
|
||||||
|
[LogLevel.ERROR]: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
const setIndex = LOG_INDEX[process.env.LOG_LEVEL as LogLevel]
|
||||||
|
|
||||||
|
if (setIndex > LOG_INDEX.trace) {
|
||||||
|
global.console.trace = jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setIndex > LOG_INDEX.debug) {
|
||||||
|
global.console.debug = jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setIndex > LOG_INDEX.info) {
|
||||||
|
global.console.info = jest.fn()
|
||||||
|
global.console.log = jest.fn()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (setIndex > LOG_INDEX.warn) {
|
||||||
|
global.console.warn = jest.fn()
|
||||||
|
}
|
|
@ -1,9 +1,6 @@
|
||||||
export * as mocks from "./mocks"
|
export * as mocks from "./mocks"
|
||||||
export * as structures from "./structures"
|
export * as structures from "./structures"
|
||||||
export { generator } from "./structures"
|
export { generator } from "./structures"
|
||||||
export * as testEnv from "./testEnv"
|
|
||||||
export * as testContainerUtils from "./testContainerUtils"
|
export * as testContainerUtils from "./testContainerUtils"
|
||||||
|
|
||||||
export * from "./jestUtils"
|
export * from "./jestUtils"
|
||||||
|
|
||||||
export { default as DBTestConfiguration } from "./DBTestConfiguration"
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
jest.mock("../../../../src/logging/alerts")
|
||||||
|
import * as _alerts from "../../../../src/logging/alerts"
|
||||||
|
export const alerts = jest.mocked(_alerts)
|
123
packages/backend-core/tests/core/utilities/mocks/events.ts
Normal file
123
packages/backend-core/tests/core/utilities/mocks/events.ts
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
beforeAll(async () => {
|
||||||
|
const processors = await import("../../../../src/events/processors")
|
||||||
|
const events = await import("../../../../src/events")
|
||||||
|
jest.spyOn(processors.analyticsProcessor, "processEvent")
|
||||||
|
|
||||||
|
jest.spyOn(events.identification, "identifyTenantGroup")
|
||||||
|
jest.spyOn(events.identification, "identifyUser")
|
||||||
|
|
||||||
|
jest.spyOn(events.backfill, "appSucceeded")
|
||||||
|
jest.spyOn(events.backfill, "tenantSucceeded")
|
||||||
|
|
||||||
|
jest.spyOn(events.account, "created")
|
||||||
|
jest.spyOn(events.account, "deleted")
|
||||||
|
jest.spyOn(events.account, "verified")
|
||||||
|
|
||||||
|
jest.spyOn(events.app, "created")
|
||||||
|
jest.spyOn(events.app, "updated")
|
||||||
|
jest.spyOn(events.app, "deleted")
|
||||||
|
jest.spyOn(events.app, "published")
|
||||||
|
jest.spyOn(events.app, "unpublished")
|
||||||
|
jest.spyOn(events.app, "templateImported")
|
||||||
|
jest.spyOn(events.app, "fileImported")
|
||||||
|
jest.spyOn(events.app, "versionUpdated")
|
||||||
|
jest.spyOn(events.app, "versionReverted")
|
||||||
|
jest.spyOn(events.app, "reverted")
|
||||||
|
jest.spyOn(events.app, "exported")
|
||||||
|
|
||||||
|
jest.spyOn(events.auth, "login")
|
||||||
|
jest.spyOn(events.auth, "logout")
|
||||||
|
jest.spyOn(events.auth, "SSOCreated")
|
||||||
|
jest.spyOn(events.auth, "SSOUpdated")
|
||||||
|
jest.spyOn(events.auth, "SSOActivated")
|
||||||
|
jest.spyOn(events.auth, "SSODeactivated")
|
||||||
|
|
||||||
|
jest.spyOn(events.automation, "created")
|
||||||
|
jest.spyOn(events.automation, "deleted")
|
||||||
|
jest.spyOn(events.automation, "tested")
|
||||||
|
jest.spyOn(events.automation, "stepCreated")
|
||||||
|
jest.spyOn(events.automation, "stepDeleted")
|
||||||
|
jest.spyOn(events.automation, "triggerUpdated")
|
||||||
|
|
||||||
|
jest.spyOn(events.datasource, "created")
|
||||||
|
jest.spyOn(events.datasource, "updated")
|
||||||
|
jest.spyOn(events.datasource, "deleted")
|
||||||
|
|
||||||
|
jest.spyOn(events.email, "SMTPCreated")
|
||||||
|
jest.spyOn(events.email, "SMTPUpdated")
|
||||||
|
|
||||||
|
jest.spyOn(events.layout, "created")
|
||||||
|
jest.spyOn(events.layout, "deleted")
|
||||||
|
|
||||||
|
jest.spyOn(events.org, "nameUpdated")
|
||||||
|
jest.spyOn(events.org, "logoUpdated")
|
||||||
|
jest.spyOn(events.org, "platformURLUpdated")
|
||||||
|
jest.spyOn(events.org, "analyticsOptOut")
|
||||||
|
|
||||||
|
jest.spyOn(events.installation, "versionChecked")
|
||||||
|
|
||||||
|
jest.spyOn(events.query, "created")
|
||||||
|
jest.spyOn(events.query, "updated")
|
||||||
|
jest.spyOn(events.query, "deleted")
|
||||||
|
jest.spyOn(events.query, "imported")
|
||||||
|
jest.spyOn(events.query, "previewed")
|
||||||
|
|
||||||
|
jest.spyOn(events.role, "created")
|
||||||
|
jest.spyOn(events.role, "updated")
|
||||||
|
jest.spyOn(events.role, "deleted")
|
||||||
|
jest.spyOn(events.role, "assigned")
|
||||||
|
jest.spyOn(events.role, "unassigned")
|
||||||
|
|
||||||
|
jest.spyOn(events.rows, "imported")
|
||||||
|
jest.spyOn(events.rows, "created")
|
||||||
|
|
||||||
|
jest.spyOn(events.screen, "created")
|
||||||
|
jest.spyOn(events.screen, "deleted")
|
||||||
|
|
||||||
|
jest.spyOn(events.user, "created")
|
||||||
|
jest.spyOn(events.user, "updated")
|
||||||
|
jest.spyOn(events.user, "deleted")
|
||||||
|
jest.spyOn(events.user, "permissionAdminAssigned")
|
||||||
|
jest.spyOn(events.user, "permissionAdminRemoved")
|
||||||
|
jest.spyOn(events.user, "permissionBuilderAssigned")
|
||||||
|
jest.spyOn(events.user, "permissionBuilderRemoved")
|
||||||
|
jest.spyOn(events.user, "invited")
|
||||||
|
jest.spyOn(events.user, "inviteAccepted")
|
||||||
|
jest.spyOn(events.user, "passwordForceReset")
|
||||||
|
jest.spyOn(events.user, "passwordUpdated")
|
||||||
|
jest.spyOn(events.user, "passwordResetRequested")
|
||||||
|
jest.spyOn(events.user, "passwordReset")
|
||||||
|
|
||||||
|
jest.spyOn(events.group, "created")
|
||||||
|
jest.spyOn(events.group, "updated")
|
||||||
|
jest.spyOn(events.group, "deleted")
|
||||||
|
jest.spyOn(events.group, "usersAdded")
|
||||||
|
jest.spyOn(events.group, "usersDeleted")
|
||||||
|
jest.spyOn(events.group, "createdOnboarding")
|
||||||
|
jest.spyOn(events.group, "permissionsEdited")
|
||||||
|
|
||||||
|
jest.spyOn(events.serve, "servedBuilder")
|
||||||
|
jest.spyOn(events.serve, "servedApp")
|
||||||
|
jest.spyOn(events.serve, "servedAppPreview")
|
||||||
|
|
||||||
|
jest.spyOn(events.table, "created")
|
||||||
|
jest.spyOn(events.table, "updated")
|
||||||
|
jest.spyOn(events.table, "deleted")
|
||||||
|
jest.spyOn(events.table, "exported")
|
||||||
|
jest.spyOn(events.table, "imported")
|
||||||
|
|
||||||
|
jest.spyOn(events.view, "created")
|
||||||
|
jest.spyOn(events.view, "updated")
|
||||||
|
jest.spyOn(events.view, "deleted")
|
||||||
|
jest.spyOn(events.view, "exported")
|
||||||
|
jest.spyOn(events.view, "filterCreated")
|
||||||
|
jest.spyOn(events.view, "filterUpdated")
|
||||||
|
jest.spyOn(events.view, "filterDeleted")
|
||||||
|
jest.spyOn(events.view, "calculationCreated")
|
||||||
|
jest.spyOn(events.view, "calculationUpdated")
|
||||||
|
jest.spyOn(events.view, "calculationDeleted")
|
||||||
|
|
||||||
|
jest.spyOn(events.plugin, "init")
|
||||||
|
jest.spyOn(events.plugin, "imported")
|
||||||
|
jest.spyOn(events.plugin, "deleted")
|
||||||
|
})
|
17
packages/backend-core/tests/core/utilities/mocks/fetch.ts
Normal file
17
packages/backend-core/tests/core/utilities/mocks/fetch.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
const mockFetch = jest.fn((url: any, opts: any) => {
|
||||||
|
const fetch = jest.requireActual("node-fetch")
|
||||||
|
const env = jest.requireActual("../../../../src/environment").default
|
||||||
|
if (url.includes(env.COUCH_DB_URL)) {
|
||||||
|
return fetch(url, opts)
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
const enable = () => {
|
||||||
|
jest.mock("node-fetch", () => mockFetch)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
...mockFetch,
|
||||||
|
enable,
|
||||||
|
}
|
|
@ -1,10 +1,10 @@
|
||||||
jest.mock("../../../src/accounts")
|
jest.mock("../../../../src/accounts")
|
||||||
import * as _accounts from "../../../src/accounts"
|
import * as _accounts from "../../../../src/accounts"
|
||||||
export const accounts = jest.mocked(_accounts)
|
export const accounts = jest.mocked(_accounts)
|
||||||
|
|
||||||
export * as date from "./date"
|
export * as date from "./date"
|
||||||
export * as licenses from "./licenses"
|
export * as licenses from "./licenses"
|
||||||
export { default as fetch } from "./fetch"
|
export { default as fetch } from "./fetch"
|
||||||
export * from "./alerts"
|
export * from "./alerts"
|
||||||
import "./posthog"
|
|
||||||
import "./events"
|
import "./events"
|
||||||
|
import "./posthog"
|
|
@ -1,5 +1,5 @@
|
||||||
import { generator, uuid } from "."
|
import { generator, uuid } from "."
|
||||||
import * as db from "../../../src/db/utils"
|
import { generateGlobalUserID } from "../../../../src/docIds"
|
||||||
import {
|
import {
|
||||||
Account,
|
Account,
|
||||||
AccountSSOProvider,
|
AccountSSOProvider,
|
||||||
|
@ -39,7 +39,7 @@ export const cloudAccount = (): CloudAccount => {
|
||||||
return {
|
return {
|
||||||
...account(),
|
...account(),
|
||||||
hosting: Hosting.CLOUD,
|
hosting: Hosting.CLOUD,
|
||||||
budibaseUserId: db.generateGlobalUserID(),
|
budibaseUserId: generateGlobalUserID(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { generator } from "."
|
import { generator } from "."
|
||||||
import { App } from "@budibase/types"
|
import { App } from "@budibase/types"
|
||||||
import { DEFAULT_TENANT_ID, DocumentType } from "../../../src/constants"
|
import { DEFAULT_TENANT_ID, DocumentType } from "../../../../src/constants"
|
||||||
|
|
||||||
export function app(id: string): App {
|
export function app(id: string): App {
|
||||||
return {
|
return {
|
|
@ -1,5 +1,5 @@
|
||||||
import { structures } from ".."
|
import { structures } from ".."
|
||||||
import { newid } from "../../../src/newid"
|
import { newid } from "../../../../src/docIds/newid"
|
||||||
|
|
||||||
export function id() {
|
export function id() {
|
||||||
return `db_${newid()}`
|
return `db_${newid()}`
|
|
@ -1,23 +1,31 @@
|
||||||
import { ScimCreateGroupRequest, ScimCreateUserRequest } from "@budibase/types"
|
import { ScimCreateGroupRequest, ScimCreateUserRequest } from "@budibase/types"
|
||||||
import { uuid } from "./common"
|
import { uuid } from "./common"
|
||||||
import { generator } from "./generator"
|
import { generator } from "./generator"
|
||||||
|
import _ from "lodash"
|
||||||
|
|
||||||
export function createUserRequest(userData?: {
|
interface CreateUserRequestFields {
|
||||||
externalId?: string
|
externalId: string
|
||||||
email?: string
|
email: string
|
||||||
firstName?: string
|
firstName: string
|
||||||
lastName?: string
|
lastName: string
|
||||||
username?: string
|
username: string
|
||||||
}) {
|
}
|
||||||
const {
|
|
||||||
externalId = uuid(),
|
|
||||||
email = generator.email(),
|
|
||||||
firstName = generator.first(),
|
|
||||||
lastName = generator.last(),
|
|
||||||
username = generator.name(),
|
|
||||||
} = userData || {}
|
|
||||||
|
|
||||||
const user: ScimCreateUserRequest = {
|
export function createUserRequest(userData?: Partial<CreateUserRequestFields>) {
|
||||||
|
const defaultValues = {
|
||||||
|
externalId: uuid(),
|
||||||
|
email: generator.email(),
|
||||||
|
firstName: generator.first(),
|
||||||
|
lastName: generator.last(),
|
||||||
|
username: generator.name(),
|
||||||
|
}
|
||||||
|
|
||||||
|
const { externalId, email, firstName, lastName, username } = _.assign(
|
||||||
|
defaultValues,
|
||||||
|
userData
|
||||||
|
)
|
||||||
|
|
||||||
|
let user: ScimCreateUserRequest = {
|
||||||
schemas: [
|
schemas: [
|
||||||
"urn:ietf:params:scim:schemas:core:2.0:User",
|
"urn:ietf:params:scim:schemas:core:2.0:User",
|
||||||
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
|
"urn:ietf:params:scim:schemas:extension:enterprise:2.0:User",
|
||||||
|
@ -35,13 +43,17 @@ export function createUserRequest(userData?: {
|
||||||
meta: {
|
meta: {
|
||||||
resourceType: "User",
|
resourceType: "User",
|
||||||
},
|
},
|
||||||
name: {
|
|
||||||
formatted: generator.name(),
|
|
||||||
familyName: lastName,
|
|
||||||
givenName: firstName,
|
|
||||||
},
|
|
||||||
roles: [],
|
roles: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (firstName || lastName) {
|
||||||
|
user.name = {
|
||||||
|
formatted: [firstName, lastName].filter(s => s).join(" "),
|
||||||
|
familyName: lastName,
|
||||||
|
givenName: firstName,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { newid } from "../../../src/newid"
|
import { newid } from "../../../../src/docIds/newid"
|
||||||
|
|
||||||
export function id() {
|
export function id() {
|
||||||
return `tenant-${newid()}`
|
return `tenant-${newid()}`
|
|
@ -1,5 +1,5 @@
|
||||||
import "./mocks"
|
import "../core/utilities/mocks"
|
||||||
import * as structures from "./structures"
|
import * as structures from "../core/utilities/structures"
|
||||||
import * as testEnv from "./testEnv"
|
import * as testEnv from "./testEnv"
|
||||||
import * as context from "../../src/context"
|
import * as context from "../../src/context"
|
||||||
|
|
2
packages/backend-core/tests/extra/index.ts
Normal file
2
packages/backend-core/tests/extra/index.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
export * as testEnv from "./testEnv"
|
||||||
|
export { default as DBTestConfiguration } from "./DBTestConfiguration"
|
|
@ -1,6 +1,6 @@
|
||||||
import env from "../../src/environment"
|
import env from "../../src/environment"
|
||||||
import * as context from "../../src/context"
|
import * as context from "../../src/context"
|
||||||
import * as structures from "./structures"
|
import * as structures from "../core/utilities/structures"
|
||||||
|
|
||||||
// TENANCY
|
// TENANCY
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
export * from "./utilities"
|
export * from "./core/utilities"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
import "./core/logging"
|
||||||
import env from "../src/environment"
|
import env from "../src/environment"
|
||||||
import { cleanup } from "../src/timers"
|
import { cleanup } from "../src/timers"
|
||||||
import { mocks, testContainerUtils } from "./utilities"
|
import { mocks, testContainerUtils } from "./core/utilities"
|
||||||
|
|
||||||
// must explicitly enable fetch mock
|
// must explicitly enable fetch mock
|
||||||
mocks.fetch.enable()
|
mocks.fetch.enable()
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
jest.mock("../../../src/logging/alerts")
|
|
||||||
import * as _alerts from "../../../src/logging/alerts"
|
|
||||||
export const alerts = jest.mocked(_alerts)
|
|
|
@ -1,122 +0,0 @@
|
||||||
import * as processors from "../../../src/events/processors"
|
|
||||||
import * as events from "../../../src/events"
|
|
||||||
|
|
||||||
jest.spyOn(processors.analyticsProcessor, "processEvent")
|
|
||||||
|
|
||||||
jest.spyOn(events.identification, "identifyTenantGroup")
|
|
||||||
jest.spyOn(events.identification, "identifyUser")
|
|
||||||
|
|
||||||
jest.spyOn(events.backfill, "appSucceeded")
|
|
||||||
jest.spyOn(events.backfill, "tenantSucceeded")
|
|
||||||
|
|
||||||
jest.spyOn(events.account, "created")
|
|
||||||
jest.spyOn(events.account, "deleted")
|
|
||||||
jest.spyOn(events.account, "verified")
|
|
||||||
|
|
||||||
jest.spyOn(events.app, "created")
|
|
||||||
jest.spyOn(events.app, "updated")
|
|
||||||
jest.spyOn(events.app, "deleted")
|
|
||||||
jest.spyOn(events.app, "published")
|
|
||||||
jest.spyOn(events.app, "unpublished")
|
|
||||||
jest.spyOn(events.app, "templateImported")
|
|
||||||
jest.spyOn(events.app, "fileImported")
|
|
||||||
jest.spyOn(events.app, "versionUpdated")
|
|
||||||
jest.spyOn(events.app, "versionReverted")
|
|
||||||
jest.spyOn(events.app, "reverted")
|
|
||||||
jest.spyOn(events.app, "exported")
|
|
||||||
|
|
||||||
jest.spyOn(events.auth, "login")
|
|
||||||
jest.spyOn(events.auth, "logout")
|
|
||||||
jest.spyOn(events.auth, "SSOCreated")
|
|
||||||
jest.spyOn(events.auth, "SSOUpdated")
|
|
||||||
jest.spyOn(events.auth, "SSOActivated")
|
|
||||||
jest.spyOn(events.auth, "SSODeactivated")
|
|
||||||
|
|
||||||
jest.spyOn(events.automation, "created")
|
|
||||||
jest.spyOn(events.automation, "deleted")
|
|
||||||
jest.spyOn(events.automation, "tested")
|
|
||||||
jest.spyOn(events.automation, "stepCreated")
|
|
||||||
jest.spyOn(events.automation, "stepDeleted")
|
|
||||||
jest.spyOn(events.automation, "triggerUpdated")
|
|
||||||
|
|
||||||
jest.spyOn(events.datasource, "created")
|
|
||||||
jest.spyOn(events.datasource, "updated")
|
|
||||||
jest.spyOn(events.datasource, "deleted")
|
|
||||||
|
|
||||||
jest.spyOn(events.email, "SMTPCreated")
|
|
||||||
jest.spyOn(events.email, "SMTPUpdated")
|
|
||||||
|
|
||||||
jest.spyOn(events.layout, "created")
|
|
||||||
jest.spyOn(events.layout, "deleted")
|
|
||||||
|
|
||||||
jest.spyOn(events.org, "nameUpdated")
|
|
||||||
jest.spyOn(events.org, "logoUpdated")
|
|
||||||
jest.spyOn(events.org, "platformURLUpdated")
|
|
||||||
jest.spyOn(events.org, "analyticsOptOut")
|
|
||||||
|
|
||||||
jest.spyOn(events.installation, "versionChecked")
|
|
||||||
|
|
||||||
jest.spyOn(events.query, "created")
|
|
||||||
jest.spyOn(events.query, "updated")
|
|
||||||
jest.spyOn(events.query, "deleted")
|
|
||||||
jest.spyOn(events.query, "imported")
|
|
||||||
jest.spyOn(events.query, "previewed")
|
|
||||||
|
|
||||||
jest.spyOn(events.role, "created")
|
|
||||||
jest.spyOn(events.role, "updated")
|
|
||||||
jest.spyOn(events.role, "deleted")
|
|
||||||
jest.spyOn(events.role, "assigned")
|
|
||||||
jest.spyOn(events.role, "unassigned")
|
|
||||||
|
|
||||||
jest.spyOn(events.rows, "imported")
|
|
||||||
jest.spyOn(events.rows, "created")
|
|
||||||
|
|
||||||
jest.spyOn(events.screen, "created")
|
|
||||||
jest.spyOn(events.screen, "deleted")
|
|
||||||
|
|
||||||
jest.spyOn(events.user, "created")
|
|
||||||
jest.spyOn(events.user, "updated")
|
|
||||||
jest.spyOn(events.user, "deleted")
|
|
||||||
jest.spyOn(events.user, "permissionAdminAssigned")
|
|
||||||
jest.spyOn(events.user, "permissionAdminRemoved")
|
|
||||||
jest.spyOn(events.user, "permissionBuilderAssigned")
|
|
||||||
jest.spyOn(events.user, "permissionBuilderRemoved")
|
|
||||||
jest.spyOn(events.user, "invited")
|
|
||||||
jest.spyOn(events.user, "inviteAccepted")
|
|
||||||
jest.spyOn(events.user, "passwordForceReset")
|
|
||||||
jest.spyOn(events.user, "passwordUpdated")
|
|
||||||
jest.spyOn(events.user, "passwordResetRequested")
|
|
||||||
jest.spyOn(events.user, "passwordReset")
|
|
||||||
|
|
||||||
jest.spyOn(events.group, "created")
|
|
||||||
jest.spyOn(events.group, "updated")
|
|
||||||
jest.spyOn(events.group, "deleted")
|
|
||||||
jest.spyOn(events.group, "usersAdded")
|
|
||||||
jest.spyOn(events.group, "usersDeleted")
|
|
||||||
jest.spyOn(events.group, "createdOnboarding")
|
|
||||||
jest.spyOn(events.group, "permissionsEdited")
|
|
||||||
|
|
||||||
jest.spyOn(events.serve, "servedBuilder")
|
|
||||||
jest.spyOn(events.serve, "servedApp")
|
|
||||||
jest.spyOn(events.serve, "servedAppPreview")
|
|
||||||
|
|
||||||
jest.spyOn(events.table, "created")
|
|
||||||
jest.spyOn(events.table, "updated")
|
|
||||||
jest.spyOn(events.table, "deleted")
|
|
||||||
jest.spyOn(events.table, "exported")
|
|
||||||
jest.spyOn(events.table, "imported")
|
|
||||||
|
|
||||||
jest.spyOn(events.view, "created")
|
|
||||||
jest.spyOn(events.view, "updated")
|
|
||||||
jest.spyOn(events.view, "deleted")
|
|
||||||
jest.spyOn(events.view, "exported")
|
|
||||||
jest.spyOn(events.view, "filterCreated")
|
|
||||||
jest.spyOn(events.view, "filterUpdated")
|
|
||||||
jest.spyOn(events.view, "filterDeleted")
|
|
||||||
jest.spyOn(events.view, "calculationCreated")
|
|
||||||
jest.spyOn(events.view, "calculationUpdated")
|
|
||||||
jest.spyOn(events.view, "calculationDeleted")
|
|
||||||
|
|
||||||
jest.spyOn(events.plugin, "init")
|
|
||||||
jest.spyOn(events.plugin, "imported")
|
|
||||||
jest.spyOn(events.plugin, "deleted")
|
|
|
@ -1,10 +0,0 @@
|
||||||
const mockFetch = jest.fn()
|
|
||||||
|
|
||||||
const enable = () => {
|
|
||||||
jest.mock("node-fetch", () => mockFetch)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
...mockFetch,
|
|
||||||
enable,
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/bbui",
|
"name": "@budibase/bbui",
|
||||||
"description": "A UI solution used in the different Budibase projects.",
|
"description": "A UI solution used in the different Budibase projects.",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"module": "dist/bbui.es.js",
|
"module": "dist/bbui.es.js",
|
||||||
|
@ -38,8 +38,8 @@
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "1.2.1",
|
"@adobe/spectrum-css-workflow-icons": "1.2.1",
|
||||||
"@budibase/shared-core": "2.4.44-alpha.11",
|
"@budibase/shared-core": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@spectrum-css/accordion": "3.0.24",
|
"@spectrum-css/accordion": "3.0.24",
|
||||||
"@spectrum-css/actionbutton": "1.0.1",
|
"@spectrum-css/actionbutton": "1.0.1",
|
||||||
"@spectrum-css/actiongroup": "1.0.1",
|
"@spectrum-css/actiongroup": "1.0.1",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -58,11 +58,11 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.4.44-alpha.11",
|
"@budibase/bbui": "2.4.44-alpha.16",
|
||||||
"@budibase/client": "2.4.44-alpha.11",
|
"@budibase/client": "2.4.44-alpha.16",
|
||||||
"@budibase/frontend-core": "2.4.44-alpha.11",
|
"@budibase/frontend-core": "2.4.44-alpha.16",
|
||||||
"@budibase/shared-core": "2.4.44-alpha.11",
|
"@budibase/shared-core": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
"@fortawesome/fontawesome-svg-core": "^6.2.1",
|
||||||
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
"@fortawesome/free-brands-svg-icons": "^6.2.1",
|
||||||
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
"@fortawesome/free-solid-svg-icons": "^6.2.1",
|
||||||
|
|
|
@ -65,6 +65,14 @@
|
||||||
label: "Must not contain",
|
label: "Must not contain",
|
||||||
value: "notContains",
|
value: "notContains",
|
||||||
},
|
},
|
||||||
|
MaxFileSize: {
|
||||||
|
label: "Max file size (MB)",
|
||||||
|
value: "maxFileSize",
|
||||||
|
},
|
||||||
|
MaxUploadSize: {
|
||||||
|
label: "Max total upload size (MB)",
|
||||||
|
value: "maxUploadSize",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
const ConstraintMap = {
|
const ConstraintMap = {
|
||||||
["string"]: [
|
["string"]: [
|
||||||
|
@ -94,7 +102,11 @@
|
||||||
Constraints.Equal,
|
Constraints.Equal,
|
||||||
Constraints.NotEqual,
|
Constraints.NotEqual,
|
||||||
],
|
],
|
||||||
["attachment"]: [Constraints.Required],
|
["attachment"]: [
|
||||||
|
Constraints.Required,
|
||||||
|
Constraints.MaxFileSize,
|
||||||
|
Constraints.MaxUploadSize,
|
||||||
|
],
|
||||||
["link"]: [
|
["link"]: [
|
||||||
Constraints.Required,
|
Constraints.Required,
|
||||||
Constraints.Contains,
|
Constraints.Contains,
|
||||||
|
@ -283,7 +295,7 @@
|
||||||
disabled={rule.constraint === "required"}
|
disabled={rule.constraint === "required"}
|
||||||
on:change={e => (rule.value = e.detail)}
|
on:change={e => (rule.value = e.detail)}
|
||||||
/>
|
/>
|
||||||
{:else if rule.type !== "array" && ["maxLength", "minLength", "regex", "notRegex", "contains", "notContains"].includes(rule.constraint)}
|
{:else if rule.type !== "array" && ["maxUploadSize", "maxFileSize", "maxLength", "minLength", "regex", "notRegex", "contains", "notContains"].includes(rule.constraint)}
|
||||||
<!-- Certain constraints always need string values-->
|
<!-- Certain constraints always need string values-->
|
||||||
<Input
|
<Input
|
||||||
bind:value={rule.value}
|
bind:value={rule.value}
|
||||||
|
@ -376,7 +388,7 @@
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
display: grid;
|
display: grid;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
grid-template-columns: 190px 120px 1fr 1fr auto auto;
|
grid-template-columns: 200px 120px 1fr 1fr auto auto;
|
||||||
border-radius: var(--border-radius-s);
|
border-radius: var(--border-radius-s);
|
||||||
transition: background-color ease-in-out 130ms;
|
transition: background-color ease-in-out 130ms;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -29,9 +29,9 @@
|
||||||
"outputPath": "build"
|
"outputPath": "build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "2.4.44-alpha.11",
|
"@budibase/backend-core": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@budibase/types": "2.4.44-alpha.11",
|
"@budibase/types": "2.4.44-alpha.16",
|
||||||
"axios": "0.21.2",
|
"axios": "0.21.2",
|
||||||
"chalk": "4.1.0",
|
"chalk": "4.1.0",
|
||||||
"cli-progress": "3.11.2",
|
"cli-progress": "3.11.2",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
|
@ -19,11 +19,11 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.4.44-alpha.11",
|
"@budibase/bbui": "2.4.44-alpha.16",
|
||||||
"@budibase/frontend-core": "2.4.44-alpha.11",
|
"@budibase/frontend-core": "2.4.44-alpha.16",
|
||||||
"@budibase/shared-core": "2.4.44-alpha.11",
|
"@budibase/shared-core": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@budibase/types": "2.4.44-alpha.11",
|
"@budibase/types": "2.4.44-alpha.16",
|
||||||
"@spectrum-css/button": "^3.0.3",
|
"@spectrum-css/button": "^3.0.3",
|
||||||
"@spectrum-css/card": "^3.0.3",
|
"@spectrum-css/card": "^3.0.3",
|
||||||
"@spectrum-css/divider": "^1.0.3",
|
"@spectrum-css/divider": "^1.0.3",
|
||||||
|
|
|
@ -241,6 +241,25 @@ const maxLengthHandler = (value, rule) => {
|
||||||
return value == null || value.length <= limit
|
return value == null || value.length <= limit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Evaluates a max file size (MB) constraint
|
||||||
|
const maxFileSizeHandler = (value, rule) => {
|
||||||
|
const limit = parseType(rule.value, "number")
|
||||||
|
return (
|
||||||
|
value == null ||
|
||||||
|
!value.some(attachment => attachment.size / 1000000 > limit)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Evaluates a max total upload size (MB) constraint
|
||||||
|
const maxUploadSizeHandler = (value, rule) => {
|
||||||
|
const limit = parseType(rule.value, "number")
|
||||||
|
return (
|
||||||
|
value == null ||
|
||||||
|
value.reduce((acc, currentItem) => acc + currentItem.size, 0) / 1000000 <=
|
||||||
|
limit
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Evaluates a min value constraint
|
// Evaluates a min value constraint
|
||||||
const minValueHandler = (value, rule) => {
|
const minValueHandler = (value, rule) => {
|
||||||
// Use same type as the value so that things can be compared
|
// Use same type as the value so that things can be compared
|
||||||
|
@ -330,6 +349,8 @@ const handlerMap = {
|
||||||
contains: containsHandler,
|
contains: containsHandler,
|
||||||
notContains: notContainsHandler,
|
notContains: notContainsHandler,
|
||||||
json: jsonHandler,
|
json: jsonHandler,
|
||||||
|
maxFileSize: maxFileSizeHandler,
|
||||||
|
maxUploadSize: maxUploadSizeHandler,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/frontend-core",
|
"name": "@budibase/frontend-core",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase frontend core libraries used in builder and client",
|
"description": "Budibase frontend core libraries used in builder and client",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"svelte": "src/index.js",
|
"svelte": "src/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "2.4.44-alpha.11",
|
"@budibase/bbui": "2.4.44-alpha.16",
|
||||||
"@budibase/shared-core": "2.4.44-alpha.11",
|
"@budibase/shared-core": "2.4.44-alpha.16",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"svelte": "^3.46.2"
|
"svelte": "^3.46.2"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/sdk",
|
"name": "@budibase/sdk",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase Public API SDK",
|
"description": "Budibase Public API SDK",
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -45,12 +45,12 @@
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apidevtools/swagger-parser": "10.0.3",
|
"@apidevtools/swagger-parser": "10.0.3",
|
||||||
"@budibase/backend-core": "2.4.44-alpha.11",
|
"@budibase/backend-core": "2.4.44-alpha.16",
|
||||||
"@budibase/client": "2.4.44-alpha.11",
|
"@budibase/client": "2.4.44-alpha.16",
|
||||||
"@budibase/pro": "2.4.44-alpha.10",
|
"@budibase/pro": "2.4.44-alpha.16",
|
||||||
"@budibase/shared-core": "2.4.44-alpha.11",
|
"@budibase/shared-core": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@budibase/types": "2.4.44-alpha.11",
|
"@budibase/types": "2.4.44-alpha.16",
|
||||||
"@bull-board/api": "3.7.0",
|
"@bull-board/api": "3.7.0",
|
||||||
"@bull-board/koa": "3.9.4",
|
"@bull-board/koa": "3.9.4",
|
||||||
"@elastic/elasticsearch": "7.10.0",
|
"@elastic/elasticsearch": "7.10.0",
|
||||||
|
|
|
@ -19,11 +19,14 @@ describe("/automations", () => {
|
||||||
|
|
||||||
afterAll(setup.afterAll)
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
// For some reason this cannot be a beforeAll or the test "tests the automation successfully" fail
|
beforeAll(async () => {
|
||||||
beforeEach(async () => {
|
|
||||||
await config.init()
|
await config.init()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
events.automation.deleted.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
describe("get definitions", () => {
|
describe("get definitions", () => {
|
||||||
it("returns a list of definitions for actions", async () => {
|
it("returns a list of definitions for actions", async () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
|
|
|
@ -6,6 +6,7 @@ interface RedisConfig {
|
||||||
port: number
|
port: number
|
||||||
username: string
|
username: string
|
||||||
password?: string
|
password?: string
|
||||||
|
db?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const SCHEMA: Integration = {
|
const SCHEMA: Integration = {
|
||||||
|
@ -32,6 +33,12 @@ const SCHEMA: Integration = {
|
||||||
type: "password",
|
type: "password",
|
||||||
required: false,
|
required: false,
|
||||||
},
|
},
|
||||||
|
db: {
|
||||||
|
type: "number",
|
||||||
|
required: false,
|
||||||
|
display: "DB",
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
|
@ -88,6 +95,7 @@ class RedisIntegration {
|
||||||
port: this.config.port,
|
port: this.config.port,
|
||||||
username: this.config.username,
|
username: this.config.username,
|
||||||
password: this.config.password,
|
password: this.config.password,
|
||||||
|
db: this.config.db,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,7 @@ describe("syncRows", () => {
|
||||||
|
|
||||||
// app 1
|
// app 1
|
||||||
const app1 = config.app
|
const app1 = config.app
|
||||||
await context.doInAppContext(app1.appId, async () => {
|
await context.doInAppContext(app1!.appId, async () => {
|
||||||
await config.createTable()
|
await config.createTable()
|
||||||
await config.createRow()
|
await config.createRow()
|
||||||
})
|
})
|
||||||
|
@ -43,7 +43,7 @@ describe("syncRows", () => {
|
||||||
usageDoc = await quotas.getQuotaUsage()
|
usageDoc = await quotas.getQuotaUsage()
|
||||||
expect(usageDoc.usageQuota.rows).toEqual(3)
|
expect(usageDoc.usageQuota.rows).toEqual(3)
|
||||||
expect(
|
expect(
|
||||||
usageDoc.apps?.[dbCore.getProdAppID(app1.appId)].usageQuota.rows
|
usageDoc.apps?.[dbCore.getProdAppID(app1!.appId)].usageQuota.rows
|
||||||
).toEqual(1)
|
).toEqual(1)
|
||||||
expect(
|
expect(
|
||||||
usageDoc.apps?.[dbCore.getProdAppID(app2.appId)].usageQuota.rows
|
usageDoc.apps?.[dbCore.getProdAppID(app2.appId)].usageQuota.rows
|
||||||
|
|
|
@ -1290,14 +1290,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@2.4.44-alpha.10":
|
"@budibase/backend-core@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.44-alpha.10.tgz#02fdc24dad37fa244b9e3c7df731f355c9a5927a"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.44-alpha.16.tgz#d7cbd0b468ffdb7a472ef99a52a1614e193770c2"
|
||||||
integrity sha512-mhwXyc366dxy+U8aU74I+tVr0b5TmVEqRH+sY/EG3u6wJgwatyz5e5Pq9srftJX/+nM+jXiu27oIKFUx4v+IkA==
|
integrity sha512-3S6NqVB1Ulq1a8Gl1r9/eBnlpfvhX0P6lTdeDG/K6u/pOUkJqjDHHNvJ936CeXcS6WQKsSr5GF+649VlylV5eg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/nano" "10.1.2"
|
"@budibase/nano" "10.1.2"
|
||||||
"@budibase/pouchdb-replication-stream" "1.2.10"
|
"@budibase/pouchdb-replication-stream" "1.2.10"
|
||||||
"@budibase/types" "2.4.44-alpha.10"
|
"@budibase/types" "2.4.44-alpha.16"
|
||||||
"@shopify/jest-koa-mocks" "5.0.1"
|
"@shopify/jest-koa-mocks" "5.0.1"
|
||||||
"@techpass/passport-openidconnect" "0.3.2"
|
"@techpass/passport-openidconnect" "0.3.2"
|
||||||
aws-cloudfront-sign "2.2.0"
|
aws-cloudfront-sign "2.2.0"
|
||||||
|
@ -1430,15 +1430,15 @@
|
||||||
pouchdb-promise "^6.0.4"
|
pouchdb-promise "^6.0.4"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
"@budibase/pro@2.4.44-alpha.10":
|
"@budibase/pro@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.44-alpha.10.tgz#48bba3422f0f90cb2948c109f7e518b98a1ef37e"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.44-alpha.16.tgz#b3f9884a279bd2135f229bd62e6696e4a2ce8de2"
|
||||||
integrity sha512-E+WmuLnYu5B6R/C8h8vtLSNdENANKI71cWbXifPwI8lfnRVZxVyW0v98EWknZWGSpF55y8G8AUlKLTkDDOkZkA==
|
integrity sha512-oTRehPQPP2LIgpmssUIQ3cvwebnkdZuE7E0WkDb/RNeID0sYQX4I5GZfeaaOhljcd81wJKXZQYGUo4+9oZwBjQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "2.4.44-alpha.10"
|
"@budibase/backend-core" "2.4.44-alpha.16"
|
||||||
"@budibase/shared-core" "2.4.44-alpha.1"
|
"@budibase/shared-core" "2.4.44-alpha.1"
|
||||||
"@budibase/string-templates" "2.4.44-alpha.1"
|
"@budibase/string-templates" "2.4.44-alpha.1"
|
||||||
"@budibase/types" "2.4.44-alpha.10"
|
"@budibase/types" "2.4.44-alpha.16"
|
||||||
"@koa/router" "8.0.8"
|
"@koa/router" "8.0.8"
|
||||||
bull "4.10.1"
|
bull "4.10.1"
|
||||||
joi "17.6.0"
|
joi "17.6.0"
|
||||||
|
@ -1491,10 +1491,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933"
|
||||||
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg==
|
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg==
|
||||||
|
|
||||||
"@budibase/types@2.4.44-alpha.10":
|
"@budibase/types@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.10.tgz#4f5a13948dc95cbe3a6fe305259159207b37b1d9"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.16.tgz#83f3745a67db566a80c34d89390ceb057a1456a9"
|
||||||
integrity sha512-ZvBRXevf3L/N/4fn0pC96CDYXDrElY7C2pn6JlysLoaJMOr3bSQWf1PaslP1/E9gS7XFIKye1wN9VIWIHa0QLg==
|
integrity sha512-F3phfLOBHsLlXKwIxDAZsNo1hneKe916JJefBSlKetLXDH0AZzHWwh3dpYdw2QKdclTVVJKH9QWrBIH8sZFvYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
scim-patch "^0.7.0"
|
scim-patch "^0.7.0"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/shared-core",
|
"name": "@budibase/shared-core",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Shared data utils",
|
"description": "Shared data utils",
|
||||||
"main": "dist/cjs/src/index.js",
|
"main": "dist/cjs/src/index.js",
|
||||||
"types": "dist/mjs/src/index.d.ts",
|
"types": "dist/mjs/src/index.d.ts",
|
||||||
|
@ -20,7 +20,7 @@
|
||||||
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
|
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/types": "2.4.44-alpha.11"
|
"@budibase/types": "2.4.44-alpha.16"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"concurrently": "^7.6.0",
|
"concurrently": "^7.6.0",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.cjs",
|
"main": "src/index.cjs",
|
||||||
"module": "dist/bundle.mjs",
|
"module": "dist/bundle.mjs",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/types",
|
"name": "@budibase/types",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase types",
|
"description": "Budibase types",
|
||||||
"main": "dist/cjs/index.js",
|
"main": "dist/cjs/index.js",
|
||||||
"types": "dist/mjs/index.d.ts",
|
"types": "dist/mjs/index.d.ts",
|
||||||
|
|
|
@ -19,10 +19,10 @@ export interface ScimUserResponse extends ScimResource {
|
||||||
}
|
}
|
||||||
userName: string
|
userName: string
|
||||||
displayName?: string
|
displayName?: string
|
||||||
name: {
|
name?: {
|
||||||
formatted: string
|
formatted?: string
|
||||||
familyName: string
|
familyName?: string
|
||||||
givenName: string
|
givenName?: string
|
||||||
}
|
}
|
||||||
active: BooleanString
|
active: BooleanString
|
||||||
emails?: Emails
|
emails?: Emails
|
||||||
|
@ -41,7 +41,7 @@ export interface ScimCreateUserRequest {
|
||||||
resourceType: "User"
|
resourceType: "User"
|
||||||
}
|
}
|
||||||
displayName?: string
|
displayName?: string
|
||||||
name: {
|
name?: {
|
||||||
formatted: string
|
formatted: string
|
||||||
familyName: string
|
familyName: string
|
||||||
givenName: string
|
givenName: string
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "2.4.44-alpha.11",
|
"version": "2.4.44-alpha.16",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -37,10 +37,10 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "GPL-3.0",
|
"license": "GPL-3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/backend-core": "2.4.44-alpha.11",
|
"@budibase/backend-core": "2.4.44-alpha.16",
|
||||||
"@budibase/pro": "2.4.44-alpha.10",
|
"@budibase/pro": "2.4.44-alpha.16",
|
||||||
"@budibase/string-templates": "2.4.44-alpha.11",
|
"@budibase/string-templates": "2.4.44-alpha.16",
|
||||||
"@budibase/types": "2.4.44-alpha.11",
|
"@budibase/types": "2.4.44-alpha.16",
|
||||||
"@koa/router": "8.0.8",
|
"@koa/router": "8.0.8",
|
||||||
"@sentry/node": "6.17.7",
|
"@sentry/node": "6.17.7",
|
||||||
"@techpass/passport-openidconnect": "0.3.2",
|
"@techpass/passport-openidconnect": "0.3.2",
|
||||||
|
|
|
@ -255,6 +255,45 @@ describe("scim", () => {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("a new user can minim information", async () => {
|
||||||
|
const userData = {
|
||||||
|
externalId: structures.uuid(),
|
||||||
|
email: structures.generator.email(),
|
||||||
|
username: structures.generator.name(),
|
||||||
|
firstName: undefined,
|
||||||
|
lastName: undefined,
|
||||||
|
}
|
||||||
|
const body = structures.scim.createUserRequest(userData)
|
||||||
|
|
||||||
|
const response = await postScimUser({ body })
|
||||||
|
|
||||||
|
const expectedScimUser = {
|
||||||
|
schemas: ["urn:ietf:params:scim:schemas:core:2.0:User"],
|
||||||
|
id: expect.any(String),
|
||||||
|
externalId: userData.externalId,
|
||||||
|
meta: {
|
||||||
|
resourceType: "User",
|
||||||
|
created: mocks.date.MOCK_DATE.toISOString(),
|
||||||
|
lastModified: mocks.date.MOCK_DATE.toISOString(),
|
||||||
|
},
|
||||||
|
userName: userData.username,
|
||||||
|
active: true,
|
||||||
|
emails: [
|
||||||
|
{
|
||||||
|
value: userData.email,
|
||||||
|
type: "work",
|
||||||
|
primary: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
expect(response).toEqual(expectedScimUser)
|
||||||
|
|
||||||
|
const persistedUsers = await config.api.scimUsersAPI.get()
|
||||||
|
expect(persistedUsers.Resources).toEqual(
|
||||||
|
expect.arrayContaining([expectedScimUser])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
it("an event is dispatched", async () => {
|
it("an event is dispatched", async () => {
|
||||||
const body = structures.scim.createUserRequest()
|
const body = structures.scim.createUserRequest()
|
||||||
|
|
||||||
|
@ -318,6 +357,15 @@ describe("scim", () => {
|
||||||
await postScimUser({ body }, { expect: 500 })
|
await postScimUser({ body }, { expect: 500 })
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("creating an existing user name returns a conflict", async () => {
|
||||||
|
const body = structures.scim.createUserRequest()
|
||||||
|
|
||||||
|
await postScimUser({ body })
|
||||||
|
|
||||||
|
const res = await postScimUser({ body }, { expect: 409 })
|
||||||
|
expect((res as any).message).toBe("Email already in use")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("GET /api/global/scim/v2/users/:id", () => {
|
describe("GET /api/global/scim/v2/users/:id", () => {
|
||||||
|
@ -389,7 +437,7 @@ describe("scim", () => {
|
||||||
name: {
|
name: {
|
||||||
...user.name,
|
...user.name,
|
||||||
familyName: newFamilyName,
|
familyName: newFamilyName,
|
||||||
formatted: `${user.name.givenName} ${newFamilyName}`,
|
formatted: `${user.name!.givenName} ${newFamilyName}`,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
expect(response).toEqual(expectedScimUser)
|
expect(response).toEqual(expectedScimUser)
|
||||||
|
|
|
@ -48,7 +48,7 @@ describe("/api/global/users", () => {
|
||||||
400
|
400
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(res.body.message).toBe("Unavailable")
|
expect(res.body.message).toBe(`Unavailable`)
|
||||||
expect(sendMailMock).toHaveBeenCalledTimes(0)
|
expect(sendMailMock).toHaveBeenCalledTimes(0)
|
||||||
expect(code).toBeUndefined()
|
expect(code).toBeUndefined()
|
||||||
expect(events.user.invited).toBeCalledTimes(0)
|
expect(events.user.invited).toBeCalledTimes(0)
|
||||||
|
@ -225,7 +225,9 @@ describe("/api/global/users", () => {
|
||||||
|
|
||||||
const response = await config.api.users.saveUser(user, 400)
|
const response = await config.api.users.saveUser(user, 400)
|
||||||
|
|
||||||
expect(response.body.message).toBe(`Unavailable`)
|
expect(response.body.message).toBe(
|
||||||
|
`Email already in use: '${user.email}'`
|
||||||
|
)
|
||||||
expect(events.user.created).toBeCalledTimes(0)
|
expect(events.user.created).toBeCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -237,7 +239,9 @@ describe("/api/global/users", () => {
|
||||||
delete user._id
|
delete user._id
|
||||||
const response = await config.api.users.saveUser(user, 400)
|
const response = await config.api.users.saveUser(user, 400)
|
||||||
|
|
||||||
expect(response.body.message).toBe(`Unavailable`)
|
expect(response.body.message).toBe(
|
||||||
|
`Email already in use: '${user.email}'`
|
||||||
|
)
|
||||||
expect(events.user.created).toBeCalledTimes(0)
|
expect(events.user.created).toBeCalledTimes(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -249,7 +253,9 @@ describe("/api/global/users", () => {
|
||||||
|
|
||||||
const response = await config.api.users.saveUser(user, 400)
|
const response = await config.api.users.saveUser(user, 400)
|
||||||
|
|
||||||
expect(response.body.message).toBe(`Unavailable`)
|
expect(response.body.message).toBe(
|
||||||
|
`Email already in use: '${user.email}'`
|
||||||
|
)
|
||||||
expect(events.user.created).toBeCalledTimes(0)
|
expect(events.user.created).toBeCalledTimes(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import {
|
||||||
ViewName,
|
ViewName,
|
||||||
env as coreEnv,
|
env as coreEnv,
|
||||||
context,
|
context,
|
||||||
|
EmailUnavailableError,
|
||||||
} from "@budibase/backend-core"
|
} from "@budibase/backend-core"
|
||||||
import {
|
import {
|
||||||
AccountMetadata,
|
AccountMetadata,
|
||||||
|
@ -158,7 +159,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
|
||||||
if (env.MULTI_TENANCY) {
|
if (env.MULTI_TENANCY) {
|
||||||
const tenantUser = await getPlatformUser(email)
|
const tenantUser = await getPlatformUser(email)
|
||||||
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
if (tenantUser != null && tenantUser.tenantId !== tenantId) {
|
||||||
throw `Unavailable`
|
throw new EmailUnavailableError(email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +167,7 @@ const validateUniqueUser = async (email: string, tenantId: string) => {
|
||||||
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
|
||||||
const account = await accounts.getAccount(email)
|
const account = await accounts.getAccount(email)
|
||||||
if (account && account.verified && account.tenantId !== tenantId) {
|
if (account && account.verified && account.tenantId !== tenantId) {
|
||||||
throw `Unavailable`
|
throw new EmailUnavailableError(email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,7 +236,7 @@ export const save = async (
|
||||||
// no id was specified - load from email instead
|
// no id was specified - load from email instead
|
||||||
dbUser = await usersCore.getGlobalUserByEmail(email)
|
dbUser = await usersCore.getGlobalUserByEmail(email)
|
||||||
if (dbUser && dbUser._id !== _id) {
|
if (dbUser && dbUser._id !== _id) {
|
||||||
throw `Unavailable`
|
throw new EmailUnavailableError(email)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -475,14 +475,14 @@
|
||||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||||
|
|
||||||
"@budibase/backend-core@2.4.44-alpha.10":
|
"@budibase/backend-core@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.44-alpha.10.tgz#02fdc24dad37fa244b9e3c7df731f355c9a5927a"
|
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.44-alpha.16.tgz#d7cbd0b468ffdb7a472ef99a52a1614e193770c2"
|
||||||
integrity sha512-mhwXyc366dxy+U8aU74I+tVr0b5TmVEqRH+sY/EG3u6wJgwatyz5e5Pq9srftJX/+nM+jXiu27oIKFUx4v+IkA==
|
integrity sha512-3S6NqVB1Ulq1a8Gl1r9/eBnlpfvhX0P6lTdeDG/K6u/pOUkJqjDHHNvJ936CeXcS6WQKsSr5GF+649VlylV5eg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/nano" "10.1.2"
|
"@budibase/nano" "10.1.2"
|
||||||
"@budibase/pouchdb-replication-stream" "1.2.10"
|
"@budibase/pouchdb-replication-stream" "1.2.10"
|
||||||
"@budibase/types" "2.4.44-alpha.10"
|
"@budibase/types" "2.4.44-alpha.16"
|
||||||
"@shopify/jest-koa-mocks" "5.0.1"
|
"@shopify/jest-koa-mocks" "5.0.1"
|
||||||
"@techpass/passport-openidconnect" "0.3.2"
|
"@techpass/passport-openidconnect" "0.3.2"
|
||||||
aws-cloudfront-sign "2.2.0"
|
aws-cloudfront-sign "2.2.0"
|
||||||
|
@ -565,15 +565,15 @@
|
||||||
pouchdb-promise "^6.0.4"
|
pouchdb-promise "^6.0.4"
|
||||||
through2 "^2.0.0"
|
through2 "^2.0.0"
|
||||||
|
|
||||||
"@budibase/pro@2.4.44-alpha.10":
|
"@budibase/pro@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.44-alpha.10.tgz#48bba3422f0f90cb2948c109f7e518b98a1ef37e"
|
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.44-alpha.16.tgz#b3f9884a279bd2135f229bd62e6696e4a2ce8de2"
|
||||||
integrity sha512-E+WmuLnYu5B6R/C8h8vtLSNdENANKI71cWbXifPwI8lfnRVZxVyW0v98EWknZWGSpF55y8G8AUlKLTkDDOkZkA==
|
integrity sha512-oTRehPQPP2LIgpmssUIQ3cvwebnkdZuE7E0WkDb/RNeID0sYQX4I5GZfeaaOhljcd81wJKXZQYGUo4+9oZwBjQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@budibase/backend-core" "2.4.44-alpha.10"
|
"@budibase/backend-core" "2.4.44-alpha.16"
|
||||||
"@budibase/shared-core" "2.4.44-alpha.1"
|
"@budibase/shared-core" "2.4.44-alpha.1"
|
||||||
"@budibase/string-templates" "2.4.44-alpha.1"
|
"@budibase/string-templates" "2.4.44-alpha.1"
|
||||||
"@budibase/types" "2.4.44-alpha.10"
|
"@budibase/types" "2.4.44-alpha.16"
|
||||||
"@koa/router" "8.0.8"
|
"@koa/router" "8.0.8"
|
||||||
bull "4.10.1"
|
bull "4.10.1"
|
||||||
joi "17.6.0"
|
joi "17.6.0"
|
||||||
|
@ -608,10 +608,10 @@
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933"
|
||||||
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg==
|
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg==
|
||||||
|
|
||||||
"@budibase/types@2.4.44-alpha.10":
|
"@budibase/types@2.4.44-alpha.16":
|
||||||
version "2.4.44-alpha.10"
|
version "2.4.44-alpha.16"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.10.tgz#4f5a13948dc95cbe3a6fe305259159207b37b1d9"
|
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.16.tgz#83f3745a67db566a80c34d89390ceb057a1456a9"
|
||||||
integrity sha512-ZvBRXevf3L/N/4fn0pC96CDYXDrElY7C2pn6JlysLoaJMOr3bSQWf1PaslP1/E9gS7XFIKye1wN9VIWIHa0QLg==
|
integrity sha512-F3phfLOBHsLlXKwIxDAZsNo1hneKe916JJefBSlKetLXDH0AZzHWwh3dpYdw2QKdclTVVJKH9QWrBIH8sZFvYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
scim-patch "^0.7.0"
|
scim-patch "^0.7.0"
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,8 @@ const yaml = require("js-yaml")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const path = require("path")
|
const path = require("path")
|
||||||
|
|
||||||
const UpgradeTypes = {
|
|
||||||
MAJOR: "major",
|
|
||||||
MINOR: "minor",
|
|
||||||
PATCH: "patch"
|
|
||||||
}
|
|
||||||
|
|
||||||
const CHART_PATH = path.join(__dirname, "../", "charts", "budibase", "Chart.yaml")
|
const CHART_PATH = path.join(__dirname, "../", "charts", "budibase", "Chart.yaml")
|
||||||
const UPGRADE_VERSION = process.env.BUDIBASE_RELEASE_VERSION
|
const UPGRADE_VERSION = process.env.BUDIBASE_RELEASE_VERSION
|
||||||
const UPGRADE_TYPE = process.env.HELM_CHART_UPGRADE_TYPE || UpgradeTypes.PATCH
|
|
||||||
|
|
||||||
if (!UPGRADE_VERSION) {
|
if (!UPGRADE_VERSION) {
|
||||||
throw new Error("BUDIBASE_RELEASE_VERSION env var must be set.")
|
throw new Error("BUDIBASE_RELEASE_VERSION env var must be set.")
|
||||||
|
|
Loading…
Reference in a new issue