binding data context in component

This commit is contained in:
Martin McKeaveney 2021-01-08 18:22:03 +00:00
parent 8e40f4b5f8
commit 102b7ce61f
16 changed files with 367 additions and 331 deletions

View File

@ -1,43 +0,0 @@
<script>
import {
DropdownMenu,
TextButton as Button,
Icon,
Modal,
ModalContent,
} from "@budibase/bbui"
import { backendUiStore } from "builderStore"
import api from "builderStore/api"
import EditIntegrationConfig from "../modals/EditIntegrationConfig.svelte"
export let table
$: console.log("The table config is", table)
let modal
// TODO: revisit
async function saveTable() {
const SAVE_TABLE_URL = `/api/tables`
const response = await api.post(SAVE_TABLE_URL, table)
const savedTable = await response.json()
await backendUiStore.actions.tables.fetch()
backendUiStore.actions.tables.select(savedTable)
}
</script>
<div>
<Button text small on:click={modal.show}>
<Icon name="edit" />
Configure Datasource
</Button>
</div>
<Modal bind:this={modal}>
<ModalContent
confirmText="Save"
cancelText="Cancel"
onConfirm={saveTable}
title={'Datasource Configuration'}>
<EditIntegrationConfig onClosed={modal.hide} bind:table />
</ModalContent>
</Modal>

View File

@ -3,15 +3,19 @@
import Portal from "svelte-portal" import Portal from "svelte-portal"
export let title export let title
export let onClose export let onClose = () => {}
</script> </script>
<Portal> <Portal>
<section class="drawer" transition:slide> <section class="drawer" transition:slide>
{#if title} <header>
<heading>{title}</heading> {title}
{/if} <div class="controls">
<slot /> <slot name="buttons" />
<i class="ri-close-fill close" on:click={onClose} />
</div>
</header>
<slot name="body" />
</section> </section>
</Portal> </Portal>
@ -20,10 +24,28 @@
height: 50vh; height: 50vh;
position: absolute; position: absolute;
bottom: 0; bottom: 0;
width: 97%; width: 100vw;
background: var(--background); background: var(--background);
padding: var(--spacing-xl);
border-top: var(--border-light); border-top: var(--border-light);
z-index: 2; z-index: 2;
} }
header {
display: flex;
justify-content: space-between;
align-items: center;
border: var(--border-light);
padding: var(--spacing-s);
}
.controls {
display: grid;
grid-auto-flow: column;
grid-gap: var(--spacing-m);
align-items: center;
}
.close {
font-size: var(--font-size-xl);
}
</style> </style>

View File

@ -44,4 +44,7 @@
background: var(--background); background: var(--background);
border-radius: var(--border-radius-m); border-radius: var(--border-radius-m);
} }
:global(.CodeMirror) {
border-radius: var(--border-radius-m);
}
</style> </style>

View File

@ -1,5 +1,5 @@
<script> <script>
import { TextArea, Label, Input, Heading } from "@budibase/bbui" import { Button, TextArea, Label, Input, Heading } from "@budibase/bbui"
import Editor from "./QueryEditor.svelte" import Editor from "./QueryEditor.svelte"
import BindableInput from "components/userInterface/BindableInput.svelte" import BindableInput from "components/userInterface/BindableInput.svelte"
@ -17,8 +17,9 @@
} }
</script> </script>
<Heading extraSmall black>Parameters</Heading> <section>
<div class="parameters"> <Heading extraSmall black>Parameters</Heading>
<div class="parameters">
<Label extraSmall grey>Parameter Name</Label> <Label extraSmall grey>Parameter Name</Label>
<Label extraSmall grey>Default</Label> <Label extraSmall grey>Default</Label>
<Label extraSmall grey>Value</Label> <Label extraSmall grey>Value</Label>
@ -35,8 +36,9 @@
class="ri-close-circle-line delete" class="ri-close-circle-line delete"
on:click={() => deleteQueryParameter(idx)} /> on:click={() => deleteQueryParameter(idx)} />
{/each} {/each}
</div> </div>
<i class="ri-add-circle-line add" on:click={newQueryParameter} /> <Button thin secondary on:click={newQueryParameter}>New Parameter</Button>
</section>
<style> <style>
.parameters { .parameters {
@ -44,9 +46,6 @@
grid-template-columns: 1fr 1fr 1fr 5%; grid-template-columns: 1fr 1fr 1fr 5%;
grid-gap: 10px; grid-gap: 10px;
align-items: center; align-items: center;
} margin-bottom: var(--spacing-xl);
.add {
margin-top: var(--spacing-m);
} }
</style> </style>

