Merge branch 'develop' of github.com:Budibase/budibase into more-grid-tweaks

This commit is contained in:
Andrew Kingston 2023-05-05 08:29:03 +01:00
commit 9b4fa75954
40 changed files with 351 additions and 298 deletions

View File

@ -222,9 +222,9 @@ http {
rewrite ^/files/signed/(.*)$ /$1 break; rewrite ^/files/signed/(.*)$ /$1 break;
} }
client_header_timeout 60; client_header_timeout 120;
client_body_timeout 60; client_body_timeout 120;
keepalive_timeout 60; keepalive_timeout 120;
# gzip # gzip
gzip on; gzip on;

View File

@ -1,5 +1,5 @@
{ {
"version": "2.5.6-alpha.39", "version": "2.5.10-alpha.1",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"packages": ["packages/*"], "packages": ["packages/*"],

View File

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

View File

@ -47,7 +47,7 @@ async function put(
type: LockType.TRY_ONCE, type: LockType.TRY_ONCE,
name: LockName.PERSIST_WRITETHROUGH, name: LockName.PERSIST_WRITETHROUGH,
resource: key, resource: key,
ttl: 1000, ttl: 15000,
}, },
async () => { async () => {
const writeDb = async (toWrite: any) => { const writeDb = async (toWrite: any) => {
@ -71,6 +71,7 @@ async function put(
} }
} }
) )
if (!lockResponse.executed) { if (!lockResponse.executed) {
logWarn(`Ignoring redlock conflict in write-through cache`) logWarn(`Ignoring redlock conflict in write-through cache`)
} }

View File

