switching between queries
This commit is contained in:
parent
0be3c722b8
commit
37ef32a173
|
@ -8,6 +8,7 @@ const INITIAL_BACKEND_UI_STATE = {
|
||||||
users: [],
|
users: [],
|
||||||
roles: [],
|
roles: [],
|
||||||
datasources: [],
|
datasources: [],
|
||||||
|
queries: [],
|
||||||
selectedDatabase: {},
|
selectedDatabase: {},
|
||||||
selectedTable: {},
|
selectedTable: {},
|
||||||
draftTable: {},
|
draftTable: {},
|
||||||
|
@ -24,10 +25,13 @@ export const getBackendUiStore = () => {
|
||||||
const tables = await tablesResponse.json()
|
const tables = await tablesResponse.json()
|
||||||
const datasourcesResponse = await api.get(`/api/datasources`)
|
const datasourcesResponse = await api.get(`/api/datasources`)
|
||||||
const datasources = await datasourcesResponse.json()
|
const datasources = await datasourcesResponse.json()
|
||||||
|
const queriesResponse = await api.get(`/api/queries`)
|
||||||
|
const queries = await queriesResponse.json()
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedDatabase = db
|
state.selectedDatabase = db
|
||||||
state.tables = tables
|
state.tables = tables
|
||||||
state.datasources = datasources
|
state.datasources = datasources
|
||||||
|
state.queries = queries
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -98,35 +102,56 @@ export const getBackendUiStore = () => {
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
saveQuery: async (datasourceId, query) => {
|
},
|
||||||
const response = await api.post(
|
queries: {
|
||||||
`/api/datasources/${datasourceId}/queries`,
|
fetch: async () => {
|
||||||
query
|
const response = await api.get(`/api/queries`)
|
||||||
)
|
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
const currentIdx = state.datasources.findIndex(
|
state.queries = json
|
||||||
ds => ds._id === json._id
|
return state
|
||||||
|
})
|
||||||
|
return json
|
||||||
|
},
|
||||||
|
save: async (datasourceId, query) => {
|
||||||
|
query.datasourceId = datasourceId
|
||||||
|
const response = await api.post(`/api/queries`, query)
|
||||||
|
const json = await response.json()
|
||||||
|
store.update(state => {
|
||||||
|
const currentIdx = state.queries.findIndex(
|
||||||
|
query => query._id === json._id
|
||||||
)
|
)
|
||||||
|
|
||||||
if (currentIdx >= 0) {
|
if (currentIdx >= 0) {
|
||||||
state.datasources.splice(currentIdx, 1, json)
|
state.queries.splice(currentIdx, 1, json)
|
||||||
} else {
|
} else {
|
||||||
state.datasources.push(json)
|
state.queries.push(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
state.datasources = state.datasources
|
state.queries = state.queries
|
||||||
|
state.selectedQueryId = json._id
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
},
|
|
||||||
queries: {
|
|
||||||
select: queryId =>
|
select: queryId =>
|
||||||
store.update(state => {
|
store.update(state => {
|
||||||
state.selectedDatasourceId = null
|
state.selectedDatasourceId = null
|
||||||
state.selectedQueryId = queryId
|
state.selectedQueryId = queryId
|
||||||
return state
|
return state
|
||||||
}),
|
}),
|
||||||
|
delete: async queryId => {
|
||||||
|
await api.delete(`/api/queries/${queryId}`)
|
||||||
|
store.update(state => {
|
||||||
|
state.datasources = state.queries.filter(
|
||||||
|
existing => existing._id !== queryId
|
||||||
|
)
|
||||||
|
if (state.selectedQueryId === queryId) {
|
||||||
|
state.selectedQueryId = null
|
||||||
|
}
|
||||||
|
|
||||||
|
return state
|
||||||
|
})
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tables: {
|
tables: {
|
||||||
fetch: async () => {
|
fetch: async () => {
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
import Table from "./Table.svelte"
|
import Table from "./Table.svelte"
|
||||||
import CreateQueryButton from "components/backend/DataTable/buttons/CreateQueryButton.svelte"
|
import CreateQueryButton from "components/backend/DataTable/buttons/CreateQueryButton.svelte"
|
||||||
|
|
||||||
export let datasource
|
|
||||||
export let query = {}
|
export let query = {}
|
||||||
|
|
||||||
let data = []
|
let data = []
|
||||||
|
@ -23,7 +22,6 @@
|
||||||
data = response.rows || []
|
data = response.rows || []
|
||||||
error = false
|
error = false
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
|
||||||
error = `${query}: Query error. (${err.message}). This could be a problem with your datasource configuration.`
|
error = `${query}: Query error. (${err.message}). This could be a problem with your datasource configuration.`
|
||||||
notifier.danger(error)
|
notifier.danger(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -32,14 +30,14 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch rows for specified query
|
// Fetch rows for specified query
|
||||||
$: fetchData()
|
$: query && fetchData()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<div class="errors">{error}</div>
|
<div class="errors">{error}</div>
|
||||||
{/if}
|
{/if}
|
||||||
<Table title={query.name} schema={query.schema} {data} {loading}>
|
<Table title={query.name} schema={query.schema} {data} {loading}>
|
||||||
<CreateQueryButton {query} {datasource} />
|
<CreateQueryButton {query} edit />
|
||||||
</Table>
|
</Table>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -32,7 +32,7 @@ export async function fetchDataForView(view) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchDataForQuery(datasourceId, queryId) {
|
export async function fetchDataForQuery(datasourceId, queryId) {
|
||||||
const FETCH_QUERY_URL = `/api/datasources/${datasourceId}/queries/${queryId}`
|
const FETCH_QUERY_URL = `/api/queries/${queryId}`
|
||||||
|
|
||||||
const response = await api.get(FETCH_QUERY_URL)
|
const response = await api.get(FETCH_QUERY_URL)
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
|
|
@ -15,15 +15,15 @@
|
||||||
import EditIntegrationConfig from "../modals/EditIntegrationConfig.svelte"
|
import EditIntegrationConfig from "../modals/EditIntegrationConfig.svelte"
|
||||||
import CreateEditQuery from "components/backend/DataTable/modals/CreateEditQuery.svelte"
|
import CreateEditQuery from "components/backend/DataTable/modals/CreateEditQuery.svelte"
|
||||||
|
|
||||||
export let datasource
|
|
||||||
export let query = {}
|
export let query = {}
|
||||||
|
export let edit
|
||||||
|
|
||||||
let modal
|
let modal
|
||||||
let fields = []
|
let fields = []
|
||||||
|
|
||||||
async function saveQuery() {
|
async function saveQuery() {
|
||||||
try {
|
try {
|
||||||
await backendUiStore.actions.datasources.saveQuery(datasource._id, query)
|
await backendUiStore.actions.queries.save(query.datasourceId, query)
|
||||||
notifier.success(`Query created successfully.`)
|
notifier.success(`Query created successfully.`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(err)
|
console.error(err)
|
||||||
|
@ -35,7 +35,7 @@
|
||||||
<div>
|
<div>
|
||||||
<Button text small on:click={modal.show}>
|
<Button text small on:click={modal.show}>
|
||||||
<Icon name="filter" />
|
<Icon name="filter" />
|
||||||
{$backendUiStore.selectedQueryId ? 'Edit' : 'Create'} Query
|
{edit ? 'Edit' : 'Create'} Query
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<Modal bind:this={modal}>
|
<Modal bind:this={modal}>
|
||||||
|
@ -43,7 +43,7 @@
|
||||||
confirmText="Save"
|
confirmText="Save"
|
||||||
cancelText="Cancel"
|
cancelText="Cancel"
|
||||||
onConfirm={saveQuery}
|
onConfirm={saveQuery}
|
||||||
title={query ? 'Edit Query' : 'Create New Query'}>
|
title={edit ? 'Edit Query' : 'Create New Query'}>
|
||||||
<CreateEditQuery {datasource} bind:query />
|
<CreateEditQuery bind:query />
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
@ -27,17 +27,20 @@
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export let datasource
|
|
||||||
export let query
|
export let query
|
||||||
export let fields = []
|
export let fields = []
|
||||||
|
|
||||||
console.log(query)
|
|
||||||
|
|
||||||
let config = {}
|
let config = {}
|
||||||
let queryType
|
|
||||||
let previewTab = "PREVIEW"
|
let previewTab = "PREVIEW"
|
||||||
let preview
|
let preview
|
||||||
|
|
||||||
|
$: datasource = $backendUiStore.datasources.find(
|
||||||
|
ds => ds._id === query.datasourceId
|
||||||
|
)
|
||||||
|
|
||||||
|
$: query.datasourceId =
|
||||||
|
query.datasourceId || $backendUiStore.selectedDatasourceId
|
||||||
|
|
||||||
$: query.schema = fields.reduce(
|
$: query.schema = fields.reduce(
|
||||||
(acc, next) => ({
|
(acc, next) => ({
|
||||||
...acc,
|
...acc,
|
||||||
|
@ -72,9 +75,8 @@
|
||||||
|
|
||||||
async function previewQuery() {
|
async function previewQuery() {
|
||||||
try {
|
try {
|
||||||
const response = await api.post(`/api/datasources/queries/preview`, {
|
const response = await api.post(`/api/queries/preview`, {
|
||||||
type: datasource.source,
|
datasourceId: datasource._id,
|
||||||
config: datasource.config,
|
|
||||||
query: query.queryString,
|
query: query.queryString,
|
||||||
})
|
})
|
||||||
const json = await response.json()
|
const json = await response.json()
|
||||||
|
@ -101,18 +103,13 @@
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<div class="config">
|
<div class="config">
|
||||||
<h6>Datasource Type</h6>
|
|
||||||
<span>{datasource.source}</span>
|
|
||||||
|
|
||||||
<Spacer medium />
|
|
||||||
|
|
||||||
<Label extraSmall grey>Query Name</Label>
|
<Label extraSmall grey>Query Name</Label>
|
||||||
<Input type="text" thin bind:value={query.name} />
|
<Input type="text" thin bind:value={query.name} />
|
||||||
|
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
|
|
||||||
<Label extraSmall grey>Query Type</Label>
|
<Label extraSmall grey>Query Type</Label>
|
||||||
<Select secondary bind:value={queryType}>
|
<Select secondary bind:value={query.queryType}>
|
||||||
<option value={''}>Select an option</option>
|
<option value={''}>Select an option</option>
|
||||||
{#each Object.keys(config) as queryType}
|
{#each Object.keys(config) as queryType}
|
||||||
<option value={queryType}>{queryType}</option>
|
<option value={queryType}>{queryType}</option>
|
||||||
|
@ -121,7 +118,9 @@
|
||||||
|
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
|
|
||||||
<IntegrationQueryEditor {queryType} bind:query={query.queryString} />
|
<IntegrationQueryEditor
|
||||||
|
type={query.queryType}
|
||||||
|
bind:query={query.queryString} />
|
||||||
|
|
||||||
<Spacer small />
|
<Spacer small />
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
import { TableNames } from "constants"
|
import { TableNames } from "constants"
|
||||||
import CreateDatasourceModal from "./modals/CreateDatasourceModal.svelte"
|
import CreateDatasourceModal from "./modals/CreateDatasourceModal.svelte"
|
||||||
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
|
import EditDatasourcePopover from "./popovers/EditDatasourcePopover.svelte"
|
||||||
|
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
|
||||||
import { Modal, Switcher } from "@budibase/bbui"
|
import { Modal, Switcher } from "@budibase/bbui"
|
||||||
import NavItem from "components/common/NavItem.svelte"
|
import NavItem from "components/common/NavItem.svelte"
|
||||||
|
|
||||||
|
@ -18,6 +19,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onClickQuery(datasourceId, queryId) {
|
function onClickQuery(datasourceId, queryId) {
|
||||||
|
console.log(backendUiStore.selectedQueryId, queryId)
|
||||||
if ($backendUiStore.selectedQueryId === queryId) {
|
if ($backendUiStore.selectedQueryId === queryId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -28,6 +30,7 @@
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
backendUiStore.actions.datasources.fetch()
|
backendUiStore.actions.datasources.fetch()
|
||||||
|
backendUiStore.actions.queries.fetch()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -42,15 +45,14 @@
|
||||||
on:click={() => selectDatasource(datasource)}>
|
on:click={() => selectDatasource(datasource)}>
|
||||||
<EditDatasourcePopover {datasource} />
|
<EditDatasourcePopover {datasource} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
{#each Object.keys(datasource.queries) as queryId}
|
{#each $backendUiStore.queries.filter(query => query.datasourceId === datasource._id) as query}
|
||||||
<NavItem
|
<NavItem
|
||||||
indentLevel={1}
|
indentLevel={1}
|
||||||
icon="ri-eye-line"
|
icon="ri-eye-line"
|
||||||
text={datasource.queries[queryId].name}
|
text={query.name}
|
||||||
selected={$backendUiStore.selectedQueryId === queryId}
|
selected={$backendUiStore.selectedQueryId === query._id}
|
||||||
on:click={() => onClickQuery(datasource._id, queryId)}>
|
on:click={() => onClickQuery(datasource._id, query._id)}>
|
||||||
<!-- <EditViewPopover
|
<EditQueryPopover {query} />
|
||||||
view={{ name: viewName, ...table.views[viewName] }} /> -->
|
|
||||||
</NavItem>
|
</NavItem>
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore, store, allScreens } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
||||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
|
||||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
|
||||||
|
export let query
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
let confirmDeleteDialog
|
||||||
|
let error = ""
|
||||||
|
let willBeDeleted
|
||||||
|
|
||||||
|
function hideEditor() {
|
||||||
|
dropdown?.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
function showModal() {
|
||||||
|
hideEditor()
|
||||||
|
confirmDeleteDialog.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function deleteQuery() {
|
||||||
|
await backendUiStore.actions.queries.delete(query._id)
|
||||||
|
notifier.success("Query deleted")
|
||||||
|
hideEditor()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div on:click|stopPropagation>
|
||||||
|
<div bind:this={anchor} class="icon" on:click={dropdown.show}>
|
||||||
|
<i class="ri-more-line" />
|
||||||
|
</div>
|
||||||
|
<DropdownMenu align="left" {anchor} bind:this={dropdown}>
|
||||||
|
<DropdownContainer>
|
||||||
|
<DropdownItem
|
||||||
|
icon="ri-delete-bin-line"
|
||||||
|
title="Delete"
|
||||||
|
on:click={showModal}
|
||||||
|
data-cy="delete-datasource" />
|
||||||
|
</DropdownContainer>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
<ConfirmDialog
|
||||||
|
bind:this={confirmDeleteDialog}
|
||||||
|
okText="Delete Query"
|
||||||
|
onOk={deleteQuery}
|
||||||
|
title="Confirm Deletion">
|
||||||
|
Are you sure you wish to delete this query?
|
||||||
|
This action cannot be undone.
|
||||||
|
</ConfirmDialog>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div.icon {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.icon i {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
display: grid;
|
||||||
|
grid-gap: var(--spacing-xl);
|
||||||
|
min-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin: 0;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,17 +1,28 @@
|
||||||
<script>
|
<script>
|
||||||
import { TextArea } from "@budibase/bbui"
|
import { TextArea, Label, Input } from "@budibase/bbui"
|
||||||
import Editor from "./Editor.svelte"
|
import Editor from "./Editor.svelte"
|
||||||
|
|
||||||
|
const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g
|
||||||
|
|
||||||
const QueryTypes = {
|
const QueryTypes = {
|
||||||
SQL: "sql",
|
SQL: "sql",
|
||||||
}
|
}
|
||||||
|
|
||||||
export let queryType
|
export let type
|
||||||
export let query
|
export let query
|
||||||
|
|
||||||
$: console.log(query)
|
// $: parameters = Array.from(
|
||||||
|
// query
|
||||||
|
// .matchAll(CAPTURE_VAR_INSIDE_MUSTACHE)
|
||||||
|
// .map(([_, paramName]) => paramName)
|
||||||
|
// )
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if queryType === QueryTypes.SQL}
|
<!-- {#each parameters as param}
|
||||||
|
<Label grey extraSmall>{param}</Label>
|
||||||
|
<Input thin bind:value={query.params[param]} />
|
||||||
|
{/each} -->
|
||||||
|
|
||||||
|
{#if type === QueryTypes.SQL}
|
||||||
<Editor label="Query" bind:value={query} />
|
<Editor label="Query" bind:value={query} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<Label size="m" color="dark">Query</Label>
|
<Label size="m" color="dark">Query</Label>
|
||||||
<Select secondary bind:value={parameters.queryId}>
|
<Select secondary bind:value={parameters.queryId}>
|
||||||
<option value="" />
|
<option value="" />
|
||||||
{#each Object.keys(datasource.queries) as query}
|
{#each $backendUiStore.queries.filter(query => query.datasourceId === datasource._id) as query}
|
||||||
<option value={query}>{datasource.queries[query].name}</option>
|
<option value={query}>{datasource.queries[query].name}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
|
|
|
@ -31,17 +31,13 @@
|
||||||
return [...acc, ...viewsArr]
|
return [...acc, ...viewsArr]
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
$: queries = $backendUiStore.datasources.reduce((acc, cur) => {
|
$: queries = $backendUiStore.queries.map(query => ({
|
||||||
let queriesArr = Object.entries(cur.queries).map(([key, value]) => ({
|
label: query.name,
|
||||||
label: value.name,
|
name: query.name,
|
||||||
name: value.name,
|
...query,
|
||||||
datasourceId: cur._id,
|
schema: query.schema,
|
||||||
queryId: key,
|
|
||||||
schema: value.schema,
|
|
||||||
type: "query",
|
type: "query",
|
||||||
}))
|
}))
|
||||||
return [...acc, ...queriesArr]
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
$: bindableProperties = fetchBindableProperties({
|
$: bindableProperties = fetchBindableProperties({
|
||||||
componentInstanceId: $store.selectedComponentId,
|
componentInstanceId: $store.selectedComponentId,
|
||||||
|
|
|
@ -3,13 +3,10 @@
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
||||||
|
|
||||||
// TODO: refactor
|
$: query = $backendUiStore.queries.find(query => query._id === $params.query)
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: console.log("updated", query)
|
||||||
ds => ds._id === $params.selectedDatasource
|
|
||||||
)
|
|
||||||
$: query = datasource && datasource.queries[$params.query]
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if $backendUiStore.selectedDatabase._id && datasource && query}
|
{#if $backendUiStore.selectedDatabase._id && query}
|
||||||
<ExternalDataSourceTable {query} {datasource} />
|
<ExternalDataSourceTable {query} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -3,9 +3,9 @@ import API from "./api"
|
||||||
/**
|
/**
|
||||||
* Fetches all rows from a query.
|
* Fetches all rows from a query.
|
||||||
*/
|
*/
|
||||||
export const fetchQueryData = async ({ datasourceId, queryId }) => {
|
export const fetchQueryData = async ({ _id }) => {
|
||||||
const response = await API.get({
|
const response = await API.get({
|
||||||
url: `/api/datasources/${datasourceId}/queries/${queryId}`,
|
url: `/api/queries/${_id}`,
|
||||||
})
|
})
|
||||||
return response.rows
|
return response.rows
|
||||||
}
|
}
|
||||||
|
@ -13,9 +13,9 @@ export const fetchQueryData = async ({ datasourceId, queryId }) => {
|
||||||
/**
|
/**
|
||||||
* Executes a query against an external data connector.
|
* Executes a query against an external data connector.
|
||||||
*/
|
*/
|
||||||
export const executeQuery = async ({ datasourceId, queryId }) => {
|
export const executeQuery = async ({ _id }) => {
|
||||||
const response = await API.post({
|
const response = await API.post({
|
||||||
url: `/api/datasources/${datasourceId}/queries/${queryId}`,
|
url: `/api/queries/${_id}`,
|
||||||
// body: params,
|
// body: params,
|
||||||
})
|
})
|
||||||
return response.rows
|
return response.rows
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
const CouchDB = require("../../db")
|
const CouchDB = require("../../db")
|
||||||
const bcrypt = require("../../utilities/bcrypt")
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
const {
|
const { generateDatasourceID, getDatasourceParams } = require("../../db/utils")
|
||||||
generateDatasourceID,
|
|
||||||
getDatasourceParams,
|
|
||||||
generateQueryID,
|
|
||||||
} = require("../../db/utils")
|
|
||||||
const { integrations } = require("../../integrations")
|
|
||||||
|
|
||||||
exports.fetch = async function(ctx) {
|
exports.fetch = async function(ctx) {
|
||||||
const database = new CouchDB(ctx.user.appId)
|
const database = new CouchDB(ctx.user.appId)
|
||||||
|
@ -30,7 +25,6 @@ exports.save = async function(ctx) {
|
||||||
const datasource = {
|
const datasource = {
|
||||||
_id: generateDatasourceID(),
|
_id: generateDatasourceID(),
|
||||||
type: "datasource",
|
type: "datasource",
|
||||||
queries: {},
|
|
||||||
...ctx.request.body,
|
...ctx.request.body,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,90 +71,3 @@ exports.find = async function(ctx) {
|
||||||
const datasource = await database.get(ctx.params.datasourceId)
|
const datasource = await database.get(ctx.params.datasourceId)
|
||||||
ctx.body = datasource
|
ctx.body = datasource
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.saveQuery = async function(ctx) {
|
|
||||||
const db = new CouchDB(ctx.user.appId)
|
|
||||||
const query = ctx.request.body
|
|
||||||
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// type: "",
|
|
||||||
// query: "",
|
|
||||||
// otherStuff: ""
|
|
||||||
// }
|
|
||||||
|
|
||||||
const datasource = await db.get(ctx.params.datasourceId)
|
|
||||||
|
|
||||||
const queryId = generateQueryID()
|
|
||||||
|
|
||||||
datasource.queries[queryId] = query
|
|
||||||
|
|
||||||
const response = await db.put(datasource)
|
|
||||||
datasource._rev = response.rev
|
|
||||||
|
|
||||||
ctx.body = datasource
|
|
||||||
ctx.message = `Query ${query.name} saved successfully.`
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.previewQuery = async function(ctx) {
|
|
||||||
const { type, config, query } = ctx.request.body
|
|
||||||
|
|
||||||
const Integration = integrations[type]
|
|
||||||
|
|
||||||
if (!Integration) {
|
|
||||||
ctx.throw(400, "Integration type does not exist.")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.body = await new Integration(config, query).query()
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.fetchQuery = 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
|
|
||||||
}
|
|
||||||
|
|
||||||
const rows = await new Integration(
|
|
||||||
datasource.config,
|
|
||||||
query.queryString
|
|
||||||
).query()
|
|
||||||
|
|
||||||
ctx.body = {
|
|
||||||
schema: query.schema,
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,39 +1,128 @@
|
||||||
// const CouchDB = require("../../../db")
|
const handlebars = require("handlebars")
|
||||||
// const { generateQueryID } = require("../../db/utils")
|
const Joi = require("joi")
|
||||||
// const viewTemplate = require("./viewBuilder")
|
const CouchDB = require("../../db")
|
||||||
|
const bcrypt = require("../../utilities/bcrypt")
|
||||||
|
const { generateQueryID, getQueryParams } = require("../../db/utils")
|
||||||
|
const { integrations } = require("../../integrations")
|
||||||
|
const joiValidator = require("../../middleware/joi-validator")
|
||||||
|
|
||||||
// exports.save = async ctx => {
|
function generateQueryValidation() {
|
||||||
// const db = new CouchDB(ctx.user.appId)
|
// prettier-ignore
|
||||||
// const { datasourceId, query } = ctx.request.body
|
return joiValidator.body(Joi.object({
|
||||||
|
name: Joi.string().required(),
|
||||||
|
queryString: Joi.string().required(),
|
||||||
|
datasourceId: Joi.string().required(),
|
||||||
|
queryType: Joi.string().required(),
|
||||||
|
schema: Joi.object({}).required().unknown(true)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
// const datasource = await db.get(datasourceId)
|
exports.fetch = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
|
||||||
// const queryId = generateQueryID()
|
const body = await db.allDocs(
|
||||||
|
getQueryParams(null, {
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
ctx.body = body.rows.map(row => row.doc)
|
||||||
|
}
|
||||||
|
|
||||||
// datasource.queries[queryId] = query
|
exports.save = async function(ctx) {
|
||||||
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
const query = ctx.request.body
|
||||||
|
|
||||||
// const response = await db.put(datasource)
|
//
|
||||||
|
// {
|
||||||
// ctx.body = query
|
// type: "",
|
||||||
// ctx.message = `View ${viewToSave.name} saved successfully.`
|
// query: "",
|
||||||
|
// otherStuff: ""
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// exports.destroy = async ctx => {
|
if (!query._id) {
|
||||||
// const db = new CouchDB(ctx.user.appId)
|
query._id = generateQueryID(query.datasourceId)
|
||||||
// const designDoc = await db.get("_design/database")
|
}
|
||||||
|
|
||||||
// const viewName = decodeURI(ctx.params.viewName)
|
const response = await db.put(query)
|
||||||
|
query._rev = response.rev
|
||||||
|
|
||||||
// const view = designDoc.views[viewName]
|
ctx.body = query
|
||||||
|
ctx.message = `Query ${query.name} saved successfully.`
|
||||||
|
}
|
||||||
|
|
||||||
// delete designDoc.views[viewName]
|
exports.preview = async function(ctx) {
|
||||||
|
const { query, datasourceId } = ctx.request.body
|
||||||
|
|
||||||
// await db.put(designDoc)
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
|
||||||
// const table = await db.get(view.meta.tableId)
|
const datasource = await db.get(datasourceId)
|
||||||
// delete table.views[viewName]
|
|
||||||
// await db.put(table)
|
|
||||||
|
|
||||||
// ctx.body = view
|
const Integration = integrations[datasource.source]
|
||||||
// ctx.message = `View ${ctx.params.viewName} saved successfully.`
|
|
||||||
// }
|
if (!Integration) {
|
||||||
|
ctx.throw(400, "Integration type does not exist.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = await new Integration(datasource.config, query).query()
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.execute = 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 queryTemplate = handlebars.compile(query.queryString)
|
||||||
|
|
||||||
|
const response = await new Integration(
|
||||||
|
datasource.config,
|
||||||
|
queryTemplate({
|
||||||
|
// pass the params here from the UI and backend contexts
|
||||||
|
})
|
||||||
|
).query()
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const db = new CouchDB(ctx.user.appId)
|
||||||
|
await db.destroy(ctx.params.queryId)
|
||||||
|
ctx.message = `Query deleted.`
|
||||||
|
ctx.status = 200
|
||||||
|
}
|
||||||
|
|
|
@ -17,26 +17,26 @@ router
|
||||||
datasourceController.find
|
datasourceController.find
|
||||||
)
|
)
|
||||||
.post("/api/datasources", authorized(BUILDER), datasourceController.save)
|
.post("/api/datasources", authorized(BUILDER), datasourceController.save)
|
||||||
.post(
|
// .post(
|
||||||
"/api/datasources/:datasourceId/queries",
|
// "/api/datasources/:datasourceId/queries",
|
||||||
authorized(BUILDER),
|
// authorized(BUILDER),
|
||||||
datasourceController.saveQuery
|
// datasourceController.saveQuery
|
||||||
)
|
// )
|
||||||
.post(
|
// .post(
|
||||||
"/api/datasources/queries/preview",
|
// "/api/datasources/queries/preview",
|
||||||
authorized(BUILDER),
|
// authorized(BUILDER),
|
||||||
datasourceController.previewQuery
|
// datasourceController.previewQuery
|
||||||
)
|
// )
|
||||||
.get(
|
// .get(
|
||||||
"/api/datasources/:datasourceId/queries/:queryId",
|
// "/api/datasources/:datasourceId/queries/:queryId",
|
||||||
authorized(BUILDER),
|
// authorized(BUILDER),
|
||||||
datasourceController.fetchQuery
|
// datasourceController.fetchQuery
|
||||||
)
|
// )
|
||||||
.post(
|
// .post(
|
||||||
"/api/datasources/:datasourceId/queries/:queryId",
|
// "/api/datasources/:datasourceId/queries/:queryId",
|
||||||
authorized(BUILDER),
|
// authorized(BUILDER),
|
||||||
datasourceController.executeQuery
|
// datasourceController.executeQuery
|
||||||
)
|
// )
|
||||||
.delete(
|
.delete(
|
||||||
"/api/datasources/:datasourceId/:revId",
|
"/api/datasources/:datasourceId/:revId",
|
||||||
authorized(BUILDER),
|
authorized(BUILDER),
|
||||||
|
|
|
@ -19,7 +19,7 @@ const routingRoutes = require("./routing")
|
||||||
const integrationRoutes = require("./integration")
|
const integrationRoutes = require("./integration")
|
||||||
const permissionRoutes = require("./permission")
|
const permissionRoutes = require("./permission")
|
||||||
const datasourceRoutes = require("./datasource")
|
const datasourceRoutes = require("./datasource")
|
||||||
// const queryRoutes = require("./query")
|
const queryRoutes = require("./query")
|
||||||
|
|
||||||
exports.mainRoutes = [
|
exports.mainRoutes = [
|
||||||
deployRoutes,
|
deployRoutes,
|
||||||
|
@ -39,7 +39,7 @@ exports.mainRoutes = [
|
||||||
integrationRoutes,
|
integrationRoutes,
|
||||||
permissionRoutes,
|
permissionRoutes,
|
||||||
datasourceRoutes,
|
datasourceRoutes,
|
||||||
// queryRoutes,
|
queryRoutes,
|
||||||
// these need to be handled last as they still use /api/:tableId
|
// these need to be handled last as they still use /api/:tableId
|
||||||
// this could be breaking as koa may recognise other routes as this
|
// this could be breaking as koa may recognise other routes as this
|
||||||
tableRoutes,
|
tableRoutes,
|
||||||
|
|
|
@ -1,28 +1,17 @@
|
||||||
// const Router = require("@koa/router")
|
const Router = require("@koa/router")
|
||||||
// const queryController = require("../controllers/query")
|
const queryController = require("../controllers/query")
|
||||||
// const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized")
|
||||||
// const { BUILDER } = require("../../utilities/security/permissions")
|
const { BUILDER } = require("../../utilities/security/permissions")
|
||||||
|
|
||||||
// const router = Router()
|
const router = Router()
|
||||||
|
|
||||||
// // TODO: send down the datasource ID as well
|
// TODO: sort out auth so apps have the right permissions
|
||||||
|
router
|
||||||
|
.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/preview", authorized(BUILDER), queryController.preview)
|
||||||
|
.post("/api/queries/:queryId", authorized(BUILDER), queryController.execute)
|
||||||
|
.delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
|
||||||
|
|
||||||
// router
|
module.exports = router
|
||||||
// // .get("/api/queries", authorized(BUILDER), queryController.fetch)
|
|
||||||
// // .get(
|
|
||||||
// // "/api/datasources/:datasourceId/queries/:id",
|
|
||||||
// // authorized(PermissionTypes.TABLE, PermissionLevels.READ),
|
|
||||||
// // queryController.find
|
|
||||||
// // )
|
|
||||||
// .post(
|
|
||||||
// "/api/datasources/:datasourceId/queries",
|
|
||||||
// authorized(BUILDER),
|
|
||||||
// queryController.save
|
|
||||||
// )
|
|
||||||
// .delete(
|
|
||||||
// "/api/datasources/:datasourceId/queries/:queryId/:revId",
|
|
||||||
// authorized(BUILDER),
|
|
||||||
// queryController.destroy
|
|
||||||
// )
|
|
||||||
|
|
||||||
// module.exports = router
|
|
||||||
|
|
|
@ -38,6 +38,6 @@ function replicateLocal() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// replicateLocal()
|
replicateLocal()
|
||||||
|
|
||||||
module.exports = Pouch
|
module.exports = Pouch
|
||||||
|
|
|
@ -245,8 +245,10 @@ exports.getDatasourceParams = (datasourceId = null, otherProps = {}) => {
|
||||||
* Generates a new query ID.
|
* Generates a new query ID.
|
||||||
* @returns {string} The new query ID which the query doc can be stored under.
|
* @returns {string} The new query ID which the query doc can be stored under.
|
||||||
*/
|
*/
|
||||||
exports.generateQueryID = () => {
|
exports.generateQueryID = datasourceId => {
|
||||||
return `${DocumentTypes.QUERY}${SEPARATOR}${newid()}`
|
return `${
|
||||||
|
DocumentTypes.QUERY
|
||||||
|
}${SEPARATOR}${datasourceId}${SEPARATOR}${newid()}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,6 +14,7 @@ const PermissionTypes = {
|
||||||
WEBHOOK: "webhook",
|
WEBHOOK: "webhook",
|
||||||
BUILDER: "builder",
|
BUILDER: "builder",
|
||||||
VIEW: "view",
|
VIEW: "view",
|
||||||
|
QUERY: "query",
|
||||||
}
|
}
|
||||||
|
|
||||||
function Permission(type, level) {
|
function Permission(type, level) {
|
||||||
|
|
Loading…
Reference in New Issue