Merge branch 'master' of github.com:Budibase/budibase into form-builder
This commit is contained in:
commit
25036d2d1b
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
GITHUB_BASE_URL=https://raw.githubusercontent.com/Budibase/budibase/master/hosting
|
||||||
|
|
||||||
|
if ! [ -x "$(command -v wget)" ]; then
|
||||||
|
echo 'Error: wget is not installed. Please install it for your operating system.' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
fetch_config_files() {
|
||||||
|
wget $GITHUB_BASE_URL/docker-compose.yaml
|
||||||
|
wget $GITHUB_BASE_URL/envoy.yaml
|
||||||
|
wget $GITHUB_BASE_URL/hosting.properties
|
||||||
|
wget $GITHUB_BASE_URL/start.sh
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_config_files
|
||||||
|
|
||||||
|
# Start budibase
|
||||||
|
docker-compose --env-file hosting.properties up -d
|
|
@ -2,6 +2,7 @@ version: "3"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
app-service:
|
app-service:
|
||||||
|
restart: always
|
||||||
image: budibase/budibase-apps
|
image: budibase/budibase-apps
|
||||||
ports:
|
ports:
|
||||||
- "${APP_PORT}:4002"
|
- "${APP_PORT}:4002"
|
||||||
|
@ -18,6 +19,7 @@ services:
|
||||||
- worker-service
|
- worker-service
|
||||||
|
|
||||||
worker-service:
|
worker-service:
|
||||||
|
restart: always
|
||||||
image: budibase/budibase-worker
|
image: budibase/budibase-worker
|
||||||
ports:
|
ports:
|
||||||
- "${WORKER_PORT}:4003"
|
- "${WORKER_PORT}:4003"
|
||||||
|
@ -36,6 +38,7 @@ services:
|
||||||
- couch-init
|
- couch-init
|
||||||
|
|
||||||
minio-service:
|
minio-service:
|
||||||
|
restart: always
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
volumes:
|
volumes:
|
||||||
- minio_data:/data
|
- minio_data:/data
|
||||||
|
@ -53,6 +56,7 @@ services:
|
||||||
retries: 3
|
retries: 3
|
||||||
|
|
||||||
proxy-service:
|
proxy-service:
|
||||||
|
restart: always
|
||||||
image: envoyproxy/envoy:v1.16-latest
|
image: envoyproxy/envoy:v1.16-latest
|
||||||
volumes:
|
volumes:
|
||||||
- ./envoy.yaml:/etc/envoy/envoy.yaml
|
- ./envoy.yaml:/etc/envoy/envoy.yaml
|
||||||
|
@ -66,6 +70,7 @@ services:
|
||||||
- couchdb-service
|
- couchdb-service
|
||||||
|
|
||||||
couchdb-service:
|
couchdb-service:
|
||||||
|
restart: always
|
||||||
image: apache/couchdb:3.0
|
image: apache/couchdb:3.0
|
||||||
environment:
|
environment:
|
||||||
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
|
- COUCHDB_PASSWORD=${COUCH_DB_PASSWORD}
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@budibase/bbui": "^1.54.0",
|
"@budibase/bbui": "^1.54.1",
|
||||||
"@budibase/client": "^0.5.3",
|
"@budibase/client": "^0.5.3",
|
||||||
"@budibase/colorpicker": "^1.0.1",
|
"@budibase/colorpicker": "^1.0.1",
|
||||||
"@budibase/string-templates": "^0.5.3",
|
"@budibase/string-templates": "^0.5.3",
|
||||||
|
|
|
@ -103,6 +103,15 @@
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.column-header-name {
|
||||||
|
white-space: normal !important;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.sort-icon {
|
.sort-icon {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
|
|
@ -43,8 +43,8 @@
|
||||||
<div class="datasource-icon" slot="icon">
|
<div class="datasource-icon" slot="icon">
|
||||||
<svelte:component
|
<svelte:component
|
||||||
this={ICONS[datasource.source]}
|
this={ICONS[datasource.source]}
|
||||||
height="15"
|
height="18"
|
||||||
width="15" />
|
width="18" />
|
||||||
</div>
|
</div>
|
||||||
<EditDatasourcePopover {datasource} />
|
<EditDatasourcePopover {datasource} />
|
||||||
</NavItem>
|
</NavItem>
|
||||||
|
@ -61,3 +61,10 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.datasource-icon {
|
||||||
|
margin-right: 3px;
|
||||||
|
padding-top: 3px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -7,10 +7,9 @@
|
||||||
<form>
|
<form>
|
||||||
{#each Object.keys(integration) as configKey}
|
{#each Object.keys(integration) as configKey}
|
||||||
<Input
|
<Input
|
||||||
thin
|
|
||||||
type={integration[configKey].type}
|
type={integration[configKey].type}
|
||||||
label={configKey}
|
label={configKey}
|
||||||
bind:value={integration[configKey]} />
|
bind:value={integration[configKey]} />
|
||||||
<Spacer medium />
|
<Spacer large />
|
||||||
{/each}
|
{/each}
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
<script>
|
||||||
|
import { backendUiStore, store, allScreens } from "builderStore"
|
||||||
|
import { notifier } from "builderStore/store/notifications"
|
||||||
|
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui"
|
||||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
|
||||||
|
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
|
||||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
|
||||||
|
import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte"
|
||||||
|
|
||||||
|
export let bindable
|
||||||
|
export let parameters
|
||||||
|
|
||||||
|
let anchor
|
||||||
|
let dropdown
|
||||||
|
let confirmDeleteDialog
|
||||||
|
|
||||||
|
function hideEditor() {
|
||||||
|
dropdown?.hide()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div on:click|stopPropagation bind:this={anchor}>
|
||||||
|
<TextButton
|
||||||
|
text
|
||||||
|
on:click={dropdown.show}
|
||||||
|
active={false}>
|
||||||
|
<Icon name="add" />
|
||||||
|
Add Parameters
|
||||||
|
</TextButton>
|
||||||
|
<DropdownMenu align="right" {anchor} bind:this={dropdown}>
|
||||||
|
<div class="wrapper">
|
||||||
|
<ParameterBuilder bind:parameters {bindable} />
|
||||||
|
</div>
|
||||||
|
</DropdownMenu>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
padding: var(--spacing-xl);
|
||||||
|
min-width: 600px;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -57,7 +57,7 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button secondary small on:click={drawer.show}>Define Actions</Button>
|
<Button secondary wide on:click={drawer.show}>Define Actions</Button>
|
||||||
<Drawer bind:this={drawer} title={'Actions'}>
|
<Drawer bind:this={drawer} title={'Actions'}>
|
||||||
<heading slot="buttons">
|
<heading slot="buttons">
|
||||||
<Button thin blue on:click={saveEventData}>Save</Button>
|
<Button thin blue on:click={saveEventData}>Save</Button>
|
||||||
|
|
|
@ -9,12 +9,17 @@
|
||||||
export let onStyleChanged = () => {}
|
export let onStyleChanged = () => {}
|
||||||
export let open = false
|
export let open = false
|
||||||
|
|
||||||
|
$: style = componentInstance["_styles"][styleCategory] || {}
|
||||||
|
$: changed = properties.some(prop => hasPropChanged(style, prop))
|
||||||
|
|
||||||
const hasPropChanged = (style, prop) => {
|
const hasPropChanged = (style, prop) => {
|
||||||
return style[prop.key] != null && style[prop.key] !== ""
|
return style[prop.key] != null && style[prop.key] !== ""
|
||||||
}
|
}
|
||||||
|
|
||||||
$: style = componentInstance["_styles"][styleCategory] || {}
|
const getControlProps = props => {
|
||||||
$: changed = properties.some(prop => hasPropChanged(style, prop))
|
const { label, key, control, ...otherProps } = props || {}
|
||||||
|
return otherProps || {}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
<DetailSummary name={`${name}${changed ? ' *' : ''}`} on:open show={open} thin>
|
||||||
|
@ -28,7 +33,7 @@
|
||||||
key={prop.key}
|
key={prop.key}
|
||||||
value={style[prop.key]}
|
value={style[prop.key]}
|
||||||
onChange={value => onStyleChanged(styleCategory, prop.key, value)}
|
onChange={value => onStyleChanged(styleCategory, prop.key, value)}
|
||||||
props={{ options: prop.options, placeholder: prop.placeholder }} />
|
props={getControlProps(prop)} />
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
|
@ -78,9 +78,7 @@
|
||||||
const source = $backendUiStore.datasources.find(
|
const source = $backendUiStore.datasources.find(
|
||||||
ds => ds._id === query.datasourceId
|
ds => ds._id === query.datasourceId
|
||||||
).source
|
).source
|
||||||
return $backendUiStore.integrations[source].query[query.queryVerb][
|
return $backendUiStore.integrations[source].query[query.queryVerb]
|
||||||
query.queryType
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -155,8 +155,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.CodeMirror) {
|
:global(.CodeMirror) {
|
||||||
height: auto !important;
|
height: 500px !important;
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-s);
|
||||||
font-family: var(--font-sans) !important;
|
font-family: monospace !important;
|
||||||
|
line-height: 1.3;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -24,18 +24,18 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form on:submit|preventDefault>
|
<form on:submit|preventDefault>
|
||||||
|
<div class="field">
|
||||||
{#each schemaKeys as field}
|
{#each schemaKeys as field}
|
||||||
<Label extraSmall grey>{field}</Label>
|
|
||||||
<div class="field">
|
|
||||||
<Input
|
<Input
|
||||||
|
placeholder="Enter {field} name"
|
||||||
|
outline
|
||||||
disabled={!editable}
|
disabled={!editable}
|
||||||
type={schema.fields[field]?.type}
|
type={schema.fields[field]?.type}
|
||||||
required={schema.fields[field]?.required}
|
required={schema.fields[field]?.required}
|
||||||
bind:value={fields[field]} />
|
bind:value={fields[field]} />
|
||||||
</div>
|
|
||||||
{/each}
|
{/each}
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<Label extraSmall grey>Data</Label>
|
|
||||||
{#if schema.customisable}
|
{#if schema.customisable}
|
||||||
<Editor
|
<Editor
|
||||||
label="Query"
|
label="Query"
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
.field {
|
.field {
|
||||||
margin-bottom: var(--spacing-m);
|
margin-bottom: var(--spacing-m);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr 2%;
|
grid-template-columns: 1fr 1fr;
|
||||||
grid-gap: var(--spacing-m);
|
grid-gap: var(--spacing-m);
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { Button, Label, Input, Heading } from "@budibase/bbui"
|
import { 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,
|
||||||
|
@ -31,19 +31,22 @@
|
||||||
|
|
||||||
<section>
|
<section>
|
||||||
<Heading extraSmall black>Parameters</Heading>
|
<Heading extraSmall black>Parameters</Heading>
|
||||||
|
<Spacer large />
|
||||||
<div class="parameters" class:bindable>
|
<div class="parameters" class:bindable>
|
||||||
<Label extraSmall grey>Parameter Name</Label>
|
|
||||||
<Label extraSmall grey>Default</Label>
|
|
||||||
{#if bindable}
|
|
||||||
<Label extraSmall grey>Value</Label>
|
|
||||||
{:else}
|
|
||||||
<div />
|
|
||||||
{/if}
|
|
||||||
{#each parameters as parameter, idx}
|
{#each parameters as parameter, idx}
|
||||||
<Input thin disabled={bindable} bind:value={parameter.name} />
|
<Input
|
||||||
<Input thin disabled={bindable} bind:value={parameter.default} />
|
placeholder="Parameter Name"
|
||||||
|
thin
|
||||||
|
disabled={bindable}
|
||||||
|
bind:value={parameter.name} />
|
||||||
|
<Input
|
||||||
|
placeholder="Default"
|
||||||
|
thin
|
||||||
|
disabled={bindable}
|
||||||
|
bind:value={parameter.default} />
|
||||||
{#if bindable}
|
{#if bindable}
|
||||||
<BindableInput
|
<BindableInput
|
||||||
|
placeholder="Value"
|
||||||
type="string"
|
type="string"
|
||||||
thin
|
thin
|
||||||
on:change={evt => onBindingChange(parameter.name, evt.detail)}
|
on:change={evt => onBindingChange(parameter.name, evt.detail)}
|
||||||
|
@ -57,9 +60,7 @@
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{#if !bindable}
|
{#if !bindable}
|
||||||
<Button thin secondary small on:click={newQueryParameter}>
|
<Button secondary on:click={newQueryParameter}>Add Parameter</Button>
|
||||||
Add Parameter
|
|
||||||
</Button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,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 { backendUiStore } from "builderStore"
|
import { backendUiStore } from "builderStore"
|
||||||
|
|
||||||
const PREVIEW_HEADINGS = [
|
const PREVIEW_HEADINGS = [
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
let tab = "JSON"
|
let tab = "JSON"
|
||||||
let parameters
|
let parameters
|
||||||
let data = []
|
let data = []
|
||||||
|
let popover
|
||||||
|
|
||||||
$: datasource = $backendUiStore.datasources.find(
|
$: datasource = $backendUiStore.datasources.find(
|
||||||
ds => ds._id === query.datasourceId
|
ds => ds._id === query.datasourceId
|
||||||
|
@ -61,7 +63,7 @@
|
||||||
$: config = $backendUiStore.integrations[datasourceType]?.query
|
$: config = $backendUiStore.integrations[datasourceType]?.query
|
||||||
$: docsLink = $backendUiStore.integrations[datasourceType]?.docs
|
$: docsLink = $backendUiStore.integrations[datasourceType]?.docs
|
||||||
|
|
||||||
$: shouldShowQueryConfig = config && query.queryVerb && query.queryType
|
$: shouldShowQueryConfig = config && query.queryVerb
|
||||||
|
|
||||||
function newField() {
|
function newField() {
|
||||||
fields = [...fields, {}]
|
fields = [...fields, {}]
|
||||||
|
@ -129,62 +131,44 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<header>
|
<header>
|
||||||
<Heading small>{query.name}</Heading>
|
<div class="input">
|
||||||
|
<Input placeholder="✎ Edit Query Name" bind:value={query.name} />
|
||||||
|
</div>
|
||||||
{#if config}
|
{#if config}
|
||||||
<div class="queryVerbs">
|
<div class="props">
|
||||||
{#each Object.keys(config) as queryVerb}
|
<div class="query-type">Query type: <span class="query-type-span">{config[query.queryVerb].type}</span></div>
|
||||||
<div
|
<div class="select">
|
||||||
class="queryVerb"
|
<Select primary thin bind:value={query.queryVerb}>
|
||||||
class:selected={queryVerb === query.queryVerb}
|
{#each Object.keys(config) as queryVerb}
|
||||||
on:click={() => {
|
<option value={queryVerb}>{queryVerb}</option>
|
||||||
query.queryVerb = queryVerb
|
|
||||||
}}>
|
|
||||||
{queryVerb}
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
{#if query.queryVerb}
|
|
||||||
<Select thin secondary bind:value={query.queryType}>
|
|
||||||
<option value={''}>Select an option</option>
|
|
||||||
{#each Object.keys(config[query.queryVerb]) as queryType}
|
|
||||||
<option value={queryType}>{queryType}</option>
|
|
||||||
{/each}
|
{/each}
|
||||||
</Select>
|
</Select>
|
||||||
{/if}
|
</div>
|
||||||
<Spacer medium />
|
</div>
|
||||||
<Button primary href={docsLink} target="_blank">
|
<EditQueryParamsPopover bind:parameters={query.parameters} bindable={false} />
|
||||||
<i class="ri-book-2-line" />
|
|
||||||
</Button>
|
|
||||||
{/if}
|
{/if}
|
||||||
</header>
|
</header>
|
||||||
|
<Spacer extraLarge />
|
||||||
<Spacer large />
|
|
||||||
|
|
||||||
{#if shouldShowQueryConfig}
|
{#if shouldShowQueryConfig}
|
||||||
<section>
|
<section>
|
||||||
<div class="config">
|
<div class="config">
|
||||||
<Label extraSmall grey>Query Name</Label>
|
|
||||||
<Input thin bind:value={query.name} />
|
|
||||||
|
|
||||||
<Spacer medium />
|
|
||||||
|
|
||||||
<IntegrationQueryEditor
|
<IntegrationQueryEditor
|
||||||
{query}
|
{query}
|
||||||
schema={config[query.queryVerb][query.queryType]}
|
schema={config[query.queryVerb]}
|
||||||
bind:parameters />
|
bind:parameters />
|
||||||
|
|
||||||
<Spacer medium />
|
<Spacer extraLarge />
|
||||||
|
<Spacer large />
|
||||||
|
|
||||||
<div class="viewer-controls">
|
<div class="viewer-controls">
|
||||||
<Button
|
<Button
|
||||||
wide
|
|
||||||
thin
|
|
||||||
blue
|
blue
|
||||||
disabled={data.length === 0}
|
disabled={data.length === 0}
|
||||||
on:click={saveQuery}>
|
on:click={saveQuery}>
|
||||||
Save
|
Save Query
|
||||||
</Button>
|
</Button>
|
||||||
<Button wide thin primary on:click={previewQuery}>Run</Button>
|
<Button primary on:click={previewQuery}>Run Query</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="viewer">
|
<section class="viewer">
|
||||||
|
@ -196,10 +180,11 @@
|
||||||
<ExternalDataSourceTable {query} {data} />
|
<ExternalDataSourceTable {query} {data} />
|
||||||
{:else if tab === 'SCHEMA'}
|
{:else if tab === 'SCHEMA'}
|
||||||
{#each fields as field, idx}
|
{#each fields as field, idx}
|
||||||
|
<Spacer small />
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<Input thin type={'text'} bind:value={field.name} />
|
<Input outline placeholder="Field Name" type={'text'} bind:value={field.name} />
|
||||||
<Select secondary thin bind:value={field.type}>
|
<Select thin border bind:value={field.type}>
|
||||||
<option value={''}>Select an option</option>
|
<option value={''}>Select a field type</option>
|
||||||
<option value={'STRING'}>Text</option>
|
<option value={'STRING'}>Text</option>
|
||||||
<option value={'NUMBER'}>Number</option>
|
<option value={'NUMBER'}>Number</option>
|
||||||
<option value={'BOOLEAN'}>Boolean</option>
|
<option value={'BOOLEAN'}>Boolean</option>
|
||||||
|
@ -210,7 +195,8 @@
|
||||||
on:click={() => deleteField(idx)} />
|
on:click={() => deleteField(idx)} />
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<Button thin secondary on:click={newField}>Add Field</Button>
|
<Spacer small />
|
||||||
|
<Button thin secondary on:click={newField}>Add Field</Button>
|
||||||
{/if}
|
{/if}
|
||||||
</Switcher>
|
</Switcher>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -220,11 +206,28 @@
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<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);
|
||||||
|
}
|
||||||
|
|
||||||
.field {
|
.field {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-gap: 10px;
|
|
||||||
grid-template-columns: 1fr 1fr 50px;
|
grid-template-columns: 1fr 1fr 50px;
|
||||||
margin-bottom: var(--spacing-m);
|
gap: var(--spacing-l);
|
||||||
}
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
|
@ -240,6 +243,16 @@
|
||||||
cursor: pointer;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
.preview {
|
.preview {
|
||||||
width: 800px;
|
width: 800px;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -253,32 +266,18 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.queryVerbs {
|
|
||||||
display: flex;
|
|
||||||
flex: 1;
|
|
||||||
font-size: var(--font-size-m);
|
|
||||||
align-items: center;
|
|
||||||
margin-left: var(--spacing-l);
|
|
||||||
}
|
|
||||||
|
|
||||||
.queryVerb {
|
|
||||||
text-transform: capitalize;
|
|
||||||
margin-right: var(--spacing-m);
|
|
||||||
color: var(--grey-5);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selected {
|
|
||||||
color: var(--white);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.viewer-controls {
|
.viewer-controls {
|
||||||
display: grid;
|
display: flex;
|
||||||
grid-gap: var(--spacing-m);
|
flex-direction: row;
|
||||||
grid-auto-flow: column;
|
margin-left: auto;
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
grid-template-columns: 10% 10% 1fr;
|
z-index: 5;
|
||||||
margin-bottom: var(--spacing-m);
|
gap: var(--spacing-m);
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.viewer {
|
||||||
|
margin-top: -28px;
|
||||||
|
z-index: -2;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -20,13 +20,6 @@
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if editable}
|
|
||||||
<ParameterBuilder bind:parameters={query.parameters} bindable={false} />
|
|
||||||
<Spacer large />
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
<Heading extraSmall black>Query</Heading>
|
|
||||||
<Spacer medium />
|
|
||||||
|
|
||||||
{#if schema}
|
{#if schema}
|
||||||
{#key query._id}
|
{#key query._id}
|
||||||
|
@ -38,7 +31,6 @@
|
||||||
readOnly={!editable}
|
readOnly={!editable}
|
||||||
value={query.fields.sql} />
|
value={query.fields.sql} />
|
||||||
{:else if schema.type === QueryTypes.JSON}
|
{:else if schema.type === QueryTypes.JSON}
|
||||||
<Spacer large />
|
|
||||||
<Editor
|
<Editor
|
||||||
label="Query"
|
label="Query"
|
||||||
mode="json"
|
mode="json"
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
<a
|
<a
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href="https://github.com/Budibase/budibase/discussions">
|
href="https://github.com/Budibase/budibase/discussions">
|
||||||
<i class="ri-question-line" />
|
<i class="ri-github-fill" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<SettingsLink />
|
<SettingsLink />
|
||||||
|
@ -89,8 +89,8 @@
|
||||||
<div class="beta">
|
<div class="beta">
|
||||||
<Button
|
<Button
|
||||||
secondary
|
secondary
|
||||||
href="https://www.budibase.com/blog/budibase-public-beta/">
|
href="https://github.com/Budibase/budibase/discussions/categories/ideas">
|
||||||
Budibase is in Beta
|
Request feature
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
query => query._id === $backendUiStore.selectedQueryId
|
query => query._id === $backendUiStore.selectedQueryId
|
||||||
) || {
|
) || {
|
||||||
datasourceId: $params.selectedDatasource,
|
datasourceId: $params.selectedDatasource,
|
||||||
name: "New Query",
|
|
||||||
parameters: [],
|
parameters: [],
|
||||||
fields: {},
|
fields: {},
|
||||||
|
queryVerb: "read",
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -35,8 +35,10 @@
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section {
|
section {
|
||||||
background: var(--background);
|
overflow: scroll;
|
||||||
padding: var(--spacing-xl);
|
}
|
||||||
border-radius: var(--border-radius-m);
|
::-webkit-scrollbar {
|
||||||
|
width: 0px;
|
||||||
|
background: transparent; /* make scrollbar transparent */
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { goto } from "@sveltech/routify"
|
import { goto } from "@sveltech/routify"
|
||||||
import { Button, Spacer, Icon, TextButton } from "@budibase/bbui"
|
import { Button, 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"
|
||||||
|
@ -14,31 +14,129 @@
|
||||||
await backendUiStore.actions.datasources.save(datasource)
|
await backendUiStore.actions.datasources.save(datasource)
|
||||||
notifier.success(`Datasource ${name} saved successfully.`)
|
notifier.success(`Datasource ${name} saved successfully.`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onClickQuery(query) {
|
||||||
|
if ($backendUiStore.selectedQueryId === query._id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
backendUiStore.actions.queries.select(query)
|
||||||
|
$goto(`../${query._id}`)
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if datasource}
|
{#if datasource}
|
||||||
<TextButton text small on:click={() => $goto('../new')}>
|
|
||||||
<Icon name="filter" />
|
|
||||||
Create Query
|
|
||||||
</TextButton>
|
|
||||||
<section>
|
<section>
|
||||||
<h4>{datasource.name}: Configuration</h4>
|
|
||||||
<IntegrationConfigForm integration={datasource.config} />
|
|
||||||
<Spacer medium />
|
<Spacer medium />
|
||||||
<footer>
|
<header>
|
||||||
<Button blue wide on:click={saveDatasource}>Save</Button>
|
<h3 class="section-title">{datasource.name}</h3>
|
||||||
</footer>
|
</header>
|
||||||
|
<Spacer extraLarge />
|
||||||
|
<div class="container">
|
||||||
|
<div class="config-header">
|
||||||
|
<h5>Configuration</h5>
|
||||||
|
<Button secondary on:click={saveDatasource}>Save</Button>
|
||||||
|
</div>
|
||||||
|
<Spacer medium />
|
||||||
|
<IntegrationConfigForm integration={datasource.config} />
|
||||||
|
</div>
|
||||||
|
<Spacer extraLarge />
|
||||||
|
<div class="container">
|
||||||
|
<div class="query-header">
|
||||||
|
<h5>Queries</h5>
|
||||||
|
<Button blue on:click={() => $goto('../new')}>Create Query</Button>
|
||||||
|
</div>
|
||||||
|
<Spacer extraLarge />
|
||||||
|
<div class="query-list">
|
||||||
|
{#each $backendUiStore.queries.filter(query => query.datasourceId === datasource._id) as query}
|
||||||
|
<div class="query-list-item" on:click={() => onClickQuery(query)}>
|
||||||
|
<p class="query-name">{query.name}</p>
|
||||||
|
<p>{query.queryVerb}</p>
|
||||||
|
<p>4000 records</p>
|
||||||
|
<p>→</p>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
<Spacer medium />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
h4 {
|
h3 {
|
||||||
margin-top: var(--spacing-xl);
|
margin: 0;
|
||||||
margin-bottom: var(--spacing-s);
|
|
||||||
}
|
}
|
||||||
section {
|
section {
|
||||||
background: var(--background);
|
margin: 0 auto;
|
||||||
|
width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
margin: 0 0 var(--spacing-xs) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin: 0 0 var(--spacing-xs) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
border-radius: var(--border-radius-m);
|
border-radius: var(--border-radius-m);
|
||||||
padding: var(--spacing-xl);
|
background: var(--background);
|
||||||
|
padding: var(--layout-s);
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h5 {
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin: 0 0 var(--spacing-s) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--spacing-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list-item {
|
||||||
|
border-radius: var(--border-radius-m);
|
||||||
|
background: var(--background);
|
||||||
|
border: var(--border-grey);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 2fr 0.75fr 0.75fr 1fr 20px;
|
||||||
|
align-items: center;
|
||||||
|
padding: var(--spacing-m) var(--layout-xs);
|
||||||
|
gap: var(--layout-xs);
|
||||||
|
transition: 200ms background ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-list-item:hover {
|
||||||
|
background: var(--grey-1);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: var(--font-size-xs);
|
||||||
|
color: var(--grey-8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.query-name {
|
||||||
|
color: var(--ink);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: var(--font-size-s);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -842,10 +842,10 @@
|
||||||
lodash "^4.17.19"
|
lodash "^4.17.19"
|
||||||
to-fast-properties "^2.0.0"
|
to-fast-properties "^2.0.0"
|
||||||
|
|
||||||
"@budibase/bbui@^1.54.0":
|
"@budibase/bbui@^1.54.1":
|
||||||
version "1.54.0"
|
version "1.54.1"
|
||||||
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.54.0.tgz#60e6c0faa3d8f1781c503e74f8b8990f75ba2c40"
|
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.54.1.tgz#ad0439c0be6a4dc818cd9dacda00f053b0daa9d5"
|
||||||
integrity sha512-98koXkueqda6oQT6q0NPNvdL878ETRevtmmm34aSz9C6B4Oz68VVCsiFzRWuHvP/7wiNaAxMgY1nsEsCwP3LpQ==
|
integrity sha512-ZY2OP/tF+ReMSyzZIGZV6wpQ4eIEzYGxZV3n+C+oNjzK5u3rwWPCDEVDlZgJSqJ61z+sEf2zuIyAh88lq9RTaA==
|
||||||
dependencies:
|
dependencies:
|
||||||
markdown-it "^12.0.2"
|
markdown-it "^12.0.2"
|
||||||
quill "^1.3.7"
|
quill "^1.3.7"
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
import { notificationStore } from "../store/notification"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API cache for cached request responses.
|
* API cache for cached request responses.
|
||||||
*/
|
*/
|
||||||
|
import { notificationStore } from "../store/notification"
|
||||||
let cache = {}
|
let cache = {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handler for API errors.
|
* Handler for API errors.
|
||||||
*/
|
*/
|
||||||
const handleError = error => {
|
const handleError = error => {
|
||||||
notificationStore.danger(error)
|
|
||||||
return { error }
|
return { error }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,10 +36,12 @@ const makeApiCall = async ({ method, url, body, json = true }) => {
|
||||||
case 200:
|
case 200:
|
||||||
return response.json()
|
return response.json()
|
||||||
case 404:
|
case 404:
|
||||||
|
notificationStore.danger("Not found")
|
||||||
return handleError(`${url}: Not Found`)
|
return handleError(`${url}: Not Found`)
|
||||||
case 400:
|
case 400:
|
||||||
return handleError(`${url}: Bad Request`)
|
return handleError(`${url}: Bad Request`)
|
||||||
case 403:
|
case 403:
|
||||||
|
notificationStore.danger("Forbidden")
|
||||||
return handleError(`${url}: Forbidden`)
|
return handleError(`${url}: Forbidden`)
|
||||||
default:
|
default:
|
||||||
if (response.status >= 200 && response.status < 400) {
|
if (response.status >= 200 && response.status < 400) {
|
||||||
|
@ -77,7 +77,7 @@ const makeCachedApiCall = async params => {
|
||||||
const requestApiCall = method => async params => {
|
const requestApiCall = method => async params => {
|
||||||
const { url, cache = false } = params
|
const { url, cache = false } = params
|
||||||
const fixedUrl = `/${url}`.replace("//", "/")
|
const fixedUrl = `/${url}`.replace("//", "/")
|
||||||
const enrichedParams = { ...params, method, fixedUrl }
|
const enrichedParams = { ...params, method, url: fixedUrl }
|
||||||
return await (cache ? makeCachedApiCall : makeApiCall)(enrichedParams)
|
return await (cache ? makeCachedApiCall : makeApiCall)(enrichedParams)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
|
import { notificationStore } from "../store/notification"
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
/**
|
/**
|
||||||
* Executes an automation. Must have "App Action" trigger.
|
* Executes an automation. Must have "App Action" trigger.
|
||||||
*/
|
*/
|
||||||
export const triggerAutomation = async (automationId, fields) => {
|
export const triggerAutomation = async (automationId, fields) => {
|
||||||
return await API.post({
|
const res = await API.post({
|
||||||
url: `/api/automations/${automationId}/trigger`,
|
url: `/api/automations/${automationId}/trigger`,
|
||||||
body: { fields },
|
body: { fields },
|
||||||
})
|
})
|
||||||
|
res.error
|
||||||
|
? notificationStore.danger("An error has occurred")
|
||||||
|
: notificationStore.success("Automation triggered")
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
|
import { notificationStore } from "../store/notification"
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes a query against an external data connector.
|
* Executes a query against an external data connector.
|
||||||
*/
|
*/
|
||||||
export const executeQuery = async ({ queryId, parameters }) => {
|
export const executeQuery = async ({ queryId, parameters }) => {
|
||||||
const response = await API.post({
|
const res = await API.post({
|
||||||
url: `/api/queries/${queryId}`,
|
url: `/api/queries/${queryId}`,
|
||||||
body: {
|
body: {
|
||||||
parameters,
|
parameters,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return response
|
if (res.error) {
|
||||||
|
notificationStore.danger("An error has occurred")
|
||||||
|
}
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { notificationStore } from "../store/notification"
|
||||||
import API from "./api"
|
import API from "./api"
|
||||||
import { fetchTableDefinition } from "./tables"
|
import { fetchTableDefinition } from "./tables"
|
||||||
|
|
||||||
|
@ -15,42 +16,58 @@ export const fetchRow = async ({ tableId, rowId }) => {
|
||||||
* Creates a row in a table.
|
* Creates a row in a table.
|
||||||
*/
|
*/
|
||||||
export const saveRow = async row => {
|
export const saveRow = async row => {
|
||||||
return await API.post({
|
const res = await API.post({
|
||||||
url: `/api/${row.tableId}/rows`,
|
url: `/api/${row.tableId}/rows`,
|
||||||
body: row,
|
body: row,
|
||||||
})
|
})
|
||||||
|
res.error
|
||||||
|
? notificationStore.danger("An error has occurred")
|
||||||
|
: notificationStore.success("Row saved")
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates a row in a table.
|
* Updates a row in a table.
|
||||||
*/
|
*/
|
||||||
export const updateRow = async row => {
|
export const updateRow = async row => {
|
||||||
return await API.patch({
|
const res = await API.patch({
|
||||||
url: `/api/${row.tableId}/rows/${row._id}`,
|
url: `/api/${row.tableId}/rows/${row._id}`,
|
||||||
body: row,
|
body: row,
|
||||||
})
|
})
|
||||||
|
res.error
|
||||||
|
? notificationStore.danger("An error has occurred")
|
||||||
|
: notificationStore.success("Row updated")
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes a row from a table.
|
* Deletes a row from a table.
|
||||||
*/
|
*/
|
||||||
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
export const deleteRow = async ({ tableId, rowId, revId }) => {
|
||||||
return await API.del({
|
const res = await API.del({
|
||||||
url: `/api/${tableId}/rows/${rowId}/${revId}`,
|
url: `/api/${tableId}/rows/${rowId}/${revId}`,
|
||||||
})
|
})
|
||||||
|
res.error
|
||||||
|
? notificationStore.danger("An error has occurred")
|
||||||
|
: notificationStore.success("Row deleted")
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deletes many rows from a table.
|
* Deletes many rows from a table.
|
||||||
*/
|
*/
|
||||||
export const deleteRows = async ({ tableId, rows }) => {
|
export const deleteRows = async ({ tableId, rows }) => {
|
||||||
return await API.post({
|
const res = await API.post({
|
||||||
url: `/api/${tableId}/rows`,
|
url: `/api/${tableId}/rows`,
|
||||||
body: {
|
body: {
|
||||||
rows,
|
rows,
|
||||||
type: "delete",
|
type: "delete",
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
res.error
|
||||||
|
? notificationStore.danger("An error has occurred")
|
||||||
|
: notificationStore.success(`${rows.length} row(s) deleted`)
|
||||||
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {
|
||||||
routeStore,
|
routeStore,
|
||||||
screenStore,
|
screenStore,
|
||||||
bindingStore,
|
bindingStore,
|
||||||
|
builderStore,
|
||||||
} from "./store"
|
} from "./store"
|
||||||
import { styleable } from "./utils/styleable"
|
import { styleable } from "./utils/styleable"
|
||||||
import { linkable } from "./utils/linkable"
|
import { linkable } from "./utils/linkable"
|
||||||
|
@ -16,6 +17,7 @@ export default {
|
||||||
notifications: notificationStore,
|
notifications: notificationStore,
|
||||||
routeStore,
|
routeStore,
|
||||||
screenStore,
|
screenStore,
|
||||||
|
builderStore,
|
||||||
styleable,
|
styleable,
|
||||||
linkable,
|
linkable,
|
||||||
DataProvider,
|
DataProvider,
|
||||||
|
|
|
@ -31,7 +31,6 @@ function generateQueryValidation() {
|
||||||
default: Joi.string()
|
default: Joi.string()
|
||||||
})),
|
})),
|
||||||
queryVerb: Joi.string().allow(...Object.values(QueryVerb)).required(),
|
queryVerb: Joi.string().allow(...Object.values(QueryVerb)).required(),
|
||||||
queryType: Joi.string().required(),
|
|
||||||
schema: Joi.object({}).required().unknown(true)
|
schema: Joi.object({}).required().unknown(true)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ const TEST_QUERY = {
|
||||||
fields:{},
|
fields:{},
|
||||||
schema:{},
|
schema:{},
|
||||||
queryVerb:"read",
|
queryVerb:"read",
|
||||||
queryType:"Table",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("/datasources", () => {
|
describe("/datasources", () => {
|
||||||
|
|
|
@ -26,7 +26,6 @@ const TEST_QUERY = {
|
||||||
fields:{},
|
fields:{},
|
||||||
schema:{},
|
schema:{},
|
||||||
queryVerb:"read",
|
queryVerb:"read",
|
||||||
queryType:"Table",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("/queries", () => {
|
describe("/queries", () => {
|
||||||
|
|
|
@ -17,48 +17,40 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
"Airtable Record": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
customisable: true,
|
||||||
customisable: true,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
Table: {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
view: {
|
||||||
view: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
Fields: {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
customisable: true,
|
||||||
customisable: true,
|
fields: {
|
||||||
fields: {
|
id: {
|
||||||
id: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
"Airtable Ids": {
|
type: FIELD_TYPES.JSON,
|
||||||
type: FIELD_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,28 +16,20 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
"CouchDB DSL": {
|
type: QUERY_TYPES.JSON,
|
||||||
type: QUERY_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
"CouchDB DSL": {
|
type: QUERY_TYPES.JSON,
|
||||||
type: QUERY_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
"CouchDB Document": {
|
type: QUERY_TYPES.JSON,
|
||||||
type: QUERY_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
"Document ID": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
id: {
|
||||||
id: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,56 +20,48 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
DynamoConfig: {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
customisable: true,
|
|
||||||
},
|
},
|
||||||
|
customisable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
DynamoConfig: {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
index: {
|
|
||||||
type: FIELD_TYPES.STRING,
|
|
||||||
},
|
|
||||||
customisable: true,
|
|
||||||
},
|
},
|
||||||
|
index: {
|
||||||
|
type: FIELD_TYPES.STRING,
|
||||||
|
},
|
||||||
|
customisable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
DynamoConfig: {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
customisable: true,
|
|
||||||
},
|
},
|
||||||
|
customisable: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
"Dynamo Partition Key": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
table: {
|
||||||
table: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
key: {
|
||||||
key: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,57 +13,49 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
"ES Query DSL": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
customisable: true,
|
||||||
customisable: true,
|
fields: {
|
||||||
fields: {
|
index: {
|
||||||
index: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
"ES Query DSL": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
customisable: true,
|
||||||
customisable: true,
|
fields: {
|
||||||
fields: {
|
index: {
|
||||||
index: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
"ES Query DSL": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
customisable: true,
|
||||||
customisable: true,
|
fields: {
|
||||||
fields: {
|
id: {
|
||||||
id: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
index: {
|
||||||
index: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
"Document ID": {
|
type: QUERY_TYPES.FIELDS,
|
||||||
type: QUERY_TYPES.FIELDS,
|
fields: {
|
||||||
fields: {
|
index: {
|
||||||
index: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
id: {
|
||||||
id: {
|
type: FIELD_TYPES.STRING,
|
||||||
type: FIELD_TYPES.STRING,
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -24,14 +24,10 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,10 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
JSON: {
|
type: QUERY_TYPES.JSON,
|
||||||
type: QUERY_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
JSON: {
|
type: QUERY_TYPES.JSON,
|
||||||
type: QUERY_TYPES.JSON,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,24 +31,16 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
create: {
|
create: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
read: {
|
read: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
update: {
|
update: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
delete: {
|
delete: {
|
||||||
SQL: {
|
type: "sql",
|
||||||
type: "sql",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,13 +19,11 @@ const SCHEMA = {
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
read: {
|
read: {
|
||||||
Bucket: {
|
type: "fields",
|
||||||
type: "fields",
|
fields: {
|
||||||
fields: {
|
bucket: {
|
||||||
bucket: {
|
type: "string",
|
||||||
type: "string",
|
required: true,
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2,12 +2,13 @@
|
||||||
import { getContext } from "svelte"
|
import { getContext } from "svelte"
|
||||||
import { isEmpty } from "lodash/fp"
|
import { isEmpty } from "lodash/fp"
|
||||||
|
|
||||||
const { API, styleable, DataProvider } = getContext("sdk")
|
const { API, styleable, DataProvider, builderStore } = getContext("sdk")
|
||||||
const component = getContext("component")
|
const component = getContext("component")
|
||||||
|
|
||||||
export let datasource = []
|
export let datasource = []
|
||||||
|
|
||||||
let rows = []
|
let rows = []
|
||||||
|
let loaded = false
|
||||||
|
|
||||||
$: fetchData(datasource)
|
$: fetchData(datasource)
|
||||||
|
|
||||||
|
@ -15,21 +16,22 @@
|
||||||
if (!isEmpty(datasource)) {
|
if (!isEmpty(datasource)) {
|
||||||
rows = await API.fetchDatasource(datasource)
|
rows = await API.fetchDatasource(datasource)
|
||||||
}
|
}
|
||||||
|
loaded = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div use:styleable={$component.styles}>
|
<div use:styleable={$component.styles}>
|
||||||
{#if rows.length > 0}
|
{#if rows.length > 0}
|
||||||
{#each rows as row}
|
{#if $component.children === 0 && $builderStore.inBuilder}
|
||||||
<DataProvider {row}>
|
<p>Add some components too</p>
|
||||||
{#if $component.children === 0}
|
{:else}
|
||||||
<p>Add some components too.</p>
|
{#each rows as row}
|
||||||
{:else}
|
<DataProvider {row}>
|
||||||
<slot />
|
<slot />
|
||||||
{/if}
|
</DataProvider>
|
||||||
</DataProvider>
|
{/each}
|
||||||
{/each}
|
{/if}
|
||||||
{:else}
|
{:else if loaded && $builderStore.inBuilder}
|
||||||
<p>Feed me some data</p>
|
<p>Feed me some data</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Reference in New Issue