@ -434,7 +434,7 @@ export class QueryBuilder<T> {
}) })
} }
if (this.#query.empty) { if (this.#query.empty) {
build(this.#query.empty, (key: string) => `!${key}:["" TO *]`) build(this.#query.empty, (key: string) => `(*:* -${key}:["" TO *])`)
} }
if (this.#query.notEmpty) { if (this.#query.notEmpty) {
build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`) build(this.#query.notEmpty, (key: string) => `${key}:["" TO *]`)

View File

@ -154,6 +154,7 @@ const environment = {
? process.env.ENABLE_SSO_MAINTENANCE_MODE ? process.env.ENABLE_SSO_MAINTENANCE_MODE
: false, : false,
VERSION: findVersion(), VERSION: findVersion(),
DISABLE_PINO_LOGGER: process.env.DISABLE_PINO_LOGGER,
_set(key: any, value: any) { _set(key: any, value: any) {
process.env[key] = value process.env[key] = value
// @ts-ignore // @ts-ignore

View File

@ -3,7 +3,6 @@ import {
Event, Event,
LicenseActivatedEvent, LicenseActivatedEvent,
LicensePlanChangedEvent, LicensePlanChangedEvent,
LicenseTierChangedEvent,
PlanType, PlanType,
Account, Account,
LicensePortalOpenedEvent, LicensePortalOpenedEvent,
@ -11,22 +10,23 @@ import {
LicenseCheckoutOpenedEvent, LicenseCheckoutOpenedEvent,
LicensePaymentFailedEvent, LicensePaymentFailedEvent,
LicensePaymentRecoveredEvent, LicensePaymentRecoveredEvent,
PriceDuration,
} from "@budibase/types" } from "@budibase/types"
async function tierChanged(account: Account, from: number, to: number) { async function planChanged(
const properties: LicenseTierChangedEvent = { account: Account,
accountId: account.accountId, opts: {
to, from: PlanType
from, to: PlanType
fromQuantity: number | undefined
toQuantity: number | undefined
fromDuration: PriceDuration | undefined
toDuration: PriceDuration | undefined
} }
await publishEvent(Event.LICENSE_TIER_CHANGED, properties) ) {
}
async function planChanged(account: Account, from: PlanType, to: PlanType) {
const properties: LicensePlanChangedEvent = { const properties: LicensePlanChangedEvent = {
accountId: account.accountId, accountId: account.accountId,
to, ...opts,
from,
} }
await publishEvent(Event.LICENSE_PLAN_CHANGED, properties) await publishEvent(Event.LICENSE_PLAN_CHANGED, properties)
} }
@ -74,7 +74,6 @@ async function paymentRecovered(account: Account) {
} }
export default { export default {
tierChanged,
planChanged, planChanged,
activated, activated,
checkoutOpened, checkoutOpened,

View File

@ -1,5 +1,5 @@
export * as correlation from "./correlation/correlation" export * as correlation from "./correlation/correlation"
export { logger, disableLogger } from "./pino/logger" export { logger } from "./pino/logger"
export * from "./alerts" export * from "./alerts"
// turn off or on context logging i.e. tenantId, appId etc // turn off or on context logging i.e. tenantId, appId etc

View File

@ -5,184 +5,169 @@ import * as correlation from "../correlation"
import { IdentityType } from "@budibase/types" import { IdentityType } from "@budibase/types"
import { LOG_CONTEXT } from "../index" import { LOG_CONTEXT } from "../index"
// CORE LOGGERS - for disabling
const BUILT_INS = {
log: console.log,
error: console.error,
info: console.info,
warn: console.warn,
trace: console.trace,
debug: console.debug,
}
// LOGGER // LOGGER
const pinoOptions: LoggerOptions = { let pinoInstance: pino.Logger | undefined
level: env.LOG_LEVEL, if (!env.DISABLE_PINO_LOGGER) {
formatters: { const pinoOptions: LoggerOptions = {
level: label => { level: env.LOG_LEVEL,
return { level: label.toUpperCase() } formatters: {
}, level: label => {
bindings: () => { return { level: label.toUpperCase() }
return {} },
}, bindings: () => {
}, return {}
timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`, },
}
if (env.isDev()) {
pinoOptions.transport = {
target: "pino-pretty",
options: {
singleLine: true,
}, },
timestamp: () => `,"timestamp":"${new Date(Date.now()).toISOString()}"`,
} }
}
export const logger = pino(pinoOptions) if (env.isDev()) {
pinoOptions.transport = {
export function disableLogger() { target: "pino-pretty",
console.log = BUILT_INS.log options: {
console.error = BUILT_INS.error singleLine: true,
console.info = BUILT_INS.info },
console.warn = BUILT_INS.warn
console.trace = BUILT_INS.trace
console.debug = BUILT_INS.debug
}
// CONSOLE OVERRIDES
interface MergingObject {
objects?: any[]
tenantId?: string
appId?: string
identityId?: string
identityType?: IdentityType
correlationId?: string
err?: Error
}
function isPlainObject(obj: any) {
return typeof obj === "object" && obj !== null && !(obj instanceof Error)
}
function isError(obj: any) {
return obj instanceof Error
}
function isMessage(obj: any) {
return typeof obj === "string"
}
/**
* Backwards compatibility between console logging statements
* and pino logging requirements.
*/
function getLogParams(args: any[]): [MergingObject, string] {
let error = undefined
let objects: any[] = []
let message = ""
args.forEach(arg => {
if (isMessage(arg)) {
message = `${message} ${arg}`.trimStart()
}
if (isPlainObject(arg)) {
objects.push(arg)
}
if (isError(arg)) {
error = arg
}
})
const identity = getIdentity()
let contextObject = {}
if (LOG_CONTEXT) {
contextObject = {
tenantId: getTenantId(),
appId: getAppId(),
identityId: identity?._id,
identityType: identity?.type,
correlationId: correlation.getId(),
} }
} }
const mergingObject = { pinoInstance = pino(pinoOptions)
objects: objects.length ? objects : undefined,
err: error, // CONSOLE OVERRIDES
...contextObject,
interface MergingObject {
objects?: any[]
tenantId?: string
appId?: string
identityId?: string
identityType?: IdentityType
correlationId?: string
err?: Error
} }
return [mergingObject, message] function isPlainObject(obj: any) {
} return typeof obj === "object" && obj !== null && !(obj instanceof Error)
console.log = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
logger.info(obj, msg)
}
console.info = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
logger.info(obj, msg)
}
console.warn = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
logger.warn(obj, msg)
}
console.error = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
logger.error(obj, msg)
}
/**
* custom trace impl - this resembles the node trace behaviour rather
* than traditional trace logging
* @param arg
*/
console.trace = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
if (!obj.err) {
// to get stack trace
obj.err = new Error()
} }
logger.trace(obj, msg)
}
console.debug = (...arg: any) => { function isError(obj: any) {
const [obj, msg] = getLogParams(arg) return obj instanceof Error
logger.debug(obj, msg) }
}
function isMessage(obj: any) {
// CONTEXT return typeof obj === "string"
}
const getTenantId = () => {
let tenantId /**
try { * Backwards compatibility between console logging statements
tenantId = context.getTenantId() * and pino logging requirements.
} catch (e: any) { */
// do nothing function getLogParams(args: any[]): [MergingObject, string] {
let error = undefined
let objects: any[] = []
let message = ""
args.forEach(arg => {
if (isMessage(arg)) {
message = `${message} ${arg}`.trimStart()
}
if (isPlainObject(arg)) {
objects.push(arg)
}
if (isError(arg)) {
error = arg
}
})
const identity = getIdentity()
let contextObject = {}
if (LOG_CONTEXT) {
contextObject = {
tenantId: getTenantId(),
appId: getAppId(),
identityId: identity?._id,
identityType: identity?.type,
correlationId: correlation.getId(),
}
}
const mergingObject = {
objects: objects.length ? objects : undefined,
err: error,
...contextObject,
}
return [mergingObject, message]
}
console.log = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
pinoInstance?.info(obj, msg)
}
console.info = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
pinoInstance?.info(obj, msg)
}
console.warn = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
pinoInstance?.warn(obj, msg)
}
console.error = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
pinoInstance?.error(obj, msg)
}
/**
* custom trace impl - this resembles the node trace behaviour rather
* than traditional trace logging
* @param arg
*/
console.trace = (...arg: any[]) => {
const [obj, msg] = getLogParams(arg)
if (!obj.err) {
// to get stack trace
obj.err = new Error()
}
pinoInstance?.trace(obj, msg)
}
console.debug = (...arg: any) => {
const [obj, msg] = getLogParams(arg)
pinoInstance?.debug(obj, msg)
}
// CONTEXT
const getTenantId = () => {
let tenantId
try {
tenantId = context.getTenantId()
} catch (e: any) {
// do nothing
}
return tenantId
}
const getAppId = () => {
let appId
try {
appId = context.getAppId()
} catch (e) {
// do nothing
}
return appId
}
const getIdentity = () => {
let identity
try {
identity = context.getIdentity()
} catch (e) {
// do nothing
}
return identity
} }
return tenantId
} }
const getAppId = () => { export const logger = pinoInstance
let appId
try {
appId = context.getAppId()
} catch (e) {
// do nothing
}
return appId
}
const getIdentity = () => {
let identity
try {
identity = context.getIdentity()
} catch (e) {
// do nothing
}
return identity
}

