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