Merge branch 'master' of github.com:Budibase/budibase into feature/budi-7607-migrate-user-relationship-columns-to-the-new-user-column

This commit is contained in:
Sam Rose 2023-10-24 17:34:41 +01:00
commit fb998502bb
No known key found for this signature in database
31 changed files with 179 additions and 32 deletions

View File

@ -54,6 +54,7 @@ jobs:
push: true push: true
pull: true pull: true
platforms: linux/amd64,linux/arm64 platforms: linux/amd64,linux/arm64
build-args: BUDIBASE_VERSION=0.0.0+test
tags: budibase/budibase-test:test tags: budibase/budibase-test:test
file: ./hosting/single/Dockerfile.v2 file: ./hosting/single/Dockerfile.v2
cache-from: type=registry,ref=budibase/budibase-test:test cache-from: type=registry,ref=budibase/budibase-test:test
@ -64,6 +65,8 @@ jobs:
context: . context: .
push: true push: true
platforms: linux/amd64 platforms: linux/amd64
build-args: TARGETBUILD=aas build-args: |
TARGETBUILD=aas
BUDIBASE_VERSION=0.0.0+test
tags: budibase/budibase-test:aas tags: budibase/budibase-test:aas
file: ./hosting/single/Dockerfile.v2 file: ./hosting/single/Dockerfile.v2

View File

@ -7,6 +7,8 @@ services:
build: build:
context: .. context: ..
dockerfile: packages/server/Dockerfile.v2 dockerfile: packages/server/Dockerfile.v2
args:
- BUDIBASE_VERSION=0.0.0+dev-docker
container_name: build-bbapps container_name: build-bbapps
environment: environment:
SELF_HOSTED: 1 SELF_HOSTED: 1
@ -30,13 +32,13 @@ services:
depends_on: depends_on:
- worker-service - worker-service
- redis-service - redis-service
# volumes:
# - /some/path/to/plugins:/plugins
worker-service: worker-service:
build: build:
context: .. context: ..
dockerfile: packages/worker/Dockerfile.v2 dockerfile: packages/worker/Dockerfile.v2
args:
- BUDIBASE_VERSION=0.0.0+dev-docker
container_name: build-bbworker container_name: build-bbworker
environment: environment:
SELF_HOSTED: 1 SELF_HOSTED: 1

View File

@ -118,6 +118,10 @@ EXPOSE 443
EXPOSE 2222 EXPOSE 2222
VOLUME /data VOLUME /data
ARG BUDIBASE_VERSION
# Ensuring the version argument is sent
RUN test -n "$BUDIBASE_VERSION"
ENV BUDIBASE_VERSION=$BUDIBASE_VERSION
HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh" HEALTHCHECK --interval=15s --timeout=15s --start-period=45s CMD "/healthcheck.sh"

View File

@ -1,5 +1,5 @@
{ {
"version": "2.11.42", "version": "2.11.43",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -75,12 +75,12 @@ function getPackageJsonFields(): {
const content = readFileSync(packageJsonFile!, "utf-8") const content = readFileSync(packageJsonFile!, "utf-8")
const parsedContent = JSON.parse(content) const parsedContent = JSON.parse(content)
return { return {
VERSION: parsedContent.version, VERSION: process.env.BUDIBASE_VERSION || parsedContent.version,
SERVICE_NAME: parsedContent.name, SERVICE_NAME: parsedContent.name,
} }
} catch { } catch {
// throwing an error here is confusing/causes backend-core to be hard to import // throwing an error here is confusing/causes backend-core to be hard to import
return { VERSION: "", SERVICE_NAME: "" } return { VERSION: process.env.BUDIBASE_VERSION || "", SERVICE_NAME: "" }
} }
} }

View File