View File

@ -123,7 +123,6 @@ beforeAll(async () => {
jest.spyOn(events.plugin, "imported") jest.spyOn(events.plugin, "imported")
jest.spyOn(events.plugin, "deleted") jest.spyOn(events.plugin, "deleted")
jest.spyOn(events.license, "tierChanged")
jest.spyOn(events.license, "planChanged") jest.spyOn(events.license, "planChanged")
jest.spyOn(events.license, "activated") jest.spyOn(events.license, "activated")
jest.spyOn(events.license, "checkoutOpened") jest.spyOn(events.license, "checkoutOpened")

View File

@ -7,16 +7,29 @@ import {
PlanType, PlanType,
PriceDuration, PriceDuration,
PurchasedPlan, PurchasedPlan,
PurchasedPrice,
Quotas, Quotas,
Subscription, Subscription,
} from "@budibase/types" } from "@budibase/types"
export function price(): PurchasedPrice {
return {
amount: 10000,
amountMonthly: 10000,
currency: "usd",
duration: PriceDuration.MONTHLY,
priceId: "price_123",
dayPasses: undefined,
isPerUser: true,
}
}
export const plan = (type: PlanType = PlanType.FREE): PurchasedPlan => { export const plan = (type: PlanType = PlanType.FREE): PurchasedPlan => {
return { return {
type, type,
usesInvoicing: false, usesInvoicing: false,
minUsers: 1,
model: PlanModel.PER_USER, model: PlanModel.PER_USER,
price: type !== PlanType.FREE ? price() : undefined,
} }
} }

View File

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

View File

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

View File

@ -42,16 +42,7 @@ export const parseFile = e => {
reader.addEventListener("load", function (e) { reader.addEventListener("load", function (e) {
const fileData = e.target.result const fileData = e.target.result
if (file.type?.includes("json")) {
if (file.type === "text/csv") {
API.csvToJson(fileData)
.then(rows => {
resolveRows(rows)
})
.catch(() => {
reject("can't convert csv to json")
})
} else if (file.type === "application/json") {
const parsedFileData = JSON.parse(fileData) const parsedFileData = JSON.parse(fileData)
if (Array.isArray(parsedFileData)) { if (Array.isArray(parsedFileData)) {
@ -62,7 +53,13 @@ export const parseFile = e => {
reject("invalid json format") reject("invalid json format")
} }
} else { } else {
reject("invalid file type") API.csvToJson(fileData)
.then(rows => {
resolveRows(rows)
})
.catch(() => {
reject("cannot parse csv")
})
} }
}) })

View File

@ -27,7 +27,7 @@
onMount(() => { onMount(() => {
unlimited = isUnlimited() unlimited = isUnlimited()
percentage = getPercentage() percentage = getPercentage()
if (warnWhenFull && percentage === 100) { if (warnWhenFull && percentage >= 100) {
showWarning = true showWarning = true
} }
}) })

View File

@ -41,7 +41,7 @@ export function createUsersStore() {
inviteCode, inviteCode,
password, password,
firstName, firstName,
lastName, lastName: !lastName?.trim() ? undefined : lastName,
}) })
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.5.6-alpha.39", "version": "2.5.10-alpha.1",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
@ -29,14 +29,14 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.5.6-alpha.39", "@budibase/backend-core": "2.5.10-alpha.1",
"@budibase/string-templates": "2.5.6-alpha.39", "@budibase/string-templates": "2.5.10-alpha.1",
"@budibase/types": "2.5.6-alpha.39", "@budibase/types": "2.5.10-alpha.1",
"axios": "0.21.2", "axios": "0.21.2",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",
"commander": "7.1.0", "commander": "7.1.0",
"docker-compose": "0.23.12", "docker-compose": "0.24.0",
"dotenv": "16.0.1", "dotenv": "16.0.1",
"download": "8.0.0", "download": "8.0.0",
"find-free-port": "^2.0.0", "find-free-port": "^2.0.0",

