design updates, changing query verb names to HTTP verbs
This commit is contained in:
parent
6dfc4a4de5
commit
d99e1a5a53
|
@ -30,7 +30,9 @@
|
||||||
<div class="popover">
|
<div class="popover">
|
||||||
<h5>Who Can Access This Data?</h5>
|
<h5>Who Can Access This Data?</h5>
|
||||||
<div class="note">
|
<div class="note">
|
||||||
<Label extraSmall grey>Specify the minimum access level role for this data.</Label>
|
<Label extraSmall grey>
|
||||||
|
Specify the minimum access level role for this data.
|
||||||
|
</Label>
|
||||||
</div>
|
</div>
|
||||||
<Spacer large />
|
<Spacer large />
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
|
@ -1,15 +1,33 @@
|
||||||
<script>
|
<script>
|
||||||
import { Input, TextArea, Spacer } from "@budibase/bbui"
|
import { Label, Input, TextArea, Spacer } from "@budibase/bbui"
|
||||||
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
|
|
||||||
export let integration
|
export let integration
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form>
|
<form>
|
||||||
{#each Object.keys(integration) as configKey}
|
{#each Object.keys(integration) as configKey}
|
||||||
<Input
|
<div class="form-row">
|
||||||
type={integration[configKey].type}
|
{#if typeof integration[configKey] === 'object'}
|
||||||
label={configKey}
|
<Label small>{configKey}</Label>
|
||||||
bind:value={integration[configKey]} />
|
<KeyValueBuilder bind:object={integration[configKey]} />
|
||||||
<Spacer large />
|
{:else}
|
||||||
|
<Label small>{configKey}</Label>
|
||||||
|
<Input
|
||||||
|
outline
|
||||||
|
type={integration[configKey].type}
|
||||||
|
bind:value={integration[configKey]} />
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.form-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20% 1fr;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: var(--spacing-m);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
import { onMount } from "svelte"
|
import { onMount } from "svelte"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import api from "builderStore/api"
|
import api from "builderStore/api"
|
||||||
import { Input, TextArea, Spacer } from "@budibase/bbui"
|
import { Input, Label, TextArea, Spacer } from "@budibase/bbui"
|
||||||
|
import KeyValueBuilder from "components/integration/KeyValueBuilder.svelte"
|
||||||
import ICONS from "../icons"
|
import ICONS from "../icons"
|
||||||
|
|
||||||
export let integration = {}
|
export let integration = {}
|
||||||
|
@ -50,16 +51,21 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if schema}
|
<!-- {#if schema}
|
||||||
{#each Object.keys(schema) as configKey}
|
{#each Object.keys(schema) as configKey}
|
||||||
<Input
|
{#if schema[configKey].type === 'object'}
|
||||||
thin
|
<Label small>{configKey}</Label>
|
||||||
type={schema[configKey].type}
|
<KeyValueBuilder bind:object={integration[configKey]} />
|
||||||
label={configKey}
|
{:else}
|
||||||
bind:value={integration[configKey]} />
|
<Label small>{configKey}</Label>
|
||||||
|
<Input
|
||||||
|
outline
|
||||||
|
type={integration[configKey].type}
|
||||||
|
bind:value={integration[configKey]} />
|
||||||
|
{/if}
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if} -->
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
<script>
|
|
||||||
import { goto, params } from "@sveltech/routify"
|
|
||||||
import { backendUiStore, store } from "builderStore"
|
|
||||||
import { notifier } from "builderStore/store/notifications"
|
|
||||||
import { Input, Label, ModalContent, Button, Spacer } from "@budibase/bbui"
|
|
||||||
import TableIntegrationMenu from "../TableIntegrationMenu/index.svelte"
|
|
||||||
import analytics from "analytics"
|
|
||||||
|
|
||||||
let modal
|
|
||||||
let error = ""
|
|
||||||
|
|
||||||
let name
|
|
||||||
let source
|
|
||||||
let integration
|
|
||||||
let datasource
|
|
||||||
|
|
||||||
function checkValid(evt) {
|
|
||||||
const datasourceName = evt.target.value
|
|
||||||
if (
|
|
||||||
$backendUiStore.datasources?.some(
|
|
||||||
datasource => datasource.name === datasourceName
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
error = `Datasource with name ${tableName} already exists. Please choose another name.`
|
|
||||||
return
|
|
||||||
}
|
|
||||||
error = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveDatasource() {
|
|
||||||
const { type, ...config } = integration
|
|
||||||
|
|
||||||
// Create datasource
|
|
||||||
await backendUiStore.actions.datasources.save({
|
|
||||||
name,
|
|
||||||
source: type,
|
|
||||||
config,
|
|
||||||
})
|
|
||||||
notifier.success(`Datasource ${name} created successfully.`)
|
|
||||||
analytics.captureEvent("Datasource Created", { name })
|
|
||||||
|
|
||||||
// Navigate to new datasource
|
|
||||||
$goto(`./datasource/${datasource._id}`)
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ModalContent
|
|
||||||
title="Create Datasource"
|
|
||||||
confirmText="Create"
|
|
||||||
onConfirm={saveDatasource}
|
|
||||||
disabled={error || !name}>
|
|
||||||
<Input
|
|
||||||
data-cy="datasource-name-input"
|
|
||||||
thin
|
|
||||||
label="Datasource Name"
|
|
||||||
on:input={checkValid}
|
|
||||||
bind:value={name}
|
|
||||||
{error} />
|
|
||||||
<Label grey extraSmall>Create Integrated Table from External Source</Label>
|
|
||||||
<TableIntegrationMenu bind:integration />
|
|
||||||
</ModalContent>
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
|
|
||||||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
|
||||||
export let datasource
|
export let datasource
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<script>
|
<!--<script>
|
||||||
import { backendUiStore, store, allScreens } from "builderStore"
|
import { backendUiStore, store, allScreens } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui"
|
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
@ -17,9 +17,7 @@
|
||||||
function hideEditor() {
|
function hideEditor() {
|
||||||
dropdown?.hide()
|
dropdown?.hide()
|
||||||
}
|
}
|
||||||
</script>
|
</script><div on:click|stopPropagation bind:this={anchor}>
|
||||||
|
|
||||||
<div on:click|stopPropagation bind:this={anchor}>
|
|
||||||
<TextButton text on:click={dropdown.show} active={false}>
|
<TextButton text on:click={dropdown.show} active={false}>
|
||||||
<Icon name="add" />
|
<Icon name="add" />
|
||||||
Add Parameters
|
Add Parameters
|
||||||
|
@ -29,11 +27,9 @@
|
||||||
<ParameterBuilder bind:parameters {bindable} />
|
<ParameterBuilder bind:parameters {bindable} />
|
||||||
</div>
|
</div>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div><style>
|
||||||
|
|
||||||
<style>
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
padding: var(--spacing-xl);
|
padding: var(--spacing-xl);
|
||||||
min-width: 600px;
|
min-width: 600px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>-->
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
import { DropdownMenu, Button, Input } from "@budibase/bbui"
|
||||||
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
|
|
||||||
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
|
||||||
export let query
|
export let query
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
export let object = {}
|
export let object = {}
|
||||||
|
|
||||||
let fields = []
|
let fields = Object.entries(object).map(([name, value]) => ({ name, value }))
|
||||||
|
|
||||||
$: object = fields.reduce(
|
$: object = fields.reduce(
|
||||||
(acc, next) => ({ ...acc, [next.name]: next.value }),
|
(acc, next) => ({ ...acc, [next.name]: next.value }),
|
||||||
|
|
|
@ -22,35 +22,36 @@
|
||||||
function updateCustomFields({ detail }) {
|
function updateCustomFields({ detail }) {
|
||||||
fields.customData = detail.value
|
fields.customData = detail.value
|
||||||
}
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form on:submit|preventDefault>
|
<form on:submit|preventDefault>
|
||||||
<div class="field">
|
<div class="field">
|
||||||
{#each schemaKeys as field}
|
{#each schemaKeys as field}
|
||||||
{#if schema.fields[field]?.type === "object"}
|
{#if schema.fields[field]?.type === 'object'}
|
||||||
<div>
|
<div>
|
||||||
<Label extraSmall grey>{field}</Label>
|
<Label extraSmall grey>{field}</Label>
|
||||||
<KeyValueBuilder bind:object={fields[field]} />
|
<KeyValueBuilder bind:object={fields[field]} />
|
||||||
</div>
|
</div>
|
||||||
{:else if schema.fields[field]?.type === "json"}
|
{:else if schema.fields[field]?.type === 'json'}
|
||||||
<div>
|
<div>
|
||||||
<Label extraSmall grey>{field}</Label>
|
<Label extraSmall grey>{field}</Label>
|
||||||
<Editor
|
<Editor
|
||||||
mode="json"
|
mode="json"
|
||||||
on:change={({ detail }) => fields[field] = detail.value}
|
on:change={({ detail }) => (fields[field] = detail.value)}
|
||||||
readOnly={!editable}
|
readOnly={!editable}
|
||||||
value={fields[field]} />
|
value={fields[field]} />
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<Input
|
<div class="horizontal">
|
||||||
label={field}
|
<Label small>{field}</Label>
|
||||||
placeholder="Enter {field} name"
|
<Input
|
||||||
outline
|
placeholder="Enter {field} name"
|
||||||
disabled={!editable}
|
outline
|
||||||
type={schema.fields[field]?.type}
|
disabled={!editable}
|
||||||
required={schema.fields[field]?.required}
|
type={schema.fields[field]?.type}
|
||||||
bind:value={fields[field]} />
|
required={schema.fields[field]?.required}
|
||||||
|
bind:value={fields[field]} />
|
||||||
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
@ -73,4 +74,10 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.horizontal {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 20% 1fr;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button, Input, Heading, Spacer } from "@budibase/bbui"
|
import { Body, Button, Input, Heading, Spacer } from "@budibase/bbui"
|
||||||
import BindableInput from "components/common/BindableInput.svelte"
|
import BindableInput from "components/common/BindableInput.svelte"
|
||||||
import {
|
import {
|
||||||
readableToRuntimeBinding,
|
readableToRuntimeBinding,
|
||||||
|
@ -30,7 +30,16 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<Heading extraSmall black>Parameters</Heading>
|
<div class="controls">
|
||||||
|
<Heading small>Parameters</Heading>
|
||||||
|
{#if !bindable}
|
||||||
|
<Button secondary on:click={newQueryParameter}>Add Param</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Body small grey>
|
||||||
|
Parameters come in two parts: the parameter name, and a default/fallback
|
||||||
|
value.
|
||||||
|
</Body>
|
||||||
<Spacer large />
|
<Spacer large />
|
||||||
<div class="parameters" class:bindable>
|
<div class="parameters" class:bindable>
|
||||||
{#each parameters as parameter, idx}
|
{#each parameters as parameter, idx}
|
||||||
|
@ -59,9 +68,6 @@
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if !bindable}
|
|
||||||
<Button secondary on:click={newQueryParameter}>Add Parameter</Button>
|
|
||||||
{/if}
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -69,6 +75,11 @@
|
||||||
grid-template-columns: 1fr 1fr 1fr;
|
grid-template-columns: 1fr 1fr 1fr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
.parameters {
|
.parameters {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 5%;
|
grid-template-columns: 1fr 1fr 5%;
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
Button,
|
Button,
|
||||||
|
Body,
|
||||||
Label,
|
Label,
|
||||||
Input,
|
Input,
|
||||||
TextArea,
|
TextArea,
|
||||||
|
@ -16,7 +17,7 @@
|
||||||
import { FIELDS } from "constants/backend"
|
import { FIELDS } from "constants/backend"
|
||||||
import IntegrationQueryEditor from "components/integration/index.svelte"
|
import IntegrationQueryEditor from "components/integration/index.svelte"
|
||||||
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
|
||||||
import EditQueryParamsPopover from "components/backend/DatasourceNavigator/popovers/EditQueryParamsPopover.svelte"
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
|
||||||
const PREVIEW_HEADINGS = [
|
const PREVIEW_HEADINGS = [
|
||||||
|
@ -60,8 +61,8 @@
|
||||||
|
|
||||||
$: datasourceType = datasource?.source
|
$: datasourceType = datasource?.source
|
||||||
|
|
||||||
$: config = $backendUiStore.integrations[datasourceType]?.query
|
$: integrationInfo = $backendUiStore.integrations[datasourceType]
|
||||||
$: docsLink = $backendUiStore.integrations[datasourceType]?.docs
|
$: queryConfig = integrationInfo?.query
|
||||||
|
|
||||||
$: shouldShowQueryConfig = config && query.queryVerb
|
$: shouldShowQueryConfig = config && query.queryVerb
|
||||||
|
|
||||||
|
@ -130,63 +131,99 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<section class="config">
|
||||||
<div class="input">
|
<Heading medium>Query {integrationInfo.friendlyName}</Heading>
|
||||||
|
<hr />
|
||||||
|
<Spacer medium />
|
||||||
|
<Heading small>Config</Heading>
|
||||||
|
<Spacer medium />
|
||||||
|
<Body small grey>Provide a name for your query and select its function.</Body>
|
||||||
|
<Spacer medium />
|
||||||
|
<div class="config-field">
|
||||||
|
<Label small>Query Name</Label>
|
||||||
|
<Input thin bind:value={query.name} />
|
||||||
|
</div>
|
||||||
|
<Spacer medium />
|
||||||
|
{#if queryConfig}
|
||||||
|
<div class="config-field">
|
||||||
|
<Label small>Function</Label>
|
||||||
|
<Select primary outline thin bind:value={query.queryVerb}>
|
||||||
|
{#each Object.keys(queryConfig) as queryVerb}
|
||||||
|
<option value={queryVerb}>{queryConfig[queryVerb]?.displayName || queryVerb}</option>
|
||||||
|
{/each}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
<Spacer medium />
|
||||||
|
<hr />
|
||||||
|
<ParameterBuilder bind:parameters={query.parameters} bindable={false} />
|
||||||
|
<hr />
|
||||||
|
{/if}
|
||||||
|
<!-- <div class="input">
|
||||||
<div class="label">Enter query name:</div>
|
<div class="label">Enter query name:</div>
|
||||||
<Input outline border bind:value={query.name} />
|
<Input outline border bind:value={query.name} />
|
||||||
</div>
|
</div> -->
|
||||||
{#if config}
|
<!-- {#if config} -->
|
||||||
<div class="props">
|
<!-- <div class="props"> -->
|
||||||
<div class="query-type">
|
<!-- <div class="query-type">
|
||||||
Query type:
|
Query type:
|
||||||
<span class="query-type-span">{config[query.queryVerb].type}</span>
|
<span class="query-type-span">{config[query.queryVerb].type}</span>
|
||||||
</div>
|
</div> -->
|
||||||
<div class="select">
|
<!-- <div class="select">
|
||||||
<Select primary thin bind:value={query.queryVerb}>
|
<Select primary thin bind:value={query.queryVerb}>
|
||||||
{#each Object.keys(config) as queryVerb}
|
{#each Object.keys(config) as queryVerb}
|
||||||
<option value={queryVerb}>{queryVerb}</option>
|
<option value={queryVerb}>{queryVerb}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
<!-- </div>
|
||||||
<EditQueryParamsPopover
|
<EditQueryParamsPopover
|
||||||
bind:parameters={query.parameters}
|
bind:parameters={query.parameters}
|
||||||
bindable={false} />
|
bindable={false} /> -->
|
||||||
{/if}
|
<!-- {/if} -->
|
||||||
</header>
|
</section>
|
||||||
<Spacer extraLarge />
|
<Spacer large />
|
||||||
|
|
||||||
{#if shouldShowQueryConfig}
|
{#if shouldShowQueryConfig}
|
||||||
<section>
|
<section>
|
||||||
<div class="config">
|
<div class="config">
|
||||||
|
<Heading small>Fields</Heading>
|
||||||
|
<Spacer medium />
|
||||||
<IntegrationQueryEditor
|
<IntegrationQueryEditor
|
||||||
{query}
|
{query}
|
||||||
schema={config[query.queryVerb]}
|
schema={config[query.queryVerb]}
|
||||||
bind:parameters />
|
bind:parameters />
|
||||||
|
|
||||||
<Spacer extraLarge />
|
<Spacer medium />
|
||||||
<Spacer large />
|
<hr />
|
||||||
|
<Spacer medium />
|
||||||
|
|
||||||
<div class="viewer-controls">
|
<div class="viewer-controls">
|
||||||
|
<Heading small>Query Results</Heading>
|
||||||
<Button
|
<Button
|
||||||
blue
|
secondary
|
||||||
|
thin
|
||||||
disabled={data.length === 0 || !query.name}
|
disabled={data.length === 0 || !query.name}
|
||||||
on:click={saveQuery}>
|
on:click={saveQuery}>
|
||||||
Save Query
|
Save Query
|
||||||
</Button>
|
</Button>
|
||||||
<Button primary on:click={previewQuery}>Run Query</Button>
|
<Button thin primary on:click={previewQuery}>Run Query</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<Body small grey>
|
||||||
|
Below, you can preview the results from your query and change the
|
||||||
|
schema.
|
||||||
|
</Body>
|
||||||
<section class="viewer">
|
<section class="viewer">
|
||||||
{#if data}
|
{#if data}
|
||||||
<Switcher headings={PREVIEW_HEADINGS} bind:value={tab}>
|
<Switcher headings={PREVIEW_HEADINGS} bind:value={tab}>
|
||||||
{#if tab === 'JSON'}
|
{#if tab === 'JSON'}
|
||||||
<pre class="preview">
|
<pre class="preview">
|
||||||
{#if !data[0]}
|
{#if !data[0]}
|
||||||
|
|
||||||
Please run your query to fetch some data.
|
Please run your query to fetch some data.
|
||||||
{:else}
|
|
||||||
{JSON.stringify(data[0], undefined, 2)}
|
{:else}
|
||||||
{/if}
|
{JSON.stringify(data[0], undefined, 2)}
|
||||||
|
{/if}
|
||||||
</pre>
|
</pre>
|
||||||
{:else if tab === 'PREVIEW'}
|
{:else if tab === 'PREVIEW'}
|
||||||
<ExternalDataSourceTable {query} {data} />
|
<ExternalDataSourceTable {query} {data} />
|
||||||
|
@ -222,28 +259,16 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.input {
|
.config-field {
|
||||||
width: 500px;
|
display: grid;
|
||||||
display: flex;
|
grid-template-columns: 20% 1fr;
|
||||||
|
grid-gap: var(--spacing-l);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select {
|
|
||||||
width: 200px;
|
|
||||||
margin-right: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.props {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
margin-left: auto;
|
|
||||||
align-items: center;
|
|
||||||
gap: var(--layout-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 1fr 50px;
|
grid-template-columns: 1fr 1fr 5%;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,12 +285,6 @@
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-type {
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
color: var(--grey-8);
|
|
||||||
font-size: var(--font-size-s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.query-type-span {
|
.query-type-span {
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
}
|
}
|
||||||
|
@ -278,31 +297,18 @@
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer-controls {
|
.viewer-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
margin-left: auto;
|
/* margin-left: auto; */
|
||||||
direction: rtl;
|
/* direction: rtl; */
|
||||||
z-index: 5;
|
/* z-index: 5; */
|
||||||
gap: var(--spacing-m);
|
gap: var(--spacing-m);
|
||||||
min-width: 150px;
|
min-width: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewer {
|
.viewer {
|
||||||
margin-top: -28px;
|
/* margin-top: -28px; */
|
||||||
z-index: -2;
|
/* z-index: -2; */
|
||||||
}
|
|
||||||
|
|
||||||
.label {
|
|
||||||
font-family: var(--font-sans);
|
|
||||||
color: var(--grey-8);
|
|
||||||
font-size: var(--font-size-s);
|
|
||||||
margin-right: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
const tabs = [
|
const tabs = [
|
||||||
{
|
{
|
||||||
title: "Tables",
|
title: "Internal",
|
||||||
key: "table",
|
key: "table",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Data Sources",
|
title: "External",
|
||||||
key: "datasource",
|
key: "datasource",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
@ -67,6 +67,7 @@
|
||||||
justify-content: flex-start;
|
justify-content: flex-start;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
gap: var(--spacing-l);
|
gap: var(--spacing-l);
|
||||||
|
background: var(--background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav {
|
.nav {
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
<style>
|
<style>
|
||||||
section {
|
section {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 0px;
|
width: 0px;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { Button, Spacer, Icon } from "@budibase/bbui"
|
import { Button, Heading, Body, Spacer, Icon } from "@budibase/bbui"
|
||||||
import { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
import { notifier } from "builderStore/store/notifications"
|
import { notifier } from "builderStore/store/notifications"
|
||||||
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
|
||||||
|
import ICONS from "components/backend/DatasourceNavigator/icons"
|
||||||
|
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: datasource = $backendUiStore.datasources.find(
|
||||||
ds => ds._id === $backendUiStore.selectedDatasourceId
|
ds => ds._id === $backendUiStore.selectedDatasourceId
|
||||||
|
@ -28,22 +29,35 @@
|
||||||
<section>
|
<section>
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
<header>
|
<header>
|
||||||
|
<div class="datasource-icon">
|
||||||
|
<svelte:component
|
||||||
|
this={ICONS[datasource.source]}
|
||||||
|
height="30"
|
||||||
|
width="30" />
|
||||||
|
</div>
|
||||||
<h3 class="section-title">{datasource.name}</h3>
|
<h3 class="section-title">{datasource.name}</h3>
|
||||||
</header>
|
</header>
|
||||||
<Spacer extraLarge />
|
<Spacer extraLarge />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="config-header">
|
<div class="config-header">
|
||||||
<h5>Configuration</h5>
|
<Heading small>Configuration</Heading>
|
||||||
<Button secondary on:click={saveDatasource}>Save</Button>
|
<Button secondary on:click={saveDatasource}>Save</Button>
|
||||||
</div>
|
</div>
|
||||||
|
<Body small grey>
|
||||||
|
Connect your database to Budibase using the config below.
|
||||||
|
</Body>
|
||||||
|
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
<IntegrationConfigForm integration={datasource.config} />
|
<IntegrationConfigForm integration={datasource.config} />
|
||||||
</div>
|
|
||||||
<Spacer extraLarge />
|
<hr />
|
||||||
<div class="container">
|
|
||||||
|
<Spacer extraLarge />
|
||||||
|
|
||||||
<div class="query-header">
|
<div class="query-header">
|
||||||
<h5>Queries</h5>
|
<Heading small>Queries</Heading>
|
||||||
<Button blue on:click={() => $goto('../new')}>Create Query</Button>
|
<Button secondary on:click={() => $goto('../new')}>Add Query</Button>
|
||||||
</div>
|
</div>
|
||||||
<Spacer extraLarge />
|
<Spacer extraLarge />
|
||||||
<div class="query-list">
|
<div class="query-list">
|
||||||
|
@ -54,7 +68,6 @@
|
||||||
<p>→</p>
|
<p>→</p>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<Spacer medium />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
@ -71,6 +84,8 @@
|
||||||
|
|
||||||
header {
|
header {
|
||||||
margin: 0 0 var(--spacing-xs) 0;
|
margin: 0 0 var(--spacing-xs) 0;
|
||||||
|
display: flex;
|
||||||
|
gap: var(--spacing-m);
|
||||||
}
|
}
|
||||||
|
|
||||||
.section-title {
|
.section-title {
|
||||||
|
@ -85,13 +100,12 @@
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
background: var(--background);
|
|
||||||
padding: var(--layout-s);
|
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
|
font-size: var(--font-size-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
.query-header {
|
.query-header {
|
||||||
|
@ -115,7 +129,8 @@
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 2fr 0.75fr 20px;
|
grid-template-columns: 2fr 0.75fr 20px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: var(--spacing-m) var(--layout-xs);
|
padding-left: var(--spacing-m);
|
||||||
|
padding-right: var(--spacing-m);
|
||||||
gap: var(--layout-xs);
|
gap: var(--layout-xs);
|
||||||
transition: 200ms background ease;
|
transition: 200ms background ease;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://airtable.com/api",
|
docs: "https://airtable.com/api",
|
||||||
|
friendlyName: "Airtable",
|
||||||
datasource: {
|
datasource: {
|
||||||
apiKey: {
|
apiKey: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/arangodb/arangojs",
|
docs: "https://github.com/arangodb/arangojs",
|
||||||
|
friendlyName: "ArangoDB",
|
||||||
datasource: {
|
datasource: {
|
||||||
url: {
|
url: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://docs.couchdb.org/en/stable/",
|
docs: "https://docs.couchdb.org/en/stable/",
|
||||||
|
friendlyName: "CouchDB",
|
||||||
datasource: {
|
datasource: {
|
||||||
url: {
|
url: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet",
|
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet",
|
||||||
|
friendlyName: "DynamoDB",
|
||||||
datasource: {
|
datasource: {
|
||||||
region: {
|
region: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -4,6 +4,7 @@ const { QUERY_TYPES, FIELD_TYPES } = require("./Integration")
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs:
|
docs:
|
||||||
"https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
|
"https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
|
||||||
|
friendlyName: "ElasticSearch",
|
||||||
datasource: {
|
datasource: {
|
||||||
url: {
|
url: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/tediousjs/node-mssql",
|
docs: "https://github.com/tediousjs/node-mssql",
|
||||||
|
friendlyName: "MS SQL Server",
|
||||||
datasource: {
|
datasource: {
|
||||||
user: {
|
user: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -3,6 +3,9 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/mongodb/node-mongodb-native",
|
docs: "https://github.com/mongodb/node-mongodb-native",
|
||||||
|
friendlyName: "MongoDB",
|
||||||
|
description:
|
||||||
|
"MongoDB is a general purpose, document-based, distributed database built for modern application developers and for the cloud era.",
|
||||||
datasource: {
|
datasource: {
|
||||||
connectionString: {
|
connectionString: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/mysqljs/mysql",
|
docs: "https://github.com/mysqljs/mysql",
|
||||||
|
friendlyName: "MySQL",
|
||||||
datasource: {
|
datasource: {
|
||||||
host: {
|
host: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
|
|
|
@ -2,6 +2,7 @@ const { Client } = require("pg")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://node-postgres.com",
|
docs: "https://node-postgres.com",
|
||||||
|
friendlyName: "PostgreSQL",
|
||||||
datasource: {
|
datasource: {
|
||||||
host: {
|
host: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|
|
@ -3,15 +3,22 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://github.com/node-fetch/node-fetch",
|
docs: "https://github.com/node-fetch/node-fetch",
|
||||||
|
friendlyName: "REST API",
|
||||||
datasource: {
|
datasource: {
|
||||||
url: {
|
url: {
|
||||||
type: FIELD_TYPES.STRING,
|
type: FIELD_TYPES.STRING,
|
||||||
default: "localhost",
|
default: "localhost",
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
|
defaultHeaders: {
|
||||||
|
type: FIELD_TYPES.OBJECT,
|
||||||
|
required: false,
|
||||||
|
default: {},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
|
displayName: "POST",
|
||||||
type: QUERY_TYPES.FIELDS,
|
type: QUERY_TYPES.FIELDS,
|
||||||
fields: {
|
fields: {
|
||||||
path: {
|
path: {
|
||||||
|
@ -26,6 +33,7 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
|
displayName: "GET",
|
||||||
type: QUERY_TYPES.FIELDS,
|
type: QUERY_TYPES.FIELDS,
|
||||||
fields: {
|
fields: {
|
||||||
path: {
|
path: {
|
||||||
|
@ -37,6 +45,7 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
|
displayName: "PUT",
|
||||||
type: QUERY_TYPES.FIELDS,
|
type: QUERY_TYPES.FIELDS,
|
||||||
fields: {
|
fields: {
|
||||||
path: {
|
path: {
|
||||||
|
@ -51,6 +60,7 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
|
displayName: "DELETE",
|
||||||
type: QUERY_TYPES.FIELDS,
|
type: QUERY_TYPES.FIELDS,
|
||||||
fields: {
|
fields: {
|
||||||
path: {
|
path: {
|
||||||
|
@ -75,7 +85,10 @@ class RestIntegration {
|
||||||
async create({ path, headers = {}, json }) {
|
async create({ path, headers = {}, json }) {
|
||||||
const response = await fetch(this.config.url + path, {
|
const response = await fetch(this.config.url + path, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers: {
|
||||||
|
...this.config.defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
body: JSON.stringify(json),
|
body: JSON.stringify(json),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -84,7 +97,10 @@ class RestIntegration {
|
||||||
|
|
||||||
async read({ path, headers = {} }) {
|
async read({ path, headers = {} }) {
|
||||||
const response = await fetch(this.config.url + path, {
|
const response = await fetch(this.config.url + path, {
|
||||||
headers,
|
headers: {
|
||||||
|
...this.config.defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
@ -93,7 +109,10 @@ class RestIntegration {
|
||||||
async update({ path, headers = {}, json }) {
|
async update({ path, headers = {}, json }) {
|
||||||
const response = await fetch(this.config.url + path, {
|
const response = await fetch(this.config.url + path, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers: {
|
||||||
|
...this.config.defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
body: JSON.stringify(json),
|
body: JSON.stringify(json),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -103,7 +122,10 @@ class RestIntegration {
|
||||||
async delete({ path, headers = {} }) {
|
async delete({ path, headers = {} }) {
|
||||||
const response = await fetch(this.config.url + path, {
|
const response = await fetch(this.config.url + path, {
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
headers,
|
headers: {
|
||||||
|
...this.config.defaultHeaders,
|
||||||
|
...headers,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
|
|
@ -2,6 +2,7 @@ const AWS = require("aws-sdk")
|
||||||
|
|
||||||
const SCHEMA = {
|
const SCHEMA = {
|
||||||
docs: "https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
docs: "https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
|
||||||
|
friendlyName: "Amazon S3",
|
||||||
datasource: {
|
datasource: {
|
||||||
region: {
|
region: {
|
||||||
type: "string",
|
type: "string",
|
||||||
|
|
Loading…
Reference in New Issue