Merge branch 'develop' into view-not-set
This commit is contained in:
commit
5b8b314476
95
README.md
95
README.md
|
@ -8,18 +8,19 @@
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<h3 align="center">
|
<h3 align="center">
|
||||||
Build, automate and self-host internal tools in minutes
|
The low code platform you'll enjoy using
|
||||||
</h3>
|
</h3>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
Budibase is an open-source low-code platform, helping developers and IT professionals build, automate, and ship internal tools on their own infrastructure in minutes.
|
Budibase is an open source low-code platform, and the easiest way to build internal tools that improve productivity.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3 align="center">
|
<h3 align="center">
|
||||||
🤖 🎨 🚀
|
🤖 🎨 🚀
|
||||||
</h3>
|
</h3>
|
||||||
|
<br>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img alt="Budibase design ui" src="https://i.imgur.com/5BnXPsN.png">
|
<img alt="Budibase design ui" src="https://res.cloudinary.com/daog6scxm/image/upload/v1633524049/ui/design-ui-wide-mobile_gdaveq.jpg">
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
|
@ -65,68 +66,25 @@
|
||||||
|
|
||||||
- **Admin paradise.** Budibase is made to scale. With Budibase, you can self-host on your own infrastructure and globally manage users, onboarding, SMTP, apps, groups, theming and more. You can also provide users/groups with an app portal and disseminate user-management to the group manager.
|
- **Admin paradise.** Budibase is made to scale. With Budibase, you can self-host on your own infrastructure and globally manage users, onboarding, SMTP, apps, groups, theming and more. You can also provide users/groups with an app portal and disseminate user-management to the group manager.
|
||||||
|
|
||||||
<br />
|
<br /><br /><br />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## 🏁 Get started
|
## 🏁 Get started
|
||||||
Currently there are two ways to get started with Budibase; Digital Ocean, and Docker.
|
|
||||||
|
<img src="https://res.cloudinary.com/daog6scxm/image/upload/v1634808888/logo/deploy_npl9za.png" />
|
||||||
|
|
||||||
|
Deploy Budibase self-Hosted in your existing infrastructure, using Docker, Kubernetes, and Digital Ocean.
|
||||||
|
Or use Budibase Cloud if you don't need to self-host, and would like to get started quickly.
|
||||||
|
|
||||||
|
### [Get started with Budibase](https://budibase.com)
|
||||||
|
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
### Get started with Digital Ocean
|
|
||||||
The easiest and quickest way to get started, is to use Digital Ocean:
|
|
||||||
<a href="https://marketplace.digitalocean.com/apps/budibase">1-click Digital Ocean deploy</a>
|
|
||||||
|
|
||||||
<a href="https://marketplace.digitalocean.com/apps/budibase">
|
|
||||||
<img src="https://user-images.githubusercontent.com/552074/87779219-5c3b7600-c824-11ea-9898-981a8ba94f6c.png" alt="digital ocean badge">
|
|
||||||
</a>
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
### Get started with Docker
|
|
||||||
To get started, you must have docker and docker compose installed on your machine.
|
|
||||||
Once you have Docker installed, the process takes 5 minutes, with these four steps:
|
|
||||||
|
|
||||||
1. Install the Budibase CLI.
|
|
||||||
|
|
||||||
```
|
|
||||||
$ npm i -g @budibase/cli
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
2. Setup Budibase (select where to store Budibase, and the port to run it on)
|
|
||||||
|
|
||||||
```
|
|
||||||
budi hosting --init
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
3. Run Budibase
|
|
||||||
|
|
||||||
```
|
|
||||||
budi hosting --start
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
4. Create your admin user
|
|
||||||
|
|
||||||
Enter the email and password for the new admin user.
|
|
||||||
|
|
||||||
Done! You are now ready to build powerful internal tools in minutes. For additional information on how to get started and learn Budibase, visit our [docs](https://docs.budibase.com/getting-started).
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## 🎓 Learning Budibase
|
## 🎓 Learning Budibase
|
||||||
|
|
||||||
The Budibase documentation [lives here](https://docs.budibase.com).
|
The Budibase documentation [lives here](https://docs.budibase.com).
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
|
@ -134,22 +92,17 @@ The Budibase documentation [lives here](https://docs.budibase.com).
|
||||||
|
|
||||||
If you have a question or would like to talk with other Budibase users and join our community, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions)
|
If you have a question or would like to talk with other Budibase users and join our community, please hop over to [Github discussions](https://github.com/Budibase/budibase/discussions)
|
||||||
|
|
||||||
<img src="https://d33wubrfki0l68.cloudfront.net/e9241201fd89f9abbbdaac4fe44bb16312752abe/84013/img/hero-images/community.webp" />
|
<br /><br /><br />
|
||||||
|
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## ❗ Code of conduct
|
## ❗ Code of conduct
|
||||||
|
|
||||||
Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it.
|
Budibase is dedicated to providing a welcoming, diverse, and harrassment-free experience for everyone. We expect everyone in the Budibase community to abide by our [**Code of Conduct**](https://github.com/Budibase/budibase/blob/HEAD/.github/CODE_OF_CONDUCT.md). Please read it.
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br />
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
## 🙌 Contributing to Budibase
|
## 🙌 Contributing to Budibase
|
||||||
|
|
||||||
|
@ -168,32 +121,22 @@ Budibase is a monorepo managed by lerna. Lerna manages the building and publishi
|
||||||
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
|
- [packages/server](https://github.com/Budibase/budibase/tree/HEAD/packages/server) - The budibase server. This Koa app is responsible for serving the JS for the builder and budibase apps, as well as providing the API for interaction with the database and file system.
|
||||||
|
|
||||||
For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
|
For more information, see [CONTRIBUTING.md](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md)
|
||||||
<br /><br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
|
|
||||||
## 📝 License
|
## 📝 License
|
||||||
|
|
||||||
Budibase is open-source, licensed as [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). The client and component libraries are licensed as [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - so the apps that you build can be licensed however you like.
|
Budibase is open-source, licensed as [GPL v3](https://www.gnu.org/licenses/gpl-3.0.en.html). The client and component libraries are licensed as [MPL](https://directory.fsf.org/wiki/License:MPL-2.0) - so the apps that you build can be licensed however you like.
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
## ⭐ Stargazers over time
|
## ⭐ Stargazers over time
|
||||||
|
|
||||||
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
|
[![Stargazers over time](https://starchart.cc/Budibase/budibase.svg)](https://starchart.cc/Budibase/budibase)
|
||||||
|
|
||||||
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
|
If you are having issues between updates of the builder, please use the guide [here](https://github.com/Budibase/budibase/blob/HEAD/.github/CONTRIBUTING.md#troubleshooting) to clear down your environment.
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<br /><br />
|
<br /><br />
|
||||||
|
|
||||||
## Contributors ✨
|
## Contributors ✨
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/auth",
|
"name": "@budibase/auth",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"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",
|
||||||
|
|
|
@ -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.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"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",
|
||||||
|
|
|
@ -31,7 +31,11 @@
|
||||||
|
|
||||||
const handleChange = event => {
|
const handleChange = event => {
|
||||||
const [dates] = event.detail
|
const [dates] = event.detail
|
||||||
dispatch("change", dates[0])
|
let newValue = dates[0]
|
||||||
|
if (newValue) {
|
||||||
|
newValue = newValue.toISOString()
|
||||||
|
}
|
||||||
|
dispatch("change", newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
const clearDateOnBackspace = event => {
|
const clearDateOnBackspace = event => {
|
||||||
|
@ -57,11 +61,38 @@
|
||||||
const els = document.querySelectorAll(`#${flatpickrId} input`)
|
const els = document.querySelectorAll(`#${flatpickrId} input`)
|
||||||
els.forEach(el => el.blur())
|
els.forEach(el => el.blur())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const parseDate = val => {
|
||||||
|
if (!val) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
let date
|
||||||
|
if (val instanceof Date) {
|
||||||
|
// Use real date obj if already parsed
|
||||||
|
date = val
|
||||||
|
} else if (isNaN(val)) {
|
||||||
|
// Treat as date string of some sort
|
||||||
|
date = new Date(val)
|
||||||
|
} else {
|
||||||
|
// Treat as numerical timestamp
|
||||||
|
date = new Date(parseInt(val))
|
||||||
|
}
|
||||||
|
const time = date.getTime()
|
||||||
|
if (isNaN(time)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
// By rounding to the nearest second we avoid locking up in an endless
|
||||||
|
// loop in the builder, caused by potentially enriching {{ now }} to every
|
||||||
|
// millisecond.
|
||||||
|
return new Date(Math.floor(time / 1000) * 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
$: console.log(value)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Flatpickr
|
<Flatpickr
|
||||||
bind:flatpickr
|
bind:flatpickr
|
||||||
{value}
|
value={parseDate(value)}
|
||||||
on:open={onOpen}
|
on:open={onOpen}
|
||||||
on:close={onClose}
|
on:close={onClose}
|
||||||
options={flatpickrOptions}
|
options={flatpickrOptions}
|
||||||
|
|
|
@ -13,10 +13,10 @@
|
||||||
export let appendTo = undefined
|
export let appendTo = undefined
|
||||||
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
const onChange = e => {
|
const onChange = e => {
|
||||||
const isoString = e.detail.toISOString()
|
value = e.detail
|
||||||
value = isoString
|
dispatch("change", e.detail)
|
||||||
dispatch("change", isoString)
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -65,10 +65,10 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.169-alpha.4",
|
"@budibase/bbui": "^0.9.169-alpha.9",
|
||||||
"@budibase/client": "^0.9.169-alpha.4",
|
"@budibase/client": "^0.9.169-alpha.9",
|
||||||
"@budibase/colorpicker": "1.1.2",
|
"@budibase/colorpicker": "1.1.2",
|
||||||
"@budibase/string-templates": "^0.9.169-alpha.4",
|
"@budibase/string-templates": "^0.9.169-alpha.9",
|
||||||
"@sentry/browser": "6.0.0",
|
"@sentry/browser": "6.0.0",
|
||||||
"@spectrum-css/page": "^3.0.1",
|
"@spectrum-css/page": "^3.0.1",
|
||||||
"@spectrum-css/vars": "^3.0.1",
|
"@spectrum-css/vars": "^3.0.1",
|
||||||
|
|
|
@ -472,7 +472,7 @@ function bindingReplacement(bindableProperties, textWithBindings, convertTo) {
|
||||||
idx = searchString.indexOf(from)
|
idx = searchString.indexOf(from)
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
let end = idx + from.length,
|
let end = idx + from.length,
|
||||||
searchReplace = Array(binding[convertTo].length).join("*")
|
searchReplace = Array(binding[convertTo].length + 1).join("*")
|
||||||
// blank out parts of the search string
|
// blank out parts of the search string
|
||||||
searchString = replaceBetween(searchString, idx, end, searchReplace)
|
searchString = replaceBetween(searchString, idx, end, searchReplace)
|
||||||
newBoundValue = replaceBetween(
|
newBoundValue = replaceBetween(
|
||||||
|
|
|
@ -90,34 +90,30 @@
|
||||||
|
|
||||||
function isMultipleChoice(field) {
|
function isMultipleChoice(field) {
|
||||||
return (
|
return (
|
||||||
(viewTable.schema[field].constraints &&
|
viewTable.schema[field]?.constraints?.inclusion?.length ||
|
||||||
viewTable.schema[field].constraints.inclusion &&
|
viewTable.schema[field]?.type === "boolean"
|
||||||
viewTable.schema[field].constraints.inclusion.length) ||
|
|
||||||
viewTable.schema[field].type === "boolean"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fieldOptions(field) {
|
function fieldOptions(field) {
|
||||||
return viewTable.schema[field].type === "options"
|
return viewTable.schema[field]?.type === "options"
|
||||||
? viewTable.schema[field].constraints.inclusion
|
? viewTable.schema[field]?.constraints.inclusion
|
||||||
: [true, false]
|
: [true, false]
|
||||||
}
|
}
|
||||||
|
|
||||||
function isDate(field) {
|
function isDate(field) {
|
||||||
return viewTable.schema[field].type === "datetime"
|
return viewTable.schema[field]?.type === "datetime"
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNumber(field) {
|
function isNumber(field) {
|
||||||
return viewTable.schema[field].type === "number"
|
return viewTable.schema[field]?.type === "number"
|
||||||
}
|
}
|
||||||
|
|
||||||
const fieldChanged = filter => ev => {
|
const fieldChanged = filter => ev => {
|
||||||
// reset if type changed
|
// Reset if type changed
|
||||||
if (
|
const oldType = viewTable.schema[filter.key]?.type
|
||||||
filter.key &&
|
const newType = viewTable.schema[ev.detail]?.type
|
||||||
ev.detail &&
|
if (filter.key && ev.detail && oldType !== newType) {
|
||||||
viewTable.schema[filter.key].type !== viewTable.schema[ev.detail].type
|
|
||||||
) {
|
|
||||||
filter.value = ""
|
filter.value = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,6 +66,11 @@
|
||||||
const checkValidity = async (values, validator) => {
|
const checkValidity = async (values, validator) => {
|
||||||
const obj = object().shape(validator)
|
const obj = object().shape(validator)
|
||||||
Object.keys(validator).forEach(key => ($errors[key] = null))
|
Object.keys(validator).forEach(key => ($errors[key] = null))
|
||||||
|
if (template?.fromFile && values.file == null) {
|
||||||
|
valid = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await obj.validate(values, { abortEarly: false })
|
await obj.validate(values, { abortEarly: false })
|
||||||
} catch (validationErrors) {
|
} catch (validationErrors) {
|
||||||
|
@ -73,6 +78,7 @@
|
||||||
$errors[error.path] = capitalise(error.message)
|
$errors[error.path] = capitalise(error.message)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = await obj.isValid(values)
|
valid = await obj.isValid(values)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/cli",
|
"name": "@budibase/cli",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"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": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"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,9 +19,9 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^0.9.169-alpha.4",
|
"@budibase/bbui": "^0.9.169-alpha.9",
|
||||||
"@budibase/standard-components": "^0.9.139",
|
"@budibase/standard-components": "^0.9.139",
|
||||||
"@budibase/string-templates": "^0.9.169-alpha.4",
|
"@budibase/string-templates": "^0.9.169-alpha.9",
|
||||||
"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"
|
||||||
|
|
|
@ -12,31 +12,6 @@
|
||||||
|
|
||||||
let fieldState
|
let fieldState
|
||||||
let fieldApi
|
let fieldApi
|
||||||
|
|
||||||
const parseDate = val => {
|
|
||||||
if (!val) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
let date
|
|
||||||
if (val instanceof Date) {
|
|
||||||
// Use real date obj if already parsed
|
|
||||||
date = val
|
|
||||||
} else if (isNaN(val)) {
|
|
||||||
// Treat as date string of some sort
|
|
||||||
date = new Date(val)
|
|
||||||
} else {
|
|
||||||
// Treat as numerical timestamp
|
|
||||||
date = new Date(parseInt(val))
|
|
||||||
}
|
|
||||||
const time = date.getTime()
|
|
||||||
if (isNaN(time)) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
// By rounding to the nearest second we avoid locking up in an endless
|
|
||||||
// loop in the builder, caused by potentially enriching {{ now }} to every
|
|
||||||
// millisecond.
|
|
||||||
return new Date(Math.floor(time / 1000) * 1000)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Field
|
<Field
|
||||||
|
@ -44,7 +19,7 @@
|
||||||
{field}
|
{field}
|
||||||
{disabled}
|
{disabled}
|
||||||
{validation}
|
{validation}
|
||||||
defaultValue={parseDate(defaultValue)}
|
{defaultValue}
|
||||||
type="datetime"
|
type="datetime"
|
||||||
bind:fieldState
|
bind:fieldState
|
||||||
bind:fieldApi
|
bind:fieldApi
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -68,9 +68,9 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.169-alpha.4",
|
"@budibase/auth": "^0.9.169-alpha.9",
|
||||||
"@budibase/client": "^0.9.169-alpha.4",
|
"@budibase/client": "^0.9.169-alpha.9",
|
||||||
"@budibase/string-templates": "^0.9.169-alpha.4",
|
"@budibase/string-templates": "^0.9.169-alpha.9",
|
||||||
"@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",
|
||||||
|
|
|
@ -9,11 +9,7 @@ const {
|
||||||
BudibaseInternalDB,
|
BudibaseInternalDB,
|
||||||
} = require("../../../db/utils")
|
} = require("../../../db/utils")
|
||||||
const { FieldTypes } = require("../../../constants")
|
const { FieldTypes } = require("../../../constants")
|
||||||
const { TableSaveFunctions, getExternalTable } = require("./utils")
|
const { TableSaveFunctions, getTable } = require("./utils")
|
||||||
const {
|
|
||||||
isExternalTable,
|
|
||||||
breakExternalTableId,
|
|
||||||
} = require("../../../integrations/utils")
|
|
||||||
|
|
||||||
exports.fetch = async function (ctx) {
|
exports.fetch = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
const db = new CouchDB(ctx.appId)
|
||||||
|
@ -48,14 +44,8 @@ exports.fetch = async function (ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.find = async function (ctx) {
|
exports.find = async function (ctx) {
|
||||||
const db = new CouchDB(ctx.appId)
|
|
||||||
const tableId = ctx.params.id
|
const tableId = ctx.params.id
|
||||||
if (isExternalTable(tableId)) {
|
ctx.body = await getTable(ctx.appId, tableId)
|
||||||
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
||||||
ctx.body = await getExternalTable(ctx.appId, datasourceId, tableName)
|
|
||||||
} else {
|
|
||||||
ctx.body = await db.get(ctx.params.id)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.save = async function (ctx) {
|
exports.save = async function (ctx) {
|
||||||
|
|
|
@ -9,6 +9,12 @@ const { isEqual } = require("lodash/fp")
|
||||||
const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
|
const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
|
||||||
const { inputProcessing } = require("../../../utilities/rowProcessor")
|
const { inputProcessing } = require("../../../utilities/rowProcessor")
|
||||||
const { USERS_TABLE_SCHEMA } = require("../../../constants")
|
const { USERS_TABLE_SCHEMA } = require("../../../constants")
|
||||||
|
const {
|
||||||
|
isExternalTable,
|
||||||
|
breakExternalTableId,
|
||||||
|
} = require("../../../integrations/utils")
|
||||||
|
const { getViews, saveView } = require("../view/utils")
|
||||||
|
const viewTemplate = require("../view/viewBuilder")
|
||||||
|
|
||||||
exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
||||||
let updatedRows = []
|
let updatedRows = []
|
||||||
|
@ -21,6 +27,7 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
||||||
}
|
}
|
||||||
// check for renaming of columns or deleted columns
|
// check for renaming of columns or deleted columns
|
||||||
if (rename || deletedColumns.length !== 0) {
|
if (rename || deletedColumns.length !== 0) {
|
||||||
|
// Update all rows
|
||||||
const rows = await db.allDocs(
|
const rows = await db.allDocs(
|
||||||
getRowParams(updatedTable._id, null, {
|
getRowParams(updatedTable._id, null, {
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
|
@ -35,6 +42,9 @@ exports.checkForColumnUpdates = async (db, oldTable, updatedTable) => {
|
||||||
}
|
}
|
||||||
return doc
|
return doc
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Update views
|
||||||
|
await exports.checkForViewUpdates(db, updatedTable, rename, deletedColumns)
|
||||||
delete updatedTable._rename
|
delete updatedTable._rename
|
||||||
}
|
}
|
||||||
return { rows: updatedRows, table: updatedTable }
|
return { rows: updatedRows, table: updatedTable }
|
||||||
|
@ -223,4 +233,86 @@ exports.getExternalTable = async (appId, datasourceId, tableName) => {
|
||||||
return entities[tableName]
|
return entities[tableName]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getTable = async (appId, tableId) => {
|
||||||
|
const db = new CouchDB(appId)
|
||||||
|
if (isExternalTable(tableId)) {
|
||||||
|
let { datasourceId, tableName } = breakExternalTableId(tableId)
|
||||||
|
return exports.getExternalTable(appId, datasourceId, tableName)
|
||||||
|
} else {
|
||||||
|
return db.get(tableId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
|
||||||
|
const views = await getViews(db)
|
||||||
|
const tableViews = views.filter(view => view.meta.tableId === table._id)
|
||||||
|
|
||||||
|
// Check each table view to see if impacted by this table action
|
||||||
|
for (let view of tableViews) {
|
||||||
|
let needsUpdated = false
|
||||||
|
|
||||||
|
// First check for renames, otherwise check for deletions
|
||||||
|
if (rename) {
|
||||||
|
// Update calculation field if required
|
||||||
|
if (view.meta.field === rename.old) {
|
||||||
|
view.meta.field = rename.updated
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update group by field if required
|
||||||
|
if (view.meta.groupBy === rename.old) {
|
||||||
|
view.meta.groupBy = rename.updated
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update filters if required
|
||||||
|
if (view.meta.filters) {
|
||||||
|
view.meta.filters.forEach(filter => {
|
||||||
|
if (filter.key === rename.old) {
|
||||||
|
filter.key = rename.updated
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else if (deletedColumns) {
|
||||||
|
deletedColumns.forEach(column => {
|
||||||
|
// Remove calculation statement if required
|
||||||
|
if (view.meta.field === column) {
|
||||||
|
delete view.meta.field
|
||||||
|
delete view.meta.calculation
|
||||||
|
delete view.meta.groupBy
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove group by field if required
|
||||||
|
if (view.meta.groupBy === column) {
|
||||||
|
delete view.meta.groupBy
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove filters referencing deleted field if required
|
||||||
|
if (view.meta.filters && view.meta.filters.length) {
|
||||||
|
const initialLength = view.meta.filters.length
|
||||||
|
view.meta.filters = view.meta.filters.filter(filter => {
|
||||||
|
return filter.key !== column
|
||||||
|
})
|
||||||
|
if (initialLength !== view.meta.filters.length) {
|
||||||
|
needsUpdated = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update view if required
|
||||||
|
if (needsUpdated) {
|
||||||
|
const newViewTemplate = viewTemplate(view.meta)
|
||||||
|
await saveView(db, null, view.name, newViewTemplate)
|
||||||
|
if (!newViewTemplate.meta.schema) {
|
||||||
|
newViewTemplate.meta.schema = table.schema
|
||||||
|
}
|
||||||
|
table.views[view.name] = newViewTemplate.meta
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
exports.TableSaveFunctions = TableSaveFunctions
|
exports.TableSaveFunctions = TableSaveFunctions
|
||||||
|
|
|
@ -1,9 +1,4 @@
|
||||||
const CouchDB = require("../db")
|
const { getTable } = require("../api/controllers/table/utils")
|
||||||
const {
|
|
||||||
isExternalTable,
|
|
||||||
breakExternalTableId,
|
|
||||||
} = require("../integrations/utils")
|
|
||||||
const { getExternalTable } = require("../api/controllers/table/utils")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When values are input to the system generally they will be of type string as this is required for template strings.
|
* When values are input to the system generally they will be of type string as this is required for template strings.
|
||||||
|
@ -21,7 +16,7 @@ const { getExternalTable } = require("../api/controllers/table/utils")
|
||||||
* @returns {object} The inputs object which has had all the various types supported by this function converted to their
|
* @returns {object} The inputs object which has had all the various types supported by this function converted to their
|
||||||
* primitive types.
|
* primitive types.
|
||||||
*/
|
*/
|
||||||
module.exports.cleanInputValues = (inputs, schema) => {
|
exports.cleanInputValues = (inputs, schema) => {
|
||||||
if (schema == null) {
|
if (schema == null) {
|
||||||
return inputs
|
return inputs
|
||||||
}
|
}
|
||||||
|
@ -63,30 +58,11 @@ module.exports.cleanInputValues = (inputs, schema) => {
|
||||||
* @param {object} row The input row structure which requires clean-up after having been through template statements.
|
* @param {object} row The input row structure which requires clean-up after having been through template statements.
|
||||||
* @returns {Promise<Object>} The cleaned up rows object, will should now have all the required primitive types.
|
* @returns {Promise<Object>} The cleaned up rows object, will should now have all the required primitive types.
|
||||||
*/
|
*/
|
||||||
module.exports.cleanUpRow = async (appId, tableId, row) => {
|
exports.cleanUpRow = async (appId, tableId, row) => {
|
||||||
const db = new CouchDB(appId)
|
let table = await getTable(appId, tableId)
|
||||||
let table
|
return exports.cleanInputValues(row, { properties: table.schema })
|
||||||
if (isExternalTable(tableId)) {
|
|
||||||
const { datasourceId, tableName } = breakExternalTableId(tableId)
|
|
||||||
table = await getExternalTable(appId, datasourceId, tableName)
|
|
||||||
} else {
|
|
||||||
table = await db.get(tableId)
|
|
||||||
}
|
|
||||||
|
|
||||||
return module.exports.cleanInputValues(row, { properties: table.schema })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
exports.getError = err => {
|
||||||
* A utility function for the cleanUpRow, which can be used if only the row ID is known (not the table ID) to clean
|
return typeof err !== "string" ? err.toString() : err
|
||||||
* up a row after template statements have been replaced. This is specifically useful for the update row action.
|
|
||||||
*
|
|
||||||
* @param {string} appId The instance which the Table/Table is contained under.
|
|
||||||
* @param {string} rowId The ID of the row from which the tableId will be extracted, to get the Table/Table schema.
|
|
||||||
* @param {object} row The input row structure which requires clean-up after having been through template statements.
|
|
||||||
* @returns {Promise<Object>} The cleaned up rows object, which will now have all the required primitive types.
|
|
||||||
*/
|
|
||||||
module.exports.cleanUpRowById = async (appId, rowId, row) => {
|
|
||||||
const db = new CouchDB(appId)
|
|
||||||
const foundRow = await db.get(rowId)
|
|
||||||
return module.exports.cleanUpRow(appId, foundRow.tableId, row)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const { execSync } = require("child_process")
|
const { execSync } = require("child_process")
|
||||||
const { processStringSync } = require("@budibase/string-templates")
|
const { processStringSync } = require("@budibase/string-templates")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
exports.definition = {
|
exports.definition = {
|
||||||
name: "Bash Scripting",
|
name: "Bash Scripting",
|
||||||
|
@ -63,7 +64,7 @@ exports.run = async function ({ inputs, context }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,7 +97,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const rowController = require("../../api/controllers/row")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const usage = require("../../utilities/usageQuota")
|
const usage = require("../../utilities/usageQuota")
|
||||||
const { buildCtx } = require("./utils")
|
const { buildCtx } = require("./utils")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
exports.definition = {
|
exports.definition = {
|
||||||
description: "Delete a row from your database",
|
description: "Delete a row from your database",
|
||||||
|
@ -85,7 +86,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const queryController = require("../../api/controllers/query")
|
const queryController = require("../../api/controllers/query")
|
||||||
const { buildCtx } = require("./utils")
|
const { buildCtx } = require("./utils")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
exports.definition = {
|
exports.definition = {
|
||||||
name: "External Data Connector",
|
name: "External Data Connector",
|
||||||
|
@ -74,7 +75,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const scriptController = require("../../api/controllers/script")
|
const scriptController = require("../../api/controllers/script")
|
||||||
const { buildCtx } = require("./utils")
|
const { buildCtx } = require("./utils")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
exports.definition = {
|
exports.definition = {
|
||||||
name: "JS Scripting",
|
name: "JS Scripting",
|
||||||
|
@ -63,7 +64,7 @@ exports.run = async function ({ inputs, appId, context, emitter }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const fetch = require("node-fetch")
|
const fetch = require("node-fetch")
|
||||||
const { getFetchResponse } = require("./utils")
|
const { getFetchResponse } = require("./utils")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
const RequestType = {
|
const RequestType = {
|
||||||
POST: "POST",
|
POST: "POST",
|
||||||
|
@ -127,7 +128,7 @@ exports.run = async function ({ inputs }) {
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ const rowController = require("../../api/controllers/row")
|
||||||
const tableController = require("../../api/controllers/table")
|
const tableController = require("../../api/controllers/table")
|
||||||
const { FieldTypes } = require("../../constants")
|
const { FieldTypes } = require("../../constants")
|
||||||
const { buildCtx } = require("./utils")
|
const { buildCtx } = require("./utils")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
const SortOrders = {
|
const SortOrders = {
|
||||||
ASCENDING: "ascending",
|
ASCENDING: "ascending",
|
||||||
|
@ -110,7 +111,7 @@ exports.run = async function ({ inputs, appId }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
const { sendSmtpEmail } = require("../../utilities/workerRequests")
|
const { sendSmtpEmail } = require("../../utilities/workerRequests")
|
||||||
|
const automationUtils = require("../automationUtils")
|
||||||
|
|
||||||
exports.definition = {
|
exports.definition = {
|
||||||
description: "Send an email using SMTP",
|
description: "Send an email using SMTP",
|
||||||
|
@ -61,7 +62,7 @@ exports.run = async function ({ inputs }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
const tableId = inputs.row.tableId
|
||||||
|
|
||||||
// clear any falsy properties so that they aren't updated
|
// clear any falsy properties so that they aren't updated
|
||||||
for (let propKey of Object.keys(inputs.row)) {
|
for (let propKey of Object.keys(inputs.row)) {
|
||||||
|
@ -80,15 +81,14 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
},
|
},
|
||||||
params: {
|
params: {
|
||||||
rowId: inputs.rowId,
|
rowId: inputs.rowId,
|
||||||
|
tableId: tableId,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
inputs.row = await automationUtils.cleanUpRowById(
|
if (tableId) {
|
||||||
appId,
|
inputs.row = await automationUtils.cleanUpRow(appId, tableId, inputs.row)
|
||||||
inputs.rowId,
|
}
|
||||||
inputs.row
|
|
||||||
)
|
|
||||||
await rowController.patch(ctx)
|
await rowController.patch(ctx)
|
||||||
return {
|
return {
|
||||||
row: ctx.body,
|
row: ctx.body,
|
||||||
|
@ -100,7 +100,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: automationUtils.getError(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"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",
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/worker",
|
"name": "@budibase/worker",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.9.169-alpha.4",
|
"version": "0.9.169-alpha.9",
|
||||||
"description": "Budibase background service",
|
"description": "Budibase background service",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -29,8 +29,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/auth": "^0.9.169-alpha.4",
|
"@budibase/auth": "^0.9.169-alpha.9",
|
||||||
"@budibase/string-templates": "^0.9.169-alpha.4",
|
"@budibase/string-templates": "^0.9.169-alpha.9",
|
||||||
"@koa/router": "^8.0.0",
|
"@koa/router": "^8.0.0",
|
||||||
"@sentry/node": "^6.0.0",
|
"@sentry/node": "^6.0.0",
|
||||||
"@techpass/passport-openidconnect": "^0.3.0",
|
"@techpass/passport-openidconnect": "^0.3.0",
|
||||||
|
|
Loading…
Reference in New Issue