@ -21,8 +21,9 @@ import {
User, User,
DatabaseQueryOpts, DatabaseQueryOpts,
} from "@budibase/types" } from "@budibase/types"
import * as context from "../context"
import { getGlobalDB } from "../context" import { getGlobalDB } from "../context"
import * as context from "../context"
import { isCreator } from "./utils"
type GetOpts = { cleanup?: boolean } type GetOpts = { cleanup?: boolean }
@ -286,6 +287,19 @@ export async function getUserCount() {
return response.total_rows return response.total_rows
} }
export async function getCreatorCount() {
let creators = 0
async function iterate(startPage?: string) {
const page = await paginatedUsers({ bookmark: startPage })
creators += page.data.filter(isCreator).length
if (page.hasNextPage) {
await iterate(page.nextPage)
}
}
await iterate()
return creators
}
// used to remove the builder/admin permissions, for processing the // used to remove the builder/admin permissions, for processing the
// user as an app user (they may have some specific role/group // user as an app user (they may have some specific role/group
export function removePortalUserPermissions(user: User | ContextUser) { export function removePortalUserPermissions(user: User | ContextUser) {

View File

@ -10,6 +10,7 @@ import { getAccountByTenantId } from "../accounts"
// extract from shared-core to make easily accessible from backend-core // extract from shared-core to make easily accessible from backend-core
export const isBuilder = sdk.users.isBuilder export const isBuilder = sdk.users.isBuilder
export const isAdmin = sdk.users.isAdmin export const isAdmin = sdk.users.isAdmin
export const isCreator = sdk.users.isCreator
export const isGlobalBuilder = sdk.users.isGlobalBuilder export const isGlobalBuilder = sdk.users.isGlobalBuilder
export const isAdminOrBuilder = sdk.users.isAdminOrBuilder export const isAdminOrBuilder = sdk.users.isAdminOrBuilder
export const hasAdminPermissions = sdk.users.hasAdminPermissions export const hasAdminPermissions = sdk.users.hasAdminPermissions

View File

@ -72,6 +72,11 @@ export function quotas(): Quotas {
value: 1, value: 1,
triggers: [], triggers: [],
}, },
creators: {
name: "Creators",
value: 1,
triggers: [],
},
userGroups: { userGroups: {
name: "User Groups", name: "User Groups",
value: 1, value: 1,

View File

@ -1,6 +1,6 @@
import { MonthlyQuotaName, QuotaUsage } from "@budibase/types" import { MonthlyQuotaName, QuotaUsage } from "@budibase/types"
export const usage = (): QuotaUsage => { export const usage = (users: number = 0, creators: number = 0): QuotaUsage => {
return { return {
_id: "usage_quota", _id: "usage_quota",
quotaReset: new Date().toISOString(), quotaReset: new Date().toISOString(),
@ -58,7 +58,8 @@ export const usage = (): QuotaUsage => {
usageQuota: { usageQuota: {
apps: 0, apps: 0,
plugins: 0, plugins: 0,
users: 0, users,
creators,
userGroups: 0, userGroups: 0,
rows: 0, rows: 0,
triggers: {}, triggers: {},

View File

@ -106,6 +106,13 @@
name: fieldName, name: fieldName,
} }
} }
// Delete numeric only widths as these are grid widths and should be
// ignored
const width = fixedSchema[fieldName].width
if (width != null && `${width}`.trim().match(/^[0-9]+$/)) {
delete fixedSchema[fieldName].width
}
}) })
return fixedSchema return fixedSchema
} }

View File

@ -16,7 +16,11 @@
<DrawerContent> <DrawerContent>
<div class="container"> <div class="container">
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<Input bind:value={column.width} label="Width" placeholder="Auto" /> <Input
bind:value={column.width}
label="Width (must include a unit like px or %)"
placeholder="Auto"
/>
<Select <Select
label="Alignment" label="Alignment"
bind:value={column.align} bind:value={column.align}

View File

