|
|
|
@ -14,33 +14,40 @@ |
|
|
|
import api from "builderStore/api" |
|
|
|
import { FIELDS } from "constants/backend" |
|
|
|
import IntegrationQueryEditor from "components/integration/index.svelte" |
|
|
|
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte" |
|
|
|
import { backendUiStore } from "builderStore" |
|
|
|
|
|
|
|
const PREVIEW_HEADINGS = [ |
|
|
|
{ |
|
|
|
title: "Preview", |
|
|
|
key: "PREVIEW", |
|
|
|
title: "JSON", |
|
|
|
key: "JSON", |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: "Schema", |
|
|
|
key: "SCHEMA", |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: "Results", |
|
|
|
key: "RESULTS", |
|
|
|
}, |
|
|
|
{ |
|
|
|
title: "Preview", |
|
|
|
key: "PREVIEW", |
|
|
|
}, |
|
|
|
] |
|
|
|
|
|
|
|
export let query |
|
|
|
export let fields = [] |
|
|
|
|
|
|
|
let config = {} |
|
|
|
let previewTab = "PREVIEW" |
|
|
|
let preview |
|
|
|
let tab = "JSON" |
|
|
|
let parameters |
|
|
|
let data |
|
|
|
|
|
|
|
$: datasource = $backendUiStore.datasources.find( |
|
|
|
ds => ds._id === query.datasourceId |
|
|
|
) |
|
|
|
|
|
|
|
$: query.datasourceId = |
|
|
|
query.datasourceId || $backendUiStore.selectedDatasourceId |
|
|
|
|
|
|
|
$: query.schema = fields.reduce( |
|
|
|
(acc, next) => ({ |
|
|
|
...acc, |
|
|
|
@ -52,6 +59,9 @@ |
|
|
|
{} |
|
|
|
) |
|
|
|
|
|
|
|
$: datasourceType = datasource.source |
|
|
|
$: datasourceType && fetchQueryConfig() |
|
|
|
|
|
|
|
function newField() { |
|
|
|
fields = [...fields, {}] |
|
|
|
} |
|
|
|
@ -76,17 +86,18 @@ |
|
|
|
async function previewQuery() { |
|
|
|
try { |
|
|
|
const response = await api.post(`/api/queries/preview`, { |
|
|
|
parameters: query.parameters, |
|
|
|
datasourceId: datasource._id, |
|
|
|
query: query.queryString, |
|
|
|
}) |
|
|
|
const json = await response.json() |
|
|
|
if (response.status !== 200) { |
|
|
|
throw new Error(json.message) |
|
|
|
} |
|
|
|
preview = json[0] || {} |
|
|
|
|
|
|
|
if (response.status !== 200) throw new Error(json.message) |
|
|
|
|
|
|
|
data = json || [] |
|
|
|
|
|
|
|
// TODO: refactor |
|
|
|
fields = Object.keys(preview).map(field => ({ |
|
|
|
fields = Object.keys(json[0]).map(field => ({ |
|
|
|
name: field, |
|
|
|
type: "STRING", |
|
|
|
})) |
|
|
|
@ -96,15 +107,21 @@ |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
onMount(() => { |
|
|
|
fetchQueryConfig() |
|
|
|
}) |
|
|
|
async function saveQuery() { |
|
|
|
try { |
|
|
|
await backendUiStore.actions.queries.save(query.datasourceId, query) |
|
|
|
notifier.success(`Query created successfully.`) |
|
|
|
} catch (err) { |
|
|
|
console.error(err) |
|
|
|
notifier.danger(`Error creating query. ${err.message}`) |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|
|
|
|
|
<section> |
|
|
|
<div class="config"> |
|
|
|
<Label extraSmall grey>Query Name</Label> |
|
|
|
<Input type="text" thin bind:value={query.name} /> |
|
|
|
<Input thin bind:value={query.name} /> |
|
|
|
|
|
|
|
<Spacer medium /> |
|
|
|
|
|
|
|
@ -118,40 +135,43 @@ |
|
|
|
|
|
|
|
<Spacer medium /> |
|
|
|
|
|
|
|
<IntegrationQueryEditor |
|
|
|
type={query.queryType} |
|
|
|
bind:query={query.queryString} /> |
|
|
|
|
|
|
|
<Spacer small /> |
|
|
|
|
|
|
|
<Button thin secondary on:click={previewQuery}>Preview Query</Button> |
|
|
|
|
|
|
|
<Spacer small /> |
|
|
|
|
|
|
|
{#if preview} |
|
|
|
<Switcher headings={PREVIEW_HEADINGS} bind:value={previewTab}> |
|
|
|
{#if previewTab === 'PREVIEW'} |
|
|
|
<pre class="preview">{JSON.stringify(preview, undefined, 2)}</pre> |
|
|
|
{:else if previewTab === 'SCHEMA'} |
|
|
|
{#each fields as field, idx} |
|
|
|
<div class="field"> |
|
|
|
<Input thin type={'text'} bind:value={field.name} /> |
|
|
|
<Select secondary thin bind:value={field.type}> |
|
|
|
<option value={''}>Select an option</option> |
|
|
|
<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} |
|
|
|
<Button thin secondary on:click={newField}>Add Field</Button> |
|
|
|
{/if} |
|
|
|
</Switcher> |
|
|
|
{/if} |
|
|
|
<IntegrationQueryEditor {query} bind:parameters /> |
|
|
|
|
|
|
|
<Spacer medium /> |
|
|
|
|
|
|
|
<section class="viewer"> |
|
|
|
<div class="viewer-controls"> |
|
|
|
<Button wide thin blue on:click={previewQuery}>Run</Button> |
|
|
|
<Button wide thin primary on:click={saveQuery}>Save</Button> |
|
|
|
</div> |
|
|
|
|
|
|
|
{#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} |
|
|
|
<div class="field"> |
|
|
|
<Input thin type={'text'} bind:value={field.name} /> |
|
|
|
<Select secondary thin bind:value={field.type}> |
|
|
|
<option value={''}>Select an option</option> |
|
|
|
<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} |
|
|
|
<Button thin secondary on:click={newField}>Add Field</Button> |
|
|
|
{/if} |
|
|
|
</Switcher> |
|
|
|
{/if} |
|
|
|
</section> |
|
|
|
</div> |
|
|
|
</section> |
|
|
|
|
|
|
|
@ -163,17 +183,6 @@ |
|
|
|
margin-bottom: var(--spacing-m); |
|
|
|
} |
|
|
|
|
|
|
|
h6 { |
|
|
|
font-family: var(--font-sans); |
|
|
|
font-weight: 600; |
|
|
|
text-rendering: var(--text-render); |
|
|
|
color: var(--ink); |
|
|
|
font-size: var(--heading-font-size-xs); |
|
|
|
color: var(--ink); |
|
|
|
margin-bottom: var(--spacing-xs); |
|
|
|
margin-top: var(--spacing-xs); |
|
|
|
} |
|
|
|
|
|
|
|
.config { |
|
|
|
margin-bottom: var(--spacing-s); |
|
|
|
} |
|
|
|
@ -185,7 +194,21 @@ |
|
|
|
|
|
|
|
.preview { |
|
|
|
width: 800px; |
|
|
|
height: 100%; |
|
|
|
overflow-y: auto; |
|
|
|
overflow-wrap: break-word; |
|
|
|
white-space: pre-wrap; |
|
|
|
} |
|
|
|
|
|
|
|
.viewer { |
|
|
|
position: relative; |
|
|
|
} |
|
|
|
|
|
|
|
.viewer-controls { |
|
|
|
position: absolute; |
|
|
|
right: 0; |
|
|
|
display: grid; |
|
|
|
grid-gap: var(--spacing-m); |
|
|
|
grid-auto-flow: column; |
|
|
|
} |
|
|
|
</style> |