View File

@ -1,5 +1,5 @@
<script> <script>
import { TextArea, Label, Input, Heading } from "@budibase/bbui" import { TextArea, Label, Input, Heading, Spacer } from "@budibase/bbui"
import Editor from "./QueryEditor.svelte" import Editor from "./QueryEditor.svelte"
import ParameterBuilder from "./QueryParameterBuilder.svelte" import ParameterBuilder from "./QueryParameterBuilder.svelte"
@ -12,5 +12,6 @@
{#if query.queryType === QueryTypes.SQL} {#if query.queryType === QueryTypes.SQL}
<ParameterBuilder bind:parameters={query.parameters} /> <ParameterBuilder bind:parameters={query.parameters} />
<Spacer large />
<Editor label="Query" bind:value={query.queryString} /> <Editor label="Query" bind:value={query.queryString} />
{/if} {/if}

View File

@ -1,35 +1,62 @@
<script> <script>
import BottomDrawer from "components/common/BottomDrawer.svelte" import BottomDrawer from "components/common/BottomDrawer.svelte"
import { store, backendUiStore } from "builderStore" import { Button } from "@budibase/bbui"
import { store, backendUiStore, currentAsset } from "builderStore"
import { slide } from "svelte/transition" import { slide } from "svelte/transition"
import QueryInterface from "components/integration/QueryViewer.svelte" import fetchBindableProperties from "builderStore/fetchBindableProperties"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
$: query = $backendUiStore.queries.find( export let query
query => query._id === $backendUiStore.selectedQueryId export let parameters = {}
)
$: console.log("CUSTOM PARAMS", parameters)
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.selectedComponentId,
components: $store.components,
screen: $currentAsset,
tables: $backendUiStore.tables,
}).map(property => ({
...property,
category: property.type === "instance" ? "Component" : "Table",
label: property.readableBinding,
path: property.runtimeBinding,
}))
function closeDatabindingDrawer() { function closeDatabindingDrawer() {
store.update(state => { store.update(state => {
state.bindingDrawerVisible = false state.bottomDrawerVisible = false
return state return state
}) })
} }
function saveComponentQuery() {
// save the parameters to the datasource of the component
}
</script> </script>
{#if query} {#if query}
<BottomDrawer> <BottomDrawer title={'Query'} onClose={closeDatabindingDrawer}>
<div class="drawer-contents"> <div slot="buttons">
<i class="ri-close-fill close" on:click={closeDatabindingDrawer} /> <Button blue thin on:click={saveComponentQuery}>Save</Button>
<QueryInterface {query} /> </div>
<div class="drawer-contents" slot="body">
<pre>{query.queryString}</pre>
<ParameterBuilder
bind:customParams={parameters}
parameters={query.parameters}
bindings={bindableProperties} />
</div> </div>
</BottomDrawer> </BottomDrawer>
{/if} {/if}
<style> <style>
i { .drawer-contents {
position: absolute; display: grid;
top: var(--spacing-xl); grid-auto-flow: column;
right: var(--spacing-xl); grid-template-columns: 20% 1fr;
font-size: var(--font-size-m); grid-gap: var(--spacing-m);
height: 100%;
padding: var(--spacing-xl);
} }
</style> </style>

View File

@ -0,0 +1,200 @@
<script>
import {
Button,
TextButton,
Body,
DropdownMenu,
ModalContent,
} from "@budibase/bbui"
import { AddIcon, ArrowDownIcon } from "components/common/Icons/"
import actionTypes from "./actions"
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
const eventTypeKey = "##eventHandlerType"
export let event
let addActionButton
let addActionDropdown
let selectedAction
let draftEventHandler = { parameters: [] }
$: actions = event || []
$: selectedActionComponent =
selectedAction &&
actionTypes.find(t => t.name === selectedAction[eventTypeKey]).component
const updateEventHandler = (updatedHandler, index) => {
actions[index] = updatedHandler
}
const deleteAction = index => {
actions.splice(index, 1)
actions = actions
}
const addAction = actionType => () => {
const newAction = {
parameters: {},
[eventTypeKey]: actionType.name,
}
actions.push(newAction)
selectedAction = newAction
actions = actions
addActionDropdown.hide()
}
const selectAction = action => () => {
selectedAction = action
}
const saveEventData = () => {
dispatch("change", actions)
}
</script>
<div class="actions-container">
<div class="actions-list">
<div>
<div bind:this={addActionButton}>
<TextButton text small blue on:click={addActionDropdown.show}>
<div style="height: 20px; width: 20px;">
<AddIcon />
</div>
Add Action
</TextButton>
</div>
<DropdownMenu
bind:this={addActionDropdown}
anchor={addActionButton}
align="right">
<div class="available-actions-container">
{#each actionTypes as actionType}
<div class="available-action" on:click={addAction(actionType)}>
<span>{actionType.name}</span>
</div>
{/each}
</div>
</DropdownMenu>
</div>
{#if actions && actions.length > 0}
{#each actions as action, index}
<div class="action-container">
<div class="action-header" on:click={selectAction(action)}>
<span class:selected={action === selectedAction}>
{index + 1}. {action[eventTypeKey]}
</span>
<!-- <Body small lh>{index + 1}. {action[eventTypeKey]}</Body> -->
<!-- <div class="row-expander" class:rotate={action !== selectedAction}>
<ArrowDownIcon />
</div> -->
</div>
<!-- {#if action === selectedAction}
<div class="selected-action-container">
<svelte:component
this={selectedActionComponent}
parameters={selectedAction.parameters} />
<div class="delete-action-button">
<TextButton text medium on:click={() => deleteAction(index)}>
Delete
</TextButton>
</div>
</div>
{/if} -->
</div>
{/each}
{/if}
</div>
<div class="action-config">
{#if selectedAction}
<div class="selected-action-container">
<svelte:component
this={selectedActionComponent}
parameters={selectedAction.parameters} />
<div class="delete-action-button">
<!-- <TextButton text medium on:click={() => deleteAction(index)}>
Delete
</TextButton> -->
</div>
</div>
{/if}
<Button thin blue on:click={saveEventData}>Save</Button>
</div>
</div>
<a href="https://docs.budibase.com">Learn more about Actions</a>
<style>
.action-header {
display: flex;
flex-direction: row;
align-items: center;
}
.action-header > span {
margin-bottom: var(--spacing-m);
}
.action-header > span:hover,
.selected {
cursor: pointer;
font-weight: 500;
}
.actions-list {
border: var(--border-light);
}
.available-action {
padding: var(--spacing-s);
font-size: var(--font-size-m);
cursor: pointer;
}
.available-action:hover {
background: var(--grey-2);
}
.actions-container {
display: grid;
grid-gap: var(--spacing-m);
grid-template-columns: 15% 1fr;
grid-auto-flow: column;
min-height: 0;
padding-top: 0;
border: var(--border-light);
border-width: 0 0 1px 0;
overflow-y: auto;
}
.action-container {
border: var(--border-light);
border-width: 1px 0 0 0;
}
.selected-action-container {
padding-bottom: var(--spacing-s);
padding-top: var(--spacing-s);
}
.delete-action-button {
padding-top: var(--spacing-l);
display: flex;
justify-content: flex-end;
flex-direction: row;
}
a {
flex: 1;
color: var(--grey-5);
font-size: var(--font-size-s);
text-decoration: none;
}
a:hover {
color: var(--blue);
}
</style>

View File

@ -1,176 +0,0 @@
<script>
import {
Button,
TextButton,
Body,
DropdownMenu,
ModalContent,
} from "@budibase/bbui"
import { AddIcon, ArrowDownIcon } from "components/common/Icons/"
import actionTypes from "./actions"
import { createEventDispatcher } from "svelte"
const dispatch = createEventDispatcher()
const eventTypeKey = "##eventHandlerType"
export let event
let addActionButton
let addActionDropdown
let selectedAction
let draftEventHandler = { parameters: [] }
$: actions = event || []
$: selectedActionComponent =
selectedAction &&
actionTypes.find(t => t.name === selectedAction[eventTypeKey]).component
const updateEventHandler = (updatedHandler, index) => {
actions[index] = updatedHandler
}
const deleteAction = index => {
actions.splice(index, 1)
actions = actions
}
const addAction = actionType => () => {
const newAction = {
parameters: {},
[eventTypeKey]: actionType.name,
}
actions.push(newAction)
selectedAction = newAction
actions = actions
addActionDropdown.hide()
}
const selectAction = action => () => {
selectedAction = action
}
const saveEventData = () => {
dispatch("change", actions)
}
</script>
<div>
<div bind:this={addActionButton}>
<TextButton text small blue on:click={addActionDropdown.show}>
<div style="height: 20px; width: 20px;">
<AddIcon />
</div>
Add Action
</TextButton>
</div>
<DropdownMenu
bind:this={addActionDropdown}
anchor={addActionButton}
align="right">
<div class="available-actions-container">
{#each actionTypes as actionType}
<div class="available-action" on:click={addAction(actionType)}>
<span>{actionType.name}</span>
</div>
{/each}
</div>
</DropdownMenu>
</div>
<div class="actions-container">
{#if actions && actions.length > 0}
{#each actions as action, index}
<div class="action-container">
<div class="action-header" on:click={selectAction(action)}>
<Body small lh>{index + 1}. {action[eventTypeKey]}</Body>
<div class="row-expander" class:rotate={action !== selectedAction}>
<ArrowDownIcon />
</div>
</div>
{#if action === selectedAction}
<div class="selected-action-container">
<svelte:component
this={selectedActionComponent}
parameters={selectedAction.parameters} />
<div class="delete-action-button">
<TextButton text medium on:click={() => deleteAction(index)}>
Delete
</TextButton>
</div>
</div>
{/if}
</div>
{/each}
{/if}
<Button thin blue on:click={saveEventData}>Save</Button>
</div>
<a href="https://docs.budibase.com">Learn more about Actions</a>
<style>
.action-header {
display: flex;
flex-direction: row;
align-items: center;
}
.action-header > p {
flex: 1;
}
.row-expander {
height: 30px;
width: 30px;
}
.available-action {
padding: var(--spacing-s);
font-size: var(--font-size-m);
cursor: pointer;
}
.available-action:hover {
background: var(--grey-2);
}
.actions-container {
flex: 1;
min-height: 0;
padding-top: 0;
border: var(--border-light);
border-width: 0 0 1px 0;
overflow-y: auto;
}
.action-container {
border: var(--border-light);
border-width: 1px 0 0 0;
}
.selected-action-container {
padding-bottom: var(--spacing-s);
padding-top: var(--spacing-s);
}
.delete-action-button {
padding-top: var(--spacing-l);
display: flex;
justify-content: flex-end;
flex-direction: row;
}
a {
flex: 1;
color: var(--grey-5);
font-size: var(--font-size-s);
text-decoration: none;
}
a:hover {
color: var(--blue);
}
.rotate :global(svg) {
transform: rotate(90deg);
}
</style>

View File

@ -2,7 +2,7 @@
import { Button, Modal } from "@budibase/bbui" import { Button, Modal } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { store } from "builderStore" import { store } from "builderStore"
import EventEditorModal from "./EventEditorModal.svelte" import EventEditor from "./EventEditor.svelte"
import BottomDrawer from "components/common/BottomDrawer.svelte" import BottomDrawer from "components/common/BottomDrawer.svelte"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
@ -20,7 +20,12 @@
<Button secondary small on:click={showDrawer}>Define Actions</Button> <Button secondary small on:click={showDrawer}>Define Actions</Button>
{#if drawerVisible} {#if drawerVisible}
<BottomDrawer> <BottomDrawer title={'Actions'} onClose={() => (drawerVisible = false)}>
<EventEditorModal event={value} eventType={name} on:change /> <heading slot="buttons">
<Button thin blue>Save</Button>
</heading>
<div slot="body">
<EventEditor event={value} eventType={name} on:change />
</div>
</BottomDrawer> </BottomDrawer>
{/if} {/if}

View File

@ -2,10 +2,14 @@
import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui" import { Button, Icon, DropdownMenu, Spacer, Heading } from "@budibase/bbui"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { store, backendUiStore, currentAsset } from "builderStore" import { store, backendUiStore, currentAsset } from "builderStore"
// import DataBindingDrawer from "components/userInterface/DataBindingDrawer/index.svelte"
import BottomDrawer from "components/common/BottomDrawer.svelte"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
import fetchBindableProperties from "../../builderStore/fetchBindableProperties" import fetchBindableProperties from "../../builderStore/fetchBindableProperties"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let anchorRight, dropdownRight let anchorRight, dropdownRight
let bindingDrawerOpen
export let value = {} export let value = {}
@ -15,14 +19,11 @@
} }
function openBindingDrawer() { function openBindingDrawer() {
backendUiStore.update(state => { bindingDrawerOpen = true
state.selectedQueryId = value._id }
return state
}) function closeDatabindingDrawer() {
store.update(state => { bindingDrawerOpen = false
state.bottomDrawerVisible = true
return state
})
} }
$: tables = $backendUiStore.tables.map(m => ({ $: tables = $backendUiStore.tables.map(m => ({
@ -47,6 +48,7 @@
name: query.name, name: query.name,
...query, ...query,
schema: query.schema, schema: query.schema,
parameters: query.parameters,
type: "query", type: "query",
})) }))
@ -57,6 +59,15 @@
tables: $backendUiStore.tables, tables: $backendUiStore.tables,
}) })
$: queryBindableProperties = bindableProperties.map(property => ({
...property,
category: property.type === "instance" ? "Component" : "Table",
label: property.readableBinding,
path: property.runtimeBinding,
}))
$: console.log("selected", value)
$: links = bindableProperties $: links = bindableProperties
.filter(x => x.fieldSchema?.type === "link") .filter(x => x.fieldSchema?.type === "link")
.map(property => { .map(property => {
@ -78,9 +89,6 @@
<span>{value.label ? value.label : 'Table / View / Query'}</span> <span>{value.label ? value.label : 'Table / View / Query'}</span>
<Icon name="arrowdown" /> <Icon name="arrowdown" />
</div> </div>
{#if value.type === "query"}
<i class="ri-settings-3-line" on:click={openBindingDrawer} />
{/if}
<DropdownMenu bind:this={dropdownRight} anchor={anchorRight}> <DropdownMenu bind:this={dropdownRight} anchor={anchorRight}>
<div class="dropdown"> <div class="dropdown">
<div class="title"> <div class="title">
@ -138,6 +146,25 @@
</div> </div>
</DropdownMenu> </DropdownMenu>
{#if value.type === "query"}
<Button blue on:click={openBindingDrawer}/>
{#if bindingDrawerOpen}
<BottomDrawer title={'Query'} onClose={closeDatabindingDrawer}>
<div slot="buttons">
<Button blue thin on:click={() => handleSelected(value)}>Save</Button>
</div>
<div class="drawer-contents" slot="body">
<pre>{value.queryString}</pre>
<ParameterBuilder
bind:customParams={value.queryParams}
parameters={value.parameters || []}
bindings={queryBindableProperties} />
</div>
</BottomDrawer>
{/if}
{/if}
<style> <style>
.dropdownbutton { .dropdownbutton {
background-color: var(--grey-2); background-color: var(--grey-2);
@ -199,4 +226,8 @@
li:hover { li:hover {
background-color: var(--grey-4); background-color: var(--grey-4);
} }
.drawer-contents {
padding: var(--spacing-xl);
}
</style> </style>

View File

@ -10,7 +10,6 @@
import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte" import ComponentPropertiesPanel from "components/userInterface/ComponentPropertiesPanel.svelte"
import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte" import ComponentSelectionList from "components/userInterface/ComponentSelectionList.svelte"
import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte" import FrontendNavigatePane from "components/userInterface/FrontendNavigatePane.svelte"
import DataBindingDrawer from "components/userInterface/DataBindingDrawer/index.svelte"
$: instance = $store.appInstance $: instance = $store.appInstance
@ -48,10 +47,6 @@
{/if} {/if}
</div> </div>
{#if $store.bottomDrawerVisible}
<DataBindingDrawer />
{/if}
{#if $selectedComponent != null} {#if $selectedComponent != null}
<div class="components-pane"> <div class="components-pane">
<ComponentPropertiesPanel /> <ComponentPropertiesPanel />

View File

@ -1,8 +1,9 @@
import { fetchTableData } from "./tables" import { fetchTableData } from "./tables"
import { fetchViewData } from "./views" import { fetchViewData } from "./views"
import { fetchRelationshipData } from "./relationships" import { fetchRelationshipData } from "./relationships"
import { fetchQueryData } from "./queries" import { executeQuery } from "./queries"
import { enrichRows } from "./rows" import { enrichRows } from "./rows"
import { enrichDataBindings } from "../utils/enrichDataBinding"
/** /**
* Fetches all rows for a particular Budibase data source. * Fetches all rows for a particular Budibase data source.
@ -20,8 +21,12 @@ export const fetchDatasource = async (datasource, dataContext) => {
} else if (type === "view") { } else if (type === "view") {
rows = await fetchViewData(datasource) rows = await fetchViewData(datasource)
} else if (type === "query") { } else if (type === "query") {
// TODO: map to schema console.log("Query Datasource", datasource)
return await fetchQueryData(datasource) console.log("Data Context", dataContext)
// TODO: You left here
const parameters = enrichDataBindings(datasource.queryParams, dataContext)
console.log("PARSED PARAMS", parameters)
return await executeQuery({ _id: datasource._id, parameters })
} else if (type === "link") { } else if (type === "link") {
const row = dataContext[datasource.providerId] const row = dataContext[datasource.providerId]
rows = await fetchRelationshipData({ rows = await fetchRelationshipData({

View File

@ -1,23 +1,13 @@
import API from "./api" import API from "./api"
/**
* Fetches all rows from a query.
*/
export const fetchQueryData = async ({ _id }) => {
const response = await API.get({
url: `/api/queries/${_id}`,
})
return response.rows
}
/** /**
* Executes a query against an external data connector. * Executes a query against an external data connector.
*/ */
export const executeQuery = async ({ queryId, params }) => { export const executeQuery = async ({ queryId, parameters }) => {
const response = await API.post({ const response = await API.post({
url: `/api/queries/${queryId}`, url: `/api/queries/${queryId}`,
body: { body: {
params, parameters,
}, },
}) })
return response.rows return response.rows

View File

@ -28,10 +28,13 @@ const navigationHandler = action => {
const queryExecutionHandler = async (action, context) => { const queryExecutionHandler = async (action, context) => {
const { datasourceId, queryId, queryParams } = action.parameters const { datasourceId, queryId, queryParams } = action.parameters
// TODO: allow context based bindings for query params
const enrichedQueryParameters = enrichDataBindings(queryParams, context) const enrichedQueryParameters = enrichDataBindings(queryParams, context)
await executeQuery({ datasourceId, queryId, params: enrichedQueryParameters }) await executeQuery({
datasourceId,
queryId,
parameters: enrichedQueryParameters,
})
} }
const handlerMap = { const handlerMap = {

View File

@ -81,7 +81,7 @@ exports.execute = async function(ctx) {
const queryTemplate = handlebars.compile(query.queryString) const queryTemplate = handlebars.compile(query.queryString)
// TODO: Take the default params into account // TODO: Take the default params into account
const parsedQuery = queryTemplate(ctx.request.body.params) const parsedQuery = queryTemplate(ctx.request.body.parameters)
const Integration = integrations[datasource.source] const Integration = integrations[datasource.source]
@ -94,31 +94,6 @@ exports.execute = async function(ctx) {
ctx.body = response ctx.body = response
} }
exports.fetchQuery = async function(ctx) {
const db = new CouchDB(ctx.user.appId)
const query = await db.get(ctx.params.queryId)
const datasource = await db.get(query.datasourceId)
const Integration = integrations[datasource.source]
if (!Integration) {
ctx.throw(400, "Integration type does not exist.")
return
}
const rows = await new Integration(
datasource.config,
query.queryString
).query()
ctx.body = {
schema: query.schema,
rows,
}
}
exports.destroy = async function(ctx) { exports.destroy = async function(ctx) {
const db = new CouchDB(ctx.user.appId) const db = new CouchDB(ctx.user.appId)
await db.destroy(ctx.params.queryId) await db.destroy(ctx.params.queryId)

View File

@ -8,10 +8,9 @@ const router = Router()
// TODO: sort out auth so apps have the right permissions // TODO: sort out auth so apps have the right permissions
router router
.get("/api/queries", authorized(BUILDER), queryController.fetch) .get("/api/queries", authorized(BUILDER), queryController.fetch)
.get("/api/queries/:queryId", authorized(BUILDER), queryController.fetchQuery)
.post("/api/queries", authorized(BUILDER), queryController.save) .post("/api/queries", authorized(BUILDER), queryController.save)
.post("/api/queries/preview", authorized(BUILDER), queryController.preview)
.post("/api/queries/:queryId", authorized(BUILDER), queryController.execute) .post("/api/queries/:queryId", authorized(BUILDER), queryController.execute)
.post("/api/queries/preview", authorized(BUILDER), queryController.preview)
.delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy) .delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
module.exports = router module.exports = router