allow execution of external connector queries from button clicks

This commit is contained in:
Martin McKeaveney 2021-01-04 18:57:16 +00:00
parent d27a264c96
commit 755fa0ac4a
18 changed files with 147 additions and 97 deletions

View File

@ -1,7 +1,6 @@
import { writable, get } from "svelte/store"
import { cloneDeep } from "lodash/fp"
import api from "../api"
import { backendUiStore } from ".."
const INITIAL_BACKEND_UI_STATE = {
tables: [],
@ -63,6 +62,7 @@ export const getBackendUiStore = () => {
select: async datasourceId => {
store.update(state => {
state.selectedDatasourceId = datasourceId
state.selectedQueryId = null
return state
})
},
@ -123,6 +123,7 @@ export const getBackendUiStore = () => {
queries: {
select: queryId =>
store.update(state => {
state.selectedDatasourceId = null
state.selectedQueryId = queryId
return state
}),

View File

@ -6,21 +6,13 @@
import Table from "./Table.svelte"
import CreateQueryButton from "components/backend/DataTable/buttons/CreateQueryButton.svelte"
export let datasourceId
export let datasource
export let query = {}
let data = []
let loading = false
let error = false
$: datasourceId = $params.selectedDatasource
// TODO: refactor
// $: query = $backendUiStore.datasources.find(
// ds => ds._id === $params.selectedDatasource
// ).queries[$params.query]
$: title = query.name
$: schema = query.schema
async function fetchData() {
try {
loading = true
@ -46,8 +38,8 @@
{#if error}
<div class="errors">{error}</div>
{/if}
<Table {title} {schema} {data} {loading}>
<CreateQueryButton {query} />
<Table title={query.name} schema={query.schema} {data} {loading}>
<CreateQueryButton {query} {datasource} />
</Table>
<style>

View File

@ -236,6 +236,7 @@
}
:global(.ag-filter) {
background: var(--background);
padding: var(--spacing-s);
outline: none;
box-sizing: border-box;

View File

@ -16,9 +16,9 @@
import CreateEditQuery from "components/backend/DataTable/modals/CreateEditQuery.svelte"
export let datasource
export let query = {}
let modal
let query = {}
let fields = []
async function saveQuery() {
@ -30,14 +30,12 @@
notifier.danger(`Error creating query. ${err.message}`)
}
}
$: console.log(query)
</script>
<div>
<Button text small on:click={modal.show}>
<Icon name="filter" />
{query ? 'Edit' : 'Create'} Query
{$backendUiStore.selectedQueryId ? 'Edit' : 'Create'} Query
</Button>
</div>
<Modal bind:this={modal}>
@ -45,7 +43,7 @@
confirmText="Save"
cancelText="Cancel"
onConfirm={saveQuery}
title="Create New Query">
title={query ? 'Edit Query' : 'Create New Query'}>
<CreateEditQuery {datasource} bind:query />
</ModalContent>
</Modal>

View File

@ -31,6 +31,8 @@
export let query
export let fields = []
console.log(query)
let config = {}
let queryType
let previewTab = "PREVIEW"

View File

@ -8,8 +8,6 @@
import { Modal, Switcher } from "@budibase/bbui"
import NavItem from "components/common/NavItem.svelte"
let modal
$: selectedView =
$backendUiStore.selectedView && $backendUiStore.selectedView.name
@ -34,12 +32,6 @@
</script>
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
<div class="title">
<i
data-cy="new-datasource"
on:click={modal.show}
class="ri-add-circle-fill" />
</div>
<div class="hierarchy-items-container">
{#each $backendUiStore.datasources as datasource, idx}
<NavItem
@ -64,23 +56,3 @@
{/each}
</div>
{/if}
<Modal bind:this={modal}>
<CreateDatasourceModal />
</Modal>
<style>
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.title i {
font-size: 20px;
}
.title i:hover {
cursor: pointer;
color: var(--blue);
}
</style>

View File

@ -2,14 +2,11 @@
import { goto } from "@sveltech/routify"
import { backendUiStore } from "builderStore"
import { TableNames } from "constants"
import CreateTableModal from "./modals/CreateTableModal.svelte"
import EditTablePopover from "./popovers/EditTablePopover.svelte"
import EditViewPopover from "./popovers/EditViewPopover.svelte"
import { Modal, Switcher } from "@budibase/bbui"
import { Switcher } from "@budibase/bbui"
import NavItem from "components/common/NavItem.svelte"
let modal
$: selectedView =
$backendUiStore.selectedView && $backendUiStore.selectedView.name
@ -35,9 +32,6 @@
</script>
{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id}
<div class="title">
<i data-cy="new-table" on:click={modal.show} class="ri-add-circle-fill" />
</div>
<div class="hierarchy-items-container">
{#each $backendUiStore.tables as table, idx}
<NavItem
@ -64,27 +58,3 @@
{/each}
</div>
{/if}
<Modal bind:this={modal}>
<CreateTableModal />
</Modal>
<style>
.title {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.title h1 {
font-size: var(--font-size-m);
font-weight: 500;
margin: 0;
}
.title i {
font-size: 20px;
}
.title i:hover {
cursor: pointer;
color: var(--blue);
}
</style>

View File

@ -13,9 +13,6 @@
tables: $backendUiStore.tables,
})
// just wraps binding in {{ ... }}
const toBindingExpression = bindingPath => `{{ ${bindingPath} }}`
const tableFields = tableId => {
const table = $backendUiStore.tables.find(m => m._id === tableId)
@ -63,12 +60,4 @@
grid-column-start: 2;
grid-column-end: 6;
}
.cannot-use {
color: var(--red);
font-size: var(--font-size-s);
text-align: center;
width: 70%;
margin: auto;
}
</style>

View File

@ -42,8 +42,6 @@
typeof tableInfo === "string" ? tableInfo : tableInfo.tableId
}
}
console.log(parameters)
}
}
</script>

View File

@ -0,0 +1,32 @@
<script>
import { Select, Label, Spacer } from "@budibase/bbui"
import { backendUiStore } from "builderStore"
export let parameters
$: datasource = $backendUiStore.datasources.find(
ds => ds._id === parameters.datasourceId
)
</script>
<div class="root">
<Label size="m" color="dark">Datasource</Label>
<Select secondary bind:value={parameters.datasourceId}>
<option value="" />
{#each $backendUiStore.datasources as datasource}
<option value={datasource._id}>{datasource.name}</option>
{/each}
</Select>
<Spacer medium />
{#if parameters.datasourceId}
<Label size="m" color="dark">Query</Label>
<Select secondary bind:value={parameters.queryId}>
<option value="" />
{#each Object.keys(datasource.queries) as query}
<option value={query}>{datasource.queries[query].name}</option>
{/each}
</Select>
{/if}
</div>

View File

@ -1,6 +1,7 @@
import NavigateTo from "./NavigateTo.svelte"
import SaveRow from "./SaveRow.svelte"
import DeleteRow from "./DeleteRow.svelte"
import ExecuteQuery from "./ExecuteQuery.svelte"
// defines what actions are available, when adding a new one
// the component is the setup panel for the action
@ -20,4 +21,8 @@ export default [
name: "Navigate To",
component: NavigateTo,
},
{
name: "Execute Query",
component: ExecuteQuery,
},
]

View File

@ -1,7 +1,10 @@
<script>
import { Switcher } from "@budibase/bbui"
import { params } from "@sveltech/routify"
import { Switcher, Modal } from "@budibase/bbui"
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
import DatasourceNavigator from "components/backend/DatasourceNavigator/DatasourceNavigator.svelte"
import CreateDatasourceModal from "components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte"
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
const tabs = [
{
@ -14,17 +17,31 @@
},
]
let tab = "table"
let tab = $params.selectedDatasource ? "datasource" : "table"
let modal
</script>
<!-- routify:options index=0 -->
<div class="root">
<div class="nav">
<Switcher headings={tabs} bind:value={tab}>
<div class="title">
<i
data-cy={`new-${tab}`}
class="ri-add-circle-fill"
on:click={modal.show} />
</div>
{#if tab === 'table'}
<TableNavigator />
<Modal bind:this={modal}>
<CreateTableModal />
</Modal>
{:else if tab === 'datasource'}
<DatasourceNavigator />
<Modal bind:this={modal}>
<CreateDatasourceModal />
</Modal>
{/if}
</Switcher>
</div>
@ -40,6 +57,7 @@
grid-template-columns: 260px minmax(0, 1fr);
background: var(--grey-2);
}
.content {
flex: 1 1 auto;
padding: var(--spacing-l) 40px;
@ -50,6 +68,7 @@
align-items: stretch;
gap: var(--spacing-l);
}
.nav {
overflow-y: auto;
background: var(--background);
@ -59,5 +78,18 @@
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-l);
position: relative;
}
i {
font-size: 20px;
position: absolute;
top: var(--spacing-l);
right: var(--spacing-xl);
}
i:hover {
cursor: pointer;
color: var(--blue);
}
</style>

View File

@ -10,6 +10,6 @@
$: query = datasource && datasource.queries[$params.query]
</script>
{#if $backendUiStore.selectedDatabase._id && datasource}
<ExternalDataSourceTable {query} datasourceId={datasource._id} />
{#if $backendUiStore.selectedDatabase._id && datasource && query}
<ExternalDataSourceTable {query} {datasource} />
{/if}

View File

@ -36,7 +36,7 @@
margin-bottom: var(--spacing-s);
}
section {
background: white;
background: var(--background);
border-radius: var(--border-radius-m);
padding: var(--spacing-xl);
}

View File

@ -9,3 +9,14 @@ export const fetchQueryData = async ({ datasourceId, queryId }) => {
})
return response.rows
}
/**
* Executes a query against an external data connector.
*/
export const executeQuery = async ({ datasourceId, queryId }) => {
const response = await API.post({
url: `/api/datasources/${datasourceId}/queries/${queryId}`,
// body: params,
})
return response.rows
}

View File

@ -1,6 +1,6 @@
import { enrichDataBinding } from "./enrichDataBinding"
import { enrichDataBinding, enrichDataBindings } from "./enrichDataBinding"
import { routeStore } from "../store"
import { saveRow, deleteRow } from "../api"
import { saveRow, deleteRow, executeQuery } from "../api"
const saveRowHandler = async (action, context) => {
let draft = context[`${action.parameters.contextPath}_draft`]
@ -25,10 +25,27 @@ const navigationHandler = action => {
routeStore.actions.navigate(action.parameters.url)
}
const queryExecutionHandler = async (action, context) => {
const { datasourceId, queryId, params } = action.parameters
console.log(context)
// TODO: allow context based bindings for query params
// const enrichedQueryParameters = enrichDataBindings(params, context)
// console.log({
// action,
// context,
// // enrichedQueryParameters,
// datasourceId,
// // queryId
// })
await executeQuery({ datasourceId, queryId })
}
const handlerMap = {
["Save Row"]: saveRowHandler,
["Delete Row"]: deleteRowHandler,
["Navigate To"]: navigationHandler,
["Execute Query"]: queryExecutionHandler,
}
/**

View File

@ -115,7 +115,7 @@ exports.previewQuery = async function(ctx) {
ctx.body = await new Integration(config, query).query()
}
exports.executeQuery = async function(ctx) {
exports.fetchQuery = async function(ctx) {
const db = new CouchDB(ctx.user.appId)
const datasource = await db.get(ctx.params.datasourceId)
@ -139,3 +139,28 @@ exports.executeQuery = async function(ctx) {
rows,
}
}
exports.executeQuery = async function(ctx) {
const db = new CouchDB(ctx.user.appId)
const datasource = await db.get(ctx.params.datasourceId)
const query = datasource.queries[ctx.params.queryId]
const Integration = integrations[datasource.source]
if (!Integration) {
ctx.throw(400, "Integration type does not exist.")
return
}
// TODO: allow the ability to POST parameters down when executing the query
// const customParams = ctx.request.body
const response = await new Integration(
datasource.config,
query.queryString
).query()
ctx.body = response
}

View File

@ -28,6 +28,11 @@ router
datasourceController.previewQuery
)
.get(
"/api/datasources/:datasourceId/queries/:queryId",
authorized(BUILDER),
datasourceController.fetchQuery
)
.post(
"/api/datasources/:datasourceId/queries/:queryId",
authorized(BUILDER),
datasourceController.executeQuery