Merge branch 'next' into fix/route-preservation
This commit is contained in:
commit
0b36b98295
|
@ -7,10 +7,12 @@ on:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
- next
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- develop
|
- develop
|
||||||
|
- next
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
|
@ -52,8 +52,8 @@ jobs:
|
||||||
|
|
||||||
mac_certs: ${{ secrets.mac_certs }}
|
mac_certs: ${{ secrets.mac_certs }}
|
||||||
mac_certs_password: ${{ secrets.mac_certs_password }}
|
mac_certs_password: ${{ secrets.mac_certs_password }}
|
||||||
windows_certs: ${{ secrets.windows_certs }}
|
# windows_certs: ${{ secrets.windows_certs }}
|
||||||
windows_certs_password: ${{ secrets.windows_certs_password }}
|
# windows_certs_password: ${{ secrets.windows_certs_password }}
|
||||||
|
|
||||||
# release the app after building
|
# release the app after building
|
||||||
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
release: ${{ startsWith(github.ref, 'refs/tags/v') }}
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
</a>
|
</a>
|
||||||
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" />
|
<img src="https://img.shields.io/badge/Contributor%20Covenant-v2.0%20adopted-ff69b4.svg" alt="Code of conduct" />
|
||||||
<a href="https://codecov.io/gh/Budibase/budibase">
|
<a href="https://codecov.io/gh/Budibase/budibase">
|
||||||
<img src="https://codecov.io/gh/Budibase/budibase/branch/master/graph/badge.svg?token=E8W2ZFXQOH"/>
|
<img src="https://codecov.io/gh/Budibase/budibase/branch/next/graph/badge.svg?token=E8W2ZFXQOH"/>
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/builder",
|
"name": "@budibase/builder",
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
@ -64,9 +64,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.58.13",
|
"@budibase/bbui": "^1.58.13",
|
||||||
"@budibase/client": "^0.8.5",
|
"@budibase/client": "^0.8.9",
|
||||||
"@budibase/colorpicker": "1.1.2",
|
"@budibase/colorpicker": "1.1.2",
|
||||||
"@budibase/string-templates": "^0.8.5",
|
"@budibase/string-templates": "^0.8.9",
|
||||||
"@budibase/svelte-ag-grid": "^1.0.4",
|
"@budibase/svelte-ag-grid": "^1.0.4",
|
||||||
"@sentry/browser": "5.19.1",
|
"@sentry/browser": "5.19.1",
|
||||||
"@svelteschool/svelte-forms": "0.7.0",
|
"@svelteschool/svelte-forms": "0.7.0",
|
||||||
|
|
|
@ -7,9 +7,10 @@ const apiCall = method => async (
|
||||||
headers = { "Content-Type": "application/json" }
|
headers = { "Content-Type": "application/json" }
|
||||||
) => {
|
) => {
|
||||||
headers["x-budibase-app-id"] = svelteGet(store).appId
|
headers["x-budibase-app-id"] = svelteGet(store).appId
|
||||||
|
const json = headers["Content-Type"] === "application/json"
|
||||||
return await fetch(url, {
|
return await fetch(url, {
|
||||||
method: method,
|
method: method,
|
||||||
body: body && JSON.stringify(body),
|
body: json ? JSON.stringify(body) : body,
|
||||||
headers,
|
headers,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,11 @@
|
||||||
$: uneditable =
|
$: uneditable =
|
||||||
$backendUiStore.selectedTable?._id === TableNames.USERS &&
|
$backendUiStore.selectedTable?._id === TableNames.USERS &&
|
||||||
UNEDITABLE_USER_FIELDS.includes(field.name)
|
UNEDITABLE_USER_FIELDS.includes(field.name)
|
||||||
$: invalid = field.type === LINK_TYPE && !field.tableId
|
$: invalid =
|
||||||
|
(field.type === LINK_TYPE && !field.tableId) ||
|
||||||
|
Object.keys($backendUiStore.draftTable.schema).some(
|
||||||
|
key => key === field.name
|
||||||
|
)
|
||||||
|
|
||||||
// used to select what different options can be displayed for column type
|
// used to select what different options can be displayed for column type
|
||||||
$: canBeSearched =
|
$: canBeSearched =
|
||||||
|
|
|
@ -15,18 +15,11 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function processFiles(fileList) {
|
async function processFiles(fileList) {
|
||||||
const fileArray = Array.from(fileList)
|
let data = new FormData()
|
||||||
|
for (let i = 0; i < fileList.length; i++) {
|
||||||
const filesToProcess = fileArray.map(({ name, path, size, type }) => ({
|
data.append("file", fileList[i])
|
||||||
name,
|
}
|
||||||
path,
|
const response = await api.post(`/api/attachments/process`, data, {})
|
||||||
size,
|
|
||||||
type,
|
|
||||||
}))
|
|
||||||
|
|
||||||
const response = await api.post(`/api/attachments/process`, {
|
|
||||||
files: filesToProcess,
|
|
||||||
})
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,38 +1,42 @@
|
||||||
<script>
|
<script>
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from "svelte"
|
||||||
import Colorpicker from "@budibase/colorpicker"
|
import Colorpicker from "@budibase/colorpicker"
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let value
|
export let value
|
||||||
|
|
||||||
const WAIT = 150;
|
const WAIT = 150
|
||||||
|
|
||||||
function throttle(callback, wait, immediate = false) {
|
function throttle(callback, wait, immediate = false) {
|
||||||
let timeout = null
|
let timeout = null
|
||||||
let initialCall = true
|
let initialCall = true
|
||||||
|
|
||||||
return function() {
|
return function() {
|
||||||
const callNow = immediate && initialCall
|
const callNow = immediate && initialCall
|
||||||
const next = () => {
|
const next = () => {
|
||||||
callback.apply(this, arguments)
|
callback.apply(this, arguments)
|
||||||
timeout = null
|
timeout = null
|
||||||
}
|
}
|
||||||
|
|
||||||
if (callNow) {
|
if (callNow) {
|
||||||
initialCall = false
|
initialCall = false
|
||||||
next()
|
next()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timeout) {
|
if (!timeout) {
|
||||||
timeout = setTimeout(next, wait)
|
timeout = setTimeout(next, wait)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onChange = throttle(e => {
|
const onChange = throttle(
|
||||||
dispatch('change', e.detail)
|
e => {
|
||||||
}, WAIT, true)
|
dispatch("change", e.detail)
|
||||||
|
},
|
||||||
|
WAIT,
|
||||||
|
true
|
||||||
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Colorpicker value={value || '#C4C4C4'} on:change={onChange} />
|
<Colorpicker value={value || '#C4C4C4'} on:change={onChange} />
|
||||||
|
|
|
@ -123,13 +123,19 @@
|
||||||
async function createNewApp() {
|
async function createNewApp() {
|
||||||
submitting = true
|
submitting = true
|
||||||
try {
|
try {
|
||||||
// Create App
|
// Create form data to create app
|
||||||
const appResp = await post("/api/applications", {
|
let data = new FormData()
|
||||||
name: $createAppStore.values.applicationName,
|
data.append("name", $createAppStore.values.applicationName)
|
||||||
template,
|
data.append("useTemplate", template != null)
|
||||||
})
|
if (template) {
|
||||||
const appJson = await appResp.json()
|
data.append("templateName", template.name)
|
||||||
|
data.append("templateKey", template.key)
|
||||||
|
data.append("templateFile", template.file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create App
|
||||||
|
const appResp = await post("/api/applications", data, {})
|
||||||
|
const appJson = await appResp.json()
|
||||||
if (!appResp.ok) {
|
if (!appResp.ok) {
|
||||||
throw new Error(appJson.message)
|
throw new Error(appJson.message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Label, Heading, Input } from "@budibase/bbui"
|
import { Label, Heading, Input } from "@budibase/bbui"
|
||||||
import Dropzone from "components/common/Dropzone.svelte"
|
|
||||||
|
|
||||||
const BYTES_IN_MB = 1000000
|
const BYTES_IN_MB = 1000000
|
||||||
const FILE_SIZE_LIMIT = BYTES_IN_MB * 5
|
const FILE_SIZE_LIMIT = BYTES_IN_MB * 5
|
||||||
|
@ -20,8 +19,8 @@
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file = fileArray[0]
|
file = evt.target.files[0]
|
||||||
template.fileImportPath = file.path
|
template.file = file
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "cli",
|
"name": "cli",
|
||||||
"version": "0.8.4",
|
"version": "0.8.7",
|
||||||
"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": "src/index.js",
|
"bin": "src/index.js",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/client",
|
"name": "@budibase/client",
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"main": "dist/budibase-client.js",
|
"main": "dist/budibase-client.js",
|
||||||
"module": "dist/budibase-client.js",
|
"module": "dist/budibase-client.js",
|
||||||
|
@ -9,14 +9,13 @@
|
||||||
"dev:builder": "rollup -cw"
|
"dev:builder": "rollup -cw"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/string-templates": "^0.8.5",
|
"@budibase/string-templates": "^0.8.9",
|
||||||
"deep-equal": "^2.0.1",
|
|
||||||
"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"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/standard-components": "^0.8.5",
|
"@budibase/standard-components": "^0.8.9",
|
||||||
"@rollup/plugin-commonjs": "^16.0.0",
|
"@rollup/plugin-commonjs": "^16.0.0",
|
||||||
"@rollup/plugin-node-resolve": "^10.0.0",
|
"@rollup/plugin-node-resolve": "^10.0.0",
|
||||||
"fs-extra": "^8.1.0",
|
"fs-extra": "^8.1.0",
|
||||||
|
@ -30,5 +29,5 @@
|
||||||
"svelte": "^3.30.0",
|
"svelte": "^3.30.0",
|
||||||
"svelte-jester": "^1.0.6"
|
"svelte-jester": "^1.0.6"
|
||||||
},
|
},
|
||||||
"gitHead": "768a9d59da12cfefcd7ccaba05f975e878669504"
|
"gitHead": "1b95326b20d1352d36305910259228b96a683dc7"
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,8 +13,17 @@ const createNotificationStore = () => {
|
||||||
_notifications.set([])
|
_notifications.set([])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
let block = false
|
||||||
|
|
||||||
|
const blockNotifications = (timeout = 1000) => {
|
||||||
|
block = true
|
||||||
|
setTimeout(() => (block = false), timeout)
|
||||||
|
}
|
||||||
|
|
||||||
const send = (message, type = "default") => {
|
const send = (message, type = "default") => {
|
||||||
|
if (block) {
|
||||||
|
return
|
||||||
|
}
|
||||||
let _id = id()
|
let _id = id()
|
||||||
_notifications.update(state => {
|
_notifications.update(state => {
|
||||||
return [...state, { id: _id, type, message }]
|
return [...state, { id: _id, type, message }]
|
||||||
|
@ -36,6 +45,7 @@ const createNotificationStore = () => {
|
||||||
warning: msg => send(msg, "warning"),
|
warning: msg => send(msg, "warning"),
|
||||||
info: msg => send(msg, "info"),
|
info: msg => send(msg, "info"),
|
||||||
success: msg => send(msg, "success"),
|
success: msg => send(msg, "success"),
|
||||||
|
blockNotifications,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -150,11 +150,6 @@ ansi-styles@^3.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert "^1.9.0"
|
color-convert "^1.9.0"
|
||||||
|
|
||||||
array-filter@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83"
|
|
||||||
integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM=
|
|
||||||
|
|
||||||
asn1.js@^5.2.0:
|
asn1.js@^5.2.0:
|
||||||
version "5.4.1"
|
version "5.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||||
|
@ -182,13 +177,6 @@ asynckit@^0.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
|
||||||
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
|
||||||
|
|
||||||
available-typed-arrays@^1.0.0, available-typed-arrays@^1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
|
|
||||||
integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ==
|
|
||||||
dependencies:
|
|
||||||
array-filter "^1.0.0"
|
|
||||||
|
|
||||||
aws-sign2@~0.7.0:
|
aws-sign2@~0.7.0:
|
||||||
version "0.7.0"
|
version "0.7.0"
|
||||||
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8"
|
||||||
|
@ -507,26 +495,6 @@ decimal.js@^10.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231"
|
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.2.0.tgz#39466113a9e036111d02f82489b5fd6b0b5ed231"
|
||||||
integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==
|
integrity sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==
|
||||||
|
|
||||||
deep-equal@^2.0.1:
|
|
||||||
version "2.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.3.tgz#cad1c15277ad78a5c01c49c2dee0f54de8a6a7b0"
|
|
||||||
integrity sha512-Spqdl4H+ky45I9ByyJtXteOm9CaIrPmnIPmOhrkKGNYWeDgCvJ8jNYVCTjChxW4FqGuZnLHADc8EKRMX6+CgvA==
|
|
||||||
dependencies:
|
|
||||||
es-abstract "^1.17.5"
|
|
||||||
es-get-iterator "^1.1.0"
|
|
||||||
is-arguments "^1.0.4"
|
|
||||||
is-date-object "^1.0.2"
|
|
||||||
is-regex "^1.0.5"
|
|
||||||
isarray "^2.0.5"
|
|
||||||
object-is "^1.1.2"
|
|
||||||
object-keys "^1.1.1"
|
|
||||||
object.assign "^4.1.0"
|
|
||||||
regexp.prototype.flags "^1.3.0"
|
|
||||||
side-channel "^1.0.2"
|
|
||||||
which-boxed-primitive "^1.0.1"
|
|
||||||
which-collection "^1.0.1"
|
|
||||||
which-typed-array "^1.1.2"
|
|
||||||
|
|
||||||
deep-is@~0.1.3:
|
deep-is@~0.1.3:
|
||||||
version "0.1.3"
|
version "0.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
|
||||||
|
@ -544,13 +512,6 @@ deferred-leveldown@~0.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
abstract-leveldown "~0.12.1"
|
abstract-leveldown "~0.12.1"
|
||||||
|
|
||||||
define-properties@^1.1.2, define-properties@^1.1.3:
|
|
||||||
version "1.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
|
|
||||||
integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
|
|
||||||
dependencies:
|
|
||||||
object-keys "^1.0.12"
|
|
||||||
|
|
||||||
delayed-stream@~1.0.0:
|
delayed-stream@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
|
||||||
|
@ -615,63 +576,6 @@ error-ex@^1.3.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-arrayish "^0.2.1"
|
is-arrayish "^0.2.1"
|
||||||
|
|
||||||
es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5:
|
|
||||||
version "1.17.6"
|
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.6.tgz#9142071707857b2cacc7b89ecb670316c3e2d52a"
|
|
||||||
integrity sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==
|
|
||||||
dependencies:
|
|
||||||
es-to-primitive "^1.2.1"
|
|
||||||
function-bind "^1.1.1"
|
|
||||||
has "^1.0.3"
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
is-callable "^1.2.0"
|
|
||||||
is-regex "^1.1.0"
|
|
||||||
object-inspect "^1.7.0"
|
|
||||||
object-keys "^1.1.1"
|
|
||||||
object.assign "^4.1.0"
|
|
||||||
string.prototype.trimend "^1.0.1"
|
|
||||||
string.prototype.trimstart "^1.0.1"
|
|
||||||
|
|
||||||
es-abstract@^1.18.0-next.0:
|
|
||||||
version "1.18.0-next.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.0.tgz#b302834927e624d8e5837ed48224291f2c66e6fc"
|
|
||||||
integrity sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==
|
|
||||||
dependencies:
|
|
||||||
es-to-primitive "^1.2.1"
|
|
||||||
function-bind "^1.1.1"
|
|
||||||
has "^1.0.3"
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
is-callable "^1.2.0"
|
|
||||||
is-negative-zero "^2.0.0"
|
|
||||||
is-regex "^1.1.1"
|
|
||||||
object-inspect "^1.8.0"
|
|
||||||
object-keys "^1.1.1"
|
|
||||||
object.assign "^4.1.0"
|
|
||||||
string.prototype.trimend "^1.0.1"
|
|
||||||
string.prototype.trimstart "^1.0.1"
|
|
||||||
|
|
||||||
es-get-iterator@^1.1.0:
|
|
||||||
version "1.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
|
|
||||||
integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==
|
|
||||||
dependencies:
|
|
||||||
es-abstract "^1.17.4"
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
is-arguments "^1.0.4"
|
|
||||||
is-map "^2.0.1"
|
|
||||||
is-set "^2.0.1"
|
|
||||||
is-string "^1.0.5"
|
|
||||||
isarray "^2.0.5"
|
|
||||||
|
|
||||||
es-to-primitive@^1.2.1:
|
|
||||||
version "1.2.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
|
|
||||||
integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
|
|
||||||
dependencies:
|
|
||||||
is-callable "^1.1.4"
|
|
||||||
is-date-object "^1.0.1"
|
|
||||||
is-symbol "^1.0.2"
|
|
||||||
|
|
||||||
escape-string-regexp@^1.0.5:
|
escape-string-regexp@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||||
|
@ -762,7 +666,7 @@ fast-levenshtein@~2.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
|
||||||
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
|
||||||
|
|
||||||
foreach@^2.0.5, foreach@~2.0.1:
|
foreach@~2.0.1:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99"
|
||||||
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
|
integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k=
|
||||||
|
@ -859,11 +763,6 @@ has-flag@^4.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||||
|
|
||||||
has-symbols@^1.0.0, has-symbols@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
|
||||||
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
|
||||||
|
|
||||||
has@^1.0.3:
|
has@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
|
||||||
|
@ -956,31 +855,11 @@ ip-regex@^2.1.0:
|
||||||
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
|
resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9"
|
||||||
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
|
integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=
|
||||||
|
|
||||||
is-arguments@^1.0.4:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3"
|
|
||||||
integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==
|
|
||||||
|
|
||||||
is-arrayish@^0.2.1:
|
is-arrayish@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
|
||||||
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=
|
||||||
|
|
||||||
is-bigint@^1.0.0:
|
|
||||||
version "1.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4"
|
|
||||||
integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g==
|
|
||||||
|
|
||||||
is-boolean-object@^1.0.0:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e"
|
|
||||||
integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ==
|
|
||||||
|
|
||||||
is-callable@^1.1.4, is-callable@^1.2.0:
|
|
||||||
version "1.2.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.0.tgz#83336560b54a38e35e3a2df7afd0454d691468bb"
|
|
||||||
integrity sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==
|
|
||||||
|
|
||||||
is-core-module@^2.1.0:
|
is-core-module@^2.1.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946"
|
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.1.0.tgz#a4cc031d9b1aca63eecbd18a650e13cb4eeab946"
|
||||||
|
@ -988,31 +867,11 @@ is-core-module@^2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
has "^1.0.3"
|
has "^1.0.3"
|
||||||
|
|
||||||
is-date-object@^1.0.1, is-date-object@^1.0.2:
|
|
||||||
version "1.0.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
|
|
||||||
integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
|
|
||||||
|
|
||||||
is-map@^2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
|
|
||||||
integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
|
|
||||||
|
|
||||||
is-module@^1.0.0:
|
is-module@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
|
||||||
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=
|
||||||
|
|
||||||
is-negative-zero@^2.0.0:
|
|
||||||
version "2.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461"
|
|
||||||
integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=
|
|
||||||
|
|
||||||
is-number-object@^1.0.3:
|
|
||||||
version "1.0.4"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197"
|
|
||||||
integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==
|
|
||||||
|
|
||||||
is-object@~0.1.2:
|
is-object@~0.1.2:
|
||||||
version "0.1.2"
|
version "0.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7"
|
resolved "https://registry.yarnpkg.com/is-object/-/is-object-0.1.2.tgz#00efbc08816c33cfc4ac8251d132e10dc65098d7"
|
||||||
|
@ -1030,55 +889,11 @@ is-reference@^1.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/estree" "*"
|
"@types/estree" "*"
|
||||||
|
|
||||||
is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9"
|
|
||||||
integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==
|
|
||||||
dependencies:
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
|
|
||||||
is-set@^2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
|
|
||||||
integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==
|
|
||||||
|
|
||||||
is-string@^1.0.4, is-string@^1.0.5:
|
|
||||||
version "1.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
|
|
||||||
integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
|
|
||||||
|
|
||||||
is-symbol@^1.0.2:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
|
|
||||||
integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
|
|
||||||
dependencies:
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
|
|
||||||
is-typed-array@^1.1.3:
|
|
||||||
version "1.1.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.3.tgz#a4ff5a5e672e1a55f99c7f54e59597af5c1df04d"
|
|
||||||
integrity sha512-BSYUBOK/HJibQ30wWkWold5txYwMUXQct9YHAQJr8fSwvZoiglcqB0pd7vEN23+Tsi9IUEjztdOSzl4qLVYGTQ==
|
|
||||||
dependencies:
|
|
||||||
available-typed-arrays "^1.0.0"
|
|
||||||
es-abstract "^1.17.4"
|
|
||||||
foreach "^2.0.5"
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
|
|
||||||
is-typedarray@~1.0.0:
|
is-typedarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||||
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=
|
||||||
|
|
||||||
is-weakmap@^2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2"
|
|
||||||
integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==
|
|
||||||
|
|
||||||
is-weakset@^2.0.1:
|
|
||||||
version "2.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83"
|
|
||||||
integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw==
|
|
||||||
|
|
||||||
is@~0.2.6:
|
is@~0.2.6:
|
||||||
version "0.2.7"
|
version "0.2.7"
|
||||||
resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562"
|
resolved "https://registry.yarnpkg.com/is/-/is-0.2.7.tgz#3b34a2c48f359972f35042849193ae7264b63562"
|
||||||
|
@ -1089,11 +904,6 @@ isarray@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf"
|
||||||
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
integrity sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=
|
||||||
|
|
||||||
isarray@^2.0.5:
|
|
||||||
version "2.0.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
|
|
||||||
integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
|
|
||||||
|
|
||||||
isarray@~1.0.0:
|
isarray@~1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
|
||||||
|
@ -1390,24 +1200,6 @@ oauth-sign@~0.9.0:
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
|
||||||
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
|
||||||
|
|
||||||
object-inspect@^1.7.0, object-inspect@^1.8.0:
|
|
||||||
version "1.8.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0"
|
|
||||||
integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==
|
|
||||||
|
|
||||||
object-is@^1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.2.tgz#c5d2e87ff9e119f78b7a088441519e2eec1573b6"
|
|
||||||
integrity sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
es-abstract "^1.17.5"
|
|
||||||
|
|
||||||
object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
|
|
||||||
version "1.1.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
|
|
||||||
integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
|
|
||||||
|
|
||||||
object-keys@~0.2.0:
|
object-keys@~0.2.0:
|
||||||
version "0.2.0"
|
version "0.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.2.0.tgz#cddec02998b091be42bf1035ae32e49f1cb6ea67"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.2.0.tgz#cddec02998b091be42bf1035ae32e49f1cb6ea67"
|
||||||
|
@ -1422,16 +1214,6 @@ object-keys@~0.4.0:
|
||||||
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336"
|
resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336"
|
||||||
integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=
|
integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=
|
||||||
|
|
||||||
object.assign@^4.1.0:
|
|
||||||
version "4.1.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
|
|
||||||
integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.2"
|
|
||||||
function-bind "^1.1.1"
|
|
||||||
has-symbols "^1.0.0"
|
|
||||||
object-keys "^1.0.11"
|
|
||||||
|
|
||||||
octal@^1.0.0:
|
octal@^1.0.0:
|
||||||
version "1.0.0"
|
version "1.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/octal/-/octal-1.0.0.tgz#63e7162a68efbeb9e213588d58e989d1e5c4530b"
|
resolved "https://registry.yarnpkg.com/octal/-/octal-1.0.0.tgz#63e7162a68efbeb9e213588d58e989d1e5c4530b"
|
||||||
|
@ -1634,14 +1416,6 @@ readable-stream@~1.0.26, readable-stream@~1.0.26-4:
|
||||||
isarray "0.0.1"
|
isarray "0.0.1"
|
||||||
string_decoder "~0.10.x"
|
string_decoder "~0.10.x"
|
||||||
|
|
||||||
regexp.prototype.flags@^1.3.0:
|
|
||||||
version "1.3.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75"
|
|
||||||
integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
es-abstract "^1.17.0-next.1"
|
|
||||||
|
|
||||||
regexparam@1.3.0, regexparam@^1.3.0:
|
regexparam@1.3.0, regexparam@^1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f"
|
resolved "https://registry.yarnpkg.com/regexparam/-/regexparam-1.3.0.tgz#2fe42c93e32a40eff6235d635e0ffa344b92965f"
|
||||||
|
@ -1830,14 +1604,6 @@ shortid@^2.2.15:
|
||||||
dependencies:
|
dependencies:
|
||||||
nanoid "^2.1.0"
|
nanoid "^2.1.0"
|
||||||
|
|
||||||
side-channel@^1.0.2:
|
|
||||||
version "1.0.3"
|
|
||||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.3.tgz#cdc46b057550bbab63706210838df5d4c19519c3"
|
|
||||||
integrity sha512-A6+ByhlLkksFoUepsGxfj5x1gTSrs+OydsRptUxeNCabQpCFUvcwIczgOigI8vhY/OJCnPnyE9rGiwgvr9cS1g==
|
|
||||||
dependencies:
|
|
||||||
es-abstract "^1.18.0-next.0"
|
|
||||||
object-inspect "^1.8.0"
|
|
||||||
|
|
||||||
source-map-support@~0.5.19:
|
source-map-support@~0.5.19:
|
||||||
version "0.5.19"
|
version "0.5.19"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61"
|
||||||
|
@ -1886,22 +1652,6 @@ string-range@~1.2, string-range@~1.2.1:
|
||||||
resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd"
|
resolved "https://registry.yarnpkg.com/string-range/-/string-range-1.2.2.tgz#a893ed347e72299bc83befbbf2a692a8d239d5dd"
|
||||||
integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0=
|
integrity sha1-qJPtNH5yKZvIO++78qaSqNI51d0=
|
||||||
|
|
||||||
string.prototype.trimend@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz#85812a6b847ac002270f5808146064c995fb6913"
|
|
||||||
integrity sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
es-abstract "^1.17.5"
|
|
||||||
|
|
||||||
string.prototype.trimstart@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz#14af6d9f34b053f7cfc89b72f8f2ee14b9039a54"
|
|
||||||
integrity sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==
|
|
||||||
dependencies:
|
|
||||||
define-properties "^1.1.3"
|
|
||||||
es-abstract "^1.17.5"
|
|
||||||
|
|
||||||
string_decoder@^1.1.1:
|
string_decoder@^1.1.1:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
|
||||||
|
@ -2102,39 +1852,6 @@ whatwg-url@^8.0.0:
|
||||||
tr46 "^2.0.2"
|
tr46 "^2.0.2"
|
||||||
webidl-conversions "^6.1.0"
|
webidl-conversions "^6.1.0"
|
||||||
|
|
||||||
which-boxed-primitive@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1"
|
|
||||||
integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ==
|
|
||||||
dependencies:
|
|
||||||
is-bigint "^1.0.0"
|
|
||||||
is-boolean-object "^1.0.0"
|
|
||||||
is-number-object "^1.0.3"
|
|
||||||
is-string "^1.0.4"
|
|
||||||
is-symbol "^1.0.2"
|
|
||||||
|
|
||||||
which-collection@^1.0.1:
|
|
||||||
version "1.0.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906"
|
|
||||||
integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==
|
|
||||||
dependencies:
|
|
||||||
is-map "^2.0.1"
|
|
||||||
is-set "^2.0.1"
|
|
||||||
is-weakmap "^2.0.1"
|
|
||||||
is-weakset "^2.0.1"
|
|
||||||
|
|
||||||
which-typed-array@^1.1.2:
|
|
||||||
version "1.1.2"
|
|
||||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.2.tgz#e5f98e56bda93e3dac196b01d47c1156679c00b2"
|
|
||||||
integrity sha512-KT6okrd1tE6JdZAy3o2VhMoYPh3+J6EMZLyrxBQsZflI1QCZIxMrIYLkosd8Twf+YfknVIHmYQPgJt238p8dnQ==
|
|
||||||
dependencies:
|
|
||||||
available-typed-arrays "^1.0.2"
|
|
||||||
es-abstract "^1.17.5"
|
|
||||||
foreach "^2.0.5"
|
|
||||||
function-bind "^1.1.1"
|
|
||||||
has-symbols "^1.0.1"
|
|
||||||
is-typed-array "^1.1.3"
|
|
||||||
|
|
||||||
word-wrap@~1.2.3:
|
word-wrap@~1.2.3:
|
||||||
version "1.2.3"
|
version "1.2.3"
|
||||||
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
class Email {
|
||||||
|
constructor() {
|
||||||
|
this.apiKey = null
|
||||||
|
}
|
||||||
|
|
||||||
|
setApiKey(apiKey) {
|
||||||
|
this.apiKey = apiKey
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(msg) {
|
||||||
|
if (msg.to === "invalid@test.com") {
|
||||||
|
throw "Invalid"
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = new Email()
|
|
@ -1,17 +1,35 @@
|
||||||
const fetch = jest.requireActual("node-fetch")
|
const fetch = jest.requireActual("node-fetch")
|
||||||
|
|
||||||
module.exports = async (url, opts) => {
|
module.exports = async (url, opts) => {
|
||||||
// mocked data based on url
|
function json(body, status = 200) {
|
||||||
if (url.includes("api/apps")) {
|
|
||||||
return {
|
return {
|
||||||
|
status,
|
||||||
json: async () => {
|
json: async () => {
|
||||||
return {
|
return body
|
||||||
app1: {
|
|
||||||
url: "/app1",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mocked data based on url
|
||||||
|
if (url.includes("api/apps")) {
|
||||||
|
return json({
|
||||||
|
app1: {
|
||||||
|
url: "/app1",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else if (url.includes("test.com")) {
|
||||||
|
return json({
|
||||||
|
body: opts.body,
|
||||||
|
url,
|
||||||
|
method: opts.method,
|
||||||
|
})
|
||||||
|
} else if (url.includes("invalid.com")) {
|
||||||
|
return json(
|
||||||
|
{
|
||||||
|
invalid: true,
|
||||||
|
},
|
||||||
|
404
|
||||||
|
)
|
||||||
|
}
|
||||||
return fetch(url, opts)
|
return fetch(url, opts)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/server",
|
"name": "@budibase/server",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"description": "Budibase Web Server",
|
"description": "Budibase Web Server",
|
||||||
"main": "src/electron.js",
|
"main": "src/electron.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "jest --testPathIgnorePatterns=routes && npm run test:integration",
|
"test": "jest --testPathIgnorePatterns=routes && npm run test:integration",
|
||||||
"test:integration": "jest --runInBand --coverage",
|
"test:integration": "jest --coverage --detectOpenHandles",
|
||||||
"test:watch": "jest --watch",
|
"test:watch": "jest --watch",
|
||||||
"run:docker": "node src/index",
|
"run:docker": "node src/index",
|
||||||
"dev:builder": "cross-env PORT=4001 nodemon src/index.js",
|
"dev:builder": "cross-env PORT=4001 nodemon src/index.js",
|
||||||
|
@ -53,11 +53,16 @@
|
||||||
"src/**/*.js",
|
"src/**/*.js",
|
||||||
"!**/node_modules/**",
|
"!**/node_modules/**",
|
||||||
"!src/db/views/*.js",
|
"!src/db/views/*.js",
|
||||||
"!src/api/routes/tests/**/*.js",
|
|
||||||
"!src/api/controllers/deploy/**/*.js",
|
"!src/api/controllers/deploy/**/*.js",
|
||||||
"!src/api/controllers/static/templates/**/*",
|
"!src/*.js",
|
||||||
"!src/api/controllers/static/selfhost/**/*",
|
"!src/api/controllers/static/**/*",
|
||||||
"!src/*.js"
|
"!src/db/dynamoClient.js",
|
||||||
|
"!src/utilities/usageQuota.js",
|
||||||
|
"!src/api/routes/tests/**/*",
|
||||||
|
"!src/tests/**/*",
|
||||||
|
"!src/automations/tests/**/*",
|
||||||
|
"!src/utilities/fileProcessor.js",
|
||||||
|
"!src/utilities/initialiseBudibase.js"
|
||||||
],
|
],
|
||||||
"coverageReporters": [
|
"coverageReporters": [
|
||||||
"lcov",
|
"lcov",
|
||||||
|
@ -71,8 +76,8 @@
|
||||||
"author": "Budibase",
|
"author": "Budibase",
|
||||||
"license": "AGPL-3.0-or-later",
|
"license": "AGPL-3.0-or-later",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/client": "^0.8.5",
|
"@budibase/client": "^0.8.9",
|
||||||
"@budibase/string-templates": "^0.8.5",
|
"@budibase/string-templates": "^0.8.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",
|
||||||
|
@ -126,7 +131,7 @@
|
||||||
"zlib": "1.0.5"
|
"zlib": "1.0.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@budibase/standard-components": "^0.8.5",
|
"@budibase/standard-components": "^0.8.9",
|
||||||
"@jest/test-sequencer": "^24.8.0",
|
"@jest/test-sequencer": "^24.8.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"electron": "10.1.3",
|
"electron": "10.1.3",
|
||||||
|
@ -138,5 +143,5 @@
|
||||||
"pouchdb-adapter-memory": "^7.2.1",
|
"pouchdb-adapter-memory": "^7.2.1",
|
||||||
"supertest": "^4.0.2"
|
"supertest": "^4.0.2"
|
||||||
},
|
},
|
||||||
"gitHead": "768a9d59da12cfefcd7ccaba05f975e878669504"
|
"gitHead": "1b95326b20d1352d36305910259228b96a683dc7"
|
||||||
}
|
}
|
||||||
|
|
|
@ -91,7 +91,6 @@ async function getAppUrlIfNotInUse(ctx) {
|
||||||
|
|
||||||
async function createInstance(template) {
|
async function createInstance(template) {
|
||||||
const appId = generateAppID()
|
const appId = generateAppID()
|
||||||
|
|
||||||
const db = new CouchDB(appId)
|
const db = new CouchDB(appId)
|
||||||
await db.put({
|
await db.put({
|
||||||
_id: "_design/database",
|
_id: "_design/database",
|
||||||
|
@ -106,10 +105,10 @@ async function createInstance(template) {
|
||||||
// replicate the template data to the instance DB
|
// replicate the template data to the instance DB
|
||||||
// this is currently very hard to test, downloading and importing template files
|
// this is currently very hard to test, downloading and importing template files
|
||||||
/* istanbul ignore next */
|
/* istanbul ignore next */
|
||||||
if (template) {
|
if (template && template.useTemplate === "true") {
|
||||||
let dbDumpReadStream
|
let dbDumpReadStream
|
||||||
if (template.fileImportPath) {
|
if (template.file) {
|
||||||
dbDumpReadStream = fs.createReadStream(template.fileImportPath)
|
dbDumpReadStream = fs.createReadStream(template.file.path)
|
||||||
} else {
|
} else {
|
||||||
const templatePath = await downloadTemplate(...template.key.split("/"))
|
const templatePath = await downloadTemplate(...template.key.split("/"))
|
||||||
dbDumpReadStream = fs.createReadStream(
|
dbDumpReadStream = fs.createReadStream(
|
||||||
|
@ -162,8 +161,17 @@ exports.fetchAppPackage = async function(ctx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.create = async function(ctx) {
|
exports.create = async function(ctx) {
|
||||||
|
const { useTemplate, templateKey } = ctx.request.body
|
||||||
|
const instanceConfig = {
|
||||||
|
useTemplate,
|
||||||
|
key: templateKey,
|
||||||
|
}
|
||||||
|
if (ctx.request.files && ctx.request.files.templateFile) {
|
||||||
|
instanceConfig.file = ctx.request.files.templateFile
|
||||||
|
}
|
||||||
|
const instance = await createInstance(instanceConfig)
|
||||||
|
|
||||||
const url = await getAppUrlIfNotInUse(ctx)
|
const url = await getAppUrlIfNotInUse(ctx)
|
||||||
const instance = await createInstance(ctx.request.body.template)
|
|
||||||
const appId = instance._id
|
const appId = instance._id
|
||||||
const version = packageJson.version
|
const version = packageJson.version
|
||||||
const newApplication = {
|
const newApplication = {
|
||||||
|
|
|
@ -152,26 +152,6 @@ async function processLocalFileUploads({ files, outputPath, appId }) {
|
||||||
return processedFiles
|
return processedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.performLocalFileProcessing = async function(ctx) {
|
|
||||||
const { files } = ctx.request.body
|
|
||||||
|
|
||||||
const processedFileOutputPath = resolve(
|
|
||||||
budibaseAppsDir(),
|
|
||||||
ctx.user.appId,
|
|
||||||
"attachments"
|
|
||||||
)
|
|
||||||
|
|
||||||
try {
|
|
||||||
ctx.body = await processLocalFileUploads({
|
|
||||||
files,
|
|
||||||
outputPath: processedFileOutputPath,
|
|
||||||
appId: ctx.user.appId,
|
|
||||||
})
|
|
||||||
} catch (err) {
|
|
||||||
ctx.throw(500, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.serveApp = async function(ctx) {
|
exports.serveApp = async function(ctx) {
|
||||||
let appId = ctx.params.appId
|
let appId = ctx.params.appId
|
||||||
if (env.SELF_HOSTED) {
|
if (env.SELF_HOSTED) {
|
||||||
|
|
|
@ -65,12 +65,14 @@ exports.save = async function(ctx) {
|
||||||
|
|
||||||
// Don't rename if the name is the same
|
// Don't rename if the name is the same
|
||||||
let { _rename } = tableToSave
|
let { _rename } = tableToSave
|
||||||
|
/* istanbul ignore next */
|
||||||
if (_rename && _rename.old === _rename.updated) {
|
if (_rename && _rename.old === _rename.updated) {
|
||||||
_rename = null
|
_rename = null
|
||||||
delete tableToSave._rename
|
delete tableToSave._rename
|
||||||
}
|
}
|
||||||
|
|
||||||
// rename row fields when table column is renamed
|
// rename row fields when table column is renamed
|
||||||
|
/* istanbul ignore next */
|
||||||
if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
|
if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
|
||||||
ctx.throw(400, "Cannot rename a linked column.")
|
ctx.throw(400, "Cannot rename a linked column.")
|
||||||
} else if (_rename && tableToSave.primaryDisplay === _rename.old) {
|
} else if (_rename && tableToSave.primaryDisplay === _rename.old) {
|
||||||
|
@ -159,7 +161,7 @@ exports.destroy = async function(ctx) {
|
||||||
ctx.eventEmitter &&
|
ctx.eventEmitter &&
|
||||||
ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete)
|
ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete)
|
||||||
ctx.status = 200
|
ctx.status = 200
|
||||||
ctx.message = `Table ${ctx.params.tableId} deleted.`
|
ctx.body = { message: `Table ${ctx.params.tableId} deleted.` }
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.validateCSVSchema = async function(ctx) {
|
exports.validateCSVSchema = async function(ctx) {
|
||||||
|
|
|
@ -90,7 +90,8 @@ exports.handleDataImport = async (user, table, dataImport) => {
|
||||||
return table
|
return table
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.handleSearchIndexes = async (db, table) => {
|
exports.handleSearchIndexes = async (appId, table) => {
|
||||||
|
const db = new CouchDB(appId)
|
||||||
// create relevant search indexes
|
// create relevant search indexes
|
||||||
if (table.indexes && table.indexes.length > 0) {
|
if (table.indexes && table.indexes.length > 0) {
|
||||||
const currentIndexes = await db.getIndexes()
|
const currentIndexes = await db.getIndexes()
|
||||||
|
@ -150,6 +151,9 @@ class TableSaveFunctions {
|
||||||
constructor({ db, ctx, oldTable, dataImport }) {
|
constructor({ db, ctx, oldTable, dataImport }) {
|
||||||
this.db = db
|
this.db = db
|
||||||
this.ctx = ctx
|
this.ctx = ctx
|
||||||
|
if (this.ctx && this.ctx.user) {
|
||||||
|
this.appId = this.ctx.user.appId
|
||||||
|
}
|
||||||
this.oldTable = oldTable
|
this.oldTable = oldTable
|
||||||
this.dataImport = dataImport
|
this.dataImport = dataImport
|
||||||
// any rows that need updated
|
// any rows that need updated
|
||||||
|
@ -178,7 +182,7 @@ class TableSaveFunctions {
|
||||||
|
|
||||||
// after saving
|
// after saving
|
||||||
async after(table) {
|
async after(table) {
|
||||||
table = await exports.handleSearchIndexes(this.db, table)
|
table = await exports.handleSearchIndexes(this.appId, table)
|
||||||
table = await exports.handleDataImport(
|
table = await exports.handleDataImport(
|
||||||
this.ctx.user,
|
this.ctx.user,
|
||||||
table,
|
table,
|
||||||
|
|
|
@ -29,11 +29,13 @@ const controller = {
|
||||||
save: async ctx => {
|
save: async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const { originalName, ...viewToSave } = ctx.request.body
|
const { originalName, ...viewToSave } = ctx.request.body
|
||||||
|
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
|
|
||||||
const view = viewTemplate(viewToSave)
|
const view = viewTemplate(viewToSave)
|
||||||
|
|
||||||
|
if (!viewToSave.name) {
|
||||||
|
ctx.throw(400, "Cannot create view without a name")
|
||||||
|
}
|
||||||
|
|
||||||
designDoc.views = {
|
designDoc.views = {
|
||||||
...designDoc.views,
|
...designDoc.views,
|
||||||
[viewToSave.name]: view,
|
[viewToSave.name]: view,
|
||||||
|
@ -60,17 +62,16 @@ const controller = {
|
||||||
|
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
|
|
||||||
ctx.body = table.views[viewToSave.name]
|
ctx.body = {
|
||||||
ctx.message = `View ${viewToSave.name} saved successfully.`
|
...table.views[viewToSave.name],
|
||||||
|
name: viewToSave.name,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
destroy: async ctx => {
|
destroy: async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
|
|
||||||
const viewName = decodeURI(ctx.params.viewName)
|
const viewName = decodeURI(ctx.params.viewName)
|
||||||
|
|
||||||
const view = designDoc.views[viewName]
|
const view = designDoc.views[viewName]
|
||||||
|
|
||||||
delete designDoc.views[viewName]
|
delete designDoc.views[viewName]
|
||||||
|
|
||||||
await db.put(designDoc)
|
await db.put(designDoc)
|
||||||
|
@ -80,16 +81,17 @@ const controller = {
|
||||||
await db.put(table)
|
await db.put(table)
|
||||||
|
|
||||||
ctx.body = view
|
ctx.body = view
|
||||||
ctx.message = `View ${ctx.params.viewName} saved successfully.`
|
|
||||||
},
|
},
|
||||||
exportView: async ctx => {
|
exportView: async ctx => {
|
||||||
const db = new CouchDB(ctx.user.appId)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
const designDoc = await db.get("_design/database")
|
const designDoc = await db.get("_design/database")
|
||||||
|
|
||||||
const viewName = decodeURI(ctx.query.view)
|
const viewName = decodeURI(ctx.query.view)
|
||||||
|
|
||||||
const view = designDoc.views[viewName]
|
const view = designDoc.views[viewName]
|
||||||
const format = ctx.query.format
|
const format = ctx.query.format
|
||||||
|
if (!format) {
|
||||||
|
ctx.throw(400, "Format must be specified, either csv or json")
|
||||||
|
}
|
||||||
|
|
||||||
if (view) {
|
if (view) {
|
||||||
ctx.params.viewName = viewName
|
ctx.params.viewName = viewName
|
||||||
|
@ -102,6 +104,7 @@ const controller = {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// table all_ view
|
// table all_ view
|
||||||
|
/* istanbul ignore next */
|
||||||
ctx.params.viewName = viewName
|
ctx.params.viewName = viewName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,11 +29,7 @@ if (env.SELF_HOSTED) {
|
||||||
}
|
}
|
||||||
|
|
||||||
router
|
router
|
||||||
.post(
|
.post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
|
||||||
"/api/attachments/process",
|
|
||||||
authorized(BUILDER),
|
|
||||||
controller.performLocalFileProcessing
|
|
||||||
)
|
|
||||||
.post("/api/attachments/upload", usage, controller.uploadFile)
|
.post("/api/attachments/upload", usage, controller.uploadFile)
|
||||||
.get("/componentlibrary", controller.serveComponentLibrary)
|
.get("/componentlibrary", controller.serveComponentLibrary)
|
||||||
.get("/assets/:file*", controller.serveAppAsset)
|
.get("/assets/:file*", controller.serveAppAsset)
|
||||||
|
|
|
@ -3,8 +3,8 @@ const {
|
||||||
getAllTableRows,
|
getAllTableRows,
|
||||||
clearAllAutomations,
|
clearAllAutomations,
|
||||||
} = require("./utilities/TestFunctions")
|
} = require("./utilities/TestFunctions")
|
||||||
const { basicAutomation } = require("./utilities/structures")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicAutomation } = setup.structures
|
||||||
|
|
||||||
const MAX_RETRIES = 4
|
const MAX_RETRIES = 4
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,12 @@ describe("/component", () => {
|
||||||
function mock() {
|
function mock() {
|
||||||
const manifestFile = "manifest.json"
|
const manifestFile = "manifest.json"
|
||||||
const appId = config.getAppId()
|
const appId = config.getAppId()
|
||||||
const libraries = ["@budibase/standard-components"]
|
const libraries = [join("@budibase", "standard-components")]
|
||||||
for (let library of libraries) {
|
for (let library of libraries) {
|
||||||
let appDirectory = resolve(budibaseAppsDir(), appId, "node_modules", library, "package")
|
let appDirectory = resolve(budibaseAppsDir(), appId, "node_modules", library, "package")
|
||||||
fs.mkdirSync(appDirectory, { recursive: true })
|
fs.mkdirSync(appDirectory, { recursive: true })
|
||||||
const file = require.resolve(library).split("dist/index.js")[0] + manifestFile
|
|
||||||
|
const file = require.resolve(library).split(join("dist", "index.js"))[0] + manifestFile
|
||||||
fs.copyFileSync(file, join(appDirectory, manifestFile))
|
fs.copyFileSync(file, join(appDirectory, manifestFile))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
let {basicDatasource} = require("./utilities/structures")
|
|
||||||
let {checkBuilderEndpoint} = require("./utilities/TestFunctions")
|
|
||||||
let setup = require("./utilities")
|
let setup = require("./utilities")
|
||||||
|
let { basicDatasource } = setup.structures
|
||||||
|
let { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
|
|
||||||
describe("/datasources", () => {
|
describe("/datasources", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicLayout } = require("./utilities/structures")
|
const { basicLayout } = setup.structures
|
||||||
|
|
||||||
describe("/layouts", () => {
|
describe("/layouts", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const tableUtils = require("../../controllers/table/utils")
|
||||||
|
|
||||||
describe("/analytics", () => {
|
describe("run misc tests", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
let config = setup.getConfig()
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
@ -10,29 +11,44 @@ describe("/analytics", () => {
|
||||||
await config.init()
|
await config.init()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("isEnabled", () => {
|
describe("/analytics", () => {
|
||||||
it("check if analytics enabled", async () => {
|
it("check if analytics enabled", async () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/analytics`)
|
.get(`/api/analytics`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(typeof res.body.enabled).toEqual("boolean")
|
expect(typeof res.body.enabled).toEqual("boolean")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("/health", () => {
|
||||||
|
it("should confirm healthy", async () => {
|
||||||
|
await request.get("/health").expect(200)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe("/health", () => {
|
describe("/version", () => {
|
||||||
it("should confirm healthy", async () => {
|
it("should confirm version", async () => {
|
||||||
let config = setup.getConfig()
|
const res = await request.get("/version").expect(200)
|
||||||
await config.getRequest().get("/health").expect(200)
|
expect(res.text.split(".").length).toEqual(3)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
describe("/version", () => {
|
describe("test table utilities", () => {
|
||||||
it("should confirm version", async () => {
|
it("should be able to import a CSV", async () => {
|
||||||
const config = setup.getConfig()
|
const table = await config.createTable()
|
||||||
const res = await config.getRequest().get("/version").expect(200)
|
const dataImport = {
|
||||||
expect(res.text.split(".").length).toEqual(3)
|
csvString: "a,b,c,d\n1,2,3,4"
|
||||||
|
}
|
||||||
|
await tableUtils.handleDataImport({
|
||||||
|
appId: config.getAppId(),
|
||||||
|
userId: "test",
|
||||||
|
}, table, dataImport)
|
||||||
|
const rows = await config.getRows()
|
||||||
|
expect(rows[0].a).toEqual("1")
|
||||||
|
expect(rows[0].b).toEqual("2")
|
||||||
|
expect(rows[0].c).toEqual("3")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
|
@ -1,6 +1,6 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicRow } = require("./utilities/structures")
|
const { basicRow } = setup.structures
|
||||||
|
|
||||||
const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC
|
const HIGHER_ROLE_ID = BUILTIN_ROLE_IDS.BASIC
|
||||||
const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC
|
const STD_ROLE_ID = BUILTIN_ROLE_IDS.PUBLIC
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
// mock out postgres for this
|
// mock out postgres for this
|
||||||
jest.mock("pg")
|
jest.mock("pg")
|
||||||
|
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
|
||||||
const { basicQuery, basicDatasource } = require("./utilities/structures")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
|
const { basicQuery, basicDatasource } = setup.structures
|
||||||
|
|
||||||
describe("/queries", () => {
|
describe("/queries", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -2,8 +2,8 @@ const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
BUILTIN_PERMISSION_IDS,
|
BUILTIN_PERMISSION_IDS,
|
||||||
} = require("../../../utilities/security/permissions")
|
} = require("../../../utilities/security/permissions")
|
||||||
const { basicRole } = require("./utilities/structures")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicRole } = setup.structures
|
||||||
|
|
||||||
describe("/roles", () => {
|
describe("/roles", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicScreen } = require("./utilities/structures")
|
const { basicScreen } = setup.structures
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const { outputProcessing } = require("../../../utilities/rowProcessor")
|
const { outputProcessing } = require("../../../utilities/rowProcessor")
|
||||||
const env = require("../../../environment")
|
|
||||||
const { basicRow } = require("./utilities/structures")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicRow } = setup.structures
|
||||||
|
|
||||||
describe("/rows", () => {
|
describe("/rows", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
@ -349,7 +348,7 @@ describe("/rows", () => {
|
||||||
const view = await config.createView()
|
const view = await config.createView()
|
||||||
const row = await config.createRow()
|
const row = await config.createRow()
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/views/${view._id}`)
|
.get(`/api/views/${view.name}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { basicScreen } = require("./utilities/structures")
|
const { basicScreen } = setup.structures
|
||||||
|
|
||||||
describe("/screens", () => {
|
describe("/screens", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
const { checkBuilderEndpoint, getDB } = require("./utilities/TestFunctions")
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicTable } = setup.structures
|
||||||
|
|
||||||
describe("/tables", () => {
|
describe("/tables", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
@ -12,25 +13,22 @@ describe("/tables", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("create", () => {
|
describe("create", () => {
|
||||||
it("returns a success message when the table is successfully created", done => {
|
it("returns a success message when the table is successfully created", async () => {
|
||||||
request
|
const res = await request
|
||||||
.post(`/api/tables`)
|
.post(`/api/tables`)
|
||||||
.send({
|
.send({
|
||||||
name: "TestTable",
|
name: "TestTable",
|
||||||
key: "name",
|
key: "name",
|
||||||
schema: {
|
schema: {
|
||||||
name: { type: "string" }
|
name: {type: "string"}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (err, res) => {
|
expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.")
|
||||||
expect(res.res.statusMessage).toEqual("Table TestTable saved successfully.")
|
expect(res.body.name).toEqual("TestTable")
|
||||||
expect(res.body.name).toEqual("TestTable")
|
})
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("renames all the row fields for a table when a schema key is renamed", async () => {
|
it("renames all the row fields for a table when a schema key is renamed", async () => {
|
||||||
const testTable = await config.createTable()
|
const testTable = await config.createTable()
|
||||||
|
@ -46,7 +44,7 @@ describe("/tables", () => {
|
||||||
|
|
||||||
const updatedTable = await request
|
const updatedTable = await request
|
||||||
.post(`/api/tables`)
|
.post(`/api/tables`)
|
||||||
.send({
|
.send({
|
||||||
_id: testTable._id,
|
_id: testTable._id,
|
||||||
_rev: testTable._rev,
|
_rev: testTable._rev,
|
||||||
name: "TestTable",
|
name: "TestTable",
|
||||||
|
@ -56,41 +54,40 @@ describe("/tables", () => {
|
||||||
updated: "updatedName"
|
updated: "updatedName"
|
||||||
},
|
},
|
||||||
schema: {
|
schema: {
|
||||||
updatedName: { type: "string" }
|
updatedName: {type: "string"}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully.")
|
||||||
|
expect(updatedTable.body.name).toEqual("TestTable")
|
||||||
|
|
||||||
expect(updatedTable.res.statusMessage).toEqual("Table TestTable saved successfully.")
|
const res = await request
|
||||||
expect(updatedTable.body.name).toEqual("TestTable")
|
.get(`/api/${testTable._id}/rows/${testRow.body._id}`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
|
||||||
const res = await request
|
expect(res.body.updatedName).toEqual("test")
|
||||||
.get(`/api/${testTable._id}/rows/${testRow.body._id}`)
|
expect(res.body.name).toBeUndefined()
|
||||||
.set(config.defaultHeaders())
|
})
|
||||||
.expect('Content-Type', /json/)
|
|
||||||
.expect(200)
|
|
||||||
|
|
||||||
expect(res.body.updatedName).toEqual("test")
|
it("should apply authorization to endpoint", async () => {
|
||||||
expect(res.body.name).toBeUndefined()
|
await checkBuilderEndpoint({
|
||||||
})
|
config,
|
||||||
|
method: "POST",
|
||||||
it("should apply authorization to endpoint", async () => {
|
url: `/api/tables`,
|
||||||
await checkBuilderEndpoint({
|
body: {
|
||||||
config,
|
name: "TestTable",
|
||||||
method: "POST",
|
key: "name",
|
||||||
url: `/api/tables`,
|
schema: {
|
||||||
body: {
|
name: {type: "string"}
|
||||||
name: "TestTable",
|
|
||||||
key: "name",
|
|
||||||
schema: {
|
|
||||||
name: { type: "string" }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("fetch", () => {
|
describe("fetch", () => {
|
||||||
let testTable
|
let testTable
|
||||||
|
@ -103,28 +100,91 @@ describe("/tables", () => {
|
||||||
delete testTable._rev
|
delete testTable._rev
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns all the tables for that instance in the response body", done => {
|
it("returns all the tables for that instance in the response body", async () => {
|
||||||
request
|
const res = await request
|
||||||
.get(`/api/tables`)
|
.get(`/api/tables`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (_, res) => {
|
const fetchedTable = res.body[0]
|
||||||
const fetchedTable = res.body[0]
|
expect(fetchedTable.name).toEqual(testTable.name)
|
||||||
expect(fetchedTable.name).toEqual(testTable.name)
|
expect(fetchedTable.type).toEqual("table")
|
||||||
expect(fetchedTable.type).toEqual("table")
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await checkBuilderEndpoint({
|
await checkBuilderEndpoint({
|
||||||
config,
|
config,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `/api/tables`,
|
url: `/api/tables`,
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("indexing", () => {
|
||||||
|
it("should be able to create a table with indexes", async () => {
|
||||||
|
const db = getDB(config)
|
||||||
|
const indexCount = (await db.getIndexes()).total_rows
|
||||||
|
const table = basicTable()
|
||||||
|
table.indexes = ["name"]
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/tables`)
|
||||||
|
.send(table)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body._id).toBeDefined()
|
||||||
|
expect(res.body._rev).toBeDefined()
|
||||||
|
expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1)
|
||||||
|
// update index to see what happens
|
||||||
|
table.indexes = ["name", "description"]
|
||||||
|
await request
|
||||||
|
.post(`/api/tables`)
|
||||||
|
.send({
|
||||||
|
...table,
|
||||||
|
_id: res.body._id,
|
||||||
|
_rev: res.body._rev,
|
||||||
|
})
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
// shouldn't have created a new index
|
||||||
|
expect((await db.getIndexes()).total_rows).toEqual(indexCount + 1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("updating user table", () => {
|
||||||
|
it("should add roleId and email field when adjusting user table schema", async () => {
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/tables`)
|
||||||
|
.send({
|
||||||
|
...basicTable(),
|
||||||
|
_id: "ta_users",
|
||||||
|
})
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body.schema.email).toBeDefined()
|
||||||
|
expect(res.body.schema.roleId).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("validate csv", () => {
|
||||||
|
it("should be able to validate a CSV layout", async () => {
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/tables/csv/validate`)
|
||||||
|
.send({
|
||||||
|
csvString: "a,b,c,d\n1,2,3,4"
|
||||||
|
})
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect('Content-Type', /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body.schema).toBeDefined()
|
||||||
|
expect(res.body.schema.a).toEqual({
|
||||||
|
type: "string",
|
||||||
|
success: true,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe("destroy", () => {
|
describe("destroy", () => {
|
||||||
let testTable
|
let testTable
|
||||||
|
@ -137,19 +197,16 @@ describe("/tables", () => {
|
||||||
delete testTable._rev
|
delete testTable._rev
|
||||||
})
|
})
|
||||||
|
|
||||||
it("returns a success response when a table is deleted.", async done => {
|
it("returns a success response when a table is deleted.", async () => {
|
||||||
request
|
const res = await request
|
||||||
.delete(`/api/tables/${testTable._id}/${testTable._rev}`)
|
.delete(`/api/tables/${testTable._id}/${testTable._rev}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (_, res) => {
|
expect(res.body.message).toEqual(`Table ${testTable._id} deleted.`)
|
||||||
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`)
|
})
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("deletes linked references to the table after deletion", async done => {
|
it("deletes linked references to the table after deletion", async () => {
|
||||||
const linkedTable = await config.createTable({
|
const linkedTable = await config.createTable({
|
||||||
name: "LinkedTable",
|
name: "LinkedTable",
|
||||||
type: "table",
|
type: "table",
|
||||||
|
@ -171,18 +228,15 @@ describe("/tables", () => {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
request
|
const res = await request
|
||||||
.delete(`/api/tables/${testTable._id}/${testTable._rev}`)
|
.delete(`/api/tables/${testTable._id}/${testTable._rev}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect('Content-Type', /json/)
|
.expect('Content-Type', /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
.end(async (_, res) => {
|
expect(res.body.message).toEqual(`Table ${testTable._id} deleted.`)
|
||||||
expect(res.res.statusMessage).toEqual(`Table ${testTable._id} deleted.`)
|
const dependentTable = await config.getTable(linkedTable._id)
|
||||||
const dependentTable = await config.getTable(linkedTable._id)
|
expect(dependentTable.schema.TestTable).not.toBeDefined()
|
||||||
expect(dependentTable.schema.TestTable).not.toBeDefined()
|
})
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await checkBuilderEndpoint({
|
await checkBuilderEndpoint({
|
||||||
|
@ -191,6 +245,5 @@ describe("/tables", () => {
|
||||||
url: `/api/tables/${testTable._id}/${testTable._rev}`,
|
url: `/api/tables/${testTable._id}/${testTable._rev}`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../../utilities/security/roles")
|
||||||
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
|
const { checkPermissionsEndpoint } = require("./utilities/TestFunctions")
|
||||||
const { basicUser } = require("./utilities/structures")
|
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
|
const { basicUser } = setup.structures
|
||||||
|
|
||||||
describe("/users", () => {
|
describe("/users", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const rowController = require("../../../controllers/row")
|
const rowController = require("../../../controllers/row")
|
||||||
const appController = require("../../../controllers/application")
|
const appController = require("../../../controllers/application")
|
||||||
|
const CouchDB = require("../../../../db")
|
||||||
|
|
||||||
function Request(appId, params) {
|
function Request(appId, params) {
|
||||||
this.user = { appId }
|
this.user = { appId }
|
||||||
|
@ -77,3 +78,7 @@ exports.checkPermissionsEndpoint = async ({
|
||||||
.set(failHeader)
|
.set(failHeader)
|
||||||
.expect(403)
|
.expect(403)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.getDB = config => {
|
||||||
|
return new CouchDB(config.getAppId())
|
||||||
|
}
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
table: require("../../../controllers/table"),
|
|
||||||
row: require("../../../controllers/row"),
|
|
||||||
role: require("../../../controllers/role"),
|
|
||||||
perms: require("../../../controllers/permission"),
|
|
||||||
view: require("../../../controllers/view"),
|
|
||||||
app: require("../../../controllers/application"),
|
|
||||||
user: require("../../../controllers/user"),
|
|
||||||
automation: require("../../../controllers/automation"),
|
|
||||||
datasource: require("../../../controllers/datasource"),
|
|
||||||
query: require("../../../controllers/query"),
|
|
||||||
screen: require("../../../controllers/screen"),
|
|
||||||
webhook: require("../../../controllers/webhook"),
|
|
||||||
layout: require("../../../controllers/layout"),
|
|
||||||
}
|
|
|
@ -1,4 +1,5 @@
|
||||||
const TestConfig = require("./TestConfiguration")
|
const TestConfig = require("../../../../tests/utilities/TestConfiguration")
|
||||||
|
const structures = require("../../../../tests/utilities/structures")
|
||||||
const env = require("../../../../environment")
|
const env = require("../../../../environment")
|
||||||
|
|
||||||
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
exports.delay = ms => new Promise(resolve => setTimeout(resolve, ms))
|
||||||
|
@ -51,3 +52,5 @@ exports.switchToCloudForFunction = async func => {
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exports.structures = structures
|
||||||
|
|
|
@ -29,9 +29,7 @@ describe("/views", () => {
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual(
|
expect(res.body.tableId).toBe(table._id)
|
||||||
"View TestView saved successfully."
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("updates the table row with the new view metadata", async () => {
|
it("updates the table row with the new view metadata", async () => {
|
||||||
|
@ -46,10 +44,8 @@ describe("/views", () => {
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect("Content-Type", /json/)
|
.expect("Content-Type", /json/)
|
||||||
.expect(200)
|
.expect(200)
|
||||||
|
expect(res.body.tableId).toBe(table._id)
|
||||||
|
|
||||||
expect(res.res.statusMessage).toEqual(
|
|
||||||
"View TestView saved successfully."
|
|
||||||
)
|
|
||||||
const updatedTable = await config.getTable(table._id)
|
const updatedTable = await config.getTable(table._id)
|
||||||
expect(updatedTable.views).toEqual({
|
expect(updatedTable.views).toEqual({
|
||||||
TestView: {
|
TestView: {
|
||||||
|
@ -173,4 +169,49 @@ describe("/views", () => {
|
||||||
expect(res.body).toMatchSnapshot()
|
expect(res.body).toMatchSnapshot()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe("destroy", () => {
|
||||||
|
it("should be able to delete a view", async () => {
|
||||||
|
const table = await config.createTable()
|
||||||
|
const view = await config.createView()
|
||||||
|
const res = await request
|
||||||
|
.delete(`/api/views/${view.name}`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect("Content-Type", /json/)
|
||||||
|
.expect(200)
|
||||||
|
expect(res.body.map).toBeDefined()
|
||||||
|
expect(res.body.meta.tableId).toEqual(table._id)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("exportView", () => {
|
||||||
|
it("should be able to delete a view", async () => {
|
||||||
|
await config.createTable()
|
||||||
|
await config.createRow()
|
||||||
|
const view = await config.createView()
|
||||||
|
let res = await request
|
||||||
|
.get(`/api/views/export?view=${view.name}&format=json`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect(200)
|
||||||
|
let error
|
||||||
|
try {
|
||||||
|
const obj = JSON.parse(res.text)
|
||||||
|
expect(obj.length).toBe(1)
|
||||||
|
} catch (err) {
|
||||||
|
error = err
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined()
|
||||||
|
res = await request
|
||||||
|
.get(`/api/views/export?view=${view.name}&format=csv`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
.expect(200)
|
||||||
|
// this shouldn't be JSON
|
||||||
|
try {
|
||||||
|
JSON.parse(res.text)
|
||||||
|
} catch (err) {
|
||||||
|
error = err
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined()
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
const setup = require("./utilities")
|
const setup = require("./utilities")
|
||||||
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
const { checkBuilderEndpoint } = require("./utilities/TestFunctions")
|
||||||
const { basicWebhook, basicAutomation } = require("./utilities/structures")
|
const { basicWebhook, basicAutomation } = setup.structures
|
||||||
|
|
||||||
describe("/webhooks", () => {
|
describe("/webhooks", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
|
|
@ -9,7 +9,6 @@ const env = require("./environment")
|
||||||
const eventEmitter = require("./events")
|
const eventEmitter = require("./events")
|
||||||
const automations = require("./automations/index")
|
const automations = require("./automations/index")
|
||||||
const Sentry = require("@sentry/node")
|
const Sentry = require("@sentry/node")
|
||||||
const selfhost = require("./selfhost")
|
|
||||||
|
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
|
|
||||||
|
@ -66,11 +65,7 @@ module.exports = server.listen(env.PORT || 0, async () => {
|
||||||
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
console.log(`Budibase running on ${JSON.stringify(server.address())}`)
|
||||||
env._set("PORT", server.address().port)
|
env._set("PORT", server.address().port)
|
||||||
eventEmitter.emitPort(env.PORT)
|
eventEmitter.emitPort(env.PORT)
|
||||||
automations.init()
|
await automations.init()
|
||||||
// only init the self hosting DB info in the Pouch, not needed in self hosting prod
|
|
||||||
if (!env.CLOUD) {
|
|
||||||
await selfhost.init()
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
process.on("uncaughtException", err => {
|
process.on("uncaughtException", err => {
|
||||||
|
|
|
@ -37,10 +37,12 @@ let AUTOMATION_BUCKET = env.AUTOMATION_BUCKET
|
||||||
let AUTOMATION_DIRECTORY = env.AUTOMATION_DIRECTORY
|
let AUTOMATION_DIRECTORY = env.AUTOMATION_DIRECTORY
|
||||||
let MANIFEST = null
|
let MANIFEST = null
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
function buildBundleName(pkgName, version) {
|
function buildBundleName(pkgName, version) {
|
||||||
return `${pkgName}@${version}.min.js`
|
return `${pkgName}@${version}.min.js`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
async function downloadPackage(name, version, bundleName) {
|
async function downloadPackage(name, version, bundleName) {
|
||||||
await download(
|
await download(
|
||||||
`${AUTOMATION_BUCKET}/${name}/${version}/${bundleName}`,
|
`${AUTOMATION_BUCKET}/${name}/${version}/${bundleName}`,
|
||||||
|
@ -49,6 +51,7 @@ async function downloadPackage(name, version, bundleName) {
|
||||||
return require(join(AUTOMATION_DIRECTORY, bundleName))
|
return require(join(AUTOMATION_DIRECTORY, bundleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
module.exports.getAction = async function(actionName) {
|
module.exports.getAction = async function(actionName) {
|
||||||
if (BUILTIN_ACTIONS[actionName] != null) {
|
if (BUILTIN_ACTIONS[actionName] != null) {
|
||||||
return BUILTIN_ACTIONS[actionName]
|
return BUILTIN_ACTIONS[actionName]
|
||||||
|
@ -96,5 +99,6 @@ module.exports.init = async function() {
|
||||||
return MANIFEST
|
return MANIFEST
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// definitions will have downloaded ones added to it, while builtin won't
|
||||||
module.exports.DEFINITIONS = BUILTIN_DEFINITIONS
|
module.exports.DEFINITIONS = BUILTIN_DEFINITIONS
|
||||||
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
||||||
|
|
|
@ -30,23 +30,22 @@ async function updateQuota(automation) {
|
||||||
/**
|
/**
|
||||||
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
* This module is built purely to kick off the worker farm and manage the inputs/outputs
|
||||||
*/
|
*/
|
||||||
module.exports.init = function() {
|
module.exports.init = async function() {
|
||||||
actions.init().then(() => {
|
await actions.init()
|
||||||
triggers.automationQueue.process(async job => {
|
triggers.automationQueue.process(async job => {
|
||||||
try {
|
try {
|
||||||
if (env.CLOUD && job.data.automation && !env.SELF_HOSTED) {
|
if (env.CLOUD && job.data.automation && !env.SELF_HOSTED) {
|
||||||
job.data.automation.apiKey = await updateQuota(job.data.automation)
|
job.data.automation.apiKey = await updateQuota(job.data.automation)
|
||||||
}
|
|
||||||
if (env.BUDIBASE_ENVIRONMENT === "PRODUCTION") {
|
|
||||||
await runWorker(job)
|
|
||||||
} else {
|
|
||||||
await singleThread(job)
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
console.error(
|
|
||||||
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
})
|
if (env.BUDIBASE_ENVIRONMENT === "PRODUCTION") {
|
||||||
|
await runWorker(job)
|
||||||
|
} else {
|
||||||
|
await singleThread(job)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(
|
||||||
|
`${job.data.automation.appId} automation ${job.data.automation._id} was unable to run - ${err}`
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,15 +59,14 @@ module.exports.definition = {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
||||||
// TODO: better logging of when actions are missed due to missing parameters
|
|
||||||
if (inputs.row == null || inputs.row.tableId == null) {
|
if (inputs.row == null || inputs.row.tableId == null) {
|
||||||
return
|
return {
|
||||||
|
success: false,
|
||||||
|
response: {
|
||||||
|
message: "Invalid inputs",
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
inputs.row = await automationUtils.cleanUpRow(
|
|
||||||
appId,
|
|
||||||
inputs.row.tableId,
|
|
||||||
inputs.row
|
|
||||||
)
|
|
||||||
// have to clean up the row, remove the table from it
|
// have to clean up the row, remove the table from it
|
||||||
const ctx = {
|
const ctx = {
|
||||||
params: {
|
params: {
|
||||||
|
@ -81,6 +80,11 @@ module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
inputs.row = await automationUtils.cleanUpRow(
|
||||||
|
appId,
|
||||||
|
inputs.row.tableId,
|
||||||
|
inputs.row
|
||||||
|
)
|
||||||
if (env.CLOUD) {
|
if (env.CLOUD) {
|
||||||
await usage.update(apiKey, usage.Properties.ROW, 1)
|
await usage.update(apiKey, usage.Properties.ROW, 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,9 +51,13 @@ module.exports.definition = {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
module.exports.run = async function({ inputs, appId, apiKey, emitter }) {
|
||||||
// TODO: better logging of when actions are missed due to missing parameters
|
|
||||||
if (inputs.id == null || inputs.revision == null) {
|
if (inputs.id == null || inputs.revision == null) {
|
||||||
return
|
return {
|
||||||
|
success: false,
|
||||||
|
response: {
|
||||||
|
message: "Invalid inputs",
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let ctx = {
|
let ctx = {
|
||||||
params: {
|
params: {
|
||||||
|
|
|
@ -12,6 +12,9 @@ const PrettyLogicConditions = {
|
||||||
[LogicConditions.LESS_THAN]: "Less than",
|
[LogicConditions.LESS_THAN]: "Less than",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.LogicConditions = LogicConditions
|
||||||
|
module.exports.PrettyLogicConditions = PrettyLogicConditions
|
||||||
|
|
||||||
module.exports.definition = {
|
module.exports.definition = {
|
||||||
name: "Filter",
|
name: "Filter",
|
||||||
tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}",
|
tagline: "{{inputs.field}} {{inputs.condition}} {{inputs.value}}",
|
||||||
|
@ -64,7 +67,7 @@ module.exports.run = async function filter({ inputs }) {
|
||||||
value = Date.parse(value)
|
value = Date.parse(value)
|
||||||
field = Date.parse(field)
|
field = Date.parse(field)
|
||||||
}
|
}
|
||||||
let success
|
let success = false
|
||||||
if (typeof field !== "object" && typeof value !== "object") {
|
if (typeof field !== "object" && typeof value !== "object") {
|
||||||
switch (condition) {
|
switch (condition) {
|
||||||
case LogicConditions.EQUAL:
|
case LogicConditions.EQUAL:
|
||||||
|
@ -79,8 +82,6 @@ module.exports.run = async function filter({ inputs }) {
|
||||||
case LogicConditions.LESS_THAN:
|
case LogicConditions.LESS_THAN:
|
||||||
success = field < value
|
success = field < value
|
||||||
break
|
break
|
||||||
default:
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
success = false
|
success = false
|
||||||
|
|
|
@ -87,6 +87,7 @@ module.exports.run = async function({ inputs }) {
|
||||||
success: response.status === 200,
|
success: response.status === 200,
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
/* istanbul ignore next */
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
response: err,
|
response: err,
|
||||||
|
|
|
@ -55,14 +55,14 @@ module.exports.definition = {
|
||||||
|
|
||||||
module.exports.run = async function({ inputs, appId, emitter }) {
|
module.exports.run = async function({ inputs, appId, emitter }) {
|
||||||
if (inputs.rowId == null || inputs.row == null) {
|
if (inputs.rowId == null || inputs.row == null) {
|
||||||
return
|
return {
|
||||||
|
success: false,
|
||||||
|
response: {
|
||||||
|
message: "Invalid inputs",
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inputs.row = await automationUtils.cleanUpRowById(
|
|
||||||
appId,
|
|
||||||
inputs.rowId,
|
|
||||||
inputs.row
|
|
||||||
)
|
|
||||||
// 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)) {
|
||||||
if (!inputs.row[propKey] || inputs.row[propKey] === "") {
|
if (!inputs.row[propKey] || inputs.row[propKey] === "") {
|
||||||
|
@ -73,7 +73,7 @@ module.exports.run = async function({ inputs, appId, emitter }) {
|
||||||
// have to clean up the row, remove the table from it
|
// have to clean up the row, remove the table from it
|
||||||
const ctx = {
|
const ctx = {
|
||||||
params: {
|
params: {
|
||||||
id: inputs.rowId,
|
rowId: inputs.rowId,
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
body: inputs.row,
|
body: inputs.row,
|
||||||
|
@ -83,6 +83,11 @@ module.exports.run = async function({ inputs, appId, emitter }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
inputs.row = await automationUtils.cleanUpRowById(
|
||||||
|
appId,
|
||||||
|
inputs.rowId,
|
||||||
|
inputs.row
|
||||||
|
)
|
||||||
await rowController.patch(ctx)
|
await rowController.patch(ctx)
|
||||||
return {
|
return {
|
||||||
row: ctx.body,
|
row: ctx.body,
|
||||||
|
|
|
@ -0,0 +1,152 @@
|
||||||
|
const automation = require("../index")
|
||||||
|
const usageQuota = require("../../utilities/usageQuota")
|
||||||
|
const thread = require("../thread")
|
||||||
|
const triggers = require("../triggers")
|
||||||
|
const { basicAutomation, basicTable } = require("../../tests/utilities/structures")
|
||||||
|
const { wait } = require("../../utilities")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const { makePartial } = require("../../tests/utilities")
|
||||||
|
const { cleanInputValues } = require("../automationUtils")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
let workerJob
|
||||||
|
|
||||||
|
jest.mock("../../utilities/usageQuota")
|
||||||
|
usageQuota.getAPIKey.mockReturnValue({ apiKey: "test" })
|
||||||
|
jest.mock("../thread")
|
||||||
|
jest.spyOn(global.console, "error")
|
||||||
|
jest.mock("worker-farm", () => {
|
||||||
|
return () => {
|
||||||
|
const value = jest
|
||||||
|
.fn()
|
||||||
|
.mockReturnValueOnce(undefined)
|
||||||
|
.mockReturnValueOnce("Error")
|
||||||
|
return (input, callback) => {
|
||||||
|
workerJob = input
|
||||||
|
if (callback) {
|
||||||
|
callback(value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Run through some parts of the automations system", () => {
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await automation.init()
|
||||||
|
await config.init()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to init in builder", async () => {
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
expect(workerJob).toBeUndefined()
|
||||||
|
expect(thread).toHaveBeenCalled()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to init in cloud", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION"
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
// haven't added a mock implementation so getAPIKey of usageQuota just returns undefined
|
||||||
|
expect(usageQuota.update).toHaveBeenCalledWith("test", "automationRuns", 1)
|
||||||
|
expect(workerJob).toBeDefined()
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "JEST"
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("try error scenario", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "PRODUCTION"
|
||||||
|
// the second call will throw an error
|
||||||
|
await triggers.externalTrigger(basicAutomation(), { a: 1 })
|
||||||
|
await wait(100)
|
||||||
|
expect(console.error).toHaveBeenCalled()
|
||||||
|
env.BUDIBASE_ENVIRONMENT = "JEST"
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to check triggering row filling", async () => {
|
||||||
|
const automation = basicAutomation()
|
||||||
|
let table = basicTable()
|
||||||
|
table.schema.boolean = {
|
||||||
|
type: "boolean",
|
||||||
|
constraints: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table.schema.number = {
|
||||||
|
type: "number",
|
||||||
|
constraints: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table.schema.datetime = {
|
||||||
|
type: "datetime",
|
||||||
|
constraints: {
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
table = await config.createTable(table)
|
||||||
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
const params = await triggers.fillRowOutput(automation, { appId: config.getAppId() })
|
||||||
|
expect(params.row).toBeDefined()
|
||||||
|
const date = new Date(params.row.datetime)
|
||||||
|
expect(typeof params.row.name).toBe("string")
|
||||||
|
expect(typeof params.row.boolean).toBe("boolean")
|
||||||
|
expect(typeof params.row.number).toBe("number")
|
||||||
|
expect(date.getFullYear()).toBe(1970)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check coercion", async () => {
|
||||||
|
const table = await config.createTable()
|
||||||
|
const automation = basicAutomation()
|
||||||
|
automation.definition.trigger.inputs.tableId = table._id
|
||||||
|
automation.definition.trigger.stepId = "APP"
|
||||||
|
automation.definition.trigger.inputs.fields = { a: "number" }
|
||||||
|
await triggers.externalTrigger(automation, {
|
||||||
|
appId: config.getAppId(),
|
||||||
|
fields: {
|
||||||
|
a: "1"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
await wait(100)
|
||||||
|
expect(thread).toHaveBeenCalledWith(makePartial({
|
||||||
|
data: {
|
||||||
|
event: {
|
||||||
|
fields: {
|
||||||
|
a: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to clean inputs with the utilities", () => {
|
||||||
|
// can't clean without a schema
|
||||||
|
let output = cleanInputValues({a: "1"})
|
||||||
|
expect(output.a).toBe("1")
|
||||||
|
output = cleanInputValues({a: "1", b: "true", c: "false", d: 1, e: "help"}, {
|
||||||
|
properties: {
|
||||||
|
a: {
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
b: {
|
||||||
|
type: "boolean",
|
||||||
|
},
|
||||||
|
c: {
|
||||||
|
type: "boolean",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(output.a).toBe(1)
|
||||||
|
expect(output.b).toBe(true)
|
||||||
|
expect(output.c).toBe(false)
|
||||||
|
expect(output.d).toBe(1)
|
||||||
|
expect(output.e).toBe("help")
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,57 @@
|
||||||
|
const usageQuota = require("../../utilities/usageQuota")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
jest.mock("../../utilities/usageQuota")
|
||||||
|
|
||||||
|
describe("test the create row action", () => {
|
||||||
|
let table, row
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
table = await config.createTable()
|
||||||
|
row = {
|
||||||
|
tableId: table._id,
|
||||||
|
name: "test",
|
||||||
|
description: "test",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {
|
||||||
|
row,
|
||||||
|
})
|
||||||
|
expect(res.id).toBeDefined()
|
||||||
|
expect(res.revision).toBeDefined()
|
||||||
|
const gottenRow = await config.getRow(table._id, res.id)
|
||||||
|
expect(gottenRow.name).toEqual("test")
|
||||||
|
expect(gottenRow.description).toEqual("test")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error (not throw) when bad info provided", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {
|
||||||
|
row: {
|
||||||
|
tableId: "invalid",
|
||||||
|
invalid: "invalid",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check usage quota attempts", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
await setup.runStep(setup.actions.CREATE_ROW.stepId, {
|
||||||
|
row
|
||||||
|
})
|
||||||
|
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", 1)
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check invalid inputs return an error", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.CREATE_ROW.stepId, {})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,43 @@
|
||||||
|
const usageQuota = require("../../utilities/usageQuota")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
|
||||||
|
const { ViewNames } = require("../../db/utils")
|
||||||
|
|
||||||
|
jest.mock("../../utilities/usageQuota")
|
||||||
|
|
||||||
|
describe("test the create user action", () => {
|
||||||
|
let config = setup.getConfig()
|
||||||
|
let user
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
user = {
|
||||||
|
email: "test@test.com",
|
||||||
|
password: "password",
|
||||||
|
roleId: BUILTIN_ROLE_IDS.POWER
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, user)
|
||||||
|
expect(res.id).toBeDefined()
|
||||||
|
expect(res.revision).toBeDefined()
|
||||||
|
const userDoc = await config.getRow(ViewNames.USERS, res.id)
|
||||||
|
expect(userDoc.email).toEqual(user.email)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error if no inputs provided", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.CREATE_USER.stepId, {})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check usage quota attempts", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
await setup.runStep(setup.actions.CREATE_USER.stepId, user)
|
||||||
|
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "users", 1)
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,12 @@
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
describe("test the delay logic", () => {
|
||||||
|
it("should be able to run the delay", async () => {
|
||||||
|
const time = 100
|
||||||
|
const before = Date.now()
|
||||||
|
await setup.runStep(setup.logic.DELAY.stepId, { time: time })
|
||||||
|
const now = Date.now()
|
||||||
|
// divide by two just so that test will always pass as long as there was some sort of delay
|
||||||
|
expect(now - before).toBeGreaterThanOrEqual(time / 2)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,58 @@
|
||||||
|
const usageQuota = require("../../utilities/usageQuota")
|
||||||
|
const env = require("../../environment")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
jest.mock("../../utilities/usageQuota")
|
||||||
|
|
||||||
|
describe("test the delete row action", () => {
|
||||||
|
let table, row, inputs
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
table = await config.createTable()
|
||||||
|
row = await config.createRow()
|
||||||
|
inputs = {
|
||||||
|
tableId: table._id,
|
||||||
|
id: row._id,
|
||||||
|
revision: row._rev,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs)
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
expect(res.response).toBeDefined()
|
||||||
|
expect(res.row._id).toEqual(row._id)
|
||||||
|
let error
|
||||||
|
try {
|
||||||
|
await config.getRow(table._id, res.id)
|
||||||
|
} catch (err) {
|
||||||
|
error = err
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined()
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check usage quota attempts", async () => {
|
||||||
|
env.CLOUD = true
|
||||||
|
await setup.runStep(setup.actions.DELETE_ROW.stepId, inputs)
|
||||||
|
expect(usageQuota.update).toHaveBeenCalledWith(setup.apiKey, "rows", -1)
|
||||||
|
env.CLOUD = false
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check invalid inputs return an error", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.DELETE_ROW.stepId, {})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error when table doesn't exist", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.DELETE_ROW.stepId, {
|
||||||
|
tableId: "invalid",
|
||||||
|
id: "invalid",
|
||||||
|
revision: "invalid",
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,48 @@
|
||||||
|
const setup = require("./utilities")
|
||||||
|
const { LogicConditions } = require("../steps/filter")
|
||||||
|
|
||||||
|
describe("test the filter logic", () => {
|
||||||
|
async function checkFilter(field, condition, value, pass = true) {
|
||||||
|
let res = await setup.runStep(setup.logic.FILTER.stepId,
|
||||||
|
{ field, condition, value }
|
||||||
|
)
|
||||||
|
expect(res.success).toEqual(pass)
|
||||||
|
}
|
||||||
|
|
||||||
|
it("should be able test equality", async () => {
|
||||||
|
await checkFilter("hello", LogicConditions.EQUAL, "hello", true)
|
||||||
|
await checkFilter("hello", LogicConditions.EQUAL, "no", false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to test greater than", async () => {
|
||||||
|
await checkFilter(10, LogicConditions.GREATER_THAN, 5, true)
|
||||||
|
await checkFilter(10, LogicConditions.GREATER_THAN, 15, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to test less than", async () => {
|
||||||
|
await checkFilter(5, LogicConditions.LESS_THAN, 10, true)
|
||||||
|
await checkFilter(15, LogicConditions.LESS_THAN, 10, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should be able to in-equality", async () => {
|
||||||
|
await checkFilter("hello", LogicConditions.NOT_EQUAL, "no", true)
|
||||||
|
await checkFilter(10, LogicConditions.NOT_EQUAL, 10, false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check number coercion", async () => {
|
||||||
|
await checkFilter("10", LogicConditions.GREATER_THAN, "5", true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check date coercion", async () => {
|
||||||
|
await checkFilter(
|
||||||
|
(new Date()).toISOString(),
|
||||||
|
LogicConditions.GREATER_THAN,
|
||||||
|
(new Date(-10000)).toISOString(),
|
||||||
|
true
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("check objects always false", async () => {
|
||||||
|
await checkFilter({}, LogicConditions.EQUAL, {}, false)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,39 @@
|
||||||
|
const setup = require("./utilities")
|
||||||
|
const fetch = require("node-fetch")
|
||||||
|
|
||||||
|
jest.mock("node-fetch")
|
||||||
|
|
||||||
|
describe("test the outgoing webhook action", () => {
|
||||||
|
let inputs
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
inputs = {
|
||||||
|
requestMethod: "POST",
|
||||||
|
url: "www.test.com",
|
||||||
|
requestBody: JSON.stringify({
|
||||||
|
a: 1,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, inputs)
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
expect(res.response.url).toEqual("http://www.test.com")
|
||||||
|
expect(res.response.method).toEqual("POST")
|
||||||
|
expect(res.response.body.a).toEqual(1)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error if something goes wrong in fetch", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.OUTGOING_WEBHOOK.stepId, {
|
||||||
|
requestMethod: "GET",
|
||||||
|
url: "www.invalid.com"
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -0,0 +1,36 @@
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
jest.mock("@sendgrid/mail")
|
||||||
|
|
||||||
|
describe("test the send email action", () => {
|
||||||
|
let inputs
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
inputs = {
|
||||||
|
to: "me@test.com",
|
||||||
|
from: "budibase@test.com",
|
||||||
|
subject: "Testing",
|
||||||
|
text: "Email contents",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.SEND_EMAIL.stepId, inputs)
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
// the mocked module throws back the input
|
||||||
|
expect(res.response.to).toEqual("me@test.com")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error if input an invalid email address", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.SEND_EMAIL.stepId, {
|
||||||
|
...inputs,
|
||||||
|
to: "invalid@test.com",
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
|
@ -0,0 +1,45 @@
|
||||||
|
const env = require("../../environment")
|
||||||
|
const setup = require("./utilities")
|
||||||
|
|
||||||
|
describe("test the update row action", () => {
|
||||||
|
let table, row, inputs
|
||||||
|
let config = setup.getConfig()
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await config.init()
|
||||||
|
table = await config.createTable()
|
||||||
|
row = await config.createRow()
|
||||||
|
inputs = {
|
||||||
|
rowId: row._id,
|
||||||
|
row: {
|
||||||
|
...row,
|
||||||
|
name: "Updated name",
|
||||||
|
// put a falsy option in to be removed
|
||||||
|
description: "",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(setup.afterAll)
|
||||||
|
|
||||||
|
it("should be able to run the action", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, inputs)
|
||||||
|
expect(res.success).toEqual(true)
|
||||||
|
const updatedRow = await config.getRow(table._id, res.id)
|
||||||
|
expect(updatedRow.name).toEqual("Updated name")
|
||||||
|
expect(updatedRow.description).not.toEqual("")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should check invalid inputs return an error", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should return an error when table doesn't exist", async () => {
|
||||||
|
const res = await setup.runStep(setup.actions.UPDATE_ROW.stepId, {
|
||||||
|
row: { _id: "invalid" },
|
||||||
|
rowId: "invalid",
|
||||||
|
})
|
||||||
|
expect(res.success).toEqual(false)
|
||||||
|
})
|
||||||
|
})
|
|
@ -0,0 +1,43 @@
|
||||||
|
const TestConfig = require("../../../tests/utilities/TestConfiguration")
|
||||||
|
const actions = require("../../actions")
|
||||||
|
const logic = require("../../logic")
|
||||||
|
const emitter = require("../../../events/index")
|
||||||
|
|
||||||
|
let config
|
||||||
|
|
||||||
|
exports.getConfig = () => {
|
||||||
|
if (!config) {
|
||||||
|
config = new TestConfig(false)
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.afterAll = () => {
|
||||||
|
config.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.runStep = async function runStep(stepId, inputs) {
|
||||||
|
let step
|
||||||
|
if (
|
||||||
|
Object.values(exports.actions)
|
||||||
|
.map(action => action.stepId)
|
||||||
|
.includes(stepId)
|
||||||
|
) {
|
||||||
|
step = await actions.getAction(stepId)
|
||||||
|
} else {
|
||||||
|
step = logic.getLogic(stepId)
|
||||||
|
}
|
||||||
|
expect(step).toBeDefined()
|
||||||
|
return step({
|
||||||
|
inputs,
|
||||||
|
appId: config ? config.getAppId() : null,
|
||||||
|
// don't really need an API key, mocked out usage quota, not being tested here
|
||||||
|
apiKey: exports.apiKey,
|
||||||
|
emitter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.apiKey = "test"
|
||||||
|
|
||||||
|
exports.actions = actions.BUILTIN_DEFINITIONS
|
||||||
|
exports.logic = logic.BUILTIN_DEFINITIONS
|
|
@ -225,6 +225,7 @@ async function queueRelevantRowAutomations(event, eventType) {
|
||||||
}
|
}
|
||||||
|
|
||||||
emitter.on("row:save", async function(event) {
|
emitter.on("row:save", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -232,6 +233,7 @@ emitter.on("row:save", async function(event) {
|
||||||
})
|
})
|
||||||
|
|
||||||
emitter.on("row:update", async function(event) {
|
emitter.on("row:update", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -239,6 +241,7 @@ emitter.on("row:update", async function(event) {
|
||||||
})
|
})
|
||||||
|
|
||||||
emitter.on("row:delete", async function(event) {
|
emitter.on("row:delete", async function(event) {
|
||||||
|
/* istanbul ignore next */
|
||||||
if (!event || !event.row || !event.row.tableId) {
|
if (!event || !event.row || !event.row.tableId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -272,6 +275,7 @@ async function fillRowOutput(automation, params) {
|
||||||
}
|
}
|
||||||
params.row = row
|
params.row = row
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
/* istanbul ignore next */
|
||||||
throw "Failed to find table for trigger"
|
throw "Failed to find table for trigger"
|
||||||
}
|
}
|
||||||
return params
|
return params
|
||||||
|
@ -297,6 +301,7 @@ module.exports.externalTrigger = async function(automation, params) {
|
||||||
automationQueue.add({ automation, event: params })
|
automationQueue.add({ automation, event: params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.fillRowOutput = fillRowOutput
|
||||||
module.exports.automationQueue = automationQueue
|
module.exports.automationQueue = automationQueue
|
||||||
|
|
||||||
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
module.exports.BUILTIN_DEFINITIONS = BUILTIN_DEFINITIONS
|
||||||
|
|
|
@ -30,6 +30,7 @@ const Pouch = PouchDB.defaults(POUCH_DB_DEFAULTS)
|
||||||
allDbs(Pouch)
|
allDbs(Pouch)
|
||||||
|
|
||||||
// replicate your local levelDB pouch to a running HTTP compliant couch or pouchdb server.
|
// replicate your local levelDB pouch to a running HTTP compliant couch or pouchdb server.
|
||||||
|
/* istanbul ignore next */
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
function replicateLocal() {
|
function replicateLocal() {
|
||||||
Pouch.allDbs().then(dbs => {
|
Pouch.allDbs().then(dbs => {
|
||||||
|
|
|
@ -3,7 +3,7 @@ const usageQuota = require("../../utilities/usageQuota")
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
|
|
||||||
jest.mock("../../db");
|
jest.mock("../../db")
|
||||||
jest.mock("../../utilities/usageQuota")
|
jest.mock("../../utilities/usageQuota")
|
||||||
jest.mock("../../environment")
|
jest.mock("../../environment")
|
||||||
|
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
### Self hosting
|
|
||||||
This directory contains utilities that are needed for self hosted platforms to operate.
|
|
||||||
These will mostly be utilities, necessary to the operation of the server e.g. storing self
|
|
||||||
hosting specific options and attributes to CouchDB.
|
|
||||||
|
|
||||||
All the internal operations should be exposed through the `index.js` so importing
|
|
||||||
the self host directory should give you everything you need.
|
|
|
@ -1,44 +0,0 @@
|
||||||
const CouchDB = require("../db")
|
|
||||||
const env = require("../environment")
|
|
||||||
const newid = require("../db/newid")
|
|
||||||
|
|
||||||
const SELF_HOST_DB = "self-host-db"
|
|
||||||
const SELF_HOST_DOC = "self-host-info"
|
|
||||||
|
|
||||||
async function createSelfHostDB(db) {
|
|
||||||
await db.put({
|
|
||||||
_id: "_design/database",
|
|
||||||
views: {},
|
|
||||||
})
|
|
||||||
const selfHostInfo = {
|
|
||||||
_id: SELF_HOST_DOC,
|
|
||||||
apiKeyId: newid(),
|
|
||||||
}
|
|
||||||
await db.put(selfHostInfo)
|
|
||||||
return selfHostInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.init = async () => {
|
|
||||||
if (!env.SELF_HOSTED) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const db = new CouchDB(SELF_HOST_DB)
|
|
||||||
try {
|
|
||||||
await db.get(SELF_HOST_DOC)
|
|
||||||
} catch (err) {
|
|
||||||
// failed to retrieve
|
|
||||||
if (err.status === 404) {
|
|
||||||
await createSelfHostDB(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getSelfHostInfo = async () => {
|
|
||||||
const db = new CouchDB(SELF_HOST_DB)
|
|
||||||
return db.get(SELF_HOST_DOC)
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.getSelfHostAPIKey = async () => {
|
|
||||||
const info = await exports.getSelfHostInfo()
|
|
||||||
return info ? info.apiKeyId : null
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
|
||||||
const jwt = require("jsonwebtoken")
|
const jwt = require("jsonwebtoken")
|
||||||
const env = require("../../../../environment")
|
const env = require("../../environment")
|
||||||
const {
|
const {
|
||||||
basicTable,
|
basicTable,
|
||||||
basicRow,
|
basicRow,
|
||||||
|
@ -15,18 +15,20 @@ const {
|
||||||
const controllers = require("./controllers")
|
const controllers = require("./controllers")
|
||||||
const supertest = require("supertest")
|
const supertest = require("supertest")
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const { budibaseAppsDir } = require("../../../../utilities/budibaseDir")
|
const { budibaseAppsDir } = require("../../utilities/budibaseDir")
|
||||||
const { join } = require("path")
|
const { join } = require("path")
|
||||||
|
|
||||||
const EMAIL = "babs@babs.com"
|
const EMAIL = "babs@babs.com"
|
||||||
const PASSWORD = "babs_password"
|
const PASSWORD = "babs_password"
|
||||||
|
|
||||||
class TestConfiguration {
|
class TestConfiguration {
|
||||||
constructor() {
|
constructor(openServer = true) {
|
||||||
env.PORT = 4002
|
if (openServer) {
|
||||||
this.server = require("../../../../app")
|
env.PORT = 4002
|
||||||
// we need the request for logging in, involves cookies, hard to fake
|
this.server = require("../../app")
|
||||||
this.request = supertest(this.server)
|
// we need the request for logging in, involves cookies, hard to fake
|
||||||
|
this.request = supertest(this.server)
|
||||||
|
}
|
||||||
this.appId = null
|
this.appId = null
|
||||||
this.allApps = []
|
this.allApps = []
|
||||||
}
|
}
|
||||||
|
@ -61,7 +63,9 @@ class TestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
end() {
|
end() {
|
||||||
this.server.close()
|
if (this.server) {
|
||||||
|
this.server.close()
|
||||||
|
}
|
||||||
const appDir = budibaseAppsDir()
|
const appDir = budibaseAppsDir()
|
||||||
const files = fs.readdirSync(appDir)
|
const files = fs.readdirSync(appDir)
|
||||||
for (let file of files) {
|
for (let file of files) {
|
||||||
|
@ -163,6 +167,17 @@ class TestConfiguration {
|
||||||
return this._req(config, { tableId: this.table._id }, controllers.row.save)
|
return this._req(config, { tableId: this.table._id }, controllers.row.save)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getRow(tableId, rowId) {
|
||||||
|
return this._req(null, { tableId, rowId }, controllers.row.find)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getRows(tableId) {
|
||||||
|
if (!tableId && this.table) {
|
||||||
|
tableId = this.table._id
|
||||||
|
}
|
||||||
|
return this._req(null, { tableId }, controllers.row.fetchTableRows)
|
||||||
|
}
|
||||||
|
|
||||||
async createRole(config = null) {
|
async createRole(config = null) {
|
||||||
config = config || basicRole()
|
config = config || basicRole()
|
||||||
return this._req(config, null, controllers.role.save)
|
return this._req(config, null, controllers.role.save)
|
||||||
|
@ -187,6 +202,7 @@ class TestConfiguration {
|
||||||
const view = config || {
|
const view = config || {
|
||||||
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
|
map: "function(doc) { emit(doc[doc.key], doc._id); } ",
|
||||||
tableId: this.table._id,
|
tableId: this.table._id,
|
||||||
|
name: "ViewTest",
|
||||||
}
|
}
|
||||||
return this._req(view, null, controllers.view.save)
|
return this._req(view, null, controllers.view.save)
|
||||||
}
|
}
|
||||||
|
@ -285,6 +301,9 @@ class TestConfiguration {
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(email, password) {
|
async login(email, password) {
|
||||||
|
if (!this.request) {
|
||||||
|
throw "Server has not been opened, cannot login."
|
||||||
|
}
|
||||||
if (!email || !password) {
|
if (!email || !password) {
|
||||||
await this.createUser()
|
await this.createUser()
|
||||||
email = EMAIL
|
email = EMAIL
|
|
@ -0,0 +1,15 @@
|
||||||
|
module.exports = {
|
||||||
|
table: require("../../api/controllers/table"),
|
||||||
|
row: require("../../api/controllers/row"),
|
||||||
|
role: require("../../api/controllers/role"),
|
||||||
|
perms: require("../../api/controllers/permission"),
|
||||||
|
view: require("../../api/controllers/view"),
|
||||||
|
app: require("../../api/controllers/application"),
|
||||||
|
user: require("../../api/controllers/user"),
|
||||||
|
automation: require("../../api/controllers/automation"),
|
||||||
|
datasource: require("../../api/controllers/datasource"),
|
||||||
|
query: require("../../api/controllers/query"),
|
||||||
|
screen: require("../../api/controllers/screen"),
|
||||||
|
webhook: require("../../api/controllers/webhook"),
|
||||||
|
layout: require("../../api/controllers/layout"),
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
exports.makePartial = obj => {
|
||||||
|
const newObj = {}
|
||||||
|
for (let key of Object.keys(obj)) {
|
||||||
|
if (typeof obj[key] === "object") {
|
||||||
|
newObj[key] = exports.makePartial(obj[key])
|
||||||
|
} else {
|
||||||
|
newObj[key] = obj[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return expect.objectContaining(newObj)
|
||||||
|
}
|
|
@ -1,9 +1,9 @@
|
||||||
const { BUILTIN_ROLE_IDS } = require("../../../../utilities/security/roles")
|
const { BUILTIN_ROLE_IDS } = require("../../utilities/security/roles")
|
||||||
const {
|
const {
|
||||||
BUILTIN_PERMISSION_IDS,
|
BUILTIN_PERMISSION_IDS,
|
||||||
} = require("../../../../utilities/security/permissions")
|
} = require("../../utilities/security/permissions")
|
||||||
const { createHomeScreen } = require("../../../../constants/screens")
|
const { createHomeScreen } = require("../../constants/screens")
|
||||||
const { EMPTY_LAYOUT } = require("../../../../constants/layouts")
|
const { EMPTY_LAYOUT } = require("../../constants/layouts")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
exports.basicTable = () => {
|
exports.basicTable = () => {
|
|
@ -7,6 +7,8 @@ const packageJson = require("../../package.json")
|
||||||
|
|
||||||
const streamPipeline = promisify(stream.pipeline)
|
const streamPipeline = promisify(stream.pipeline)
|
||||||
|
|
||||||
|
// can't really test this due to the downloading nature of it, wouldn't be a great test case
|
||||||
|
/* istanbul ignore next */
|
||||||
exports.downloadExtractComponentLibraries = async appFolder => {
|
exports.downloadExtractComponentLibraries = async appFolder => {
|
||||||
const LIBRARIES = ["standard-components"]
|
const LIBRARIES = ["standard-components"]
|
||||||
|
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
const statusCodes = require("./statusCodes")
|
|
||||||
|
|
||||||
const errorWithStatus = (message, statusCode) => {
|
|
||||||
const e = new Error(message)
|
|
||||||
e.statusCode = statusCode
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports.unauthorized = message =>
|
|
||||||
errorWithStatus(message, statusCodes.UNAUTHORIZED)
|
|
||||||
|
|
||||||
module.exports.forbidden = message =>
|
|
||||||
errorWithStatus(message, statusCodes.FORBIDDEN)
|
|
||||||
|
|
||||||
module.exports.notfound = message =>
|
|
||||||
errorWithStatus(message, statusCodes.NOT_FOUND)
|
|
|
@ -12,6 +12,7 @@ exports.getRoutingInfo = async appId => {
|
||||||
return allRouting.rows.map(row => row.value)
|
return allRouting.rows.map(row => row.value)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// check if the view doesn't exist, it should for all new instances
|
// check if the view doesn't exist, it should for all new instances
|
||||||
|
/* istanbul ignore next */
|
||||||
if (err != null && err.name === "not_found") {
|
if (err != null && err.name === "not_found") {
|
||||||
await createRoutingView(appId)
|
await createRoutingView(appId)
|
||||||
return exports.getRoutingInfo(appId)
|
return exports.getRoutingInfo(appId)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
const { apiKeyTable } = require("../../db/dynamoClient")
|
const { apiKeyTable } = require("../../db/dynamoClient")
|
||||||
const env = require("../../environment")
|
const env = require("../../environment")
|
||||||
const { getSelfHostAPIKey } = require("../../selfhost")
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This file purely exists so that we can centralise all logic pertaining to API keys, as their usage differs
|
* This file purely exists so that we can centralise all logic pertaining to API keys, as their usage differs
|
||||||
|
@ -8,16 +7,13 @@ const { getSelfHostAPIKey } = require("../../selfhost")
|
||||||
*/
|
*/
|
||||||
|
|
||||||
exports.isAPIKeyValid = async apiKeyId => {
|
exports.isAPIKeyValid = async apiKeyId => {
|
||||||
if (env.CLOUD && !env.SELF_HOSTED) {
|
if (!env.SELF_HOSTED) {
|
||||||
let apiKeyInfo = await apiKeyTable.get({
|
let apiKeyInfo = await apiKeyTable.get({
|
||||||
primary: apiKeyId,
|
primary: apiKeyId,
|
||||||
})
|
})
|
||||||
return apiKeyInfo != null
|
return apiKeyInfo != null
|
||||||
}
|
} else {
|
||||||
if (env.SELF_HOSTED) {
|
|
||||||
const selfHostKey = await getSelfHostAPIKey()
|
|
||||||
// if the api key supplied is correct then return structure similar
|
// if the api key supplied is correct then return structure similar
|
||||||
return apiKeyId === selfHostKey ? { pk: apiKeyId } : null
|
return apiKeyId === env.HOSTING_KEY
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,9 +35,9 @@
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte"
|
"svelte"
|
||||||
],
|
],
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"gitHead": "768a9d59da12cfefcd7ccaba05f975e878669504",
|
"gitHead": "1b95326b20d1352d36305910259228b96a683dc7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
|
"@adobe/spectrum-css-workflow-icons": "^1.1.0",
|
||||||
"@budibase/bbui": "^1.58.13",
|
"@budibase/bbui": "^1.58.13",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/string-templates",
|
"name": "@budibase/string-templates",
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"description": "Handlebars wrapper for Budibase templating.",
|
"description": "Handlebars wrapper for Budibase templating.",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"module": "src/index.js",
|
"module": "src/index.js",
|
||||||
|
@ -33,5 +33,5 @@
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
"rollup-plugin-terser": "^7.0.2",
|
||||||
"typescript": "^4.1.3"
|
"typescript": "^4.1.3"
|
||||||
},
|
},
|
||||||
"gitHead": "768a9d59da12cfefcd7ccaba05f975e878669504"
|
"gitHead": "1b95326b20d1352d36305910259228b96a683dc7"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "@budibase/deployment",
|
"name": "@budibase/deployment",
|
||||||
"email": "hi@budibase.com",
|
"email": "hi@budibase.com",
|
||||||
"version": "0.8.5",
|
"version": "0.8.9",
|
||||||
"description": "Budibase Deployment Server",
|
"description": "Budibase Deployment Server",
|
||||||
"main": "src/index.js",
|
"main": "src/index.js",
|
||||||
"repository": {
|
"repository": {
|
||||||
|
@ -34,5 +34,5 @@
|
||||||
"pouchdb-all-dbs": "^1.0.2",
|
"pouchdb-all-dbs": "^1.0.2",
|
||||||
"server-destroy": "^1.0.1"
|
"server-destroy": "^1.0.1"
|
||||||
},
|
},
|
||||||
"gitHead": "768a9d59da12cfefcd7ccaba05f975e878669504"
|
"gitHead": "1b95326b20d1352d36305910259228b96a683dc7"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue