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

255 lines
6.4 KiB
Svelte
Raw Normal View History

2020-12-19 07:19:43 +13:00
<script>
2021-03-18 00:40:24 +13:00
import { goto } from "@roxi/routify"
2020-12-19 07:19:43 +13:00
import {
2021-04-23 21:55:27 +12:00
Icon,
2020-12-19 07:19:43 +13:00
Select,
Button,
2021-04-29 01:46:11 +12:00
ButtonGroup,
Body,
2020-12-19 07:19:43 +13:00
Label,
Layout,
2020-12-19 07:19:43 +13:00
Input,
Heading,
2021-04-21 23:41:44 +12:00
Tabs,
2021-04-23 21:55:27 +12:00
Tab,
2020-12-19 07:19:43 +13:00
} from "@budibase/bbui"
import { notifications, Divider } from "@budibase/bbui"
2020-12-19 07:19:43 +13:00
import api from "builderStore/api"
import IntegrationQueryEditor from "components/integration/index.svelte"
2021-01-08 02:13:46 +13:00
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
2021-04-01 22:29:47 +13:00
import { datasources, integrations, queries } from "stores/backend"
import { capitalise } from "../../helpers"
2020-12-19 07:19:43 +13:00
export let query
export let fields = []
2021-01-08 02:13:46 +13:00
let parameters
2021-01-16 06:29:46 +13:00
let data = []
const typeOptions = [
{ label: "Text", value: "STRING" },
{ label: "Number", value: "NUMBER" },
{ label: "Boolean", value: "BOOLEAN" },
{ label: "Datetime", value: "DATETIME" },
]
2020-12-19 07:19:43 +13:00
2021-04-23 21:55:27 +12:00
$: datasource = $datasources.list.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
$: integrationInfo = $integrations[datasourceType]
$: queryConfig = integrationInfo?.query
2021-02-19 07:55:08 +13:00
$: shouldShowQueryConfig = queryConfig && 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.rows || []
2020-12-19 07:19:43 +13:00
2021-01-16 06:29:46 +13:00
if (data.length === 0) {
notifications.info(
2021-01-19 04:40:26 +13:00
"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
notifications.success("Query executed successfully.")
2021-01-16 06:29:46 +13:00
// Assume all the fields are strings and create a basic schema from the
// unique fields returned by the server
2021-04-23 21:55:27 +12:00
fields = json.schemaFields.map((field) => ({
2020-12-19 07:19:43 +13:00
name: field,
type: "STRING",
}))
} catch (err) {
notifications.error(`Query Error: ${err.message}`)
2020-12-19 07:19:43 +13:00
console.error(err)
}
}
2021-01-08 02:13:46 +13:00
async function saveQuery() {
try {
2021-04-01 22:29:47 +13:00
const { _id } = await queries.save(query.datasourceId, query)
notifications.success(`Query saved successfully.`)
$goto(`../${_id}`)
2021-01-08 02:13:46 +13:00
} catch (err) {
console.error(err)
notifications.error(`Error creating query. ${err.message}`)
2021-01-08 02:13:46 +13:00
}
}
2020-12-19 07:19:43 +13:00
</script>
<Layout gap="S" noPadding>
<Heading size="M">Query {integrationInfo?.friendlyName}</Heading>
<Divider />
<Heading size="S">Config</Heading>
<div class="config">
<div class="config-field">
<Label>Query Name</Label>
<Input bind:value={query.name} />
</div>
{#if queryConfig}
<div class="config-field">
<Label>Function</Label>
<Select
bind:value={query.queryVerb}
options={Object.keys(queryConfig)}
getOptionLabel={(verb) =>
queryConfig[verb]?.displayName || capitalise(verb)}
/>
</div>
<ParameterBuilder bind:parameters={query.parameters} bindable={false} />
{/if}
</div>
{#if shouldShowQueryConfig}
<Divider />
2021-01-13 05:49:11 +13:00
<div class="config">
<Heading size="S">Fields</Heading>
<Body size="S">Fill in the fields specific to this query.</Body>
2021-01-14 03:11:53 +13:00
<IntegrationQueryEditor
2021-02-19 07:55:08 +13:00
{datasource}
2021-01-14 03:11:53 +13:00
{query}
height={300}
2021-02-19 07:55:08 +13:00
schema={queryConfig[query.queryVerb]}
2021-04-23 21:55:27 +12:00
bind:parameters
/>
<Divider />
</div>
<div class="viewer-controls">
<Heading size="S">Results</Heading>
2021-04-29 01:46:11 +12:00
<ButtonGroup>
<Button
2021-04-29 01:46:11 +12:00
cta
disabled={data.length === 0 || !query.name}
on:click={saveQuery}
>
Save Query
</Button>
2021-04-29 01:46:11 +12:00
<Button secondary on:click={previewQuery}>Run Query</Button>
</ButtonGroup>
</div>
<Body size="S">
2021-04-29 01:46:11 +12:00
Below, you can preview the results from your query and change the schema.
</Body>
<section class="viewer">
{#if data}
<Tabs selected="JSON">
<Tab title="JSON">
<pre
class="preview">
<!-- prettier-ignore -->
{#if !data[0]}
Please run your query to fetch some data.
{:else}
{JSON.stringify(data[0], undefined, 2)}
{/if}
2021-04-21 23:41:44 +12:00
</pre>
2021-04-29 01:46:11 +12:00
</Tab>
<Tab title="Schema">
<Layout gap="S">
2021-01-13 05:49:11 +13:00
{#each fields as field, idx}
<div class="field">
<Input placeholder="Field Name" bind:value={field.name} />
<Select bind:value={field.type} options={typeOptions} />
2021-04-23 21:55:27 +12:00
<Icon name="bleClose" on:click={() => deleteField(idx)} />
2021-01-13 05:49:11 +13:00
</div>
{/each}
2021-04-29 01:46:11 +12:00
<div>
<Button secondary on:click={newField}>Add Field</Button>
</div>
</Layout>
</Tab>
<Tab title="Preview">
<ExternalDataSourceTable {query} {data} />
</Tab>
</Tabs>
{/if}
</section>
{/if}
</Layout>
2020-12-19 07:19:43 +13:00
<style>
.config {
display: grid;
grid-gap: var(--spacing-s);
}
.config-field {
display: grid;
grid-template-columns: 20% 1fr;
grid-gap: var(--spacing-l);
align-items: center;
}
2020-12-19 07:19:43 +13:00
.field {
display: grid;
grid-template-columns: 1fr 1fr 5%;
gap: var(--spacing-l);
2020-12-19 07:19:43 +13:00
}
2021-02-20 01:07:37 +13:00
.button-container {
display: flex;
2021-01-19 06:01:41 +13:00
}
2020-12-19 07:19:43 +13:00
.delete {
align-self: center;
cursor: pointer;
}
.viewer {
min-height: 200px;
}
2020-12-19 07:19:43 +13:00
.preview {
2021-01-08 02:13:46 +13:00
height: 100%;
min-height: 120px;
2021-01-08 02:13:46 +13:00
overflow-y: auto;
2020-12-19 07:19:43 +13:00
overflow-wrap: break-word;
white-space: pre-wrap;
background-color: var(--grey-2);
padding: var(--spacing-m);
border-radius: 8px;
color: var(--ink);
2020-12-19 07:19:43 +13:00
}
2021-01-08 02:13:46 +13:00
.viewer-controls {
2021-01-13 05:49:11 +13:00
display: flex;
flex-direction: row;
2021-02-19 07:55:08 +13:00
justify-content: space-between;
gap: var(--spacing-m);
min-width: 150px;
2021-02-20 03:31:07 +13:00
align-items: center;
}
2020-12-19 07:19:43 +13:00
</style>