1
0
Fork 0
mirror of synced 2024-07-02 21:10:43 +12:00

Merge remote-tracking branch 'origin/develop' into feature/whitelabelling

This commit is contained in:
Dean 2023-03-15 09:13:36 +00:00
commit f3b58923cf
69 changed files with 685 additions and 496 deletions

View file

@ -1,6 +1,10 @@
name: "deploy-preprod"
on:
workflow_dispatch:
inputs:
version:
description: Budibase release version. For example - 1.0.0
required: false
workflow_call:
jobs:
@ -8,10 +12,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
- name: Get the latest budibase release version
id: version
run: |
if [ -z "${{ github.event.inputs.version }}" ]; then
release_version=$(cat lerna.json | jq -r '.version')
else
release_version=${{ github.event.inputs.version }}
fi
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v1
with:
@ -26,7 +36,6 @@ jobs:
-o values.preprod.yaml \
-L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml
wc -l values.preprod.yaml
- name: Deploy to Preprod Environment
uses: budibase/helm@v1.8.0
with:
@ -37,7 +46,7 @@ jobs:
helm: helm3
values: |
globals:
appVersion: ${{ steps.previoustag.outputs.tag }}
appVersion: v${{ env.RELEASE_VERSION }}
ingress:
enabled: true
nginx: true
@ -52,5 +61,5 @@ jobs:
uses: tsickert/discord-webhook@v4.0.0
with:
webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }}
content: "Preprod Deployment Complete: ${{ steps.previoustag.outputs.tag }} deployed to Budibase Pre-prod."
embed-title: ${{ steps.previoustag.outputs.tag }}
content: "Preprod Deployment Complete: ${{ env.RELEASE_VERSION }} deployed to Budibase Pre-prod."
embed-title: ${{ env.RELEASE_VERSION }}

View file

@ -91,9 +91,11 @@ jobs:
uses: azure/setup-helm@v1
id: helm-install
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
- name: Get the latest budibase release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
# due to helm repo index issue: https://github.com/helm/helm/issues/7363
# we need to create new package in a different dir, merge the index and move the package back
@ -116,8 +118,6 @@ jobs:
git add -A
git commit -m "Helm Release: ${{ env.RELEASE_VERSION }}"
git push
env:
RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }}
deploy-to-legacy-preprod-env:
needs: [release-images]
@ -130,13 +130,16 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 'Get Previous tag'
id: previoustag
uses: "WyriHaximus/github-action-get-previous-tag@v1"
- name: Get the latest budibase release version
id: version
run: |
release_version=$(cat lerna.json | jq -r '.version')
echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV
- uses: passeidireto/trigger-external-workflow-action@main
env:
PAYLOAD_VERSION: ${{ steps.previoustag.outputs.tag }}
PAYLOAD_VERSION: ${{ env.RELEASE_VERSION }}
with:
repository: budibase/budibase-deploys
event: budicloud-preprod-deploy

View file

@ -62,16 +62,22 @@ spec:
{{ end }}
- name: ENABLE_ANALYTICS
value: {{ .Values.globals.enableAnalytics | quote }}
- name: API_ENCRYPTION_KEY
value: {{ .Values.globals.apiEncryptionKey | quote }}
- name: INTERNAL_API_KEY
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
value: {{ .Values.globals.internalApiKeyFallback | quote }}
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: jwtSecret
- name: JWT_SECRET_FALLBACK
value: {{ .Values.globals.jwtSecretFallback | quote }}
{{ if .Values.services.objectStore.region }}
- name: AWS_REGION
value: {{ .Values.services.objectStore.region }}
@ -125,9 +131,9 @@ spec:
- name: SELF_HOSTED
value: {{ .Values.globals.selfHosted | quote }}
- name: SENTRY_DSN
value: {{ .Values.globals.sentryDSN }}
value: {{ .Values.globals.sentryDSN | quote }}
- name: POSTHOG_TOKEN
value: {{ .Values.globals.posthogToken }}
value: {{ .Values.globals.posthogToken | quote }}
- name: WORKER_URL
value: http://worker-service:{{ .Values.services.worker.port }}
- name: PLATFORM_URL
@ -198,8 +204,6 @@ spec:
- name: GLOBAL_AGENT_NO_PROXY
value: {{ .Values.globals.globalAgentNoProxy | quote }}
{{ end }}
- name: CDN_URL
value: {{ .Values.globals.cdnUrl }}
{{ if .Values.services.tlsRejectUnauthorized }}
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }}

View file

@ -62,16 +62,22 @@ spec:
{{ else }}
value: http://{{ .Release.Name }}-svc-couchdb:{{ .Values.services.couchdb.port }}
{{ end }}
- name: API_ENCRYPTION_KEY
value: {{ .Values.globals.apiEncryptionKey | quote }}
- name: INTERNAL_API_KEY
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: internalApiKey
- name: INTERNAL_API_KEY_FALLBACK
value: {{ .Values.globals.internalApiKeyFallback | quote }}
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: {{ template "budibase.fullname" . }}
key: jwtSecret
- name: JWT_SECRET_FALLBACK
value: {{ .Values.globals.jwtSecretFallback | quote }}
{{ if .Values.services.objectStore.region }}
- name: AWS_REGION
value: {{ .Values.services.objectStore.region }}
@ -188,8 +194,6 @@ spec:
- name: GLOBAL_AGENT_NO_PROXY
value: {{ .Values.globals.globalAgentNoProxy | quote }}
{{ end }}
- name: CDN_URL
value: {{ .Values.globals.cdnUrl }}
{{ if .Values.services.tlsRejectUnauthorized }}
- name: NODE_TLS_REJECT_UNAUTHORIZED
value: {{ .Values.services.tlsRejectUnauthorized }}

View file

@ -96,9 +96,13 @@ globals:
createSecrets: true # creates an internal API key, JWT secrets and redis password for you
# if createSecrets is set to false, you can hard-code your secrets here
apiEncryptionKey: ""
internalApiKey: ""
jwtSecret: ""
cdnUrl: ""
# fallback values used during live rotation
internalApiKeyFallback: ""
jwtSecretFallback: ""
smtp:
enabled: false

View file

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system
# These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase

View file

@ -17,6 +17,7 @@ services:
INTERNAL_API_KEY: ${INTERNAL_API_KEY}
BUDIBASE_ENVIRONMENT: ${BUDIBASE_ENVIRONMENT}
PORT: 4002
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET}
LOG_LEVEL: info
SENTRY_DSN: https://a34ae347621946bf8acded18e5b7d4b8@o420233.ingest.sentry.io/5338131
@ -40,6 +41,7 @@ services:
SELF_HOSTED: 1
PORT: 4003
CLUSTER_PORT: ${MAIN_PORT}
API_ENCRYPTION_KEY: ${API_ENCRYPTION_KEY}
JWT_SECRET: ${JWT_SECRET}
MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY}
MINIO_SECRET_KEY: ${MINIO_SECRET_KEY}

View file

@ -3,6 +3,7 @@ MAIN_PORT=10000
# This section contains all secrets pertaining to the system
# These should be updated
API_ENCRYPTION_KEY=testsecret
JWT_SECRET=testsecret
MINIO_ACCESS_KEY=budibase
MINIO_SECRET_KEY=budibase

View file

