diff --git a/charts/budibase/templates/proxy-service-deployment.yaml b/charts/budibase/templates/proxy-service-deployment.yaml index e4825ea5d0..c087627100 100644 --- a/charts/budibase/templates/proxy-service-deployment.yaml +++ b/charts/budibase/templates/proxy-service-deployment.yaml @@ -40,6 +40,24 @@ spec: - image: budibase/proxy:{{ .Values.globals.appVersion | default .Chart.AppVersion }} imagePullPolicy: Always name: proxy-service + livenessProbe: + httpGet: + path: /health + port: {{ .Values.services.proxy.port }} + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + failureThreshold: 2 + timeoutSeconds: 3 + readinessProbe: + httpGet: + path: /health + port: {{ .Values.services.proxy.port }} + initialDelaySeconds: 0 + periodSeconds: 5 + successThreshold: 1 + failureThreshold: 2 + timeoutSeconds: 3 ports: - containerPort: {{ .Values.services.proxy.port }} env: diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index ac35929be1..2fb4c36fa8 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -231,18 +231,33 @@ An overview of the CI pipelines can be found [here](../.github/workflows/README. ### Pro -@budibase/pro is the closed source package that supports licensed features in budibase. By default the package will be pulled from NPM and will not normally need to be touched in local development. If you require to update code inside the pro package it can be cloned to the same root level as budibase, e.g. +@budibase/pro is the closed source package that supports licensed features in budibase. By default the package will be pulled from NPM and will not normally need to be touched in local development. If you need to make an update to pro and have access to the repo, then you can update your submodule within the mono-repo by running `git submodule update --init` - from here you can use normal submodule flow to develop a change within pro. + +Once you have updated to use the pro submodule, it will be linked into all of your local dependencies by NX as with all other monorepo packages. If you have been using the NPM version of `@budibase/pro` then you may need to run a `git reset --hard` to fix all of the pro versions back to `0.0.0` to be monorepo aware. + +From here - to develop a change in pro, you can follow the below flow: ``` -. -|_ budibase -|_ budibase-pro +# enter the pro submodule +cd packages/pro +# get the base branch you are working from (same as monorepo) +git fetch +git checkout +# create a branch, named the same as the branch in your monorepo +git checkout -b +... make changes +# commit the changes you've made, with a message for pro +git commit +# within the monorepo, add the pro reference to your branch, commit it with a message like "Update pro ref" +cd ../.. +git add packages/pro +git commit ``` +From here, you will have created a branch in the pro repository and commited the reference to your branch on the monorepo. When you eventually PR this work back into the mainline branch, you will need to first merge your pro PR to the pro mainline, then go into your PR in the monorepo and update the reference again to the new mainline. + Note that only budibase maintainers will be able to access the pro repo. -By default, NX will make sure that dependencies are replaced with local source aware version. This is achieved using the `yarn link` command. To see specifically how dependencies are linked see [scripts/link-dependencies.sh](../scripts/link-dependencies.sh). The same link script is used to link dependencies to account-portal in local dev. - ### Troubleshooting Sometimes, things go wrong. This can be due to incompatible updates on the budibase platform. To clear down your development environment and start again follow **Step 6. Cleanup**, then proceed from **Step 3. Install and Build** in the setup guide above to create a fresh Budibase installation. diff --git a/lerna.json b/lerna.json index f9c639e296..dd49bd32bb 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "2.8.16-alpha.4", + "version": "2.8.22-alpha.1", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/src/cache/appMetadata.ts b/packages/backend-core/src/cache/appMetadata.ts index 5b66c356d3..0c320ec776 100644 --- a/packages/backend-core/src/cache/appMetadata.ts +++ b/packages/backend-core/src/cache/appMetadata.ts @@ -2,9 +2,14 @@ import { getAppClient } from "../redis/init" import { doWithDB, DocumentType } from "../db" import { Database, App } from "@budibase/types" -const AppState = { - INVALID: "invalid", +export enum AppState { + INVALID = "invalid", } + +export interface DeletedApp { + state: AppState +} + const EXPIRY_SECONDS = 3600 /** @@ -31,7 +36,7 @@ function isInvalid(metadata?: { state: string }) { * @param {string} appId the id of the app to get metadata from. * @returns {object} the app metadata. */ -export async function getAppMetadata(appId: string) { +export async function getAppMetadata(appId: string): Promise { const client = await getAppClient() // try cache let metadata = await client.get(appId) @@ -61,11 +66,8 @@ export async function getAppMetadata(appId: string) { } await client.store(appId, metadata, expiry) } - // we've stored in the cache an object to tell us that it is currently invalid - if (isInvalid(metadata)) { - throw { status: 404, message: "No app metadata found" } - } - return metadata as App + + return metadata } /** diff --git a/packages/backend-core/src/db/utils.ts b/packages/backend-core/src/db/utils.ts index 6034296996..4ebf8392b5 100644 --- a/packages/backend-core/src/db/utils.ts +++ b/packages/backend-core/src/db/utils.ts @@ -2,7 +2,7 @@ import env from "../environment" import { DEFAULT_TENANT_ID, SEPARATOR, DocumentType } from "../constants" import { getTenantId, getGlobalDBName } from "../context" import { doWithDB, directCouchAllDbs } from "./db" -import { getAppMetadata } from "../cache/appMetadata" +import { AppState, DeletedApp, getAppMetadata } from "../cache/appMetadata" import { isDevApp, isDevAppID, getProdAppID } from "../docIds/conversions" import { App, Database } from "@budibase/types" import { getStartEndKeyURL } from "../docIds" @@ -101,7 +101,9 @@ export async function getAllApps({ const response = await Promise.allSettled(appPromises) const apps = response .filter( - (result: any) => result.status === "fulfilled" && result.value != null + (result: any) => + result.status === "fulfilled" && + result.value?.state !== AppState.INVALID ) .map(({ value }: any) => value) if (!all) { @@ -126,7 +128,11 @@ export async function getAppsByIDs(appIds: string[]) { ) // have to list the apps which exist, some may have been deleted return settled - .filter(promise => promise.status === "fulfilled") + .filter( + promise => + promise.status === "fulfilled" && + (promise.value as DeletedApp).state !== AppState.INVALID + ) .map(promise => (promise as PromiseFulfilledResult).value) } diff --git a/packages/backend-core/src/logging/pino/logger.ts b/packages/backend-core/src/logging/pino/logger.ts index f2024db72b..7c444a3a59 100644 --- a/packages/backend-core/src/logging/pino/logger.ts +++ b/packages/backend-core/src/logging/pino/logger.ts @@ -12,29 +12,44 @@ import { localFileDestination } from "../system" let pinoInstance: pino.Logger | undefined if (!env.DISABLE_PINO_LOGGER) { + const level = env.LOG_LEVEL const pinoOptions: LoggerOptions = { - level: env.LOG_LEVEL, + level, formatters: { - level: label => { - return { level: label.toUpperCase() } + level: level => { + return { level: level.toUpperCase() } }, bindings: () => { - return { - service: env.SERVICE_NAME, + if (env.SELF_HOSTED) { + // "service" is being injected in datadog using the pod names, + // so we should leave it blank to allow the default behaviour if it's not running self-hosted + return { + service: env.SERVICE_NAME, + } + } else { + return {} } }, }, timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, } - const destinations: pino.DestinationStream[] = [] + const destinations: pino.StreamEntry[] = [] - if (env.isDev()) { - destinations.push(pinoPretty({ singleLine: true })) - } + destinations.push( + env.isDev() + ? { + stream: pinoPretty({ singleLine: true }), + level: level as pino.Level, + } + : { stream: process.stdout, level: level as pino.Level } + ) if (env.SELF_HOSTED) { - destinations.push(localFileDestination()) + destinations.push({ + stream: localFileDestination(), + level: level as pino.Level, + }) } pinoInstance = destinations.length diff --git a/packages/bbui/src/Drawer/Drawer.svelte b/packages/bbui/src/Drawer/Drawer.svelte index 782c3c6ed2..4ff4df854b 100644 --- a/packages/bbui/src/Drawer/Drawer.svelte +++ b/packages/bbui/src/Drawer/Drawer.svelte @@ -30,6 +30,7 @@ setContext("drawer-actions", { hide, show, + headless, }) const easeInOutQuad = x => { diff --git a/packages/bbui/src/Form/Core/CheckboxGroup.svelte b/packages/bbui/src/Form/Core/CheckboxGroup.svelte index 640d5d99cd..2b8a1e438a 100644 --- a/packages/bbui/src/Form/Core/CheckboxGroup.svelte +++ b/packages/bbui/src/Form/Core/CheckboxGroup.svelte @@ -12,23 +12,24 @@ export let getOptionValue = option => option const dispatch = createEventDispatcher() + const onChange = e => { - let tempValue = value - let isChecked = e.target.checked - if (!tempValue.includes(e.target.value) && isChecked) { - tempValue.push(e.target.value) + const optionValue = e.target.value + if (e.target.checked && !value.includes(optionValue)) { + dispatch("change", [...value, optionValue]) + } else { + dispatch( + "change", + value.filter(x => x !== optionValue) + ) } - value = tempValue - dispatch( - "change", - tempValue.filter(val => val !== e.target.value || isChecked) - ) }
{#if options && Array.isArray(options)} {#each options as option} + {@const optionValue = getOptionValue(option)}
{#if tooltip && showTooltip}
- +
{/if}
@@ -80,15 +80,14 @@ position: absolute; pointer-events: none; left: 50%; - top: calc(100% + 4px); - width: 100vw; - max-width: 150px; + bottom: calc(100% + 4px); transform: translateX(-50%); text-align: center; + z-index: 1; } .spectrum-Icon--sizeXS { - width: 10px; - height: 10px; + width: var(--spectrum-global-dimension-size-150); + height: var(--spectrum-global-dimension-size-150); } diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 3c5b93df89..4569586762 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -109,6 +109,7 @@ {disableSorting} {customPlaceholder} allowEditRows={allowEditing} + allowEditColumns={allowEditing} showAutoColumns={!hideAutocolumns} {allowClickRows} on:clickrelationship={e => selectRelationship(e.detail)} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 208739a540..4761ccee02 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -18,7 +18,7 @@ import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" import { FIELDS, - RelationshipTypes, + RelationshipType, ALLOWABLE_STRING_OPTIONS, ALLOWABLE_NUMBER_OPTIONS, ALLOWABLE_STRING_TYPES, @@ -33,6 +33,7 @@ import { getBindings } from "components/backend/DataTable/formula" import { getContext } from "svelte" import JSONSchemaModal from "./JSONSchemaModal.svelte" + import { ValidColumnNameRegex } from "@budibase/shared-core" const AUTO_TYPE = "auto" const FORMULA_TYPE = FIELDS.FORMULA.type @@ -183,7 +184,7 @@ dispatch("updatecolumns") if ( saveColumn.type === LINK_TYPE && - saveColumn.relationshipType === RelationshipTypes.MANY_TO_MANY + saveColumn.relationshipType === RelationshipType.MANY_TO_MANY ) { // Fetching the new tables tables.fetch() @@ -237,7 +238,7 @@ // Default relationships many to many if (editableColumn.type === LINK_TYPE) { - editableColumn.relationshipType = RelationshipTypes.MANY_TO_MANY + editableColumn.relationshipType = RelationshipType.MANY_TO_MANY } if (editableColumn.type === FORMULA_TYPE) { editableColumn.formulaType = "dynamic" @@ -285,17 +286,17 @@ { name: `Many ${thisName} rows → many ${linkName} rows`, alt: `Many ${table.name} rows → many ${linkTable.name} rows`, - value: RelationshipTypes.MANY_TO_MANY, + value: RelationshipType.MANY_TO_MANY, }, { name: `One ${linkName} row → many ${thisName} rows`, alt: `One ${linkTable.name} rows → many ${table.name} rows`, - value: RelationshipTypes.ONE_TO_MANY, + value: RelationshipType.ONE_TO_MANY, }, { name: `One ${thisName} row → many ${linkName} rows`, alt: `One ${table.name} rows → many ${linkTable.name} rows`, - value: RelationshipTypes.MANY_TO_ONE, + value: RelationshipType.MANY_TO_ONE, }, ] } @@ -375,7 +376,7 @@ const newError = {} if (!external && fieldInfo.name?.startsWith("_")) { newError.name = `Column name cannot start with an underscore.` - } else if (fieldInfo.name && !fieldInfo.name.match(/^[_a-zA-Z0-9\s]*$/g)) { + } else if (fieldInfo.name && !fieldInfo.name.match(ValidColumnNameRegex)) { newError.name = `Illegal character; must be alpha-numeric.` } else if (PROHIBITED_COLUMN_NAMES.some(name => fieldInfo.name === name)) { newError.name = `${PROHIBITED_COLUMN_NAMES.join( diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte index 01964aed75..c18ba313e0 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditUser.svelte @@ -95,9 +95,9 @@ {#if !creating}
A user's email, role, first and last names cannot be changed from within - the app builder. Please go to the user portal to do this. + the app builder. Please go to the + user portal + to do this.
{/if} - import { RelationshipTypes } from "constants/backend" + import { RelationshipType } from "constants/backend" import { keepOpen, Button, @@ -25,11 +25,11 @@ const relationshipTypes = [ { label: "One to Many", - value: RelationshipTypes.MANY_TO_ONE, + value: RelationshipType.MANY_TO_ONE, }, { label: "Many to Many", - value: RelationshipTypes.MANY_TO_MANY, + value: RelationshipType.MANY_TO_MANY, }, ] @@ -58,8 +58,8 @@ value: table._id, })) $: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet() - $: isManyToMany = relationshipType === RelationshipTypes.MANY_TO_MANY - $: isManyToOne = relationshipType === RelationshipTypes.MANY_TO_ONE + $: isManyToMany = relationshipType === RelationshipType.MANY_TO_MANY + $: isManyToOne = relationshipType === RelationshipType.MANY_TO_ONE function getTable(id) { return plusTables.find(table => table._id === id) @@ -116,7 +116,7 @@ function allRequiredAttributesSet() { const base = getTable(fromId) && getTable(toId) && fromColumn && toColumn - if (relationshipType === RelationshipTypes.MANY_TO_ONE) { + if (relationshipType === RelationshipType.MANY_TO_ONE) { return base && fromPrimary && fromForeign } else { return base && getTable(throughId) && throughFromKey && throughToKey @@ -181,12 +181,12 @@ } function otherRelationshipType(type) { - if (type === RelationshipTypes.MANY_TO_ONE) { - return RelationshipTypes.ONE_TO_MANY - } else if (type === RelationshipTypes.ONE_TO_MANY) { - return RelationshipTypes.MANY_TO_ONE - } else if (type === RelationshipTypes.MANY_TO_MANY) { - return RelationshipTypes.MANY_TO_MANY + if (type === RelationshipType.MANY_TO_ONE) { + return RelationshipType.ONE_TO_MANY + } else if (type === RelationshipType.ONE_TO_MANY) { + return RelationshipType.MANY_TO_ONE + } else if (type === RelationshipType.MANY_TO_MANY) { + return RelationshipType.MANY_TO_MANY } } @@ -218,7 +218,7 @@ // if any to many only need to check from const manyToMany = - relateFrom.relationshipType === RelationshipTypes.MANY_TO_MANY + relateFrom.relationshipType === RelationshipType.MANY_TO_MANY if (!manyToMany) { delete relateFrom.through @@ -253,7 +253,7 @@ } relateTo = { ...relateTo, - relationshipType: RelationshipTypes.ONE_TO_MANY, + relationshipType: RelationshipType.ONE_TO_MANY, foreignKey: relateFrom.fieldName, fieldName: fromPrimary, } @@ -321,7 +321,7 @@ fromColumn = toRelationship.name } relationshipType = - fromRelationship.relationshipType || RelationshipTypes.MANY_TO_ONE + fromRelationship.relationshipType || RelationshipType.MANY_TO_ONE if (selectedFromTable) { fromId = selectedFromTable._id fromColumn = selectedFromTable.name diff --git a/packages/builder/src/components/backend/Datasources/relationshipErrors.js b/packages/builder/src/components/backend/Datasources/relationshipErrors.js index 0dc9b264b9..259484e9a9 100644 --- a/packages/builder/src/components/backend/Datasources/relationshipErrors.js +++ b/packages/builder/src/components/backend/Datasources/relationshipErrors.js @@ -1,4 +1,4 @@ -import { RelationshipTypes } from "constants/backend" +import { RelationshipType } from "constants/backend" const typeMismatch = "Column type of the foreign key must match the primary key" const columnBeingUsed = "Column name cannot be an existing column" @@ -40,7 +40,7 @@ export class RelationshipErrorChecker { } isMany() { - return this.type === RelationshipTypes.MANY_TO_MANY + return this.type === RelationshipType.MANY_TO_MANY } relationshipTypeSet(type) { diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 40af470b4d..7bf2bbbbab 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -1,17 +1,9 @@
@@ -119,10 +127,8 @@ on:change={handleFile} />
{/each} @@ -167,7 +176,7 @@