Merge branch 'develop' of github.com:Budibase/budibase into public-api-sdk
This commit is contained in:
commit
ce1c801ff0
|
@ -0,0 +1,24 @@
|
|||
---
|
||||
name: Epic
|
||||
about: Plan a new project
|
||||
title: ''
|
||||
labels: epic
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
## Description
|
||||
Brief summary of what this Epic is, whether it's a larger project, goal, or user story. Describe the job to be done, which persona this Epic is mainly for, or if more multiple, break it down by user and job story.
|
||||
|
||||
## Spec
|
||||
Link to confluence spec
|
||||
|
||||
## Teams and Stakeholders
|
||||
Describe who needs to be kept up-to-date about this Epic, included in discussions, or updated along the way. Stakeholders can be both in Product/Engineering, as well as other teams like Customer Success who might want to keep customers updated on the Epic project.
|
||||
|
||||
|
||||
## Workflow
|
||||
- [ ] Spec Created and pasted above
|
||||
- [ ] Product Review
|
||||
- [ ] Designs created
|
||||
- [ ] Individual Tasks created and assigned to Epic
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
"js-yaml": "^4.1.0",
|
||||
"kill-port": "^1.6.1",
|
||||
"lerna": "3.14.1",
|
||||
"madge": "^5.0.1",
|
||||
"prettier": "^2.3.1",
|
||||
"prettier-plugin-svelte": "^2.3.0",
|
||||
"rimraf": "^3.0.2",
|
||||
|
@ -25,6 +26,7 @@
|
|||
"bootstrap": "lerna bootstrap && lerna link && ./scripts/link-dependencies.sh",
|
||||
"build": "lerna run build",
|
||||
"build:dev": "lerna run prebuild && tsc --build --watch --preserveWatchOutput",
|
||||
"deps:circular": "madge packages/server/dist/index.js packages/worker/src/index.ts packages/backend-core/dist/src/index.js packages/cli/src/index.js --circular",
|
||||
"release": "lerna publish ${RELEASE_VERSION_TYPE:-patch} --yes --force-publish && yarn release:pro",
|
||||
"release:develop": "lerna publish prerelease --yes --force-publish --dist-tag develop --exact && yarn release:pro:develop",
|
||||
"release:pro": "bash scripts/pro/release.sh",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/backend-core",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase backend core libraries used in server and worker",
|
||||
"main": "dist/src/index.js",
|
||||
"types": "dist/src/index.d.ts",
|
||||
|
@ -20,7 +20,7 @@
|
|||
"test:watch": "jest --watchAll"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/types": "1.3.19-alpha.6",
|
||||
"@budibase/types": "1.3.22-alpha.3",
|
||||
"@shopify/jest-koa-mocks": "5.0.1",
|
||||
"@techpass/passport-openidconnect": "0.3.2",
|
||||
"aws-sdk": "2.1030.0",
|
||||
|
|
|
@ -44,6 +44,7 @@ export enum DocumentType {
|
|||
DEV_INFO = "devinfo",
|
||||
AUTOMATION_LOG = "log_au",
|
||||
ACCOUNT_METADATA = "acc_metadata",
|
||||
PLUGIN = "plg",
|
||||
}
|
||||
|
||||
export const StaticDatabases = {
|
||||
|
|
|
@ -3,7 +3,7 @@ import { DEFAULT_TENANT_ID, Configs } from "../constants"
|
|||
import env from "../environment"
|
||||
import { SEPARATOR, DocumentType, UNICODE_MAX, ViewName } from "./constants"
|
||||
import { getTenantId, getGlobalDB } from "../context"
|
||||
import { getGlobalDBName } from "../tenancy/utils"
|
||||
import { getGlobalDBName } from "../tenancy"
|
||||
import fetch from "node-fetch"
|
||||
import { doWithDB, allDbs } from "./index"
|
||||
import { getCouchInfo } from "./pouch"
|
||||
|
@ -367,6 +367,21 @@ export const generateDevInfoID = (userId: any) => {
|
|||
return `${DocumentType.DEV_INFO}${SEPARATOR}${userId}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a new plugin ID - to be used in the global DB.
|
||||
* @returns {string} The new plugin ID which a plugin metadata document can be stored under.
|
||||
*/
|
||||
export const generatePluginID = (name: string) => {
|
||||
return `${DocumentType.PLUGIN}${SEPARATOR}${name}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
|
||||
*/
|
||||
export const getPluginParams = (pluginId?: string | null, otherProps = {}) => {
|
||||
return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the most granular configuration document from the DB based on the type, workspace and userID passed.
|
||||
* @param {Object} db - db instance to query
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.22-alpha.3",
|
||||
"@spectrum-css/actionbutton": "^1.0.1",
|
||||
"@spectrum-css/actiongroup": "^1.0.1",
|
||||
"@spectrum-css/avatar": "^3.0.2",
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
bottom: 0;
|
||||
background: var(--background);
|
||||
border-top: var(--border-light);
|
||||
z-index: 2;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.fillWidth {
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
display: flex;
|
||||
justify-content: center;
|
||||
top: 15px;
|
||||
z-index: 100;
|
||||
z-index: 200;
|
||||
width: 160px;
|
||||
}
|
||||
.icon {
|
||||
|
|
|
@ -2,7 +2,7 @@ import filterTests from "../support/filterTests"
|
|||
const interact = require("../support/interact")
|
||||
|
||||
filterTests(["all"], () => {
|
||||
context("Create Components", () => {
|
||||
xcontext("Create Components", () => {
|
||||
let headlineId
|
||||
|
||||
before(() => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -9,6 +9,7 @@
|
|||
"dev:builder": "routify -c dev:vite",
|
||||
"dev:vite": "vite --host 0.0.0.0",
|
||||
"rollup": "rollup -c -w",
|
||||
"test": "jest",
|
||||
"cy:setup": "ts-node ./cypress/ts/setup.ts",
|
||||
"cy:setup:ci": "node ./cypress/setup.js",
|
||||
"cy:open": "cypress open",
|
||||
|
@ -36,7 +37,8 @@
|
|||
"components(.*)$": "<rootDir>/src/components$1",
|
||||
"builderStore(.*)$": "<rootDir>/src/builderStore$1",
|
||||
"stores(.*)$": "<rootDir>/src/stores$1",
|
||||
"analytics(.*)$": "<rootDir>/src/analytics$1"
|
||||
"analytics(.*)$": "<rootDir>/src/analytics$1",
|
||||
"constants/backend": "<rootDir>/src/constants/backend/index.js"
|
||||
},
|
||||
"moduleFileExtensions": [
|
||||
"js",
|
||||
|
@ -69,10 +71,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "1.3.19-alpha.6",
|
||||
"@budibase/client": "1.3.19-alpha.6",
|
||||
"@budibase/frontend-core": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/bbui": "1.3.22-alpha.3",
|
||||
"@budibase/client": "1.3.22-alpha.3",
|
||||
"@budibase/frontend-core": "1.3.22-alpha.3",
|
||||
"@budibase/string-templates": "1.3.22-alpha.3",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -9,14 +9,14 @@ import {
|
|||
import { store } from "builderStore"
|
||||
import {
|
||||
queries as queriesStores,
|
||||
tables as tablesStore,
|
||||
roles as rolesStore,
|
||||
tables as tablesStore,
|
||||
} from "stores/backend"
|
||||
import {
|
||||
makePropSafe,
|
||||
isJSBinding,
|
||||
decodeJSBinding,
|
||||
encodeJSBinding,
|
||||
isJSBinding,
|
||||
makePropSafe,
|
||||
} from "@budibase/string-templates"
|
||||
import { TableNames } from "../constants"
|
||||
import { JSONUtils } from "@budibase/frontend-core"
|
||||
|
@ -118,8 +118,7 @@ export const readableToRuntimeMap = (bindings, ctx) => {
|
|||
return {}
|
||||
}
|
||||
return Object.keys(ctx).reduce((acc, key) => {
|
||||
let parsedQuery = readableToRuntimeBinding(bindings, ctx[key])
|
||||
acc[key] = parsedQuery
|
||||
acc[key] = readableToRuntimeBinding(bindings, ctx[key])
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
@ -132,8 +131,7 @@ export const runtimeToReadableMap = (bindings, ctx) => {
|
|||
return {}
|
||||
}
|
||||
return Object.keys(ctx).reduce((acc, key) => {
|
||||
let parsedQuery = runtimeToReadableBinding(bindings, ctx[key])
|
||||
acc[key] = parsedQuery
|
||||
acc[key] = runtimeToReadableBinding(bindings, ctx[key])
|
||||
return acc
|
||||
}, {})
|
||||
}
|
||||
|
@ -379,7 +377,7 @@ const getProviderContextBindings = (asset, dataProviders) => {
|
|||
/**
|
||||
* Gets all bindable properties from the logged in user.
|
||||
*/
|
||||
const getUserBindings = () => {
|
||||
export const getUserBindings = () => {
|
||||
let bindings = []
|
||||
const { schema } = getSchemaForTable(TableNames.USERS)
|
||||
const keys = Object.keys(schema).sort()
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
import ExtraQueryConfig from "./ExtraQueryConfig.svelte"
|
||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
||||
import BindingBuilder from "components/integration/QueryBindingBuilder.svelte"
|
||||
import BindingBuilder from "components/integration/QueryViewerBindingBuilder.svelte"
|
||||
import { datasources, integrations, queries } from "stores/backend"
|
||||
import { capitalise } from "../../helpers"
|
||||
import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte"
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<script>
|
||||
import { Body, Button, Heading, Layout } from "@budibase/bbui"
|
||||
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||
import { getUserBindings } from "builderStore/dataBinding"
|
||||
export let bindable = true
|
||||
export let queryBindings = []
|
||||
|
||||
const userBindings = getUserBindings()
|
||||
|
||||
let internalBindings = queryBindings.reduce((acc, binding) => {
|
||||
acc[binding.name] = binding.default
|
||||
return acc
|
||||
}, {})
|
||||
|
||||
function newQueryBinding() {
|
||||
queryBindings = [...queryBindings, {}]
|
||||
}
|
||||
</script>
|
||||
|
||||
<Layout noPadding={bindable} gap="S">
|
||||
<div class="controls" class:height={!bindable}>
|
||||
<Heading size="XS">Bindings</Heading>
|
||||
{#if !bindable}
|
||||
<Button secondary on:click={newQueryBinding}>Add Binding</Button>
|
||||
{/if}
|
||||
</div>
|
||||
<Body size="S">
|
||||
{#if !bindable}
|
||||
Bindings come in two parts: the binding name, and a default/fallback
|
||||
value. These bindings can be used as Handlebars expressions throughout the
|
||||
query.
|
||||
{:else}
|
||||
Enter a value for each binding. The default values will be used for any
|
||||
values left blank.
|
||||
{/if}
|
||||
</Body>
|
||||
<div class="bindings" class:bindable>
|
||||
<KeyValueBuilder
|
||||
bind:object={internalBindings}
|
||||
tooltip="Set the name of the binding which can be used in Handlebars statements throughout your query"
|
||||
name="binding"
|
||||
headings
|
||||
keyPlaceholder="Binding name"
|
||||
valuePlaceholder="Default"
|
||||
bindings={[...userBindings]}
|
||||
bindingDrawerLeft="260px"
|
||||
on:change={e => {
|
||||
queryBindings = e.detail.map(binding => {
|
||||
return {
|
||||
name: binding.name,
|
||||
default: binding.value,
|
||||
}
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
<style>
|
||||
.controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.height {
|
||||
height: 40px;
|
||||
}
|
||||
</style>
|
|
@ -1,4 +1,5 @@
|
|||
import { IntegrationTypes } from "constants/backend"
|
||||
import { findHBSBlocks } from "@budibase/string-templates"
|
||||
|
||||
export function schemaToFields(schema) {
|
||||
const response = {}
|
||||
|
@ -31,7 +32,7 @@ export function breakQueryString(qs) {
|
|||
let paramObj = {}
|
||||
for (let param of params) {
|
||||
const split = param.split("=")
|
||||
paramObj[split[0]] = split.slice(1).join("=")
|
||||
paramObj[split[0]] = decodeURIComponent(split.slice(1).join("="))
|
||||
}
|
||||
return paramObj
|
||||
}
|
||||
|
@ -46,7 +47,19 @@ export function buildQueryString(obj) {
|
|||
if (str !== "") {
|
||||
str += "&"
|
||||
}
|
||||
str += `${key}=${encodeURIComponent(value || "")}`
|
||||
const bindings = findHBSBlocks(value)
|
||||
let count = 0
|
||||
const bindingMarkers = {}
|
||||
bindings.forEach(binding => {
|
||||
const marker = `BINDING...${count++}`
|
||||
value = value.replace(binding, marker)
|
||||
bindingMarkers[marker] = binding
|
||||
})
|
||||
let encoded = encodeURIComponent(value || "")
|
||||
Object.entries(bindingMarkers).forEach(([marker, binding]) => {
|
||||
encoded = encoded.replace(marker, binding)
|
||||
})
|
||||
str += `${key}=${encoded}`
|
||||
}
|
||||
}
|
||||
return str
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import { breakQueryString, buildQueryString } from "../data/utils"
|
||||
|
||||
describe("check query string utils", () => {
|
||||
const obj1 = {
|
||||
key1: "123",
|
||||
key2: " ",
|
||||
key3: "333",
|
||||
}
|
||||
|
||||
const obj2 = {
|
||||
key1: "{{ binding.awd }}",
|
||||
key2: "{{ binding.sed }} ",
|
||||
}
|
||||
|
||||
it("should build a basic query string", () => {
|
||||
const queryString = buildQueryString(obj1)
|
||||
expect(queryString).toBe("key1=123&key2=%20%20%20&key3=333")
|
||||
})
|
||||
|
||||
it("should be able to break a basic query string", () => {
|
||||
const broken = breakQueryString("key1=123&key2=%20%20%20&key3=333")
|
||||
expect(broken.key1).toBe(obj1.key1)
|
||||
expect(broken.key2).toBe(obj1.key2)
|
||||
expect(broken.key3).toBe(obj1.key3)
|
||||
})
|
||||
|
||||
it("should be able to build with a binding", () => {
|
||||
const queryString = buildQueryString(obj2)
|
||||
expect(queryString).toBe("key1={{ binding.awd }}&key2={{ binding.sed }}%20%20")
|
||||
})
|
||||
|
||||
it("should be able to break with a binding", () => {
|
||||
const broken = breakQueryString("key1={{ binding.awd }}&key2={{ binding.sed }}%20%20")
|
||||
expect(broken.key1).toBe(obj2.key1)
|
||||
expect(broken.key2).toBe(obj2.key2)
|
||||
})
|
||||
})
|
|
@ -708,6 +708,7 @@
|
|||
.url-block {
|
||||
display: flex;
|
||||
gap: var(--spacing-s);
|
||||
z-index: 200;
|
||||
}
|
||||
.verb {
|
||||
flex: 1;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { writable, get } from "svelte/store"
|
||||
import { datasources, integrations, tables, views } from "./"
|
||||
import { API } from "api"
|
||||
import { duplicateName } from "../../helpers/duplicate"
|
||||
import { duplicateName } from "helpers/duplicate"
|
||||
|
||||
const sortQueries = queryList => {
|
||||
queryList.sort((q1, q2) => {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { get, writable } from "svelte/store"
|
|||
import { datasources, queries, views } from "./"
|
||||
import { cloneDeep } from "lodash/fp"
|
||||
import { API } from "api"
|
||||
import { SWITCHABLE_TYPES } from "../../constants/backend"
|
||||
import { SWITCHABLE_TYPES } from "constants/backend"
|
||||
|
||||
export function createTablesStore() {
|
||||
const store = writable({})
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { get } from 'svelte/store'
|
||||
import api from 'builderStore/api'
|
||||
import { get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
|
||||
jest.mock('builderStore/api');
|
||||
jest.mock("api")
|
||||
|
||||
import { SOME_DATASOURCE, SAVE_DATASOURCE} from './fixtures/datasources'
|
||||
import { SOME_DATASOURCE, SAVE_DATASOURCE } from "./fixtures/datasources"
|
||||
|
||||
import { createDatasourcesStore } from "../datasources"
|
||||
import { queries } from '../queries'
|
||||
|
@ -12,39 +12,39 @@ describe("Datasources Store", () => {
|
|||
let store = createDatasourcesStore()
|
||||
|
||||
beforeEach(async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_DATASOURCE]})
|
||||
API.getDatasources.mockReturnValue({ json: () => [SOME_DATASOURCE]})
|
||||
await store.init()
|
||||
})
|
||||
|
||||
it("Initialises correctly", async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_DATASOURCE]})
|
||||
|
||||
API.getDatasources.mockReturnValue({ json: () => [SOME_DATASOURCE]})
|
||||
|
||||
await store.init()
|
||||
expect(get(store)).toEqual({ list: [SOME_DATASOURCE], selected: null})
|
||||
})
|
||||
|
||||
it("fetches all the datasources and updates the store", async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_DATASOURCE] })
|
||||
API.getDatasources.mockReturnValue({ json: () => [SOME_DATASOURCE] })
|
||||
|
||||
await store.fetch()
|
||||
expect(get(store)).toEqual({ list: [SOME_DATASOURCE], selected: null })
|
||||
expect(get(store)).toEqual({ list: [SOME_DATASOURCE], selected: null })
|
||||
})
|
||||
|
||||
it("selects a datasource", async () => {
|
||||
store.select(SOME_DATASOURCE._id)
|
||||
|
||||
expect(get(store).select).toEqual(SOME_DATASOURCE._id)
|
||||
|
||||
expect(get(store).select).toEqual(SOME_DATASOURCE._id)
|
||||
})
|
||||
|
||||
it("resets the queries store when new datasource is selected", async () => {
|
||||
|
||||
|
||||
await store.select(SOME_DATASOURCE._id)
|
||||
const queriesValue = get(queries)
|
||||
expect(queriesValue.selected).toEqual(null)
|
||||
expect(queriesValue.selected).toEqual(null)
|
||||
})
|
||||
|
||||
it("saves the datasource, updates the store and returns status message", async () => {
|
||||
api.post.mockReturnValue({ status: 200, json: () => SAVE_DATASOURCE})
|
||||
API.createDatasource.mockReturnValue({ status: 200, json: () => SAVE_DATASOURCE})
|
||||
|
||||
await store.save({
|
||||
name: 'CoolDB',
|
||||
|
@ -56,13 +56,13 @@ describe("Datasources Store", () => {
|
|||
expect(get(store).list).toEqual(expect.arrayContaining([SAVE_DATASOURCE.datasource]))
|
||||
})
|
||||
it("deletes a datasource, updates the store and returns status message", async () => {
|
||||
api.get.mockReturnValue({ json: () => SOME_DATASOURCE})
|
||||
API.getDatasources.mockReturnValue({ json: () => SOME_DATASOURCE})
|
||||
|
||||
await store.fetch()
|
||||
|
||||
api.delete.mockReturnValue({status: 200, message: 'Datasource deleted.'})
|
||||
API.deleteDatasource.mockReturnValue({status: 200, message: 'Datasource deleted.'})
|
||||
|
||||
await store.delete(SOME_DATASOURCE[0])
|
||||
expect(get(store)).toEqual({ list: [], selected: null})
|
||||
expect(get(store)).toEqual({ list: [], selected: null})
|
||||
})
|
||||
})
|
|
@ -1,6 +1,6 @@
|
|||
import api from 'builderStore/api'
|
||||
import { API } from "api"
|
||||
|
||||
jest.mock('builderStore/api');
|
||||
jest.mock("api")
|
||||
|
||||
const PERMISSIONS_FOR_RESOURCE = {
|
||||
"write": "BASIC",
|
||||
|
@ -13,13 +13,12 @@ describe("Permissions Store", () => {
|
|||
const store = createPermissionStore()
|
||||
|
||||
it("fetches permissions for specific resource", async () => {
|
||||
api.get.mockReturnValueOnce({ json: () => PERMISSIONS_FOR_RESOURCE})
|
||||
API.getPermissionForResource.mockReturnValueOnce({ json: () => PERMISSIONS_FOR_RESOURCE})
|
||||
|
||||
const resourceId = "ta_013657543b4043b89dbb17e9d3a4723a"
|
||||
|
||||
const permissions = await store.forResource(resourceId)
|
||||
|
||||
expect(api.get).toBeCalledWith(`/api/permission/${resourceId}`)
|
||||
expect(permissions).toEqual(PERMISSIONS_FOR_RESOURCE)
|
||||
})
|
||||
})
|
|
@ -1,9 +1,9 @@
|
|||
import { get } from 'svelte/store'
|
||||
import api from 'builderStore/api'
|
||||
import { get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
|
||||
jest.mock('builderStore/api');
|
||||
jest.mock("api")
|
||||
|
||||
import { SOME_QUERY, SAVE_QUERY_RESPONSE } from './fixtures/queries'
|
||||
import { SOME_QUERY, SAVE_QUERY_RESPONSE } from "./fixtures/queries"
|
||||
|
||||
import { createQueriesStore } from "../queries"
|
||||
|
||||
|
@ -11,36 +11,36 @@ describe("Queries Store", () => {
|
|||
let store = createQueriesStore()
|
||||
|
||||
beforeEach(async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
API.getQueries.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
await store.init()
|
||||
})
|
||||
|
||||
it("Initialises correctly", async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
|
||||
API.getQueries.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
|
||||
await store.init()
|
||||
expect(get(store)).toEqual({ list: [SOME_QUERY], selected: null})
|
||||
})
|
||||
|
||||
it("fetches all the queries", async () => {
|
||||
api.get.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
API.getQueries.mockReturnValue({ json: () => [SOME_QUERY]})
|
||||
|
||||
await store.fetch()
|
||||
expect(get(store)).toEqual({ list: [SOME_QUERY], selected: null})
|
||||
expect(get(store)).toEqual({ list: [SOME_QUERY], selected: null})
|
||||
})
|
||||
|
||||
it("saves the query, updates the store and returns status message", async () => {
|
||||
api.post.mockReturnValue({ json: () => SAVE_QUERY_RESPONSE})
|
||||
API.saveQuery.mockReturnValue({ json: () => SAVE_QUERY_RESPONSE})
|
||||
|
||||
await store.select(SOME_QUERY.datasourceId, SOME_QUERY)
|
||||
|
||||
expect(get(store).list).toEqual(expect.arrayContaining([SOME_QUERY]))
|
||||
})
|
||||
it("deletes a query, updates the store and returns status message", async () => {
|
||||
|
||||
api.delete.mockReturnValue({status: 200, message: `Query deleted.`})
|
||||
|
||||
|
||||
API.deleteQuery.mockReturnValue({status: 200, message: `Query deleted.`})
|
||||
|
||||
await store.delete(SOME_QUERY)
|
||||
expect(get(store)).toEqual({ list: [], selected: null})
|
||||
expect(get(store)).toEqual({ list: [], selected: null})
|
||||
})
|
||||
})
|
|
@ -1,10 +1,10 @@
|
|||
import { get } from 'svelte/store'
|
||||
import api from 'builderStore/api'
|
||||
import { get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
|
||||
jest.mock('builderStore/api');
|
||||
jest.mock("api")
|
||||
|
||||
import { createRolesStore } from "../roles"
|
||||
import { ROLES } from './fixtures/roles'
|
||||
import { ROLES } from "./fixtures/roles"
|
||||
|
||||
describe("Roles Store", () => {
|
||||
let store = createRolesStore()
|
||||
|
@ -14,19 +14,18 @@ describe("Roles Store", () => {
|
|||
})
|
||||
|
||||
it("fetches roles from backend", async () => {
|
||||
api.get.mockReturnValue({ json: () => ROLES})
|
||||
API.getRoles.mockReturnValue({ json: () => ROLES})
|
||||
await store.fetch()
|
||||
|
||||
expect(api.get).toBeCalledWith("/api/roles")
|
||||
expect(get(store)).toEqual(ROLES)
|
||||
})
|
||||
|
||||
it("deletes a role", async () => {
|
||||
api.get.mockReturnValueOnce({ json: () => ROLES})
|
||||
API.getRoles.mockReturnValueOnce({ json: () => ROLES})
|
||||
await store.fetch()
|
||||
|
||||
api.delete.mockReturnValue({status: 200, message: `Role deleted.`})
|
||||
|
||||
|
||||
API.deleteRole.mockReturnValue({status: 200, message: `Role deleted.`})
|
||||
|
||||
const updatedRoles = [...ROLES.slice(1)]
|
||||
await store.delete(ROLES[0])
|
||||
|
|
@ -1,18 +1,16 @@
|
|||
import { get } from 'svelte/store'
|
||||
import api from 'builderStore/api'
|
||||
import { get } from "svelte/store"
|
||||
import { API } from "api"
|
||||
|
||||
jest.mock('builderStore/api');
|
||||
|
||||
import { SOME_TABLES, SAVE_TABLES_RESPONSE, A_TABLE } from './fixtures/tables'
|
||||
jest.mock("api")
|
||||
|
||||
import { SOME_TABLES, SAVE_TABLES_RESPONSE, A_TABLE } from "./fixtures/tables"
|
||||
import { createTablesStore } from "../tables"
|
||||
import { views } from '../views'
|
||||
|
||||
describe("Tables Store", () => {
|
||||
let store = createTablesStore()
|
||||
|
||||
beforeEach(async () => {
|
||||
api.get.mockReturnValue({ json: () => SOME_TABLES})
|
||||
API.getTables.mockReturnValue({ json: () => SOME_TABLES})
|
||||
await store.init()
|
||||
})
|
||||
|
||||
|
@ -21,46 +19,46 @@ describe("Tables Store", () => {
|
|||
})
|
||||
|
||||
it("fetches all the tables", async () => {
|
||||
api.get.mockReturnValue({ json: () => SOME_TABLES})
|
||||
API.getTables.mockReturnValue({ json: () => SOME_TABLES})
|
||||
|
||||
await store.fetch()
|
||||
expect(get(store)).toEqual({ list: SOME_TABLES, selected: {}, draft: {}})
|
||||
expect(get(store)).toEqual({ list: SOME_TABLES, selected: {}, draft: {}})
|
||||
})
|
||||
|
||||
it("selects a table", async () => {
|
||||
const tableToSelect = SOME_TABLES[0]
|
||||
await store.select(tableToSelect)
|
||||
|
||||
expect(get(store).selected).toEqual(tableToSelect)
|
||||
expect(get(store).draft).toEqual(tableToSelect)
|
||||
|
||||
expect(get(store).selected).toEqual(tableToSelect)
|
||||
expect(get(store).draft).toEqual(tableToSelect)
|
||||
})
|
||||
|
||||
it("selecting without a param resets the selected property", async () => {
|
||||
await store.select()
|
||||
|
||||
expect(get(store).draft).toEqual({})
|
||||
|
||||
expect(get(store).draft).toEqual({})
|
||||
})
|
||||
|
||||
it("saving a table also selects it", async () => {
|
||||
api.post.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
|
||||
API.post.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
|
||||
|
||||
await store.save(A_TABLE)
|
||||
|
||||
expect(get(store).selected).toEqual(SAVE_TABLES_RESPONSE)
|
||||
expect(get(store).selected).toEqual(SAVE_TABLES_RESPONSE)
|
||||
})
|
||||
|
||||
it("saving the table returns a response", async () => {
|
||||
api.post.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
|
||||
API.saveTable.mockReturnValue({ status: 200, json: () => SAVE_TABLES_RESPONSE})
|
||||
|
||||
const response = await store.save(A_TABLE)
|
||||
|
||||
expect(response).toEqual(SAVE_TABLES_RESPONSE)
|
||||
expect(response).toEqual(SAVE_TABLES_RESPONSE)
|
||||
})
|
||||
it("deleting a table removes it from the store", async () => {
|
||||
api.delete.mockReturnValue({status: 200, message: `Table deleted.`})
|
||||
|
||||
API.deleteTable.mockReturnValue({status: 200, message: `Table deleted.`})
|
||||
|
||||
await store.delete(A_TABLE)
|
||||
expect(get(store).list).toEqual(expect.not.arrayContaining([A_TABLE]))
|
||||
expect(get(store).list).toEqual(expect.not.arrayContaining([A_TABLE]))
|
||||
})
|
||||
|
||||
// TODO: Write tests for saving and deleting fields
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
@ -26,9 +26,7 @@
|
|||
"outputPath": "build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/types": "1.3.19-alpha.6",
|
||||
"@budibase/backend-core": "1.3.22-alpha.3",
|
||||
"axios": "0.21.2",
|
||||
"chalk": "4.1.0",
|
||||
"cli-progress": "3.11.2",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -19,9 +19,9 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "1.3.19-alpha.6",
|
||||
"@budibase/frontend-core": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/bbui": "1.3.22-alpha.3",
|
||||
"@budibase/frontend-core": "1.3.22-alpha.3",
|
||||
"@budibase/string-templates": "1.3.22-alpha.3",
|
||||
"@spectrum-css/button": "^3.0.3",
|
||||
"@spectrum-css/card": "^3.0.3",
|
||||
"@spectrum-css/divider": "^1.0.3",
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@budibase/frontend-core",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase frontend core libraries used in builder and client",
|
||||
"author": "Budibase",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "1.3.19-alpha.6",
|
||||
"@budibase/bbui": "1.3.22-alpha.3",
|
||||
"lodash": "^4.17.21",
|
||||
"svelte": "^3.46.2"
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -77,11 +77,11 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "10.0.3",
|
||||
"@budibase/backend-core": "1.3.19-alpha.6",
|
||||
"@budibase/client": "1.3.19-alpha.6",
|
||||
"@budibase/pro": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/types": "1.3.19-alpha.6",
|
||||
"@budibase/backend-core": "1.3.22-alpha.3",
|
||||
"@budibase/client": "1.3.22-alpha.3",
|
||||
"@budibase/pro": "1.3.22-alpha.3",
|
||||
"@budibase/string-templates": "1.3.22-alpha.3",
|
||||
"@budibase/types": "1.3.22-alpha.3",
|
||||
"@bull-board/api": "3.7.0",
|
||||
"@bull-board/koa": "3.9.4",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
const { DocumentType, getPluginParams } = require("../../db/utils")
|
||||
const { getComponentLibraryManifest } = require("../../utilities/fileSystem")
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||
import { DocumentType } from "../../db/utils"
|
||||
import { Plugin } from "@budibase/types"
|
||||
import { db as dbCore, context, tenancy } from "@budibase/backend-core"
|
||||
import { getComponentLibraryManifest } from "../../utilities/fileSystem"
|
||||
|
||||
exports.fetchAppComponentDefinitions = async function (ctx) {
|
||||
exports.fetchAppComponentDefinitions = async function (ctx: any) {
|
||||
try {
|
||||
const db = getAppDB()
|
||||
const db = context.getAppDB()
|
||||
const app = await db.get(DocumentType.APP_METADATA)
|
||||
|
||||
let componentManifests = await Promise.all(
|
||||
app.componentLibraries.map(async library => {
|
||||
app.componentLibraries.map(async (library: any) => {
|
||||
let manifest = await getComponentLibraryManifest(library)
|
||||
return {
|
||||
manifest,
|
||||
|
@ -17,7 +17,7 @@ exports.fetchAppComponentDefinitions = async function (ctx) {
|
|||
}
|
||||
})
|
||||
)
|
||||
const definitions = {}
|
||||
const definitions: { [key: string]: any } = {}
|
||||
for (let { manifest, library } of componentManifests) {
|
||||
for (let key of Object.keys(manifest)) {
|
||||
if (key === "features") {
|
||||
|
@ -33,16 +33,16 @@ exports.fetchAppComponentDefinitions = async function (ctx) {
|
|||
}
|
||||
|
||||
// Add custom components
|
||||
const globalDB = getGlobalDB()
|
||||
const globalDB = tenancy.getGlobalDB()
|
||||
const response = await globalDB.allDocs(
|
||||
getPluginParams(null, {
|
||||
dbCore.getPluginParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
response.rows
|
||||
.map(row => row.doc)
|
||||
.filter(plugin => plugin.schema.type === "component")
|
||||
.forEach(plugin => {
|
||||
.map((row: any) => row.doc)
|
||||
.filter((plugin: Plugin) => plugin.schema.type === "component")
|
||||
.forEach((plugin: Plugin) => {
|
||||
const fullComponentName = `plugin/${plugin.name}`
|
||||
definitions[fullComponentName] = {
|
||||
component: fullComponentName,
|
|
@ -1,22 +1,16 @@
|
|||
import { ObjectStoreBuckets } from "../../../constants"
|
||||
import { loadJSFile } from "../../../utilities/fileSystem"
|
||||
import { npmUpload, urlUpload, githubUpload, fileUpload } from "./uploaders"
|
||||
import { getGlobalDB } from "@budibase/backend-core/tenancy"
|
||||
import { validate } from "@budibase/backend-core/plugins"
|
||||
import { generatePluginID, getPluginParams } from "../../../db/utils"
|
||||
import {
|
||||
uploadDirectory,
|
||||
deleteFolder,
|
||||
} from "@budibase/backend-core/objectStore"
|
||||
import { PluginType, FileType, PluginSource, Plugin } from "@budibase/types"
|
||||
import { PluginType, FileType, PluginSource } from "@budibase/types"
|
||||
import env from "../../../environment"
|
||||
import { ClientAppSocket } from "../../../websocket"
|
||||
import { events } from "@budibase/backend-core"
|
||||
import { db as dbCore } from "@budibase/backend-core"
|
||||
import { plugins } from "@budibase/pro"
|
||||
|
||||
export async function getPlugins(type?: PluginType) {
|
||||
const db = getGlobalDB()
|
||||
const response = await db.allDocs(
|
||||
getPluginParams(null, {
|
||||
dbCore.getPluginParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
|
@ -37,7 +31,7 @@ export async function upload(ctx: any) {
|
|||
let docs = []
|
||||
// can do single or multiple plugins
|
||||
for (let plugin of plugins) {
|
||||
const doc = await processPlugin(plugin, PluginSource.FILE)
|
||||
const doc = await processUploadedPlugin(plugin, PluginSource.FILE)
|
||||
docs.push(doc)
|
||||
}
|
||||
ctx.body = {
|
||||
|
@ -91,18 +85,19 @@ export async function create(ctx: any) {
|
|||
)
|
||||
}
|
||||
|
||||
const doc = await storePlugin(metadata, directory, source)
|
||||
const doc = await plugins.storePlugin(metadata, directory, source)
|
||||
|
||||
ClientAppSocket.emit("plugins-update", { name, hash: doc.hash })
|
||||
ctx.body = {
|
||||
message: "Plugin uploaded successfully",
|
||||
plugins: [doc],
|
||||
}
|
||||
ctx.body = { plugin: doc }
|
||||
} catch (err: any) {
|
||||
const errMsg = err?.message ? err?.message : err
|
||||
|
||||
ctx.throw(400, `Failed to import plugin: ${errMsg}`)
|
||||
}
|
||||
ctx.status = 200
|
||||
}
|
||||
|
||||
export async function fetch(ctx: any) {
|
||||
|
@ -110,99 +105,21 @@ export async function fetch(ctx: any) {
|
|||
}
|
||||
|
||||
export async function destroy(ctx: any) {
|
||||
const db = getGlobalDB()
|
||||
const { pluginId } = ctx.params
|
||||
|
||||
try {
|
||||
const plugin: Plugin = await db.get(pluginId)
|
||||
const bucketPath = `${plugin.name}/`
|
||||
await deleteFolder(ObjectStoreBuckets.PLUGINS, bucketPath)
|
||||
await plugins.deletePlugin(pluginId)
|
||||
|
||||
await db.remove(pluginId, plugin._rev)
|
||||
await events.plugin.deleted(plugin)
|
||||
ctx.body = { message: `Plugin ${ctx.params.pluginId} deleted.` }
|
||||
} catch (err: any) {
|
||||
const errMsg = err?.message ? err?.message : err
|
||||
|
||||
ctx.throw(400, `Failed to delete plugin: ${errMsg}`)
|
||||
ctx.throw(400, err.message)
|
||||
}
|
||||
|
||||
ctx.message = `Plugin ${ctx.params.pluginId} deleted.`
|
||||
ctx.status = 200
|
||||
}
|
||||
|
||||
export async function storePlugin(
|
||||
metadata: any,
|
||||
directory: any,
|
||||
export async function processUploadedPlugin(
|
||||
plugin: FileType,
|
||||
source?: PluginSource
|
||||
) {
|
||||
const db = getGlobalDB()
|
||||
const version = metadata.package.version,
|
||||
name = metadata.package.name,
|
||||
description = metadata.package.description,
|
||||
hash = metadata.schema.hash
|
||||
|
||||
// first open the tarball into tmp directory
|
||||
const bucketPath = `${name}/`
|
||||
const files = await uploadDirectory(
|
||||
ObjectStoreBuckets.PLUGINS,
|
||||
directory,
|
||||
bucketPath
|
||||
)
|
||||
const jsFile = files.find((file: any) => file.name.endsWith(".js"))
|
||||
if (!jsFile) {
|
||||
throw new Error(`Plugin missing .js file.`)
|
||||
}
|
||||
// validate the JS for a datasource
|
||||
if (metadata.schema.type === PluginType.DATASOURCE) {
|
||||
const js = loadJSFile(directory, jsFile.name)
|
||||
// TODO: this isn't safe - but we need full node environment
|
||||
// in future we should do this in a thread for safety
|
||||
try {
|
||||
eval(js)
|
||||
} catch (err: any) {
|
||||
const message = err?.message ? err.message : JSON.stringify(err)
|
||||
throw new Error(`JS invalid: ${message}`)
|
||||
}
|
||||
}
|
||||
const jsFileName = jsFile.name
|
||||
const pluginId = generatePluginID(name)
|
||||
|
||||
// overwrite existing docs entirely if they exist
|
||||
let rev
|
||||
try {
|
||||
const existing = await db.get(pluginId)
|
||||
rev = existing._rev
|
||||
} catch (err) {
|
||||
rev = undefined
|
||||
}
|
||||
let doc: Plugin = {
|
||||
_id: pluginId,
|
||||
_rev: rev,
|
||||
...metadata,
|
||||
name,
|
||||
version,
|
||||
hash,
|
||||
description,
|
||||
jsUrl: `${bucketPath}${jsFileName}`,
|
||||
}
|
||||
|
||||
if (source) {
|
||||
doc = {
|
||||
...doc,
|
||||
source,
|
||||
}
|
||||
}
|
||||
|
||||
const response = await db.put(doc)
|
||||
await events.plugin.imported(doc)
|
||||
ClientAppSocket.emit("plugin-update", { name, hash })
|
||||
return {
|
||||
...doc,
|
||||
_rev: response.rev,
|
||||
}
|
||||
}
|
||||
|
||||
export async function processPlugin(plugin: FileType, source?: PluginSource) {
|
||||
const { metadata, directory } = await fileUpload(plugin)
|
||||
validate(metadata?.schema)
|
||||
|
||||
|
@ -211,5 +128,7 @@ export async function processPlugin(plugin: FileType, source?: PluginSource) {
|
|||
throw new Error("Only component plugins are supported outside of self-host")
|
||||
}
|
||||
|
||||
return await storePlugin(metadata, directory, source)
|
||||
const doc = await plugins.storePlugin(metadata, directory, source)
|
||||
ClientAppSocket.emit("plugins-update", { name: doc.name, hash: doc.hash })
|
||||
return doc
|
||||
}
|
||||
|
|
|
@ -1,17 +1,16 @@
|
|||
const {
|
||||
getScreenParams,
|
||||
generateScreenID,
|
||||
getPluginParams,
|
||||
DocumentType,
|
||||
} = require("../../db/utils")
|
||||
const { AccessController } = require("@budibase/backend-core/roles")
|
||||
const { getAppDB } = require("@budibase/backend-core/context")
|
||||
const { events } = require("@budibase/backend-core")
|
||||
const { getGlobalDB } = require("@budibase/backend-core/tenancy")
|
||||
const { updateAppPackage } = require("./application")
|
||||
import { getScreenParams, generateScreenID, DocumentType } from "../../db/utils"
|
||||
import {
|
||||
events,
|
||||
context,
|
||||
tenancy,
|
||||
db as dbCore,
|
||||
roles,
|
||||
} from "@budibase/backend-core"
|
||||
import { updateAppPackage } from "./application"
|
||||
import { Plugin, ScreenProps } from "@budibase/types"
|
||||
|
||||
exports.fetch = async ctx => {
|
||||
const db = getAppDB()
|
||||
exports.fetch = async (ctx: any) => {
|
||||
const db = context.getAppDB()
|
||||
|
||||
const screens = (
|
||||
await db.allDocs(
|
||||
|
@ -19,16 +18,16 @@ exports.fetch = async ctx => {
|
|||
include_docs: true,
|
||||
})
|
||||
)
|
||||
).rows.map(element => element.doc)
|
||||
).rows.map((el: any) => el.doc)
|
||||
|
||||
ctx.body = await new AccessController().checkScreensAccess(
|
||||
ctx.body = await new roles.AccessController().checkScreensAccess(
|
||||
screens,
|
||||
ctx.user.role._id
|
||||
)
|
||||
}
|
||||
|
||||
exports.save = async ctx => {
|
||||
const db = getAppDB()
|
||||
exports.save = async (ctx: any) => {
|
||||
const db = context.getAppDB()
|
||||
let screen = ctx.request.body
|
||||
|
||||
let eventFn
|
||||
|
@ -40,19 +39,19 @@ exports.save = async ctx => {
|
|||
const response = await db.put(screen)
|
||||
|
||||
// Find any custom components being used
|
||||
let pluginNames = []
|
||||
let pluginNames: string[] = []
|
||||
let pluginAdded = false
|
||||
findPlugins(screen.props, pluginNames)
|
||||
if (pluginNames.length) {
|
||||
const globalDB = getGlobalDB()
|
||||
const globalDB = tenancy.getGlobalDB()
|
||||
const pluginsResponse = await globalDB.allDocs(
|
||||
getPluginParams(null, {
|
||||
dbCore.getPluginParams(null, {
|
||||
include_docs: true,
|
||||
})
|
||||
)
|
||||
const requiredPlugins = pluginsResponse.rows
|
||||
.map(row => row.doc)
|
||||
.filter(plugin => {
|
||||
.map((row: any) => row.doc)
|
||||
.filter((plugin: Plugin) => {
|
||||
return (
|
||||
plugin.schema.type === "component" &&
|
||||
pluginNames.includes(`plugin/${plugin.name}`)
|
||||
|
@ -63,8 +62,8 @@ exports.save = async ctx => {
|
|||
const application = await db.get(DocumentType.APP_METADATA)
|
||||
let usedPlugins = application.usedPlugins || []
|
||||
|
||||
requiredPlugins.forEach(plugin => {
|
||||
if (!usedPlugins.find(x => x._id === plugin._id)) {
|
||||
requiredPlugins.forEach((plugin: Plugin) => {
|
||||
if (!usedPlugins.find((x: Plugin) => x._id === plugin._id)) {
|
||||
pluginAdded = true
|
||||
usedPlugins.push({
|
||||
_id: plugin._id,
|
||||
|
@ -93,8 +92,8 @@ exports.save = async ctx => {
|
|||
}
|
||||
}
|
||||
|
||||
exports.destroy = async ctx => {
|
||||
const db = getAppDB()
|
||||
exports.destroy = async (ctx: any) => {
|
||||
const db = context.getAppDB()
|
||||
const id = ctx.params.screenId
|
||||
const screen = await db.get(id)
|
||||
|
||||
|
@ -107,7 +106,7 @@ exports.destroy = async ctx => {
|
|||
ctx.status = 200
|
||||
}
|
||||
|
||||
const findPlugins = (component, foundPlugins) => {
|
||||
const findPlugins = (component: ScreenProps, foundPlugins: string[]) => {
|
||||
if (!component) {
|
||||
return
|
||||
}
|
|
@ -42,7 +42,6 @@ const DocumentType = {
|
|||
MEM_VIEW: "view",
|
||||
USER_FLAG: "flag",
|
||||
AUTOMATION_METADATA: "meta_au",
|
||||
PLUGIN: "plg",
|
||||
}
|
||||
|
||||
const InternalTables = {
|
||||
|
@ -384,10 +383,3 @@ exports.getMultiIDParams = ids => {
|
|||
include_docs: true,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets parameters for retrieving automations, this is a utility function for the getDocParams function.
|
||||
*/
|
||||
exports.getPluginParams = (pluginId = null, otherProps = {}) => {
|
||||
return getDocParams(DocumentType.PLUGIN, pluginId, otherProps)
|
||||
}
|
||||
|
|
|
@ -112,13 +112,6 @@ exports.loadHandlebarsFile = path => {
|
|||
return fs.readFileSync(path, "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as above just with a different name.
|
||||
*/
|
||||
exports.loadJSFile = (directory, name) => {
|
||||
return fs.readFileSync(join(directory, name), "utf8")
|
||||
}
|
||||
|
||||
/**
|
||||
* When return a file from the API need to write the file to the system temporarily so we
|
||||
* can create a read stream to send.
|
||||
|
@ -412,6 +405,7 @@ exports.getDatasourcePlugin = async (name, url, hash) => {
|
|||
return require(filename)
|
||||
} else {
|
||||
console.log(`Updating plugin: ${name}`)
|
||||
delete require.cache[require.resolve(filename)]
|
||||
fs.unlinkSync(filename)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import chokidar from "chokidar"
|
|||
import fs from "fs"
|
||||
import { tenancy } from "@budibase/backend-core"
|
||||
import { DEFAULT_TENANT_ID } from "@budibase/backend-core/constants"
|
||||
import { processPlugin } from "./api/controllers/plugin"
|
||||
import { processUploadedPlugin } from "./api/controllers/plugin"
|
||||
|
||||
export function watch() {
|
||||
const watchPath = path.join(env.PLUGINS_DIR, "./**/*.tar.gz")
|
||||
|
@ -28,7 +28,7 @@ export function watch() {
|
|||
const split = path.split("/")
|
||||
const name = split[split.length - 1]
|
||||
console.log("Importing plugin:", path)
|
||||
await processPlugin({ name, path })
|
||||
await processUploadedPlugin({ name, path })
|
||||
} catch (err: any) {
|
||||
const message = err?.message ? err?.message : err
|
||||
console.error("Failed to import plugin:", message)
|
||||
|
|
|
@ -1094,12 +1094,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.3.19-alpha.6.tgz#12f22db2b09ea7586cafb2eb2c375914c8467188"
|
||||
integrity sha512-Kp9OaU80fZO1fEi/EA45eSz3zFYWzQHBfuy7XKzJ0MBKYPCvPteIXcXaANeUoHJE9rDGVUHM9nxLxnSfRUGy0Q==
|
||||
"@budibase/backend-core@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.3.22-alpha.2.tgz#6ae1e42e4163f8ffaa9a7d5891145419f7484279"
|
||||
integrity sha512-bzBpsq6LmpbwVe6UScgLUcN59pYq0nokzq32VA6rXWfTuPz+Y0jKGJuIY3JkPOuTsVAoqlwQH2XLOs6BZlZ7Ag==
|
||||
dependencies:
|
||||
"@budibase/types" "1.3.19-alpha.6"
|
||||
"@budibase/types" "1.3.22-alpha.2"
|
||||
"@shopify/jest-koa-mocks" "5.0.1"
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
|
@ -1180,13 +1180,13 @@
|
|||
svelte-flatpickr "^3.2.3"
|
||||
svelte-portal "^1.0.0"
|
||||
|
||||
"@budibase/pro@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.3.19-alpha.6.tgz#a30340dbd6aa52658312155a5ce0828d0cfc4621"
|
||||
integrity sha512-ntyjKIoiAcCIPJAjwmZ19kcHOiduIcuia3htd5njzSNYrPerqmAty8g5oiBrjeK5ckMd8J3VQeCUdk0Lq1Mdfw==
|
||||
"@budibase/pro@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.3.22-alpha.2.tgz#9c0a91d664af9e6cac90d9e4e0bc8cc1d9bdb06c"
|
||||
integrity sha512-3tVyaXskgMqDFLLPwTa5JgA4OrjIAxrflB8p6jknofQd0UwxHckuInz5BfJVVDqSHi65jif1+YHE0XqSJDuDrg==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.3.19-alpha.6"
|
||||
"@budibase/types" "1.3.19-alpha.6"
|
||||
"@budibase/backend-core" "1.3.22-alpha.2"
|
||||
"@budibase/types" "1.3.22-alpha.2"
|
||||
"@koa/router" "8.0.8"
|
||||
joi "17.6.0"
|
||||
node-fetch "^2.6.1"
|
||||
|
@ -1209,10 +1209,10 @@
|
|||
svelte-apexcharts "^1.0.2"
|
||||
svelte-flatpickr "^3.1.0"
|
||||
|
||||
"@budibase/types@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.3.19-alpha.6.tgz#55ee8c67c2d4bc5ddf009ed1b22094e70d38f8ad"
|
||||
integrity sha512-ApDkaWDpD4BmsmmzxqnSBcj2Q4PGBzkh+49MFZb8F+pdzcDHUQzbVwzVgfBVUS2lHhmZSfBM4sGsfGsBMd+Izw==
|
||||
"@budibase/types@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.3.22-alpha.2.tgz#4b207cca8f9a897b7d95a074a240fbf8a05b2bd6"
|
||||
integrity sha512-zZLhm9q9pZci0R+7EAwYUqkGQ2K+3Dwc5SI9kpQTaWEecLuAmujFERcd6K5wZfWCAzjcb2LsB6lvADxcW6rj3Q==
|
||||
|
||||
"@bull-board/api@3.7.0":
|
||||
version "3.7.0"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/types",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase types",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
|
|
|
@ -1,5 +1,17 @@
|
|||
import { Document } from "../document"
|
||||
|
||||
export interface ScreenProps extends Document {
|
||||
_instanceName: string
|
||||
_styles: { [key: string]: any }
|
||||
_component: string
|
||||
_children: ScreenProps[]
|
||||
size?: string
|
||||
gap?: string
|
||||
direction?: string
|
||||
vAlign?: string
|
||||
hAlign?: string
|
||||
}
|
||||
|
||||
export interface Screen extends Document {
|
||||
layoutId?: string
|
||||
showNavigation?: boolean
|
||||
|
@ -9,4 +21,5 @@ export interface Screen extends Document {
|
|||
roleId: string
|
||||
homeScreen?: boolean
|
||||
}
|
||||
props: ScreenProps
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ export interface Plugin extends Document {
|
|||
jsUrl?: string
|
||||
source: PluginSource
|
||||
package: { [key: string]: any }
|
||||
hash: string
|
||||
schema: {
|
||||
type: PluginType
|
||||
[key: string]: any
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.3.19-alpha.6",
|
||||
"version": "1.3.22-alpha.3",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -36,10 +36,10 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "1.3.19-alpha.6",
|
||||
"@budibase/pro": "1.3.19-alpha.6",
|
||||
"@budibase/string-templates": "1.3.19-alpha.6",
|
||||
"@budibase/types": "1.3.19-alpha.6",
|
||||
"@budibase/backend-core": "1.3.22-alpha.3",
|
||||
"@budibase/pro": "1.3.22-alpha.3",
|
||||
"@budibase/string-templates": "1.3.22-alpha.3",
|
||||
"@budibase/types": "1.3.22-alpha.3",
|
||||
"@koa/router": "8.0.8",
|
||||
"@sentry/node": "6.17.7",
|
||||
"@techpass/passport-openidconnect": "0.3.2",
|
||||
|
|
|
@ -95,16 +95,15 @@ const addSessionAttributesToUser = ctx => {
|
|||
ctx.body.csrfToken = ctx.user.csrfToken
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the attributes that are session based from the current user,
|
||||
* so that stale values are not written to the db
|
||||
*/
|
||||
const removeSessionAttributesFromUser = ctx => {
|
||||
delete ctx.request.body.csrfToken
|
||||
delete ctx.request.body.account
|
||||
delete ctx.request.body.accountPortalAccess
|
||||
delete ctx.request.body.budibaseAccess
|
||||
delete ctx.request.body.license
|
||||
const sanitiseUserUpdate = ctx => {
|
||||
const allowed = ["firstName", "lastName", "password", "forceResetPassword"]
|
||||
const resp = {}
|
||||
for (let [key, value] of Object.entries(ctx.request.body)) {
|
||||
if (allowed.includes(key)) {
|
||||
resp[key] = value
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
exports.getSelf = async ctx => {
|
||||
|
@ -132,10 +131,12 @@ exports.updateSelf = async ctx => {
|
|||
const db = getGlobalDB()
|
||||
const user = await db.get(ctx.user._id)
|
||||
let passwordChange = false
|
||||
if (ctx.request.body.password) {
|
||||
|
||||
const userUpdateObj = sanitiseUserUpdate(ctx)
|
||||
if (userUpdateObj.password) {
|
||||
// changing password
|
||||
passwordChange = true
|
||||
ctx.request.body.password = await hash(ctx.request.body.password)
|
||||
userUpdateObj.password = await hash(userUpdateObj.password)
|
||||
// Log all other sessions out apart from the current one
|
||||
await platformLogout({
|
||||
ctx,
|
||||
|
@ -143,14 +144,10 @@ exports.updateSelf = async ctx => {
|
|||
keepActiveSession: true,
|
||||
})
|
||||
}
|
||||
// don't allow sending up an ID/Rev, always use the existing one
|
||||
delete ctx.request.body._id
|
||||
delete ctx.request.body._rev
|
||||
removeSessionAttributesFromUser(ctx)
|
||||
|
||||
const response = await db.put({
|
||||
...user,
|
||||
...ctx.request.body,
|
||||
...userUpdateObj,
|
||||
})
|
||||
await userCache.invalidateUser(user._id)
|
||||
ctx.body = {
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
errors,
|
||||
events,
|
||||
tenancy,
|
||||
users as usersCore,
|
||||
} from "@budibase/backend-core"
|
||||
import { checkAnyUserExists } from "../../../utilities/users"
|
||||
import { groups as groupUtils } from "@budibase/pro"
|
||||
|
@ -148,9 +147,7 @@ export const bulkDelete = async (ctx: any) => {
|
|||
}
|
||||
|
||||
try {
|
||||
let response = await users.bulkDelete(userIds)
|
||||
|
||||
ctx.body = response
|
||||
ctx.body = await users.bulkDelete(userIds)
|
||||
} catch (err) {
|
||||
ctx.throw(err)
|
||||
}
|
||||
|
|
|
@ -291,12 +291,12 @@
|
|||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
|
||||
|
||||
"@budibase/backend-core@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.3.19-alpha.6.tgz#12f22db2b09ea7586cafb2eb2c375914c8467188"
|
||||
integrity sha512-Kp9OaU80fZO1fEi/EA45eSz3zFYWzQHBfuy7XKzJ0MBKYPCvPteIXcXaANeUoHJE9rDGVUHM9nxLxnSfRUGy0Q==
|
||||
"@budibase/backend-core@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.3.22-alpha.2.tgz#6ae1e42e4163f8ffaa9a7d5891145419f7484279"
|
||||
integrity sha512-bzBpsq6LmpbwVe6UScgLUcN59pYq0nokzq32VA6rXWfTuPz+Y0jKGJuIY3JkPOuTsVAoqlwQH2XLOs6BZlZ7Ag==
|
||||
dependencies:
|
||||
"@budibase/types" "1.3.19-alpha.6"
|
||||
"@budibase/types" "1.3.22-alpha.2"
|
||||
"@shopify/jest-koa-mocks" "5.0.1"
|
||||
"@techpass/passport-openidconnect" "0.3.2"
|
||||
aws-sdk "2.1030.0"
|
||||
|
@ -327,21 +327,21 @@
|
|||
uuid "8.3.2"
|
||||
zlib "1.0.5"
|
||||
|
||||
"@budibase/pro@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.3.19-alpha.6.tgz#a30340dbd6aa52658312155a5ce0828d0cfc4621"
|
||||
integrity sha512-ntyjKIoiAcCIPJAjwmZ19kcHOiduIcuia3htd5njzSNYrPerqmAty8g5oiBrjeK5ckMd8J3VQeCUdk0Lq1Mdfw==
|
||||
"@budibase/pro@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.3.22-alpha.2.tgz#9c0a91d664af9e6cac90d9e4e0bc8cc1d9bdb06c"
|
||||
integrity sha512-3tVyaXskgMqDFLLPwTa5JgA4OrjIAxrflB8p6jknofQd0UwxHckuInz5BfJVVDqSHi65jif1+YHE0XqSJDuDrg==
|
||||
dependencies:
|
||||
"@budibase/backend-core" "1.3.19-alpha.6"
|
||||
"@budibase/types" "1.3.19-alpha.6"
|
||||
"@budibase/backend-core" "1.3.22-alpha.2"
|
||||
"@budibase/types" "1.3.22-alpha.2"
|
||||
"@koa/router" "8.0.8"
|
||||
joi "17.6.0"
|
||||
node-fetch "^2.6.1"
|
||||
|
||||
"@budibase/types@1.3.19-alpha.6":
|
||||
version "1.3.19-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.3.19-alpha.6.tgz#55ee8c67c2d4bc5ddf009ed1b22094e70d38f8ad"
|
||||
integrity sha512-ApDkaWDpD4BmsmmzxqnSBcj2Q4PGBzkh+49MFZb8F+pdzcDHUQzbVwzVgfBVUS2lHhmZSfBM4sGsfGsBMd+Izw==
|
||||
"@budibase/types@1.3.22-alpha.2":
|
||||
version "1.3.22-alpha.2"
|
||||
resolved "https://registry.yarnpkg.com/@budibase/types/-/types-1.3.22-alpha.2.tgz#4b207cca8f9a897b7d95a074a240fbf8a05b2bd6"
|
||||
integrity sha512-zZLhm9q9pZci0R+7EAwYUqkGQ2K+3Dwc5SI9kpQTaWEecLuAmujFERcd6K5wZfWCAzjcb2LsB6lvADxcW6rj3Q==
|
||||
|
||||
"@cspotcode/source-map-consumer@0.8.0":
|
||||
version "0.8.0"
|
||||
|
|
Loading…
Reference in New Issue