@ -81,6 +81,7 @@
sortOrder: $fetch.sortOrder, sortOrder: $fetch.sortOrder,
}, },
limit, limit,
primaryDisplay: $fetch.definition?.primaryDisplay,
} }
const createFetch = datasource => { const createFetch = datasource => {

View File

@ -32,7 +32,8 @@
$: loading = dataProvider?.loading ?? false $: loading = dataProvider?.loading ?? false
$: data = dataProvider?.rows || [] $: data = dataProvider?.rows || []
$: fullSchema = dataProvider?.schema ?? {} $: fullSchema = dataProvider?.schema ?? {}
$: fields = getFields(fullSchema, columns, false) $: primaryDisplay = dataProvider?.primaryDisplay
$: fields = getFields(fullSchema, columns, false, primaryDisplay)
$: schema = getFilteredSchema(fullSchema, fields, hasChildren) $: schema = getFilteredSchema(fullSchema, fields, hasChildren)
$: setSorting = getAction( $: setSorting = getAction(
dataProvider?.id, dataProvider?.id,
@ -55,18 +56,13 @@
} }
} }
const getFields = (schema, customColumns, showAutoColumns) => { const getFields = (
// Check for an invalid column selection schema,
let invalid = false customColumns,
customColumns?.forEach(column => { showAutoColumns,
const columnName = typeof column === "string" ? column : column.name primaryDisplay
if (schema[columnName] == null) { ) => {
invalid = true if (customColumns?.length) {
}
})
// Use column selection if it exists
if (!invalid && customColumns?.length) {
return customColumns return customColumns
} }
@ -74,13 +70,38 @@
let columns = [] let columns = []
let autoColumns = [] let autoColumns = []
Object.entries(schema).forEach(([field, fieldSchema]) => { Object.entries(schema).forEach(([field, fieldSchema]) => {
if (fieldSchema.visible === false) {
return
}
if (!fieldSchema?.autocolumn) { if (!fieldSchema?.autocolumn) {
columns.push(field) columns.push(field)
} else if (showAutoColumns) { } else if (showAutoColumns) {
autoColumns.push(field) autoColumns.push(field)
} }
}) })
return columns.concat(autoColumns)
// Sort columns to respect grid metadata
const allCols = columns.concat(autoColumns)
return allCols.sort((a, b) => {
if (a === primaryDisplay) {
return -1
}
if (b === primaryDisplay) {
return 1
}
const aOrder = schema[a].order
const bOrder = schema[b].order
if (aOrder === bOrder) {
return 0
}
if (aOrder == null) {
return 1
}
if (bOrder == null) {
return -1
}
return aOrder < bOrder ? -1 : 1
})
} }
const getFilteredSchema = (schema, fields, hasChildren) => { const getFilteredSchema = (schema, fields, hasChildren) => {

@ -1 +1 @@
Subproject commit f7e7cffe422086d9449c2075a74a378c16caff9d Subproject commit d24c0dc3a30014cbe61860252aa48104cad36376

View File

@ -67,6 +67,11 @@ COPY packages/server/docker_run.sh .
COPY packages/server/builder/ builder/ COPY packages/server/builder/ builder/
COPY packages/server/client/ client/ COPY packages/server/client/ client/
ARG BUDIBASE_VERSION
# Ensuring the version argument is sent
RUN test -n "$BUDIBASE_VERSION"
ENV BUDIBASE_VERSION=$BUDIBASE_VERSION
EXPOSE 4001 EXPOSE 4001
# have to add node environment production after install # have to add node environment production after install

View File

@ -18,7 +18,7 @@
"test": "bash scripts/test.sh", "test": "bash scripts/test.sh",
"test:memory": "jest --maxWorkers=2 --logHeapUsage --forceExit", "test:memory": "jest --maxWorkers=2 --logHeapUsage --forceExit",
"test:watch": "jest --watch", "test:watch": "jest --watch",
"build:docker": "yarn build && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION", "build:docker": "yarn build && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION --build-arg BUDIBASE_VERSION=$BUDIBASE_RELEASE_VERSION",
"run:docker": "node dist/index.js", "run:docker": "node dist/index.js",
"run:docker:cluster": "pm2-runtime start pm2.config.js", "run:docker:cluster": "pm2-runtime start pm2.config.js",
"dev:stack:up": "node scripts/dev/manage.js up", "dev:stack:up": "node scripts/dev/manage.js up",

View File

@ -47,6 +47,7 @@ async function init() {
TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR", TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
HTTP_MIGRATIONS: "0", HTTP_MIGRATIONS: "0",
HTTP_LOGGING: "0", HTTP_LOGGING: "0",
VERSION: "0.0.0+local",
} }
let envFile = "" let envFile = ""
Object.keys(envFileJson).forEach(key => { Object.keys(envFileJson).forEach(key => {

View File

@ -41,7 +41,7 @@ describe("/component", () => {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body).toEqual({ expect(res.body).toEqual({
budibaseVersion: "0.0.0", budibaseVersion: "0.0.0+jest",
cpuArch: "arm64", cpuArch: "arm64",
cpuCores: 1, cpuCores: 1,
cpuInfo: "test", cpuInfo: "test",

View File

@ -1,6 +1,6 @@
const setup = require("./utilities") const setup = require("./utilities")
const { events } = require("@budibase/backend-core") const { events } = require("@budibase/backend-core")
const version = require("../../../../package.json").version
describe("/dev", () => { describe("/dev", () => {
let request = setup.getRequest() let request = setup.getRequest()
@ -32,9 +32,9 @@ describe("/dev", () => {
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
expect(res.body.version).toBe(version) expect(res.body.version).toBe('0.0.0+jest')
expect(events.installation.versionChecked).toBeCalledTimes(1) expect(events.installation.versionChecked).toBeCalledTimes(1)
expect(events.installation.versionChecked).toBeCalledWith(version) expect(events.installation.versionChecked).toBeCalledWith('0.0.0+jest')
}) })
}) })
}) })

View File

@ -3,6 +3,7 @@ import * as syncApps from "./usageQuotas/syncApps"
import * as syncRows from "./usageQuotas/syncRows" import * as syncRows from "./usageQuotas/syncRows"
import * as syncPlugins from "./usageQuotas/syncPlugins" import * as syncPlugins from "./usageQuotas/syncPlugins"
import * as syncUsers from "./usageQuotas/syncUsers" import * as syncUsers from "./usageQuotas/syncUsers"
import * as syncCreators from "./usageQuotas/syncCreators"
/** /**
* Synchronise quotas to the state of the db. * Synchronise quotas to the state of the db.
@ -13,5 +14,6 @@ export const run = async () => {
await syncRows.run() await syncRows.run()
await syncPlugins.run() await syncPlugins.run()
await syncUsers.run() await syncUsers.run()
await syncCreators.run()
}) })
} }

View File

@ -0,0 +1,13 @@
import { users } from "@budibase/backend-core"
import { quotas } from "@budibase/pro"
import { QuotaUsageType, StaticQuotaName } from "@budibase/types"
export const run = async () => {
const creatorCount = await users.getCreatorCount()
console.log(`Syncing creator count: ${creatorCount}`)
await quotas.setUsage(
creatorCount,
StaticQuotaName.CREATORS,
QuotaUsageType.STATIC
)
}

View File

@ -0,0 +1,26 @@
import TestConfig from "../../../../tests/utilities/TestConfiguration"
import * as syncCreators from "../syncCreators"
import { quotas } from "@budibase/pro"
describe("syncCreators", () => {
let config = new TestConfig(false)
beforeEach(async () => {
await config.init()
})
afterAll(config.end)
it("syncs creators", async () => {
return config.doInContext(null, async () => {
await config.createUser({ admin: true })
await syncCreators.run()
const usageDoc = await quotas.getQuotaUsage()
// default + additional creator
const creatorsCount = 2
expect(usageDoc.usageQuota.creators).toBe(creatorsCount)
})
})
})

View File

@ -9,3 +9,4 @@ process.env.LOG_LEVEL = process.env.LOG_LEVEL || "error"
process.env.MOCK_REDIS = "1" process.env.MOCK_REDIS = "1"
process.env.PLATFORM_URL = "http://localhost:10000" process.env.PLATFORM_URL = "http://localhost:10000"
process.env.REDIS_PASSWORD = "budibase" process.env.REDIS_PASSWORD = "budibase"
process.env.BUDIBASE_VERSION = "0.0.0+jest"

View File

@ -6,6 +6,7 @@ import {
InternalTable, InternalTable,
} from "@budibase/types" } from "@budibase/types"
import { getProdAppID } from "./applications" import { getProdAppID } from "./applications"
import * as _ from "lodash/fp"
// checks if a user is specifically a builder, given an app ID // checks if a user is specifically a builder, given an app ID
export function isBuilder(user: User | ContextUser, appId?: string): boolean { export function isBuilder(user: User | ContextUser, appId?: string): boolean {
@ -58,6 +59,18 @@ export function hasAppBuilderPermissions(user?: User | ContextUser): boolean {
return !isGlobalBuilder && appLength != null && appLength > 0 return !isGlobalBuilder && appLength != null && appLength > 0
} }
export function hasAppCreatorPermissions(user?: User | ContextUser): boolean {
if (!user) {
return false
}
return _.flow(
_.get("roles"),
_.values,
_.find(x => ["CREATOR", "ADMIN"].includes(x)),
x => !!x
)(user)
}
// checks if a user is capable of building any app // checks if a user is capable of building any app
export function hasBuilderPermissions(user?: User | ContextUser): boolean { export function hasBuilderPermissions(user?: User | ContextUser): boolean {
if (!user) { if (!user) {
@ -74,6 +87,18 @@ export function hasAdminPermissions(user?: User | ContextUser): boolean {
return !!user.admin?.global return !!user.admin?.global
} }
export function isCreator(user?: User | ContextUser): boolean {
if (!user) {
return false
}
return (
isGlobalBuilder(user) ||
hasAdminPermissions(user) ||
hasAppBuilderPermissions(user) ||
hasAppCreatorPermissions(user)
)
}
export function getGlobalUserID(userId?: string): string | undefined { export function getGlobalUserID(userId?: string): string | undefined {
if (typeof userId !== "string") { if (typeof userId !== "string") {
return userId return userId

View File

@ -32,6 +32,7 @@ export interface StaticUsage {
[StaticQuotaName.APPS]: number [StaticQuotaName.APPS]: number
[StaticQuotaName.PLUGINS]: number [StaticQuotaName.PLUGINS]: number
[StaticQuotaName.USERS]: number [StaticQuotaName.USERS]: number
[StaticQuotaName.CREATORS]: number
[StaticQuotaName.USER_GROUPS]: number [StaticQuotaName.USER_GROUPS]: number
[StaticQuotaName.ROWS]: number [StaticQuotaName.ROWS]: number
triggers: { triggers: {

View File

@ -14,6 +14,7 @@ export enum StaticQuotaName {
ROWS = "rows", ROWS = "rows",
APPS = "apps", APPS = "apps",
USERS = "users", USERS = "users",
CREATORS = "creators",
USER_GROUPS = "userGroups", USER_GROUPS = "userGroups",
PLUGINS = "plugins", PLUGINS = "plugins",
} }
@ -67,6 +68,7 @@ export type StaticQuotas = {
[StaticQuotaName.ROWS]: Quota [StaticQuotaName.ROWS]: Quota
[StaticQuotaName.APPS]: Quota [StaticQuotaName.APPS]: Quota
[StaticQuotaName.USERS]: Quota [StaticQuotaName.USERS]: Quota
[StaticQuotaName.CREATORS]: Quota
[StaticQuotaName.USER_GROUPS]: Quota [StaticQuotaName.USER_GROUPS]: Quota
[StaticQuotaName.PLUGINS]: Quota [StaticQuotaName.PLUGINS]: Quota
} }

View File

@ -50,4 +50,9 @@ ENV POSTHOG_TOKEN=phc_bIjZL7oh2GEUd2vqvTBH8WvrX0fWTFQMs6H5KQxiUxU
ENV TENANT_FEATURE_FLAGS=*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR ENV TENANT_FEATURE_FLAGS=*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR
ENV ACCOUNT_PORTAL_URL=https://account.budibase.app ENV ACCOUNT_PORTAL_URL=https://account.budibase.app
ARG BUDIBASE_VERSION
# Ensuring the version argument is sent
RUN test -n "$BUDIBASE_VERSION"
ENV BUDIBASE_VERSION=$BUDIBASE_VERSION
CMD ["./docker_run.sh"] CMD ["./docker_run.sh"]

View File

@ -20,7 +20,7 @@
"run:docker": "node dist/index.js", "run:docker": "node dist/index.js",
"debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js", "debug": "yarn build && node --expose-gc --inspect=9223 dist/index.js",
"run:docker:cluster": "pm2-runtime start pm2.config.js", "run:docker:cluster": "pm2-runtime start pm2.config.js",
"build:docker": "yarn build && docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION", "build:docker": "yarn build && docker build . -t worker-service --label version=$BUDIBASE_RELEASE_VERSION --build-arg BUDIBASE_VERSION=$BUDIBASE_RELEASE_VERSION",
"dev:stack:init": "node ./scripts/dev/manage.js init", "dev:stack:init": "node ./scripts/dev/manage.js init",
"dev:builder": "npm run dev:stack:init && nodemon", "dev:builder": "npm run dev:stack:init && nodemon",
"dev:built": "yarn run dev:stack:init && yarn run run:docker", "dev:built": "yarn run dev:stack:init && yarn run run:docker",

View File

@ -31,6 +31,7 @@ async function init() {
TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR", TENANT_FEATURE_FLAGS: "*:LICENSING,*:USER_GROUPS,*:ONBOARDING_TOUR",
ENABLE_EMAIL_TEST_MODE: 1, ENABLE_EMAIL_TEST_MODE: 1,
HTTP_LOGGING: 0, HTTP_LOGGING: 0,
VERSION: "0.0.0+local",
} }
let envFile = "" let envFile = ""
Object.keys(envFileJson).forEach(key => { Object.keys(envFileJson).forEach(key => {

View File

@ -10,3 +10,4 @@ process.env.PLATFORM_URL = "http://localhost:10000"
process.env.INTERNAL_API_KEY = "tet" process.env.INTERNAL_API_KEY = "tet"
process.env.DISABLE_ACCOUNT_PORTAL = "0" process.env.DISABLE_ACCOUNT_PORTAL = "0"
process.env.MOCK_REDIS = "1" process.env.MOCK_REDIS = "1"
process.env.BUDIBASE_VERSION = "0.0.0+jest"

View File

@ -1,3 +1,4 @@
#!/bin/bash #!/bin/bash
yarn build --scope @budibase/server --scope @budibase/worker yarn build --scope @budibase/server --scope @budibase/worker
docker build -f hosting/single/Dockerfile.v2 -t budibase:latest . version=$(./scripts/getCurrentVersion.sh)
docker build -f hosting/single/Dockerfile.v2 -t budibase:latest --build-arg BUDIBASE_VERSION=$version .