@ -1,5 +1,5 @@
{
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"npmClient": "yarn",
"packages": [
"packages/*"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js",
"types": "dist/src/index.d.ts",
@ -24,7 +24,7 @@
"dependencies": {
"@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/types": "2.4.12-alpha.5",
"@budibase/types": "^2.4.26",
"@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0",

View file

@ -1,6 +1,5 @@
const _passport = require("koa-passport")
const LocalStrategy = require("passport-local").Strategy
const JwtStrategy = require("passport-jwt").Strategy
import { getGlobalDB } from "../context"
import { Cookie } from "../constants"
import { getSessionsForUser, invalidateSessions } from "../security/sessions"
@ -8,7 +7,6 @@ import {
authenticated,
csrf,
google,
jwt as jwtPassport,
local,
oidc,
tenancy,
@ -21,14 +19,11 @@ import {
OIDCInnerConfig,
PlatformLogoutOpts,
SSOProviderType,
User,
} from "@budibase/types"
import { logAlert } from "../logging"
import * as events from "../events"
import * as configs from "../configs"
import { clearCookie, getCookie } from "../utils"
import { ssoSaveUserNoOp } from "../middleware/passport/sso/sso"
import env from "../environment"
const refresh = require("passport-oauth2-refresh")
export {
@ -51,25 +46,6 @@ export const jwt = require("jsonwebtoken")
// Strategies
_passport.use(new LocalStrategy(local.options, local.authenticate))
if (jwtPassport.options.secretOrKey) {
_passport.use(new JwtStrategy(jwtPassport.options, jwtPassport.authenticate))
} else if (!env.DISABLE_JWT_WARNING) {
logAlert("No JWT Secret supplied, cannot configure JWT strategy")
}
_passport.serializeUser((user: User, done: any) => done(null, user))
_passport.deserializeUser(async (user: User, done: any) => {
const db = getGlobalDB()
try {
const dbUser = await db.get(user._id)
return done(null, dbUser)
} catch (err) {
console.error(`User not found`, err)
return done(null, false, { message: "User not found" })
}
})
async function refreshOIDCAccessToken(
chosenConfig: OIDCInnerConfig,

View file

@ -30,6 +30,12 @@ const DefaultBucketName = {
const selfHosted = !!parseInt(process.env.SELF_HOSTED || "")
function getAPIEncryptionKey() {
return process.env.API_ENCRYPTION_KEY
? process.env.API_ENCRYPTION_KEY
: process.env.JWT_SECRET // fallback to the JWT_SECRET used historically
}
const environment = {
isTest,
isJest,
@ -39,7 +45,9 @@ const environment = {
},
JS_BCRYPT: process.env.JS_BCRYPT,
JWT_SECRET: process.env.JWT_SECRET,
JWT_SECRET_FALLBACK: process.env.JWT_SECRET_FALLBACK,
ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
API_ENCRYPTION_KEY: getAPIEncryptionKey(),
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
COUCH_DB_USERNAME: process.env.COUCH_DB_USER,
COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD,
@ -55,6 +63,7 @@ const environment = {
MINIO_URL: process.env.MINIO_URL,
MINIO_ENABLED: process.env.MINIO_ENABLED || 1,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
INTERNAL_API_KEY_FALLBACK: process.env.INTERNAL_API_KEY_FALLBACK,
MULTI_TENANCY: process.env.MULTI_TENANCY,
ACCOUNT_PORTAL_URL:
process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app",

View file

@ -1,5 +1,10 @@
import { Cookie, Header } from "../constants"
import { getCookie, clearCookie, openJwt } from "../utils"
import {
getCookie,
clearCookie,
openJwt,
isValidInternalAPIKey,
} from "../utils"
import { getUser } from "../cache/user"
import { getSession, updateSessionTTL } from "../security/sessions"
import { buildMatcherRegex, matches } from "./matchers"
@ -35,7 +40,9 @@ function finalise(ctx: any, opts: FinaliseOpts = {}) {
}
async function checkApiKey(apiKey: string, populateUser?: Function) {
if (apiKey === env.INTERNAL_API_KEY) {
// check both the primary and the fallback internal api keys
// this allows for rotation
if (isValidInternalAPIKey(apiKey)) {
return { valid: true }
}
const decrypted = decrypt(apiKey)

View file

@ -1,4 +1,3 @@
export * as jwt from "./passport/jwt"
export * as local from "./passport/local"
export * as google from "./passport/sso/google"
export * as oidc from "./passport/sso/oidc"

View file

@ -1,13 +1,21 @@
import env from "../environment"
import { Header } from "../constants"
import { BBContext } from "@budibase/types"
import { isValidInternalAPIKey } from "../utils"
/**
* API Key only endpoint.
*/
export default async (ctx: BBContext, next: any) => {
const apiKey = ctx.request.headers[Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) {
if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized")
}

View file

@ -1,19 +0,0 @@
import { Cookie } from "../../constants"
import env from "../../environment"
import { authError } from "./utils"
import { BBContext } from "@budibase/types"
export const options = {
secretOrKey: env.JWT_SECRET,
jwtFromRequest: function (ctx: BBContext) {
return ctx.cookies.get(Cookie.Auth)
},
}
export async function authenticate(jwt: Function, done: Function) {
try {
return done(null, jwt)
} catch (err) {
return authError(done, "JWT invalid", err)
}
}

View file

@ -8,7 +8,7 @@ const RANDOM_BYTES = 16
const STRETCH_LENGTH = 32
export enum SecretOption {
JWT = "jwt",
API = "api",
ENCRYPTION = "encryption",
}
@ -19,10 +19,10 @@ function getSecret(secretOption: SecretOption): string {
secret = env.ENCRYPTION_KEY
secretName = "ENCRYPTION_KEY"
break
case SecretOption.JWT:
case SecretOption.API:
default:
secret = env.JWT_SECRET
secretName = "JWT_SECRET"
secret = env.API_ENCRYPTION_KEY
secretName = "API_ENCRYPTION_KEY"
break
}
if (!secret) {
@ -37,7 +37,7 @@ function stretchString(string: string, salt: Buffer) {
export function encrypt(
input: string,
secretOption: SecretOption = SecretOption.JWT
secretOption: SecretOption = SecretOption.API
) {
const salt = crypto.randomBytes(RANDOM_BYTES)
const stretched = stretchString(getSecret(secretOption), salt)
@ -50,7 +50,7 @@ export function encrypt(
export function decrypt(
input: string,
secretOption: SecretOption = SecretOption.JWT
secretOption: SecretOption = SecretOption.API
) {
const [salt, encrypted] = input.split(SEPARATOR)
const saltBuffer = Buffer.from(salt, "hex")

View file

@ -1,5 +1,4 @@
import { getAllApps, queryGlobalView } from "../db"
import { options } from "../middleware/passport/jwt"
import {
Header,
MAX_VALID_DATE,
@ -133,7 +132,30 @@ export function openJwt(token: string) {
if (!token) {
return token
}
return jwt.verify(token, options.secretOrKey)
try {
return jwt.verify(token, env.JWT_SECRET)
} catch (e) {
if (env.JWT_SECRET_FALLBACK) {
// fallback to enable rotation
return jwt.verify(token, env.JWT_SECRET_FALLBACK)
} else {
throw e
}
}
}
export function isValidInternalAPIKey(apiKey: string) {
if (env.INTERNAL_API_KEY && env.INTERNAL_API_KEY === apiKey) {
return true
}
// fallback to enable rotation
if (
env.INTERNAL_API_KEY_FALLBACK &&
env.INTERNAL_API_KEY_FALLBACK === apiKey
) {
return true
}
return false
}
/**
@ -165,7 +187,7 @@ export function setCookie(
opts = { sign: true }
) {
if (value && opts && opts.sign) {
value = jwt.sign(value, options.secretOrKey)
value = jwt.sign(value, env.JWT_SECRET)
}
const config: SetOption = {

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@ -38,8 +38,8 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/shared-core": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "^2.4.26",
"@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1",

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"license": "GPL-3.0",
"private": true,
"scripts": {
@ -58,11 +58,11 @@
}
},
"dependencies": {
"@budibase/bbui": "2.4.12-alpha.5",
"@budibase/client": "2.4.12-alpha.5",
"@budibase/frontend-core": "2.4.12-alpha.5",
"@budibase/shared-core": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/bbui": "^2.4.26",
"@budibase/client": "^2.4.26",
"@budibase/frontend-core": "^2.4.26",
"@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "^2.4.26",
"@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1",

View file

@ -74,6 +74,14 @@
}
return capitalise(name)
}
function getDisplayError(error, configKey) {
return error?.replace(
new RegExp(`${configKey}`, "i"),
getDisplayName(configKey)
)
}
function getFieldGroupKeys(fieldGroup) {
return Object.entries(schema[fieldGroup].fields || {})
.filter(el => filter(el))
@ -147,7 +155,7 @@
type={schema[configKey].type}
on:change
bind:value={config[configKey]}
error={$validation.errors[configKey]}
error={getDisplayError($validation.errors[configKey], configKey)}
/>
</div>
{:else if schema[configKey].type === "fieldGroup"}
@ -180,7 +188,7 @@
type={configKey === "port" ? "string" : schema[configKey].type}
on:change
bind:value={config[configKey]}
error={$validation.errors[configKey]}
error={getDisplayError($validation.errors[configKey], configKey)}
environmentVariablesEnabled={$licensing.environmentVariablesEnabled}
{handleUpgradePanel}
/>

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "dist/index.js",
"bin": {
@ -29,9 +29,9 @@
"outputPath": "build"
},
"dependencies": {
"@budibase/backend-core": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/types": "2.4.12-alpha.5",
"@budibase/backend-core": "^2.4.26",
"@budibase/string-templates": "^2.4.26",
"@budibase/types": "^2.4.26",
"axios": "0.21.2",
"chalk": "4.1.0",
"cli-progress": "3.11.2",

View file

@ -13,6 +13,7 @@ export const ENV_PATH = path.resolve("./.env")
function getSecrets(opts = { single: false }) {
const secrets = [
"API_ENCRYPTION_KEY",
"JWT_SECRET",
"MINIO_ACCESS_KEY",
"MINIO_SECRET_KEY",

View file

@ -4,6 +4,8 @@ import {
downloadDockerCompose,
handleError,
getServices,
getServiceImage,
setServiceImage,
} from "./utils"
import { confirmation } from "../questions"
import compose from "docker-compose"
@ -23,7 +25,11 @@ export async function update() {
!isSingle &&
(await confirmation("Do you wish to update you docker-compose.yaml?"))
) {
// get current MinIO image
const image = await getServiceImage("minio")
await downloadDockerCompose()
// replace MinIO image
setServiceImage("minio", image)
}
await handleError(async () => {
const status = await compose.ps()

View file

@ -9,10 +9,44 @@ const ERROR_FILE = "docker-error.log"
const COMPOSE_URL =
"https://raw.githubusercontent.com/Budibase/budibase/master/hosting/docker-compose.yaml"
export async function downloadDockerCompose() {
const fileName = COMPOSE_URL.split("/").slice(-1)[0]
function composeFilename() {
return COMPOSE_URL.split("/").slice(-1)[0]
}
export function getServiceImage(service: string) {
const filename = composeFilename()
try {
await downloadFile(COMPOSE_URL, `./${fileName}`)
const { services } = getServices(filename)
const serviceKey = Object.keys(services).find(name =>
name.includes(service)
)
if (serviceKey) {
return services[serviceKey].image
} else {
return null
}
} catch (err) {
return null
}
}
export function setServiceImage(service: string, image: string) {
const filename = composeFilename()
if (!fs.existsSync(filename)) {
throw new Error(
`File ${filename} not found, cannot update ${service} image.`
)
}
const current = getServiceImage(service)!
let contents = fs.readFileSync(filename, "utf8")
contents = contents.replace(`image: ${current}`, `image: ${image}`)
fs.writeFileSync(filename, contents)
}
export async function downloadDockerCompose() {
const filename = composeFilename()
try {
await downloadFile(COMPOSE_URL, `./${filename}`)
} catch (err) {
console.error(error(`Failed to retrieve compose file - ${err}`))
}
@ -49,6 +83,9 @@ export async function handleError(func: Function) {
}
export function getServices(path: string) {
if (!fs.existsSync(path)) {
throw new Error(`No yaml found at path: ${path}`)
}
const dockerYaml = fs.readFileSync(path, "utf8")
const parsedYaml = yaml.parse(dockerYaml)
return { yaml: parsedYaml, services: parsedYaml.services }

View file

@ -17,10 +17,7 @@
"description": "This component is specific only to layouts",
"icon": "Sandbox",
"hasChildren": true,
"styles": [
"padding",
"background"
],
"styles": ["padding", "background"],
"settings": [
{
"type": "text",
@ -36,23 +33,14 @@
"type": "select",
"label": "Navigation",
"key": "navigation",
"options": [
"Top",
"Left",
"None"
],
"options": ["Top", "Left", "None"],
"defaultValue": "Top"
},
{
"type": "select",
"label": "Width",
"key": "width",
"options": [
"Small",
"Medium",
"Large",
"Max"
],
"options": ["Small", "Medium", "Large", "Max"],
"defaultValue": "Large"
},
{
@ -89,13 +77,7 @@
"width": 400,
"height": 200
},
"styles": [
"padding",
"size",
"background",
"border",
"shadow"
],
"styles": ["padding", "size", "background", "border", "shadow"],
"settings": [
{
"type": "select",
@ -255,9 +237,7 @@
"description": "Add a section to your application",
"icon": "ColumnTwoB",
"hasChildren": true,
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"showEmptyState": false,
"size": {
"width": 400,
@ -376,9 +356,7 @@
"name": "Divider",
"description": "A basic divider",
"icon": "Separator",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"size": {
"width": 400,
"height": 10
@ -415,9 +393,7 @@
"name": "Repeater",
"description": "A configurable data list that attaches to your backend tables.",
"icon": "JourneyData",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"hasChildren": true,
"size": {
"width": 400,
@ -574,9 +550,7 @@
"name": "Stacked List",
"icon": "TaskList",
"description": "A basic card component that can contain content and actions.",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"settings": [
{
"type": "text",
@ -606,9 +580,7 @@
"name": "Vertical Card",
"description": "A basic card component that can contain content and actions.",
"icon": "ViewColumn",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"settings": [
{
"type": "text",
@ -652,24 +624,14 @@
"type": "select",
"label": "Image Height",
"key": "imageHeight",
"options": [
"auto",
"12rem",
"16rem",
"20rem",
"24rem"
],
"options": ["auto", "12rem", "16rem", "20rem", "24rem"],
"defaultValue": "auto"
},
{
"type": "select",
"label": "Card Width",
"key": "cardWidth",
"options": [
"16rem",
"20rem",
"24rem"
],
"options": ["16rem", "20rem", "24rem"],
"defaultValue": "20rem"
}
]
@ -678,9 +640,7 @@
"name": "Paragraph",
"description": "A component for displaying paragraph text.",
"icon": "TextParagraph",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"editable": true,
"size": {
"width": 400,
@ -803,9 +763,7 @@
"name": "Headline",
"icon": "TextBold",
"description": "A component for displaying heading text",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"editable": true,
"size": {
"width": 400,
@ -982,9 +940,7 @@
"name": "Image",
"description": "A basic component for displaying images",
"icon": "Image",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 400,
"height": 300
@ -1002,9 +958,8 @@
"name": "Background Image",
"description": "A background image",
"icon": "Images",
"styles": [
"size"
],
"hasChildren": true,
"styles": ["size"],
"size": {
"width": 400,
"height": 300
@ -1162,9 +1117,7 @@
"name": "Nav Bar",
"description": "A component for handling the navigation within your app.",
"icon": "BreadcrumbNavigation",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"hasChildren": true,
"settings": [
{
@ -1365,25 +1318,14 @@
"type": "select",
"label": "Image Width",
"key": "imageWidth",
"options": [
"auto",
"8rem",
"12rem",
"16rem"
],
"options": ["auto", "8rem", "12rem", "16rem"],
"defaultValue": "8rem"
},
{
"type": "select",
"label": "Image Height",
"key": "imageHeight",
"options": [
"auto",
"8rem",
"12rem",
"16rem",
"auto"
],
"options": ["auto", "8rem", "12rem", "16rem", "auto"],
"defaultValue": "auto"
}
]
@ -1424,9 +1366,7 @@
"name": "Embed",
"icon": "Code",
"description": "Embed content from 3rd party sources",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 400,
"height": 100
@ -1478,11 +1418,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -1640,11 +1576,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -1736,11 +1668,7 @@
"type": "select",
"label": "Curve",
"key": "curve",
"options": [
"Smooth",
"Straight",
"Stepline"
],
"options": ["Smooth", "Straight", "Stepline"],
"defaultValue": "Smooth"
},
{
@ -1801,11 +1729,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -1897,11 +1821,7 @@
"type": "select",
"label": "Curve",
"key": "curve",
"options": [
"Smooth",
"Straight",
"Stepline"
],
"options": ["Smooth", "Straight", "Stepline"],
"defaultValue": "Smooth"
},
{
@ -2253,11 +2173,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -2293,19 +2209,14 @@
"name": "Form",
"icon": "Form",
"hasChildren": true,
"illegalChildren": [
"section",
"form"
],
"illegalChildren": ["section", "form"],
"actions": [
"ValidateForm",
"ClearForm",
"ChangeFormStep",
"UpdateFieldValue"
],
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 400,
"height": 400
@ -2315,10 +2226,7 @@
"type": "select",
"label": "Type",
"key": "actionType",
"options": [
"Create",
"Update"
],
"options": ["Create", "Update"],
"defaultValue": "Create"
},
{
@ -2388,14 +2296,8 @@
"name": "Form Step",
"icon": "AssetsAdded",
"hasChildren": true,
"illegalChildren": [
"section",
"form",
"form step"
],
"styles": [
"size"
],
"illegalChildren": ["section", "form", "form step"],
"styles": ["size"],
"size": {
"width": 400,
"height": 400
@ -2413,12 +2315,8 @@
"fieldgroup": {
"name": "Field Group",
"icon": "Group",
"illegalChildren": [
"section"
],
"styles": [
"size"
],
"illegalChildren": ["section"],
"styles": ["size"],
"hasChildren": true,
"size": {
"width": 400,
@ -2451,9 +2349,7 @@
"skeleton": false,
"name": "Text Field",
"icon": "Text",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -2543,9 +2439,7 @@
"skeleton": false,
"name": "Number Field",
"icon": "123",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -2601,9 +2495,7 @@
"skeleton": false,
"name": "Password Field",
"icon": "LockClosed",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -2659,9 +2551,7 @@
"skeleton": false,
"name": "Options Picker",
"icon": "Menu",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -2828,9 +2718,7 @@
"skeleton": false,
"name": "Multi-select Picker",
"icon": "ViewList",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3070,9 +2958,7 @@
"skeleton": false,
"name": "Long Form Field",
"icon": "TextAlignLeft",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3150,9 +3036,7 @@
"skeleton": false,
"name": "Date Picker",
"icon": "Date",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3232,9 +3116,7 @@
"skeleton": false,
"name": "Barcode/QR Scanner",
"icon": "Camera",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 400,
"height": 50
@ -3283,9 +3165,7 @@
"embeddedmap": {
"name": "Embedded Map",
"icon": "Location",
"styles": [
"size"
],
"styles": ["size"],
"draggable": false,
"size": {
"width": 400,
@ -3398,9 +3278,7 @@
"skeleton": false,
"name": "Attachment",
"icon": "Attach",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3463,9 +3341,7 @@
"skeleton": false,
"name": "Relationship Picker",
"icon": "TaskList",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3527,9 +3403,7 @@
"skeleton": false,
"name": "JSON Field",
"icon": "Brackets",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3579,9 +3453,7 @@
"s3upload": {
"name": "S3 File Upload",
"icon": "UploadToCloud",
"styles": [
"size"
],
"styles": ["size"],
"editable": true,
"size": {
"width": 400,
@ -3642,13 +3514,9 @@
"dataprovider": {
"name": "Data Provider",
"icon": "Data",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"hasChildren": true,
"actions": [
"RefreshDatasource"
],
"actions": ["RefreshDatasource"],
"size": {
"width": 400,
"height": 100
@ -3674,10 +3542,7 @@
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"options": ["Ascending", "Descending"],
"defaultValue": "Ascending"
},
{
@ -3729,9 +3594,7 @@
"skeleton": false,
"name": "Table",
"icon": "Table",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"hasChildren": true,
"showEmptyState": false,
"size": {
@ -3815,9 +3678,7 @@
"daterangepicker": {
"name": "Date Range",
"icon": "Calendar",
"styles": [
"size"
],
"styles": ["size"],
"hasChildren": false,
"size": {
"width": 200,
@ -3856,9 +3717,7 @@
"spectrumcard": {
"name": "Card",
"icon": "PersonalizationField",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 300,
"height": 120
@ -4031,10 +3890,7 @@
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"options": ["Ascending", "Descending"],
"defaultValue": "Ascending"
},
{
@ -4213,11 +4069,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -4271,11 +4123,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -4292,11 +4140,7 @@
"type": "select",
"label": "Curve",
"key": "curve",
"options": [
"Smooth",
"Straight",
"Stepline"
],
"options": ["Smooth", "Straight", "Stepline"],
"defaultValue": "Smooth"
}
]
@ -4328,11 +4172,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -4349,11 +4189,7 @@
"type": "select",
"label": "Curve",
"key": "curve",
"options": [
"Smooth",
"Straight",
"Stepline"
],
"options": ["Smooth", "Straight", "Stepline"],
"defaultValue": "Smooth"
},
{
@ -4418,11 +4254,7 @@
"type": "select",
"label": "Format",
"key": "yAxisUnits",
"options": [
"Default",
"Thousands",
"Millions"
],
"options": ["Default", "Thousands", "Millions"],
"defaultValue": "Default"
},
{
@ -4443,9 +4275,7 @@
"block": true,
"name": "Table block",
"icon": "Table",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 600,
"height": 400
@ -4483,10 +4313,7 @@
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"options": ["Ascending", "Descending"],
"defaultValue": "Ascending"
},
{
@ -4638,9 +4465,7 @@
"block": true,
"name": "Cards block",
"icon": "PersonalizationField",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 600,
"height": 400
@ -4679,10 +4504,7 @@
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"options": ["Ascending", "Descending"],
"defaultValue": "Descending"
},
{
@ -4816,9 +4638,7 @@
"block": true,
"name": "Repeater block",
"icon": "ViewList",
"illegalChildren": [
"section"
],
"illegalChildren": ["section"],
"hasChildren": true,
"size": {
"width": 400,
@ -4846,10 +4666,7 @@
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"options": ["Ascending", "Descending"],
"defaultValue": "Descending"
},
{
@ -5044,9 +4861,7 @@
"markdownviewer": {
"name": "Markdown Viewer",
"icon": "Preview",
"styles": [
"size"
],
"styles": ["size"],
"size": {
"width": 400,
"height": 100
@ -5063,9 +4878,7 @@
"formblock": {
"name": "Form Block",
"icon": "Form",
"styles": [
"size"
],
"styles": ["size"],
"block": true,
"info": "Form blocks are only compatible with internal or SQL tables",
"size": {
@ -5077,11 +4890,7 @@
"type": "select",
"label": "Type",
"key": "actionType",
"options": [
"Create",
"Update",
"View"
],
"options": ["Create", "Update", "View"],
"defaultValue": "Create"
},
{
@ -5215,10 +5024,7 @@
"name": "Side Panel",
"icon": "RailRight",
"hasChildren": true,
"illegalChildren": [
"section",
"sidepanel"
],
"illegalChildren": ["section", "sidepanel"],
"showEmptyState": false,
"draggable": false,
"info": "Side panels are hidden by default. They will only be revealed when triggered by the 'Open Side Panel' action."
@ -5307,4 +5113,4 @@
"suffix": "repeater"
}
}
}
}

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/client",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"license": "MPL-2.0",
"module": "dist/budibase-client.js",
"main": "dist/budibase-client.js",
@ -19,11 +19,11 @@
"dev:builder": "rollup -cw"
},
"dependencies": {
"@budibase/bbui": "2.4.12-alpha.5",
"@budibase/frontend-core": "2.4.12-alpha.5",
"@budibase/shared-core": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/types": "2.4.12-alpha.5",
"@budibase/bbui": "^2.4.26",
"@budibase/frontend-core": "^2.4.26",
"@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "^2.4.26",
"@budibase/types": "^2.4.26",
"@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3",

View file

@ -21,7 +21,9 @@
{#if url}
<div class="outer" use:styleable={$component.styles}>
<div class="inner" {style} />
<div class="inner" {style}>
<slot />
</div>
</div>
{:else if $builderStore.inBuilder}
<div

View file

@ -1,13 +1,13 @@
{
"name": "@budibase/frontend-core",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase",
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "2.4.12-alpha.5",
"@budibase/shared-core": "2.4.12-alpha.5",
"@budibase/bbui": "^2.4.26",
"@budibase/shared-core": "^2.4.26",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/sdk",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase Public API SDK",
"author": "Budibase",
"license": "MPL-2.0",

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@ -43,12 +43,12 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.4.12-alpha.5",
"@budibase/client": "2.4.12-alpha.5",
"@budibase/pro": "2.4.12-alpha.5",
"@budibase/shared-core": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/types": "2.4.12-alpha.5",
"@budibase/backend-core": "^2.4.26",
"@budibase/client": "^2.4.26",
"@budibase/pro": "2.4.26",
"@budibase/shared-core": "^2.4.26",
"@budibase/string-templates": "^2.4.26",
"@budibase/types": "^2.4.26",
"@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0",

View file

@ -29,13 +29,6 @@ router
br: false,
})
)
.use(async (ctx, next) => {
ctx.config = {
jwtSecret: env.JWT_SECRET,
useAppRootPath: true,
}
await next()
})
// re-direct before any middlewares occur
.redirect("/", "/builder")
.use(

View file

@ -39,17 +39,14 @@ let inThread = false
const environment = {
// important - prefer app port to generic port
PORT: process.env.APP_PORT || process.env.PORT,
JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL,
MINIO_URL: process.env.MINIO_URL,
WORKER_URL: process.env.WORKER_URL,
AWS_REGION: process.env.AWS_REGION,
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
CDN_URL: process.env.CDN_URL || "https://cdn.budi.live",
REDIS_URL: process.env.REDIS_URL,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
HTTP_MIGRATIONS: process.env.HTTP_MIGRATIONS,
API_REQ_LIMIT_PER_SEC: process.env.API_REQ_LIMIT_PER_SEC,
GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,

View file

@ -67,6 +67,7 @@ const SCHEMA: Integration = {
database: {
type: DatasourceFieldType.STRING,
required: true,
display: "Service Name",
},
user: {
type: DatasourceFieldType.STRING,

View file

@ -107,6 +107,7 @@ const SCHEMA: Integration = {
readCsv: {
displayName: "Read CSV",
type: QueryType.FIELDS,
readable: true,
fields: {
bucket: {
type: DatasourceFieldType.STRING,

View file

@ -205,7 +205,6 @@ class TestConfiguration {
request.appId = appId
// fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { appId, tenantId: this.getTenantId() }
request.query = {}
request.request = {
@ -332,8 +331,8 @@ class TestConfiguration {
roleId: roleId,
appId,
}
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET)
const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
// returning necessary request headers
await cache.user.invalidateUser(userId)
@ -361,8 +360,8 @@ class TestConfiguration {
roleId: roles.BUILTIN_ROLE_IDS.ADMIN,
appId: this.appId,
}
const authToken = auth.jwt.sign(authObj, env.JWT_SECRET)
const appToken = auth.jwt.sign(app, env.JWT_SECRET)
const authToken = auth.jwt.sign(authObj, coreEnv.JWT_SECRET)
const appToken = auth.jwt.sign(app, coreEnv.JWT_SECRET)
const headers: any = {
Accept: "application/json",
Cookie: [

View file

@ -6,6 +6,7 @@ import {
constants,
tenancy,
logging,
env as coreEnv,
} from "@budibase/backend-core"
import { updateAppRole } from "./global"
import { BBContext, User } from "@budibase/types"
@ -15,7 +16,7 @@ export function request(ctx?: BBContext, request?: any) {
request.headers = {}
}
if (!ctx) {
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY
request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
}

View file

@ -1278,14 +1278,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.5.tgz#6fc37b439e05f0806909954c5c9f01f37e99f4d8"
integrity sha512-TVXjKXT/67ZWK3L6Rs1eJ1+8li4o3+zxOisVuSzgAHTepm6tbF9GLNWIVlzMoLGh5k9M9GHjCkhRKmxozMrBYw==
"@budibase/backend-core@2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4"
integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow==
dependencies:
"@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.12-alpha.5"
"@budibase/types" "^2.4.26"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
@ -1417,14 +1417,14 @@
pouchdb-promise "^6.0.4"
through2 "^2.0.0"
"@budibase/pro@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.5.tgz#ebdaf6fe987a35c9dee00a36bbcf5acb738015de"
integrity sha512-j749G4I9NHnEE+0AlFckFjBa3Hkx8M93Raw5s+C7YxaPpChws2HfN/7fCSgY33aeCCGqB0SpwCKAm48BSwbwwQ==
"@budibase/pro@2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f"
integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w==
dependencies:
"@budibase/backend-core" "2.4.12-alpha.5"
"@budibase/backend-core" "2.4.26"
"@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.12-alpha.5"
"@budibase/types" "2.4.26"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -1463,10 +1463,10 @@
lodash "^4.17.20"
vm2 "^3.9.4"
"@budibase/types@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.5.tgz#3727ddef178aebb354e43de0efe03a329b37b91f"
integrity sha512-ddtKzLjNcqdQjwYv1lNRo1t5XHdxiHRsFl+xMFsMwpB/8IY8LDw7zvkoC58sFYPUvOP4c1cBA0Wne9YNxM5IiA==
"@budibase/types@2.4.26", "@budibase/types@^2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94"
integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag==
"@bull-board/api@3.7.0":
version "3.7.0"

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/shared-core",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Shared data utils",
"main": "dist/cjs/src/index.js",
"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\""
},
"dependencies": {
"@budibase/types": "2.4.12-alpha.5"
"@budibase/types": "^2.4.26"
},
"devDependencies": {
"concurrently": "^7.6.0",

View file

@ -62,7 +62,7 @@ export const getValidOperatorsForType = (
// Only allow equal/not equal for _id in SQL tables
if (field === "_id" && externalTable) {
ops = [Op.Equals, Op.NotEquals]
ops = [Op.Equals, Op.NotEquals, Op.In]
}
return ops

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/string-templates",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs",
"module": "dist/bundle.mjs",

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/types",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase types",
"main": "dist/cjs/index.js",
"types": "dist/mjs/index.d.ts",

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "2.4.12-alpha.5",
"version": "2.4.26",
"description": "Budibase background service",
"main": "src/index.ts",
"repository": {
@ -36,10 +36,10 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
"@budibase/backend-core": "2.4.12-alpha.5",
"@budibase/pro": "2.4.12-alpha.5",
"@budibase/string-templates": "2.4.12-alpha.5",
"@budibase/types": "2.4.12-alpha.5",
"@budibase/backend-core": "^2.4.26",
"@budibase/pro": "2.4.26",
"@budibase/string-templates": "^2.4.26",
"@budibase/types": "^2.4.26",
"@koa/router": "8.0.8",
"@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2",

View file

@ -295,7 +295,7 @@ export async function publicSettings(
// google
const googleConfig = await configs.getGoogleConfig()
const preActivated = googleConfig?.activated == null
const preActivated = googleConfig && googleConfig.activated == null
const google = preActivated || !!googleConfig?.activated
const _googleCallbackUrl = await googleCallbackUrl(googleConfig)

View file

@ -288,7 +288,7 @@ describe("configs", () => {
company: "Budibase",
logoUrl: "",
analyticsEnabled: false,
google: true,
google: false,
googleCallbackUrl: `http://localhost:10000/api/global/auth/${config.tenantId}/google/callback`,
isSSOEnforced: false,
oidc: false,

View file

@ -30,10 +30,8 @@ const environment = {
// auth
MINIO_ACCESS_KEY: process.env.MINIO_ACCESS_KEY,
MINIO_SECRET_KEY: process.env.MINIO_SECRET_KEY,
JWT_SECRET: process.env.JWT_SECRET,
SALT_ROUNDS: process.env.SALT_ROUNDS,
REDIS_PASSWORD: process.env.REDIS_PASSWORD,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
// urls
MINIO_URL: process.env.MINIO_URL,
@ -42,7 +40,6 @@ const environment = {
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
PLATFORM_URL: process.env.PLATFORM_URL,
APPS_URL: process.env.APPS_URL,
CDN_URL: process.env.CDN_URL || "https://tenants.cdn.budi.live",
// ports
// prefer worker port to generic port
PORT: process.env.WORKER_PORT || process.env.PORT,

View file

@ -1,5 +1,5 @@
import env from "../environment"
import { constants } from "@budibase/backend-core"
import { constants, utils } from "@budibase/backend-core"
import { BBContext } from "@budibase/types"
/**
@ -9,7 +9,15 @@ import { BBContext } from "@budibase/types"
export default async (ctx: BBContext, next: any) => {
if (!env.SELF_HOSTED && !env.DISABLE_ACCOUNT_PORTAL) {
const apiKey = ctx.request.headers[constants.Header.API_KEY]
if (apiKey !== env.INTERNAL_API_KEY) {
if (!apiKey) {
ctx.throw(403, "Unauthorized")
}
if (Array.isArray(apiKey)) {
ctx.throw(403, "Unauthorized")
}
if (!utils.isValidInternalAPIKey(apiKey)) {
ctx.throw(403, "Unauthorized")
}
}

View file

@ -5,10 +5,10 @@ import {
sessions,
events,
HTTPError,
env as coreEnv,
} from "@budibase/backend-core"
import { PlatformLogoutOpts, User } from "@budibase/types"
import jwt from "jsonwebtoken"
import env from "../../environment"
import * as userSdk from "../users"
import * as emails from "../../utilities/email"
import * as redis from "../../utilities/redis"
@ -26,7 +26,7 @@ export async function loginUser(user: User) {
sessionId,
tenantId,
},
env.JWT_SECRET!
coreEnv.JWT_SECRET!
)
return token
}

View file

@ -74,7 +74,6 @@ class TestConfiguration {
const request: any = {}
// fake cookies, we don't need them
request.cookies = { set: () => {}, get: () => {} }
request.config = { jwtSecret: env.JWT_SECRET }
request.user = { tenantId: this.getTenantId() }
request.query = {}
request.request = {
@ -180,7 +179,7 @@ class TestConfiguration {
sessionId: "sessionid",
tenantId: user.tenantId,
}
const authCookie = auth.jwt.sign(authToken, env.JWT_SECRET)
const authCookie = auth.jwt.sign(authToken, coreEnv.JWT_SECRET)
return {
Accept: "application/json",
...this.cookieHeader([`${constants.Cookie.Auth}=${authCookie}`]),
@ -197,7 +196,7 @@ class TestConfiguration {
}
internalAPIHeaders() {
return { [constants.Header.API_KEY]: env.INTERNAL_API_KEY }
return { [constants.Header.API_KEY]: coreEnv.INTERNAL_API_KEY }
}
adminOnlyResponse = () => {
@ -277,7 +276,7 @@ class TestConfiguration {
// CONFIGS - OIDC
getOIDConfigCookie(configId: string) {
const token = auth.jwt.sign(configId, env.JWT_SECRET)
const token = auth.jwt.sign(configId, coreEnv.JWT_SECRET)
return this.cookieHeader([[`${constants.Cookie.OIDC_CONFIG}=${token}`]])
}

View file

@ -1,5 +1,10 @@
import fetch from "node-fetch"
import { constants, tenancy, logging } from "@budibase/backend-core"
import {
constants,
tenancy,
logging,
env as coreEnv,
} from "@budibase/backend-core"
import { checkSlashesInUrl } from "../utilities"
import env from "../environment"
import { SyncUserRequest, User } from "@budibase/types"
@ -9,7 +14,7 @@ async function makeAppRequest(url: string, method: string, body: any) {
return
}
const request: any = { headers: {} }
request.headers[constants.Header.API_KEY] = env.INTERNAL_API_KEY
request.headers[constants.Header.API_KEY] = coreEnv.INTERNAL_API_KEY
if (tenancy.isTenantIdSet()) {
request.headers[constants.Header.TENANT_ID] = tenancy.getTenantId()
}

View file

@ -475,14 +475,14 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.12-alpha.5.tgz#6fc37b439e05f0806909954c5c9f01f37e99f4d8"
integrity sha512-TVXjKXT/67ZWK3L6Rs1eJ1+8li4o3+zxOisVuSzgAHTepm6tbF9GLNWIVlzMoLGh5k9M9GHjCkhRKmxozMrBYw==
"@budibase/backend-core@2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-2.4.26.tgz#ae9679f20e86ce1706d6d549aed78a342365a4b4"
integrity sha512-9QYJbAT9WPiOckBIR6a/CoqqbUiP9vlmc/Iy5TR5Yj2wy1JnWsf09ReTuL3CsHmh+8bCJlUHZZC4m6PUMg7+ow==
dependencies:
"@budibase/nano" "10.1.2"
"@budibase/pouchdb-replication-stream" "1.2.10"
"@budibase/types" "2.4.12-alpha.5"
"@budibase/types" "^2.4.26"
"@shopify/jest-koa-mocks" "5.0.1"
"@techpass/passport-openidconnect" "0.3.2"
aws-cloudfront-sign "2.2.0"
@ -564,14 +564,14 @@
pouchdb-promise "^6.0.4"
through2 "^2.0.0"
"@budibase/pro@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.12-alpha.5.tgz#ebdaf6fe987a35c9dee00a36bbcf5acb738015de"
integrity sha512-j749G4I9NHnEE+0AlFckFjBa3Hkx8M93Raw5s+C7YxaPpChws2HfN/7fCSgY33aeCCGqB0SpwCKAm48BSwbwwQ==
"@budibase/pro@2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.4.26.tgz#37ca2b94f5dfc28ee4ff0ffa088e29112de5b66f"
integrity sha512-PXpsj5DFnUaSlp3AHZRZa/N4CD02HPpvVFv35/FUGkeGwGJ5AihhmzxlD54U9Q9X3Ln8miejYTFoWvEnV5Ei8w==
dependencies:
"@budibase/backend-core" "2.4.12-alpha.5"
"@budibase/backend-core" "2.4.26"
"@budibase/string-templates" "2.3.20"
"@budibase/types" "2.4.12-alpha.5"
"@budibase/types" "2.4.26"
"@koa/router" "8.0.8"
bull "4.10.1"
joi "17.6.0"
@ -592,10 +592,10 @@
lodash "^4.17.20"
vm2 "^3.9.4"
"@budibase/types@2.4.12-alpha.5":
version "2.4.12-alpha.5"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.12-alpha.5.tgz#3727ddef178aebb354e43de0efe03a329b37b91f"
integrity sha512-ddtKzLjNcqdQjwYv1lNRo1t5XHdxiHRsFl+xMFsMwpB/8IY8LDw7zvkoC58sFYPUvOP4c1cBA0Wne9YNxM5IiA==
"@budibase/types@2.4.26", "@budibase/types@^2.4.26":
version "2.4.26"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.26.tgz#c4efd9286e736feee56d623c21a9f6fd7c922b94"
integrity sha512-q2QfDXJAopmHNq6Y25udmVJoEtnoskZEtaMy5d7/hX4jePJX3QnBd9sjgnAoOeSC3NOuXDjmvcRGtqXz6ao/Ag==
"@cspotcode/source-map-support@^0.8.0":
version "0.8.1"

View file

@ -1,6 +1,5 @@
BB_ADMIN_USER_EMAIL=qa@budibase.com
BB_ADMIN_USER_PASSWORD=budibase
ENCRYPTED_TEST_PUBLIC_API_KEY=a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f
COUCH_DB_URL=http://budibase:budibase@localhost:4567
COUCH_DB_USER=budibase
COUCH_DB_PASSWORD=budibase

View file

@ -1,11 +1,3 @@
const env = require("../src/environment")
env._set("BUDIBASE_SERVER_URL", "http://localhost:4100")
env._set(
"BUDIBASE_PUBLIC_API_KEY",
"a65722f06bee5caeadc5d7ca2f543a43-d610e627344210c643bb726f"
)
// mock all dates to 2020-01-01T00:00:00.000Z
// use tk.reset() to use real dates in individual tests
const MOCK_DATE = new Date("2020-01-01T00:00:00.000Z")

View file

@ -49,14 +49,16 @@ class InternalAPIClient {
// @ts-ignore
const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
if (response.status == 404 || response.status == 500) {
if (
response.status == 404 ||
response.status == 500 ||
response.status == 403
) {
console.error("Error in apiCall")
console.error("Response:")
console.error(response)
console.error("Response body:")
console.error(response.body)
console.error("Request body:")
console.error(requestOptions.body)
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
}
return response
}

View file

@ -44,12 +44,10 @@ class AccountsAPIClient {
const response = await fetch(`${this.host}${url}`, requestOptions)
if (response.status == 404 || response.status == 500) {
console.error("Error in apiCall")
console.error("Response:")
console.error(response)
console.error("Response body:")
console.error(response.body)
console.error("Request body:")
console.error(requestOptions.body)
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
}
return response
}

View file

@ -5,7 +5,8 @@ import { Hosting } from "@budibase/types"
export const generateAccount = (): Partial<NewAccount> => {
const randomGuid = generator.guid()
let tenant: string = "a" + randomGuid
//Needs to start with a letter
let tenant: string = "tenant" + randomGuid
tenant = tenant.replace(/-/g, "")
return {

View file

@ -11,20 +11,32 @@ interface ApiOptions {
class PublicAPIClient {
host: string
apiKey: string
apiKey?: string
tenantName?: string
appId?: string
cookie?: string
constructor(appId?: string) {
if (!env.BUDIBASE_PUBLIC_API_KEY || !env.BUDIBASE_SERVER_URL) {
if (!env.BUDIBASE_HOST) {
throw new Error(
"Must set BUDIBASE_PUBLIC_API_KEY and BUDIBASE_SERVER_URL env vars"
)
}
this.host = `${env.BUDIBASE_SERVER_URL}/api/public/v1`
this.apiKey = env.BUDIBASE_PUBLIC_API_KEY
this.host = `${env.BUDIBASE_HOST}/api/public/v1`
this.appId = appId
}
setTenantName(tenantName: string) {
this.tenantName = tenantName
}
setApiKey(apiKey: string) {
this.apiKey = apiKey
process.env.BUDIBASE_PUBLIC_API_KEY = apiKey
this.host = `${env.BUDIBASE_HOST}/api/public/v1`
}
apiCall =
(method: APIMethod) =>
async (url = "", options: ApiOptions = {}) => {
@ -32,18 +44,27 @@ class PublicAPIClient {
method,
body: JSON.stringify(options.body),
headers: {
"x-budibase-api-key": this.apiKey,
"x-budibase-api-key": this.apiKey || null,
"x-budibase-app-id": this.appId,
"Content-Type": "application/json",
Accept: "application/json",
...options.headers,
cookie: this.cookie,
redirect: "follow",
follow: 20,
},
}
// prettier-ignore
// @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions)
if (response.status !== 200) {
console.error(response)
const response = await fetch(`https://${process.env.TENANT_ID}.${this.host}${url}`, requestOptions)
if (response.status == 500 || response.status == 403) {
console.error("Error in apiCall")
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
}
return response
}

View file

@ -0,0 +1,38 @@
import { Response } from "node-fetch"
import { Account } from "@budibase/types"
import AccountsAPIClient from "./accountsAPIClient"
import { NewAccount } from "../fixtures/types/newAccount"
export default class AccountsApi {
api: AccountsAPIClient
constructor(AccountsAPIClient: AccountsAPIClient) {
this.api = AccountsAPIClient
}
async validateEmail(email: string): Promise<Response> {
const response = await this.api.post(`/accounts/validate/email`, {
body: { email },
})
expect(response).toHaveStatusCode(200)
return response
}
async validateTenantId(tenantId: string): Promise<Response> {
const response = await this.api.post(`/accounts/validate/tenantId`, {
body: { tenantId },
})
expect(response).toHaveStatusCode(200)
return response
}
async create(body: Partial<NewAccount>): Promise<[Response, Account]> {
const headers = {
"no-verify": "1",
}
const response = await this.api.post(`/accounts`, { body, headers })
const json = await response.json()
expect(response).toHaveStatusCode(201)
return [response, json]
}
}

View file

@ -0,0 +1,66 @@
import env from "../../../environment"
import fetch, { HeadersInit } from "node-fetch"
type APIMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE"
interface ApiOptions {
method?: APIMethod
body?: object
headers?: HeadersInit | undefined
}
class AccountsAPIClient {
host: string
appId?: string
cookie?: string
constructor(appId?: string) {
if (!env.BUDIBASE_ACCOUNTS_URL) {
throw new Error("Must set BUDIBASE_SERVER_URL env var")
}
this.host = `${env.BUDIBASE_ACCOUNTS_URL}/api`
this.appId = appId
}
apiCall =
(method: APIMethod) =>
async (url = "", options: ApiOptions = {}) => {
const requestOptions = {
method,
body: JSON.stringify(options.body),
headers: {
"x-budibase-app-id": this.appId,
"Content-Type": "application/json",
Accept: "application/json",
cookie: this.cookie,
redirect: "follow",
follow: 20,
...options.headers,
},
credentials: "include",
}
// @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions)
if (
response.status == 404 ||
response.status == 500 ||
response.status == 400
) {
console.error("Error in apiCall")
console.error("Response:", response)
const json = await response.json()
console.error("Response body:", json)
console.error("Request body:", requestOptions.body)
}
return response
}
post = this.apiCall("POST")
get = this.apiCall("GET")
patch = this.apiCall("PATCH")
del = this.apiCall("DELETE")
put = this.apiCall("PUT")
}
export default AccountsAPIClient

View file

@ -63,4 +63,15 @@ export default class AppApi {
const response = await this.api.post(`/applications/${id}/unpublish`)
return [response]
}
async createFirstApp() {
const body = {
name: "My first app",
url: "my-first-app",
useTemplate: false,
sampleData: true,
}
const response = await this.api.post("/applications", { body })
expect(response).toHaveStatusCode(200)
}
}

View file

@ -0,0 +1,48 @@
import { Response } from "node-fetch"
import AccountsAPIClient from "./accountsAPIClient"
import { ApiKeyResponse } from "../fixtures/types/apiKeyResponse"
export default class AuthApi {
api: AccountsAPIClient
constructor(apiClient: AccountsAPIClient) {
this.api = apiClient
}
async loginAsAdmin(): Promise<[Response, any]> {
const response = await this.api.post(`/auth/login`, {
body: {
username: process.env.BB_ADMIN_USER_EMAIL,
password: process.env.BB_ADMIN_USER_PASSWORD,
},
})
const cookie = response.headers.get("set-cookie")
this.api.cookie = cookie as any
return [response, cookie]
}
async login(email: String, password: String): Promise<[Response, any]> {
const response = await this.api.post(`/global/auth/default/login`, {
body: {
username: email,
password: password,
},
})
expect(response).toHaveStatusCode(200)
const cookie = response.headers.get("set-cookie")
this.api.cookie = cookie as any
return [response, cookie]
}
async logout(): Promise<any> {
return this.api.post(`/global/auth/logout`)
}
async getApiKey(): Promise<ApiKeyResponse> {
const response = await this.api.get(`/global/self/api_key`)
const json = await response.json()
expect(response).toHaveStatusCode(200)
expect(json).toHaveProperty("apiKey")
return json
}
}

View file

@ -3,19 +3,82 @@ import ApplicationApi from "./applications"
import TableApi from "./tables"
import UserApi from "./users"
import RowApi from "./rows"
import AuthApi from "./auth"
import AccountsApiClient from "./accountsAPIClient"
import AccountsApi from "./accounts"
import { generateAccount } from "../fixtures/accounts"
import internalApplicationsApi from "../../internal-api/TestConfiguration/applications"
import InternalAPIClient from "../../internal-api/TestConfiguration/InternalAPIClient"
export default class TestConfiguration<T> {
applications: ApplicationApi
auth: AuthApi
users: UserApi
tables: TableApi
rows: RowApi
context: T
accounts: AccountsApi
apiClient: PublicAPIClient
accountsApiClient: AccountsApiClient
internalApiClient: InternalAPIClient
internalApplicationsApi: internalApplicationsApi
constructor(apiClient: PublicAPIClient) {
constructor(
apiClient: PublicAPIClient,
accountsApiClient: AccountsApiClient,
internalApiClient: InternalAPIClient
) {
this.apiClient = apiClient
this.accountsApiClient = accountsApiClient
this.internalApiClient = internalApiClient
this.auth = new AuthApi(this.internalApiClient)
this.accounts = new AccountsApi(this.accountsApiClient)
this.applications = new ApplicationApi(apiClient)
this.users = new UserApi(apiClient)
this.tables = new TableApi(apiClient)
this.rows = new RowApi(apiClient)
this.internalApplicationsApi = new internalApplicationsApi(
internalApiClient
)
this.context = <T>{}
}
async setupAccountAndTenant() {
// This step is required to create a new account and tenant for the tests, its part of
// the support for running tests in multiple environments.
const account = generateAccount()
await this.accounts.validateEmail(<string>account.email)
await this.accounts.validateTenantId(<string>account.tenantId)
process.env.TENANT_ID = <string>account.tenantId
await this.accounts.create(account)
await this.updateApiClients(<string>account.tenantName)
await this.auth.login(<string>account.email, <string>account.password)
const body = {
name: "My first app",
url: "my-first-app",
useTemplate: false,
sampleData: true,
}
await this.internalApplicationsApi.create(body)
}
// After the account and tenant have been created, we need to get and set the API key for the test
async setApiKey() {
const apiKeyResponse = await this.auth.getApiKey()
this.apiClient.setApiKey(apiKeyResponse.apiKey)
}
async updateApiClients(tenantName: string) {
this.apiClient.setTenantName(tenantName)
this.applications = new ApplicationApi(this.apiClient)
this.rows = new RowApi(this.apiClient)
this.internalApiClient.setTenantName(tenantName)
this.internalApplicationsApi = new internalApplicationsApi(
this.internalApiClient
)
this.auth = new AuthApi(this.internalApiClient)
this.context = <T>{}
}

View file

@ -0,0 +1,22 @@
import { NewAccount } from "./types/newAccount"
import generator from "../../generator"
import { Hosting } from "@budibase/types"
export const generateAccount = (): Partial<NewAccount> => {
const randomGuid = generator.guid()
//Needs to start with a letter
let tenant: string = "tenant" + randomGuid
tenant = tenant.replace(/-/g, "")
return {
email: `qa+${randomGuid}@budibase.com`,
hosting: Hosting.CLOUD,
name: `qa+${randomGuid}@budibase.com`,
password: `${randomGuid}`,
profession: "software_engineer",
size: "10+",
tenantId: `${tenant}`,
tenantName: `${tenant}`,
}
}

View file

@ -0,0 +1,6 @@
export interface ApiKeyResponse {
apiKey: string
createdAt: string
updatedAt: string
userId: string
}

View file

@ -0,0 +1,5 @@
import { Account } from "@budibase/types"
export interface NewAccount extends Account {
password: string
}

View file

@ -1,15 +1,25 @@
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import generateApp from "../../../config/public-api/fixtures/applications"
import { Application } from "@budibase/server/api/controllers/public/mapping/types"
import { db as dbCore } from "@budibase/backend-core"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /applications endpoints", () => {
const api = new PublicAPIClient()
const config = new TestConfiguration<Application>(api)
const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Application>(
api,
accountsAPI,
internalAPI
)
beforeAll(async () => {
await config.beforeAll()
await config.setupAccountAndTenant()
await config.setApiKey()
const [response, app] = await config.applications.seed()
config.context = app
})

View file

@ -2,14 +2,19 @@ import { Row } from "@budibase/server/api/controllers/public/mapping/types"
import { generateRow } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /rows endpoints", () => {
let api = new PublicAPIClient()
const config = new TestConfiguration<Row>(api)
const api = new PublicAPIClient()
const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Row>(api, accountsAPI, internalAPI)
beforeAll(async () => {
await config.beforeAll()
await config.setupAccountAndTenant()
await config.setApiKey()
const [aResp, app] = await config.applications.seed()
config.tables.api.appId = app._id

View file

@ -2,13 +2,19 @@ import { Table } from "@budibase/server/api/controllers/public/mapping/types"
import { generateTable } from "../../../config/public-api/fixtures/tables"
import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /tables endpoints", () => {
let api = new PublicAPIClient()
const config = new TestConfiguration<Table>(api)
const api = new PublicAPIClient()
const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<Table>(api, accountsAPI, internalAPI)
beforeAll(async () => {
await config.beforeAll()
await config.setupAccountAndTenant()
await config.setApiKey()
const [appResp, app] = await config.applications.seed()
config.tables.api.appId = app._id

View file

@ -2,13 +2,18 @@ import TestConfiguration from "../../../config/public-api/TestConfiguration"
import PublicAPIClient from "../../../config/public-api/TestConfiguration/PublicAPIClient"
import generateUser from "../../../config/public-api/fixtures/users"
import { User } from "@budibase/server/api/controllers/public/mapping/types"
import AccountsAPIClient from "../../../config/public-api/TestConfiguration/accountsAPIClient"
import InternalAPIClient from "../../../config/internal-api/TestConfiguration/InternalAPIClient"
describe("Public API - /users endpoints", () => {
const api = new PublicAPIClient()
const config = new TestConfiguration<User>(api)
const accountsAPI = new AccountsAPIClient()
const internalAPI = new InternalAPIClient()
const config = new TestConfiguration<User>(api, accountsAPI, internalAPI)
beforeAll(async () => {
await config.beforeAll()
await config.setupAccountAndTenant()
await config.setApiKey()
const [_, user] = await config.users.seed()
config.context = user
})