consolidate data sources and queries together in one UI

This commit is contained in:
Martin McKeaveney 2021-06-15 19:48:05 +01:00
parent ce0e06cf3e
commit 9a35e332ca
22 changed files with 257 additions and 47 deletions

View File

@ -1,10 +1,12 @@
<script>
import { onMount } from "svelte"
import { goto } from "@roxi/routify"
import { database, datasources, queries } from "stores/backend"
import { BUDIBASE_INTERNAL_DB } from "constants"
import { database, datasources, queries, tables } from "stores/backend"
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
import NavItem from "components/common/NavItem.svelte"
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
import ICONS from "./icons"
function selectDatasource(datasource) {
@ -20,6 +22,11 @@
$goto(`./datasource/${query.datasourceId}/${query._id}`)
}
function onClickTable(table) {
tables.select(table)
$goto(`./table/${table._id}`)
}
onMount(() => {
datasources.fetch()
queries.fetch()
@ -42,8 +49,13 @@
width="18"
/>
</div>
{#if datasource._id !== BUDIBASE_INTERNAL_DB}
<EditDatasourcePopover {datasource} />
{/if}
</NavItem>
<TableNavigator sourceId={datasource._id} />
{#each $queries.list.filter(query => query.datasourceId === datasource._id) as query}
<NavItem
indentLevel={1}

View File

@ -0,0 +1,82 @@
<script>
export let width = "100"
export let height = "100"
</script>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve" {height} {width}>
<style type="text/css">
.st0{fill:#393C44;}
.st1{fill:#FFFFFF;}
.st2{fill:#4285F4;}
</style>
<rect x="-152.17" y="-24.17" class="st0" width="96.17" height="96.17"/>
<path class="st1" d="M-83.19,48h-41.79c-1.76,0-3.19-1.43-3.19-3.19V3.02c0-1.76,1.43-3.19,3.19-3.19h41.79
c1.76,0,3.19,1.43,3.19,3.19v41.79C-80,46.57-81.43,48-83.19,48z"/>
<g>
<g>
<path class="st0" d="M-99.62,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35h-4.89V12.57H-99.62z
M-93.46,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C-93.55,28.92-93.46,28.52-93.46,28.11z"/>
</g>
<g>
<path class="st0" d="M-114.76,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58
c0.86,0.39,1.59,0.91,2.19,1.57c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89
c-0.35,0.9-0.84,1.68-1.47,2.35c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35
h-4.89V12.57H-114.76z M-108.6,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C-108.68,28.92-108.6,28.52-108.6,28.11z"/>
</g>
</g>
<path class="st2" d="M44.81,159H3.02c-1.76,0-3.19-1.43-3.19-3.19v-41.79c0-1.76,1.43-3.19,3.19-3.19h41.79
c1.76,0,3.19,1.43,3.19,3.19v41.79C48,157.57,46.57,159,44.81,159z"/>
<g>
<g>
<path class="st1" d="M28.38,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146h-4.89v-22.43H28.38z
M34.54,139.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C34.45,139.92,34.54,139.52,34.54,139.11z"/>
</g>
<g>
<path class="st1" d="M13.24,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146H8.35v-22.43H13.24z M19.4,139.11
c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69c-0.38-0.17-0.79-0.26-1.24-0.26
c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01c-0.17,0.39-0.26,0.8-0.26,1.23
c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68c0.39,0.17,0.8,0.26,1.23,0.26
c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1C19.32,139.92,19.4,139.52,19.4,139.11z"/>
</g>
</g>
<g>
<path class="st0" d="M44,48H4c-2.21,0-4-1.79-4-4V4c0-2.21,1.79-4,4-4h40c2.21,0,4,1.79,4,4v40C48,46.21,46.21,48,44,48z"/>
<g>
<path class="st1" d="M28.48,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65
c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47
c-0.64,0.7-1.4,1.25-2.28,1.66C34.8,35.8,33.86,36,32.84,36c-1.84,0-3.3-0.69-4.37-2.07v1.62h-5V12H28.48z M34.78,28.31
c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27
c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29
s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27
c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C34.69,29.16,34.78,28.75,34.78,28.31z"
/>
</g>
<g>
<path class="st1" d="M13,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65
c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47
c-0.64,0.7-1.4,1.25-2.28,1.66C19.32,35.8,18.38,36,17.37,36c-1.84,0-3.3-0.69-4.37-2.07v1.62H8V12H13z M19.3,28.31
c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27
c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29
s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27
c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C19.21,29.16,19.3,28.75,19.3,28.31z"/>
</g>
</g>
</svg>

View File

@ -9,8 +9,10 @@ import SqlServer from "./SQLServer.svelte"
import MySQL from "./MySQL.svelte"
import ArangoDB from "./ArangoDB.svelte"
import Rest from "./Rest.svelte"
import Budibase from "./Budibase.svelte"
export default {
BUDIBASE: Budibase,
POSTGRES: Postgres,
DYNAMODB: DynamoDB,
MONGODB: MongoDB,

View File

@ -1,6 +1,6 @@
<script>
import { goto } from "@roxi/routify"
import { datasources } from "stores/backend"
import { datasources, tables } from "stores/backend"
import { notifications } from "@budibase/bbui"
import { Input, Label, ModalContent } from "@budibase/bbui"
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte"

View File

@ -6,6 +6,8 @@
import EditViewPopover from "./popovers/EditViewPopover.svelte"
import NavItem from "components/common/NavItem.svelte"
export let sourceId
$: selectedView = $views.selected && $views.selected.name
function selectTable(table) {
@ -31,8 +33,9 @@
{#if $database?._id}
<div class="hierarchy-items-container">
{#each $tables.list as table, idx}
{#each $tables.list.filter(table => table.sourceId === sourceId) as table, idx}
<NavItem
indentLevel={1}
border={idx > 0}
icon={table._id === TableNames.USERS ? "UserGroup" : "Table"}
text={table.name}
@ -45,7 +48,7 @@
</NavItem>
{#each Object.keys(table.views || {}) as viewName, idx (idx)}
<NavItem
indentLevel={1}
indentLevel={2}
icon="Remove"
text={viewName}
selected={selectedView === viewName}

View File

@ -39,7 +39,7 @@
type: "table",
}))
$: views = $tablesStore.list.reduce((acc, cur) => {
let viewsArr = Object.entries(cur.views).map(([key, value]) => ({
let viewsArr = Object.entries(cur.views || {}).map(([key, value]) => ({
label: key,
name: key,
...value,

View File

@ -31,3 +31,5 @@ export const LAYOUT_NAMES = {
PUBLIC: "layout_private_master",
},
}
export const BUDIBASE_INTERNAL_DB = "bb_internal"

View File

@ -6,26 +6,10 @@
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
const tabs = [
{
title: "Databases",
key: "table",
},
{
title: "External",
key: "datasource",
},
]
let selected = "Sources"
let selected = $isActive("./datasource") ? "External" : "Databases"
function selectFirstTableOrSource({ detail }) {
const { key } = tabs.find(t => t.title === detail)
if (key === "datasource") {
function selectFirstDatasource({ detail }) {
$goto("./datasource")
} else {
$goto("./table")
}
}
let modal
@ -34,16 +18,8 @@
<!-- routify:options index=0 -->
<div class="root">
<div class="nav">
<Tabs {selected} on:select={selectFirstTableOrSource}>
<Tab title="Databases">
<div class="tab-content-padding">
<TableNavigator />
<Modal bind:this={modal}>
<CreateTableModal />
</Modal>
</div>
</Tab>
<Tab title="External">
<Tabs {selected} on:select={selectFirstDatasource}>
<Tab title="Sources">
<div class="tab-content-padding">
<DatasourceNavigator />
<Modal bind:this={modal}>

View File

@ -1,7 +1,7 @@
<script>
import { goto, beforeUrlChange } from "@roxi/routify"
import { Button, Heading, Body, Divider, Layout } from "@budibase/bbui"
import { datasources, integrations, queries } from "stores/backend"
import { datasources, integrations, queries, tables } from "stores/backend"
import { notifications } from "@budibase/bbui"
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
import ICONS from "components/backend/DatasourceNavigator/icons"
@ -15,9 +15,10 @@
async function saveDatasource() {
try {
// Create datasource
await datasources.save(datasource)
await datasources.save(datasource, { refresh: true })
notifications.success(`Datasource ${name} saved successfully.`)
unsaved = false
await tables.fetch()
} catch (err) {
notifications.error(`Error saving datasource: ${err}`)
}
@ -43,7 +44,7 @@
})
</script>
{#if datasource}
{#if datasource && integration}
<section>
<Layout>
<header>

View File

@ -0,0 +1,13 @@
<script>
import { params } from "@roxi/routify"
import { tables } from "stores/backend"
if ($params.selectedTable) {
const table = $tables.list.find(m => m._id === $params.selectedTable)
if (table) {
tables.select(table)
}
}
</script>
<slot />

View File

@ -0,0 +1,16 @@
<script>
import TableDataTable from "components/backend/DataTable/DataTable.svelte"
import { tables, database } from "stores/backend"
</script>
{#if $database?._id && $tables?.selected?.name}
<TableDataTable />
{:else}<i>Create your first table to start building</i>{/if}
<style>
i {
font-size: var(--font-size-m);
color: var(--grey-5);
margin-top: 2px;
}
</style>

View File

@ -0,0 +1,10 @@
<script>
import { params } from "@roxi/routify"
import RelationshipDataTable from "components/backend/DataTable/RelationshipDataTable.svelte"
</script>
<RelationshipDataTable
tableId={$params.selectedTable}
rowId={$params.selectedRow}
fieldName={decodeURI($params.selectedField)}
/>

View File

@ -0,0 +1,6 @@
<script>
import { goto } from "@roxi/routify"
$goto("../../")
</script>
<!-- routify:options index=false -->

View File

@ -0,0 +1,6 @@
<script>
import { goto } from "@roxi/routify"
$goto("../")
</script>
<!-- routify:options index=false -->

View File

@ -0,0 +1,19 @@
<script>
import { tables } from "stores/backend"
import { goto, leftover } from "@roxi/routify"
import { onMount } from "svelte"
onMount(async () => {
// navigate to first table in list, if not already selected
// and this is the final url (i.e. no selectedTable)
if (
!$leftover &&
$tables.list.length > 0 &&
(!$tables.selected || !$tables.selected._id)
) {
$goto(`./${$tables.list[0]._id}`)
}
})
</script>
<slot />

View File

@ -0,0 +1,21 @@
<script>
import { goto } from "@roxi/routify"
import { onMount } from "svelte"
import { tables } from "stores/backend"
onMount(async () => {
$tables.list.length > 0 && $goto(`./${$tables.list[0]._id}`)
})
</script>
{#if $tables.list.length === 0}
<i>Create your first table to start building</i>
{:else}<i>Select a table to edit</i>{/if}
<style>
i {
font-size: var(--font-size-m);
color: var(--grey-5);
margin-top: 2px;
}
</style>

View File

@ -28,10 +28,11 @@ export function createDatasourcesStore() {
update(state => ({ ...state, selected: datasourceId }))
queries.update(state => ({ ...state, selected: null }))
},
save: async datasource => {
save: async (datasource, opts = {}) => {
let url = "/api/datasources"
if (datasource.plus) {
if (datasource.plus && opts.refresh) {
// Pull the latest tables from the datasource
url += "?refresh=1"
}

View File

@ -4,6 +4,8 @@ const {
getDatasourceParams,
getQueryParams,
DocumentTypes,
BudibaseInternalDB,
getTableParams,
} = require("../../db/utils")
const { integrations } = require("../../integrations")
const plusIntegrations = require("../../integrations/plus")
@ -11,13 +13,31 @@ const { makeExternalQuery } = require("./row/utils")
exports.fetch = async function (ctx) {
const database = new CouchDB(ctx.appId)
ctx.body = (
// Get internal tables
const db = new CouchDB(ctx.appId)
const internalTables = await db.allDocs(
getTableParams(null, {
include_docs: true,
})
)
const internal = internalTables.rows.map(row => row.doc)
const bbInternalDb = {
...BudibaseInternalDB,
entities: internal,
}
// Get external datasources
const datasources = (
await database.allDocs(
getDatasourceParams(null, {
include_docs: true,
})
)
).rows.map(row => row.doc)
ctx.body = [bbInternalDb, ...datasources]
}
exports.save = async function (ctx) {

View File

@ -6,27 +6,38 @@ const {
getTableParams,
generateTableID,
getDatasourceParams,
DocumentTypes,
BudibaseInternalDB,
} = require("../../../db/utils")
const { FieldTypes } = require("../../../constants")
const { TableSaveFunctions } = require("./utils")
exports.fetch = async function (ctx) {
const db = new CouchDB(ctx.appId)
const internalTables = await db.allDocs(
getTableParams(null, {
include_docs: true,
})
)
const internal = internalTables.rows.map(row => row.doc)
const internal = internalTables.rows.map(row => ({
...row.doc,
sourceId: BudibaseInternalDB._id,
}))
const externalTables = await db.allDocs(
getDatasourceParams("plus", {
include_docs: true,
})
)
const external = externalTables.rows.flatMap(row =>
Object.values(row.doc.entities)
)
const external = externalTables.rows.flatMap(row => {
return Object.values(row.doc.entities).map(entity => ({
...entity,
sourceId: row.doc._id,
}))
})
ctx.body = [...internal, ...external]
}

View File

@ -17,7 +17,7 @@ function generateDatasourceSchema() {
return joiValidator.body(Joi.object({
_id: Joi.string(),
_rev: Joi.string(),
source: Joi.string().valid("POSTGRES_PLUS"),
// source: Joi.string().valid("POSTGRES_PLUS"),
type: Joi.string().allow("datasource_plus"),
relationships: Joi.array().items(Joi.object({
from: Joi.string().required(),

View File

@ -57,6 +57,14 @@ exports.StaticDatabases = {
...StaticDatabases,
}
const BudibaseInternalDB = {
_id: "bb_internal",
type: "budibase",
name: "Budibase Internal",
source: "BUDIBASE",
config: {},
}
exports.APP_PREFIX = APP_PREFIX
exports.APP_DEV_PREFIX = APP_DEV_PREFIX
exports.USER_METDATA_PREFIX = `${DocumentTypes.ROW}${SEPARATOR}${InternalTables.USER_METADATA}${SEPARATOR}`
@ -68,6 +76,7 @@ exports.SEPARATOR = SEPARATOR
exports.UNICODE_MAX = UNICODE_MAX
exports.SearchIndexes = SearchIndexes
exports.AppStatus = AppStatus
exports.BudibaseInternalDB = BudibaseInternalDB
exports.generateRoleID = generateRoleID
exports.getRoleParams = getRoleParams

View File

@ -57,7 +57,7 @@ const SCHEMA = {
async function internalQuery(client, sql) {
try {
return await client.query(sql.sql, sql.bindings)
return await client.query(sql)
} catch (err) {
throw new Error(err)
}