View File

@ -1,6 +1,5 @@
#!/usr/bin/env node #!/usr/bin/env node
import { logging } from "@budibase/backend-core" process.env.DISABLE_PINO_LOGGER = "1"
logging.disableLogger()
import "./prebuilds" import "./prebuilds"
import "./environment" import "./environment"
import { env } from "@budibase/backend-core" import { env } from "@budibase/backend-core"

View File

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

View File

@ -1,13 +1,13 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "2.5.6-alpha.39", "version": "2.5.10-alpha.1",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "2.5.6-alpha.39", "@budibase/bbui": "2.5.10-alpha.1",
"@budibase/shared-core": "2.5.6-alpha.39", "@budibase/shared-core": "2.5.10-alpha.1",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",

View File

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

View File

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

View File

@ -37,7 +37,7 @@ import {
Table, Table,
} from "@budibase/types" } from "@budibase/types"
const { cleanExportRows } = require("./utils") import { cleanExportRows } from "./utils"
const CALCULATION_TYPES = { const CALCULATION_TYPES = {
SUM: "sum", SUM: "sum",
@ -391,6 +391,9 @@ export async function exportRows(ctx: UserCtx) {
const table = await db.get(ctx.params.tableId) const table = await db.get(ctx.params.tableId)
const rowIds = ctx.request.body.rows const rowIds = ctx.request.body.rows
let format = ctx.query.format let format = ctx.query.format
if (typeof format !== "string") {
ctx.throw(400, "Format parameter is not valid")
}
const { columns, query } = ctx.request.body const { columns, query } = ctx.request.body
let result let result

View File

@ -137,8 +137,8 @@ export function cleanExportRows(
delete schema[column] delete schema[column]
}) })
// Intended to avoid 'undefined' in export
if (format === Format.CSV) { if (format === Format.CSV) {
// Intended to append empty values in export
const schemaKeys = Object.keys(schema) const schemaKeys = Object.keys(schema)
for (let key of schemaKeys) { for (let key of schemaKeys) {
if (columns?.length && columns.indexOf(key) > 0) { if (columns?.length && columns.indexOf(key) > 0) {
@ -146,7 +146,7 @@ export function cleanExportRows(
} }
for (let row of cleanRows) { for (let row of cleanRows) {
if (row[key] == null) { if (row[key] == null) {
row[key] = "" row[key] = undefined
} }
} }
} }

View File

@ -10,7 +10,7 @@ import { getDatasourceParams } from "../../../db/utils"
import { context, events } from "@budibase/backend-core" import { context, events } from "@budibase/backend-core"
import { Table, UserCtx } from "@budibase/types" import { Table, UserCtx } from "@budibase/types"
import sdk from "../../../sdk" import sdk from "../../../sdk"
import csv from "csvtojson" import { jsonFromCsvString } from "../../../utilities/csv"
function pickApi({ tableId, table }: { tableId?: string; table?: Table }) { function pickApi({ tableId, table }: { tableId?: string; table?: Table }) {
if (table && !tableId) { if (table && !tableId) {
@ -104,7 +104,7 @@ export async function bulkImport(ctx: UserCtx) {
export async function csvToJson(ctx: UserCtx) { export async function csvToJson(ctx: UserCtx) {
const { csvString } = ctx.request.body const { csvString } = ctx.request.body
const result = await csv().fromString(csvString) const result = await jsonFromCsvString(csvString)
ctx.status = 200 ctx.status = 200
ctx.body = result ctx.body = result

View File

@ -10,7 +10,9 @@ export function csv(headers: string[], rows: Row[]) {
val = val =
typeof val === "object" && !(val instanceof Date) typeof val === "object" && !(val instanceof Date)
? `"${JSON.stringify(val).replace(/"/g, "'")}"` ? `"${JSON.stringify(val).replace(/"/g, "'")}"`
: `"${val}"` : val !== undefined
? `"${val}"`
: ""
return val.trim() return val.trim()
}) })
.join(",")}` .join(",")}`

View File

@ -105,7 +105,7 @@ describe("internal search", () => {
"column": "", "column": "",
}, },
}, PARAMS) }, PARAMS)
checkLucene(response, `*:* AND !column:["" TO *]`, PARAMS) checkLucene(response, `*:* AND (*:* -column:["" TO *])`, PARAMS)
}) })
it("test notEmpty query", async () => { it("test notEmpty query", async () => {

View File

@ -0,0 +1,22 @@
import csv from "csvtojson"
export async function jsonFromCsvString(csvString: string) {
const castedWithEmptyValues = await csv({ ignoreEmpty: true }).fromString(
csvString
)
// By default the csvtojson library casts empty values as empty strings. This is causing issues on conversion.
// ignoreEmpty will remove the key completly if empty, so creating this empty object will ensure we return the values with the keys but empty values
const result = await csv({ ignoreEmpty: false }).fromString(csvString)
result.forEach((r, i) => {
for (const [key] of Object.entries(r).filter(
([key, value]) => value === ""
)) {
if (castedWithEmptyValues[i][key] === undefined) {
r[key] = null
}
}
})
return result
}

View File

@ -4,6 +4,9 @@ interface SchemaColumn {
readonly name: string readonly name: string
readonly type: FieldTypes readonly type: FieldTypes
readonly autocolumn?: boolean readonly autocolumn?: boolean
readonly constraints?: {
presence: boolean
}
} }
interface Schema { interface Schema {
@ -76,6 +79,11 @@ export function validate(rows: Rows, schema: Schema): ValidationResults {
// If the columnType is not a string, then it's not present in the schema, and should be added to the invalid columns array // If the columnType is not a string, then it's not present in the schema, and should be added to the invalid columns array
if (typeof columnType !== "string") { if (typeof columnType !== "string") {
results.invalidColumns.push(columnName) results.invalidColumns.push(columnName)
} else if (
columnData == null &&
!schema[columnName].constraints?.presence
) {
results.schemaValidation[columnName] = true
} else if ( } else if (
// If there's no data for this field don't bother with further checks // If there's no data for this field don't bother with further checks
// If the field is already marked as invalid there's no need for further checks // If the field is already marked as invalid there's no need for further checks

View File

@ -0,0 +1,33 @@
import { jsonFromCsvString } from "../csv"
describe("csv", () => {
describe("jsonFromCsvString", () => {
test("multiple lines csv can be casted", async () => {
const csvString = '"id","title"\n"1","aaa"\n"2","bbb"'
const result = await jsonFromCsvString(csvString)
expect(result).toEqual([
{ id: "1", title: "aaa" },
{ id: "2", title: "bbb" },
])
result.forEach(r => expect(Object.keys(r)).toEqual(["id", "title"]))
})
test("empty values are casted as undefined", async () => {
const csvString =
'"id","optional","title"\n1,,"aaa"\n2,"value","bbb"\n3,,"ccc"'
const result = await jsonFromCsvString(csvString)
expect(result).toEqual([
{ id: "1", optional: null, title: "aaa" },
{ id: "2", optional: "value", title: "bbb" },
{ id: "3", optional: null, title: "ccc" },
])
result.forEach(r =>
expect(Object.keys(r)).toEqual(["id", "optional", "title"])
)
})
})
})

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/shared-core", "name": "@budibase/shared-core",
"version": "2.5.6-alpha.39", "version": "2.5.10-alpha.1",
"description": "Shared data utils", "description": "Shared data utils",
"main": "dist/cjs/src/index.js", "main": "dist/cjs/src/index.js",
"types": "dist/mjs/src/index.d.ts", "types": "dist/mjs/src/index.d.ts",
@ -20,7 +20,7 @@
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
}, },
"dependencies": { "dependencies": {
"@budibase/types": "2.5.6-alpha.39" "@budibase/types": "2.5.10-alpha.1"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.6.0", "concurrently": "^7.6.0",

View File

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

View File

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

View File

@ -39,6 +39,7 @@ export interface Account extends CreateAccount {
// licensing // licensing
tier: string // deprecated tier: string // deprecated
planType?: PlanType planType?: PlanType
/** @deprecated */
planTier?: number planTier?: number
license?: License license?: License
installId?: string installId?: string
@ -47,6 +48,7 @@ export interface Account extends CreateAccount {
stripeCustomerId?: string stripeCustomerId?: string
licenseKey?: string licenseKey?: string
licenseKeyActivatedAt?: number licenseKeyActivatedAt?: number
licenseRequestedAt?: number
licenseOverrides?: LicenseOverrides licenseOverrides?: LicenseOverrides
quotaUsage?: QuotaUsage quotaUsage?: QuotaUsage
} }

View File

@ -138,7 +138,6 @@ export enum Event {
// LICENSE // LICENSE
LICENSE_PLAN_CHANGED = "license:plan:changed", LICENSE_PLAN_CHANGED = "license:plan:changed",
LICENSE_TIER_CHANGED = "license:tier:changed",
LICENSE_ACTIVATED = "license:activated", LICENSE_ACTIVATED = "license:activated",
LICENSE_PAYMENT_FAILED = "license:payment:failed", LICENSE_PAYMENT_FAILED = "license:payment:failed",
LICENSE_PAYMENT_RECOVERED = "license:payment:recovered", LICENSE_PAYMENT_RECOVERED = "license:payment:recovered",
@ -328,7 +327,6 @@ export const AuditedEventFriendlyName: Record<Event, string | undefined> = {
// LICENSE - NOT AUDITED // LICENSE - NOT AUDITED
[Event.LICENSE_PLAN_CHANGED]: undefined, [Event.LICENSE_PLAN_CHANGED]: undefined,
[Event.LICENSE_TIER_CHANGED]: undefined,
[Event.LICENSE_ACTIVATED]: undefined, [Event.LICENSE_ACTIVATED]: undefined,
[Event.LICENSE_PAYMENT_FAILED]: undefined, [Event.LICENSE_PAYMENT_FAILED]: undefined,
[Event.LICENSE_PAYMENT_RECOVERED]: undefined, [Event.LICENSE_PAYMENT_RECOVERED]: undefined,

View File

@ -1,15 +1,14 @@
import { PlanType } from "../licensing" import { PlanType, PriceDuration } from "../licensing"
export interface LicenseTierChangedEvent {
accountId: string
from: number
to: number
}
export interface LicensePlanChangedEvent { export interface LicensePlanChangedEvent {
accountId: string accountId: string
from: PlanType from: PlanType
to: PlanType to: PlanType
// may not be on historical events
fromDuration: PriceDuration | undefined
toDuration: PriceDuration | undefined
fromQuantity: number | undefined
toQuantity: number | undefined
} }
export interface LicenseActivatedEvent { export interface LicenseActivatedEvent {

View File

@ -17,7 +17,6 @@ export enum PriceDuration {
export interface AvailablePlan { export interface AvailablePlan {
type: PlanType type: PlanType
maxUsers: number maxUsers: number
minUsers: number
prices: AvailablePrice[] prices: AvailablePrice[]
} }
@ -38,7 +37,6 @@ export interface PurchasedPlan {
type: PlanType type: PlanType
model: PlanModel model: PlanModel
usesInvoicing: boolean usesInvoicing: boolean
minUsers: number
price?: PurchasedPrice price?: PurchasedPrice
} }

View File

@ -55,12 +55,6 @@ export const isConstantQuota = (
return quotaType === QuotaType.CONSTANT return quotaType === QuotaType.CONSTANT
} }
export interface Minimums {
users: number
}
export type PlanMinimums = { [key in PlanType]: Minimums }
export type PlanQuotas = { [key in PlanType]: Quotas | undefined } export type PlanQuotas = { [key in PlanType]: Quotas | undefined }
export type MonthlyQuotas = { export type MonthlyQuotas = {

View File

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

View File

@ -1486,15 +1486,15 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.5.6-alpha.38": "@budibase/pro@2.5.10-alpha.0":
version "2.5.6-alpha.38" version "2.5.10-alpha.0"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.38.tgz#09e8dfbb6cefe856b5c01845a5a5c02a406faedf" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.10-alpha.0.tgz#f3688cc61d2130fbf8b191e515b8ac9789f2a9df"
integrity sha512-CBZv6V+163USHPN0SuEIrXeGA+9gB1QNmHrMEPSmwlOCZbNW2dhniz00EVSuVh3ypHSjDECgozasDJTNRhiufQ== integrity sha512-vYa1F4NhMh7veSZyhgrE54iziwZLHiuRwXbU+unEo3xqyFZIvERAAQZ88nrnwssubWBWnPhpN/8JMHrsyM8mXg==
dependencies: dependencies:
"@budibase/backend-core" "2.5.6-alpha.38" "@budibase/backend-core" "2.5.10-alpha.0"
"@budibase/shared-core" "2.4.44-alpha.1" "@budibase/shared-core" "2.5.9"
"@budibase/string-templates" "2.4.44-alpha.1" "@budibase/string-templates" "2.5.9"
"@budibase/types" "2.5.6-alpha.38" "@budibase/types" "2.5.10-alpha.0"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -1505,12 +1505,12 @@
scim-patch "^0.7.0" scim-patch "^0.7.0"
scim2-parse-filter "^0.2.8" scim2-parse-filter "^0.2.8"
"@budibase/shared-core@2.4.44-alpha.1": "@budibase/shared-core@2.5.9":
version "2.4.44-alpha.1" version "2.5.9"
resolved "https://registry.yarnpkg.com/@budibase/shared-core/-/shared-core-2.4.44-alpha.1.tgz#3d499e40e7e6c646e13a87cd08e01ba116c2ff1d" resolved "https://registry.yarnpkg.com/@budibase/shared-core/-/shared-core-2.5.9.tgz#f22f22637fb7618ded1c7292b10793d7969827ce"
integrity sha512-cN8LaDczijtsfWUYiXC4sg9Z+US4020i3Mb8TwCbf8TQyA1b06U5PwPCp+GHVA/wDFqfwcpcE1GXf8GwVuYs7A== integrity sha512-l417Rb2+1tuXbjNL42wJHmqIQQyla2pPglOnapxfOdRuvzng+5GqlrTV1caLNf/53TS9U6ueGJdPOtxZTTFGUA==
dependencies: dependencies:
"@budibase/types" "2.4.44-alpha.1" "@budibase/types" "^2.5.9"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":
version "0.9.139" version "0.9.139"
@ -1530,22 +1530,22 @@
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
"@budibase/string-templates@2.4.44-alpha.1": "@budibase/string-templates@2.5.9":
version "2.4.44-alpha.1" version "2.5.9"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.4.44-alpha.1.tgz#6c2aee594d16eac1f173c509e087a817dd3172f0" resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-2.5.9.tgz#64582730421801e1e829b430136b449b1adc0314"
integrity sha512-4gC2+0kccK0ilLnd0i/dmJzC0Ur7UgSAmV6zbzDDYNL4spU0qSy5VhBh7E3qKieg5RKMMzbpXLMWERpoPLlnqA== integrity sha512-Szu06M0JFHuUVIil2aHZWU8hvsROYpDx9raX9uIv4DcwBOAtyvVzD16wOCHzlmj8wWeV8fbKe4JF4q4GXnilJg==
dependencies: dependencies:
"@budibase/handlebars-helpers" "^0.11.8" "@budibase/handlebars-helpers" "^0.11.8"
dayjs "^1.10.4" dayjs "^1.10.4"
handlebars "^4.7.6" handlebars "^4.7.6"
handlebars-utils "^1.0.6" handlebars-utils "^1.0.6"
lodash "^4.17.20" lodash "^4.17.20"
vm2 "^3.9.4" vm2 "^3.9.15"
"@budibase/types@2.4.44-alpha.1": "@budibase/types@^2.5.9":
version "2.4.44-alpha.1" version "2.5.9"
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.4.44-alpha.1.tgz#1679657aa180d9c59afa1dffa611bff0638bd933" resolved "https://registry.yarnpkg.com/@budibase/types/-/types-2.5.9.tgz#4b1253e76b95cd8d6a42e86ebc3363a305c62653"
integrity sha512-Sq+8HfM75EBMoOvKYFwELdlxmVN6wNZMofDjT/2G+9aF+Zfe5Tzw69C+unmdBgcGGjGCHEYWSz4mF0v8FPAGbg== integrity sha512-7NELdWB3iV5y+pmXhm9g/v1AlN+wqHtoy67DZxKY5dlcK4DCcTdlqkToGiiw9d3sDPgDAVznjdZYnB5pGzd3TQ==
"@bull-board/api@3.7.0": "@bull-board/api@3.7.0":
version "3.7.0" version "3.7.0"
@ -9545,13 +9545,6 @@ dir-glob@^3.0.1:
dependencies: dependencies:
path-type "^4.0.0" path-type "^4.0.0"
docker-compose@0.23.12:
version "0.23.12"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.12.tgz#fa883b98be08f6926143d06bf9e522ef7ed3210c"
integrity sha512-KFbSMqQBuHjTGZGmYDOCO0L4SaML3BsWTId5oSUyaBa22vALuFHNv+UdDWs3HcMylHWKsxCbLB7hnM/nCosWZw==
dependencies:
yaml "^1.10.2"
docker-compose@0.23.17: docker-compose@0.23.17:
version "0.23.17" version "0.23.17"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.17.tgz#8816bef82562d9417dc8c790aa4871350f93a2ba" resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.17.tgz#8816bef82562d9417dc8c790aa4871350f93a2ba"
@ -9559,6 +9552,13 @@ docker-compose@0.23.17:
dependencies: dependencies:
yaml "^1.10.2" yaml "^1.10.2"
docker-compose@0.24.0:
version "0.24.0"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.24.0.tgz#474cd38b05b3887a56ffb2c39f16a7ae4d7d5077"
integrity sha512-RN/oSPLPa6ZG5e4dHg8tD8EMpd1WJqomNMBQT+d2M5MwcmfrPW/xHTent4TVqX0zZvHemv7qhhNlzXjxCnFaQw==
dependencies:
yaml "^1.10.2"
docker-compose@^0.23.5: docker-compose@^0.23.5:
version "0.23.19" version "0.23.19"
resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.19.tgz#9947726e2fe67bdfa9e8efe1ff15aa0de2e10eb8" resolved "https://registry.yarnpkg.com/docker-compose/-/docker-compose-0.23.19.tgz#9947726e2fe67bdfa9e8efe1ff15aa0de2e10eb8"
@ -24502,7 +24502,7 @@ vm2@3.9.17:
acorn "^8.7.0" acorn "^8.7.0"
acorn-walk "^8.2.0" acorn-walk "^8.2.0"
vm2@^3.9.11, vm2@^3.9.15, vm2@^3.9.4: vm2@^3.9.11, vm2@^3.9.15:
version "3.9.16" version "3.9.16"
resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.16.tgz#0fbc2a265f7bf8b837cea6f4a908f88a3f93b8e6" resolved "https://registry.yarnpkg.com/vm2/-/vm2-3.9.16.tgz#0fbc2a265f7bf8b837cea6f4a908f88a3f93b8e6"
integrity sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w== integrity sha512-3T9LscojNTxdOyG+e8gFeyBXkMlOBYDoF6dqZbj+MPVHi9x10UfiTAJIobuchRCp3QvC+inybTbMJIUrLsig0w==