1
0
Fork 0
mirror of synced 2024-08-23 14:01:34 +12:00
budibase/packages/builder/src/components/integration/QueryViewer.svelte

292 lines
6.7 KiB
Svelte
Raw Normal View History

2020-12-19 07:19:43 +13:00
<script>
import { onMount } from "svelte"
2021-01-15 03:22:24 +13:00
import { goto } from "@sveltech/routify"
2020-12-19 07:19:43 +13:00
import {
Select,
Button,
Label,
Input,
TextArea,
Heading,
Spacer,
Switcher,
} from "@budibase/bbui"
import { notifier } from "builderStore/store/notifications"
import api from "builderStore/api"
import { FIELDS } from "constants/backend"
import IntegrationQueryEditor from "components/integration/index.svelte"
2021-01-08 02:13:46 +13:00
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
import EditQueryParamsPopover from "components/backend/DatasourceNavigator/popovers/EditQueryParamsPopover.svelte"
2020-12-19 07:19:43 +13:00
import { backendUiStore } from "builderStore"
const PREVIEW_HEADINGS = [
{
2021-01-08 02:13:46 +13:00
title: "JSON",
key: "JSON",
2020-12-19 07:19:43 +13:00
},
{
title: "Schema",
key: "SCHEMA",
},
2021-01-08 02:13:46 +13:00
{
title: "Preview",
key: "PREVIEW",
},
2020-12-19 07:19:43 +13:00
]
export let query
export let fields = []
2021-01-14 03:11:53 +13:00
let config
2021-01-08 02:13:46 +13:00
let tab = "JSON"
let parameters
2021-01-16 06:29:46 +13:00
let data = []
let popover
2020-12-19 07:19:43 +13:00
2021-01-07 01:28:51 +13:00
$: datasource = $backendUiStore.datasources.find(
ds => ds._id === query.datasourceId
)
2020-12-19 07:19:43 +13:00
$: query.schema = fields.reduce(
(acc, next) => ({
...acc,
[next.name]: {
name: next.name,
type: "string",
},
}),
{}
)
2021-01-15 03:22:24 +13:00
$: datasourceType = datasource?.source
2021-01-16 02:42:55 +13:00
2021-01-15 03:22:24 +13:00
$: config = $backendUiStore.integrations[datasourceType]?.query
2021-01-19 06:01:41 +13:00
$: docsLink = $backendUiStore.integrations[datasourceType]?.docs
2021-01-08 02:13:46 +13:00
$: shouldShowQueryConfig = config && query.queryVerb
2021-01-14 03:11:53 +13:00
2020-12-19 07:19:43 +13:00
function newField() {
fields = [...fields, {}]
}
function deleteField(idx) {
fields.splice(idx, 1)
fields = fields
}
async function previewQuery() {
try {
2021-01-07 01:28:51 +13:00
const response = await api.post(`/api/queries/preview`, {
2021-01-14 03:11:53 +13:00
fields: query.fields,
2021-01-13 05:49:11 +13:00
queryVerb: query.queryVerb,
parameters: query.parameters.reduce(
(acc, next) => ({
...acc,
[next.name]: next.default,
}),
{}
),
2021-01-07 01:28:51 +13:00
datasourceId: datasource._id,
2020-12-19 07:19:43 +13:00
})
const json = await response.json()
2021-01-08 02:13:46 +13:00
if (response.status !== 200) throw new Error(json.message)
data = json || []
2020-12-19 07:19:43 +13:00
2021-01-16 06:29:46 +13:00
if (data.length === 0) {
2021-01-19 04:40:26 +13:00
notifier.info(
"Query results empty. Please execute a query with results to create your schema."
)
2021-01-16 06:29:46 +13:00
return
2021-01-19 04:40:26 +13:00
}
2021-01-16 06:29:46 +13:00
notifier.success("Query executed successfully.")
// Assume all the fields are strings and create a basic schema
// from the first record returned by the query
2021-01-08 02:13:46 +13:00
fields = Object.keys(json[0]).map(field => ({
2020-12-19 07:19:43 +13:00
name: field,
type: "STRING",
}))
} catch (err) {
notifier.danger(`Query Error: ${err.message}`)
console.error(err)
}
}
2021-01-08 02:13:46 +13:00
async function saveQuery() {
try {
2021-01-15 03:22:24 +13:00
const { _id } = await backendUiStore.actions.queries.save(
query.datasourceId,
query
)
2021-01-12 09:17:56 +13:00
notifier.success(`Query saved successfully.`)
2021-01-15 03:22:24 +13:00
$goto(`../../${_id}`)
2021-01-08 02:13:46 +13:00
} catch (err) {
console.error(err)
notifier.danger(`Error creating query. ${err.message}`)
}
}
2020-12-19 07:19:43 +13:00
</script>
2021-01-13 05:49:11 +13:00
<header>
<div class="input">
<Input placeholder="✎ Edit Query Name" bind:value={query.name} />
</div>
2021-01-14 05:39:47 +13:00
{#if config}
2021-01-28 06:29:30 +13:00
<div class="props">
<div class="query-type">
Query type:
<span class="query-type-span">{config[query.queryVerb].type}</span>
</div>
<div class="select">
<Select primary thin bind:value={query.queryVerb}>
{#each Object.keys(config) as queryVerb}
<option value={queryVerb}>{queryVerb}</option>
{/each}
</Select>
</div>
</div>
2021-01-28 06:29:30 +13:00
<EditQueryParamsPopover
bind:parameters={query.parameters}
bindable={false} />
2021-01-14 03:11:53 +13:00
{/if}
2021-01-13 05:49:11 +13:00
</header>
<Spacer extraLarge />
2020-12-19 07:19:43 +13:00
2021-01-14 03:11:53 +13:00
{#if shouldShowQueryConfig}
2021-01-13 05:49:11 +13:00
<section>
<div class="config">
2021-01-14 03:11:53 +13:00
<IntegrationQueryEditor
{query}
schema={config[query.queryVerb]}
2021-01-16 02:42:55 +13:00
bind:parameters />
2021-01-08 02:13:46 +13:00
<Spacer extraLarge />
<Spacer large />
2021-01-08 02:13:46 +13:00
2021-01-13 05:49:11 +13:00
<div class="viewer-controls">
2021-01-19 04:40:26 +13:00
<Button
blue
2021-01-27 04:23:23 +13:00
disabled={data.length === 0 || !query.name}
2021-01-19 04:40:26 +13:00
on:click={saveQuery}>
Save Query
2021-01-13 05:49:11 +13:00
</Button>
<Button primary on:click={previewQuery}>Run Query</Button>
2021-01-13 05:49:11 +13:00
</div>
2021-01-08 02:13:46 +13:00
2021-01-13 05:49:11 +13:00
<section class="viewer">
{#if data}
<Switcher headings={PREVIEW_HEADINGS} bind:value={tab}>
{#if tab === 'JSON'}
<pre class="preview">{JSON.stringify(data[0], undefined, 2)}</pre>
{:else if tab === 'PREVIEW'}
<ExternalDataSourceTable {query} {data} />
{:else if tab === 'SCHEMA'}
{#each fields as field, idx}
<Spacer small />
2021-01-13 05:49:11 +13:00
<div class="field">
2021-01-28 06:29:30 +13:00
<Input
outline
placeholder="Field Name"
type={'text'}
bind:value={field.name} />
<Select thin border bind:value={field.type}>
<option value={''}>Select a field type</option>
2021-01-13 05:49:11 +13:00
<option value={'STRING'}>Text</option>
<option value={'NUMBER'}>Number</option>
<option value={'BOOLEAN'}>Boolean</option>
<option value={'DATETIME'}>Datetime</option>
</Select>
<i
class="ri-close-circle-line delete"
on:click={() => deleteField(idx)} />
</div>
{/each}
2021-01-28 06:29:30 +13:00
<Spacer small />
<Button thin secondary on:click={newField}>Add Field</Button>
2021-01-13 05:49:11 +13:00
{/if}
</Switcher>
{/if}
</section>
</div>
</section>
{/if}
2020-12-19 07:19:43 +13:00
<style>
.input {
width: 300px;
}
.select {
width: 200px;
margin-right: 40px;
}
.props {
display: flex;
flex-direction: row;
margin-left: auto;
align-items: center;
gap: var(--layout-l);
}
2020-12-19 07:19:43 +13:00
.field {
display: grid;
grid-template-columns: 1fr 1fr 50px;
gap: var(--spacing-l);
2020-12-19 07:19:43 +13:00
}
2021-01-19 06:01:41 +13:00
a {
font-size: var(--font-size-s);
}
2020-12-19 07:19:43 +13:00
.config {
margin-bottom: var(--spacing-s);
}
.delete {
align-self: center;
cursor: pointer;
}
.query-type {
font-family: var(--font-sans);
color: var(--grey-8);
font-size: var(--font-size-s);
}
.query-type-span {
text-transform: uppercase;
}
2020-12-19 07:19:43 +13:00
.preview {
width: 800px;
2021-01-08 02:13:46 +13:00
height: 100%;
overflow-y: auto;
2020-12-19 07:19:43 +13:00
overflow-wrap: break-word;
white-space: pre-wrap;
}
2021-01-08 02:13:46 +13:00
2021-01-13 05:49:11 +13:00
header {
display: flex;
align-items: center;
}
.viewer-controls {
2021-01-13 05:49:11 +13:00
display: flex;
flex-direction: row;
margin-left: auto;
direction: rtl;
z-index: 5;
gap: var(--spacing-m);
min-width: 150px;
2021-01-08 02:13:46 +13:00
}
.viewer {
margin-top: -28px;
z-index: -2;
2021-01-08 02:13:46 +13:00
}
2020-12-19 07:19:43 +13:00
</style>