Merge branch 'master' of github.com:Budibase/budibase into develop

This commit is contained in:
mike12345567 2021-08-06 13:04:54 +01:00
commit bc84bab52c
26 changed files with 110 additions and 63 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "0.9.99-alpha.4", "version": "0.9.102",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/auth", "name": "@budibase/auth",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"description": "Authentication middlewares for budibase builder and apps", "description": "Authentication middlewares for budibase builder and apps",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

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": "0.9.99-alpha.4", "version": "0.9.102",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4", "@budibase/bbui": "^0.9.102",
"@budibase/client": "^0.9.99-alpha.4", "@budibase/client": "^0.9.102",
"@budibase/colorpicker": "^1.1.2", "@budibase/colorpicker": "1.1.2",
"@budibase/string-templates": "^0.9.99-alpha.4", "@budibase/string-templates": "^0.9.102",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -2,6 +2,7 @@
import { Select, Label, notifications, ModalContent } from "@budibase/bbui" import { Select, Label, notifications, ModalContent } from "@budibase/bbui"
import { tables, views } from "stores/backend" import { tables, views } from "stores/backend"
import analytics from "analytics" import analytics from "analytics"
import { FIELDS } from "constants/backend"
const CALCULATIONS = [ const CALCULATIONS = [
{ {
@ -25,13 +26,16 @@
) )
$: fields = $: fields =
viewTable && viewTable &&
Object.keys(viewTable.schema).filter( Object.keys(viewTable.schema).filter(fieldName => {
field => const field = viewTable.schema[fieldName]
view.calculation === "count" || return (
field.type !== FIELDS.FORMULA.type &&
field.type !== FIELDS.LINK.type &&
(view.calculation === "count" ||
// don't want to perform calculations based on auto ID // don't want to perform calculations based on auto ID
(viewTable.schema[field].type === "number" && (field.type === "number" && !field.autocolumn))
!viewTable.schema[field].autocolumn)
) )
})
function saveView() { function saveView() {
views.save(view) views.save(view)

View File

@ -18,10 +18,12 @@
let exportFormat = FORMATS[0].key let exportFormat = FORMATS[0].key
async function exportView() { async function exportView() {
const filename = `export.${exportFormat}`
download( download(
`/api/views/export?view=${encodeURIComponent( `/api/views/export?view=${encodeURIComponent(
view view
)}&format=${exportFormat}` )}&format=${exportFormat}`,
filename
) )
} }
</script> </script>

View File

@ -11,7 +11,11 @@
$: fields = $: fields =
viewTable && viewTable &&
Object.entries(viewTable.schema) Object.entries(viewTable.schema)
.filter(entry => entry[1].type !== FIELDS.LINK.type) .filter(
entry =>
entry[1].type !== FIELDS.LINK.type &&
entry[1].type !== FIELDS.FORMULA.type
)
.map(([key]) => key) .map(([key]) => key)
function saveView() { function saveView() {

View File

@ -199,6 +199,9 @@
delete datasource.entities[toTable.name].schema[originalToName] delete datasource.entities[toTable.name].schema[originalToName]
} }
// store the original names so it won't cause an error
originalToName = toRelationship.name
originalFromName = fromRelationship.name
await save() await save()
await tables.fetch() await tables.fetch()
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"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",
@ -18,9 +18,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4", "@budibase/bbui": "^0.9.102",
"@budibase/standard-components": "^0.9.99-alpha.4", "@budibase/standard-components": "^0.9.102",
"@budibase/string-templates": "^0.9.99-alpha.4", "@budibase/string-templates": "^0.9.102",
"regexparam": "^1.3.0", "regexparam": "^1.3.0",
"shortid": "^2.2.15", "shortid": "^2.2.15",
"svelte-spa-router": "^3.0.5" "svelte-spa-router": "^3.0.5"

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -62,9 +62,9 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.99-alpha.4", "@budibase/auth": "^0.9.102",
"@budibase/client": "^0.9.99-alpha.4", "@budibase/client": "^0.9.102",
"@budibase/string-templates": "^0.9.99-alpha.4", "@budibase/string-templates": "^0.9.102",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",
"@koa/router": "8.0.0", "@koa/router": "8.0.0",
"@sendgrid/mail": "7.1.1", "@sendgrid/mail": "7.1.1",
@ -117,7 +117,7 @@
"devDependencies": { "devDependencies": {
"@babel/core": "^7.14.3", "@babel/core": "^7.14.3",
"@babel/preset-env": "^7.14.4", "@babel/preset-env": "^7.14.4",
"@budibase/standard-components": "^0.9.99-alpha.4", "@budibase/standard-components": "^0.9.102",
"@jest/test-sequencer": "^24.8.0", "@jest/test-sequencer": "^24.8.0",
"@types/bull": "^3.15.1", "@types/bull": "^3.15.1",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",

View File

@ -25,10 +25,10 @@ interface ManyRelationship {
interface RunConfig { interface RunConfig {
id: string id: string
row: Row
filters: SearchFilters filters: SearchFilters
sort: SortJson sort: SortJson
paginate: PaginationJson paginate: PaginationJson
row: Row
} }
module External { module External {
@ -89,8 +89,9 @@ module External {
// build id array // build id array
let idParts = [] let idParts = []
for (let field of primary) { for (let field of primary) {
if (row[field]) { const fieldValue = row[`${table.name}.${field}`]
idParts.push(row[field]) if (fieldValue) {
idParts.push(fieldValue)
} }
} }
if (idParts.length === 0) { if (idParts.length === 0) {
@ -115,7 +116,11 @@ module External {
const thisRow: { [key: string]: any } = {} const thisRow: { [key: string]: any } = {}
// filter the row down to what is actually the row (not joined) // filter the row down to what is actually the row (not joined)
for (let fieldName of Object.keys(table.schema)) { for (let fieldName of Object.keys(table.schema)) {
thisRow[fieldName] = row[fieldName] const value = row[`${table.name}.${fieldName}`]
// all responses include "select col as table.col" so that overlaps are handled
if (value) {
thisRow[fieldName] = value
}
} }
thisRow._id = generateIdForRow(row, table) thisRow._id = generateIdForRow(row, table)
thisRow.tableId = table._id thisRow.tableId = table._id
@ -191,7 +196,7 @@ module External {
const isUpdate = !field.through const isUpdate = !field.through
const thisKey: string = isUpdate ? "id" : linkTablePrimary const thisKey: string = isUpdate ? "id" : linkTablePrimary
// @ts-ignore // @ts-ignore
const otherKey: string = isUpdate ? field.foreignKey : tablePrimary const otherKey: string = isUpdate ? field.fieldName : tablePrimary
row[key].map((relationship: any) => { row[key].map((relationship: any) => {
// we don't really support composite keys for relationships, this is why [0] is used // we don't really support composite keys for relationships, this is why [0] is used
manyRelationships.push({ manyRelationships.push({
@ -359,7 +364,7 @@ module External {
} }
} }
if (cache[fullKey] == null) { if (cache[fullKey] == null) {
cache[fullKey] = await makeExternalQuery(this.appId, { const response = await makeExternalQuery(this.appId, {
endpoint: getEndpoint(tableId, DataSourceOperation.READ), endpoint: getEndpoint(tableId, DataSourceOperation.READ),
filters: { filters: {
equal: { equal: {
@ -367,8 +372,12 @@ module External {
}, },
}, },
}) })
// this is the response from knex if no rows found
if (!response[0].read) {
cache[fullKey] = response
} }
return { rows: cache[fullKey], table } }
return { rows: cache[fullKey] || [], table }
} }
/** /**
@ -418,14 +427,18 @@ module External {
const { tableName } = breakExternalTableId(tableId) const { tableName } = breakExternalTableId(tableId)
const table = this.tables[tableName] const table = this.tables[tableName]
for (let row of rows) { for (let row of rows) {
const filters = buildFilters(generateIdForRow(row, table), {}, table)
// safety check, if there are no filters on deletion bad things happen
if (Object.keys(filters).length !== 0) {
promises.push( promises.push(
makeExternalQuery(this.appId, { makeExternalQuery(this.appId, {
endpoint: getEndpoint(tableId, DataSourceOperation.DELETE), endpoint: getEndpoint(tableId, DataSourceOperation.DELETE),
filters: buildFilters(generateIdForRow(row, table), {}, table), filters,
}) })
) )
} }
} }
}
await Promise.all(promises) await Promise.all(promises)
} }
@ -442,7 +455,7 @@ module External {
.filter( .filter(
column => column =>
column[1].type !== FieldTypes.LINK && column[1].type !== FieldTypes.LINK &&
!existing.find((field: string) => field.includes(column[0])) !existing.find((field: string) => field === column[0])
) )
.map(column => `${table.name}.${column[0]}`) .map(column => `${table.name}.${column[0]}`)
} }

View File

@ -3,7 +3,11 @@ exports.csv = function (headers, rows) {
for (let row of rows) { for (let row of rows) {
csv = `${csv}\n${headers csv = `${csv}\n${headers
.map(header => `"${row[header]}"`.trim()) .map(header => {
let val = row[header]
val = typeof val === "object" ? JSON.stringify(val) : val
return `"${val}"`.trim()
})
.join(",")}` .join(",")}`
} }
return csv return csv

View File

@ -82,7 +82,7 @@ describe("/datasources", () => {
entityId: "users", entityId: "users",
}, },
resource: { resource: {
fields: ["name", "age"], fields: ["users.name", "users.age"],
}, },
filters: { filters: {
string: { string: {
@ -94,7 +94,7 @@ describe("/datasources", () => {
.expect(200) .expect(200)
// this is mock data, can't test it // this is mock data, can't test it
expect(res.body).toBeDefined() expect(res.body).toBeDefined()
expect(pg.queryMock).toHaveBeenCalledWith(`select "name", "age" from "users" where "users"."name" like $1 limit $2`, ["John%", 5000]) expect(pg.queryMock).toHaveBeenCalledWith(`select "users"."name" as "users.name", "users"."age" as "users.age" from "users" where "users"."name" like $1 limit $2`, ["John%", 5000])
}) })
}) })

View File

@ -322,7 +322,7 @@ class LinkController {
// remove schema from other table // remove schema from other table
let linkedTable = await this._db.get(field.tableId) let linkedTable = await this._db.get(field.tableId)
delete linkedTable.schema[field.fieldName] delete linkedTable.schema[field.fieldName]
this._db.put(linkedTable) await this._db.put(linkedTable)
} }
/** /**

View File

@ -151,7 +151,9 @@ function buildRead(knex: Knex, json: QueryJson, limit: number): KnexQuery {
} }
// handle select // handle select
if (resource.fields && resource.fields.length > 0) { if (resource.fields && resource.fields.length > 0) {
query = query.select(resource.fields) // select the resources as the format "table.columnName" - this is what is provided
// by the resource builder further up
query = query.select(resource.fields.map(field => `${field} as ${field}`))
} else { } else {
query = query.select("*") query = query.select("*")
} }

View File

@ -31,8 +31,8 @@ const INTEGRATIONS = {
[SourceNames.MONGODB]: mongodb.integration, [SourceNames.MONGODB]: mongodb.integration,
[SourceNames.ELASTICSEARCH]: elasticsearch.integration, [SourceNames.ELASTICSEARCH]: elasticsearch.integration,
[SourceNames.COUCHDB]: couchdb.integration, [SourceNames.COUCHDB]: couchdb.integration,
[SourceNames.SQL_SERVER]: s3.integration, [SourceNames.SQL_SERVER]: sqlServer.integration,
[SourceNames.S3]: sqlServer.integration, [SourceNames.S3]: s3.integration,
[SourceNames.AIRTABLE]: airtable.integration, [SourceNames.AIRTABLE]: airtable.integration,
[SourceNames.MYSQL]: mysql.integration, [SourceNames.MYSQL]: mysql.integration,
[SourceNames.ARANGODB]: arangodb.integration, [SourceNames.ARANGODB]: arangodb.integration,

View File

@ -242,7 +242,7 @@ module MySQLModule {
const input = this._query(json, { disableReturning: true }) const input = this._query(json, { disableReturning: true })
let row let row
// need to manage returning, a feature mySQL can't do // need to manage returning, a feature mySQL can't do
if (operation === "awdawd") { if (operation === operation.DELETE) {
row = this.getReturningRow(json) row = this.getReturningRow(json)
} }
const results = await internalQuery(this.client, input, false) const results = await internalQuery(this.client, input, false)

View File

@ -62,12 +62,13 @@ describe("SQL query builder", () => {
}) })
it("should test a read with specific columns", () => { it("should test a read with specific columns", () => {
const nameProp = `${TABLE_NAME}.name`, ageProp = `${TABLE_NAME}.age`
const query = sql._query(generateReadJson({ const query = sql._query(generateReadJson({
fields: ["name", "age"] fields: [nameProp, ageProp]
})) }))
expect(query).toEqual({ expect(query).toEqual({
bindings: [limit], bindings: [limit],
sql: `select "name", "age" from "${TABLE_NAME}" limit $1` sql: `select "${TABLE_NAME}"."name" as "${nameProp}", "${TABLE_NAME}"."age" as "${ageProp}" from "${TABLE_NAME}" limit $1`
}) })
}) })

View File

@ -40,8 +40,13 @@ export function breakRowIdField(_id: string): any[] {
// have to replace on the way back as we swapped out the double quotes // have to replace on the way back as we swapped out the double quotes
// when encoding, but JSON can't handle the single quotes // when encoding, but JSON can't handle the single quotes
const decoded: string = decodeURIComponent(_id).replace(/'/g, '"') const decoded: string = decodeURIComponent(_id).replace(/'/g, '"')
try {
const parsed = JSON.parse(decoded) const parsed = JSON.parse(decoded)
return Array.isArray(parsed) ? parsed : [parsed] return Array.isArray(parsed) ? parsed : [parsed]
} catch (err) {
// wasn't json - likely was handlebars for a many to many
return [_id]
}
} }
export function convertType(type: string, map: { [key: string]: any }) { export function convertType(type: string, map: { [key: string]: any }) {

View File

@ -10,7 +10,7 @@ const CouchDB = require("../db")
module.exports = async (ctx, next) => { module.exports = async (ctx, next) => {
// try to get the appID from the request // try to get the appID from the request
const requestAppId = getAppId(ctx) let requestAppId = getAppId(ctx)
// get app cookie if it exists // get app cookie if it exists
let appCookie = null let appCookie = null
try { try {
@ -29,6 +29,8 @@ module.exports = async (ctx, next) => {
clearCookie(ctx, Cookies.CurrentApp) clearCookie(ctx, Cookies.CurrentApp)
return next() return next()
} }
// if the request app ID wasn't set, update it with the cookie
requestAppId = requestAppId || appId
} }
let appId, let appId,

View File

@ -1,5 +1,6 @@
const env = require("../environment") const env = require("../environment")
const { OBJ_STORE_DIRECTORY, ObjectStoreBuckets } = require("../constants") const { OBJ_STORE_DIRECTORY } = require("../constants")
const { getAllApps } = require("@budibase/auth/db")
const { sanitizeKey } = require("@budibase/auth/src/objectStore") const { sanitizeKey } = require("@budibase/auth/src/objectStore")
const BB_CDN = "https://cdn.app.budi.live/assets" const BB_CDN = "https://cdn.app.budi.live/assets"
@ -52,6 +53,6 @@ exports.clientLibraryPath = appId => {
exports.attachmentsRelativeURL = attachmentKey => { exports.attachmentsRelativeURL = attachmentKey => {
return exports.checkSlashesInUrl( return exports.checkSlashesInUrl(
`/${ObjectStoreBuckets.APPS}/${attachmentKey}` `${exports.objectStoreUrl()}/${attachmentKey}`
) )
} }

View File

@ -184,8 +184,14 @@ exports.inputProcessing = (user = {}, table, row) => {
} }
continue continue
} }
// specific case to delete formula values if they get saved
// type coercion cannot completely remove the field, so have to do it here
if (field.type === FieldTypes.FORMULA) {
delete clonedRow[key]
} else {
clonedRow[key] = exports.coerce(value, field.type) clonedRow[key] = exports.coerce(value, field.type)
} }
}
// handle auto columns - this returns an object like {table, row} // handle auto columns - this returns an object like {table, row}
return processAutoColumn(user, copiedTable, clonedRow) return processAutoColumn(user, copiedTable, clonedRow)
} }

View File

@ -29,11 +29,11 @@
"keywords": [ "keywords": [
"svelte" "svelte"
], ],
"version": "0.9.99-alpha.4", "version": "0.9.102",
"license": "MIT", "license": "MIT",
"gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc", "gitHead": "d1836a898cab3f8ab80ee6d8f42be1a9eed7dcdc",
"dependencies": { "dependencies": {
"@budibase/bbui": "^0.9.99-alpha.4", "@budibase/bbui": "^0.9.102",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",
"@spectrum-css/link": "^3.1.3", "@spectrum-css/link": "^3.1.3",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"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,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "0.9.99-alpha.4", "version": "0.9.102",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.js", "main": "src/index.js",
"repository": { "repository": {
@ -23,8 +23,8 @@
"author": "Budibase", "author": "Budibase",
"license": "AGPL-3.0-or-later", "license": "AGPL-3.0-or-later",
"dependencies": { "dependencies": {
"@budibase/auth": "^0.9.99-alpha.4", "@budibase/auth": "^0.9.102",
"@budibase/string-templates": "^0.9.99-alpha.4", "@budibase/string-templates": "^0.9.102",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",
"aws-sdk": "^2.811.0", "aws-sdk": "^2.811.0",