design updates, changing query verb names to HTTP verbs

This commit is contained in:
Martin McKeaveney 2021-02-18 16:58:10 +00:00
parent 13c51f61d9
commit 6e121b6045
25 changed files with 224 additions and 189 deletions

View File

@ -30,7 +30,9 @@
<div class="popover">
<h5>Who Can Access This Data?</h5>
<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>
<Spacer large />
<div class="row">

View File

@ -1,15 +1,33 @@
<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
</script>
<form>
{#each Object.keys(integration) as configKey}
<Input
type={integration[configKey].type}
label={configKey}
bind:value={integration[configKey]} />
<Spacer large />
<div class="form-row">
{#if typeof integration[configKey] === 'object'}
<Label small>{configKey}</Label>
<KeyValueBuilder bind:object={integration[configKey]} />
{:else}
<Label small>{configKey}</Label>
<Input
outline
type={integration[configKey].type}
bind:value={integration[configKey]} />
{/if}
</div>
{/each}
</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>

View File

@ -2,7 +2,8 @@
import { onMount } from "svelte"
import { backendUiStore } from "builderStore"
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"
export let integration = {}
@ -50,16 +51,21 @@
{/each}
</div>
{#if schema}
<!-- {#if schema}
{#each Object.keys(schema) as configKey}
<Input
thin
type={schema[configKey].type}
label={configKey}
bind:value={integration[configKey]} />
{#if schema[configKey].type === 'object'}
<Label small>{configKey}</Label>
<KeyValueBuilder bind:object={integration[configKey]} />
{:else}
<Label small>{configKey}</Label>
<Input
outline
type={integration[configKey].type}
bind:value={integration[configKey]} />
{/if}
<Spacer medium />
{/each}
{/if}
{/if} -->
</section>
<style>

View File

@ -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>

View File

@ -3,7 +3,6 @@
import { notifier } from "builderStore/store/notifications"
import { DropdownMenu, Button, Input } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
export let datasource

View File

@ -1,4 +1,4 @@
<script>
<!--<script>
import { backendUiStore, store, allScreens } from "builderStore"
import { notifier } from "builderStore/store/notifications"
import { DropdownMenu, Button, Input, TextButton, Icon } from "@budibase/bbui"
@ -17,9 +17,7 @@
function hideEditor() {
dropdown?.hide()
}
</script>
<div on:click|stopPropagation bind:this={anchor}>
</script><div on:click|stopPropagation bind:this={anchor}>
<TextButton text on:click={dropdown.show} active={false}>
<Icon name="add" />
Add Parameters
@ -29,11 +27,9 @@
<ParameterBuilder bind:parameters {bindable} />
</div>
</DropdownMenu>
</div>
<style>
</div><style>
.wrapper {
padding: var(--spacing-xl);
min-width: 600px;
}
</style>
</style>-->

View File

@ -3,7 +3,6 @@
import { notifier } from "builderStore/store/notifications"
import { DropdownMenu, Button, Input } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte"
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns"
export let query

View File

@ -3,7 +3,7 @@
export let object = {}
let fields = []
let fields = Object.entries(object).map(([name, value]) => ({ name, value }))
$: object = fields.reduce(
(acc, next) => ({ ...acc, [next.name]: next.value }),

View File

@ -22,35 +22,36 @@
function updateCustomFields({ detail }) {
fields.customData = detail.value
}
</script>
<form on:submit|preventDefault>
<div class="field">
{#each schemaKeys as field}
{#if schema.fields[field]?.type === "object"}
{#if schema.fields[field]?.type === 'object'}
<div>
<Label extraSmall grey>{field}</Label>
<KeyValueBuilder bind:object={fields[field]} />
<KeyValueBuilder bind:object={fields[field]} />
</div>
{:else if schema.fields[field]?.type === "json"}
{:else if schema.fields[field]?.type === 'json'}
<div>
<Label extraSmall grey>{field}</Label>
<Editor
mode="json"
on:change={({ detail }) => fields[field] = detail.value}
on:change={({ detail }) => (fields[field] = detail.value)}
readOnly={!editable}
value={fields[field]} />
</div>
{:else}
<Input
label={field}
placeholder="Enter {field} name"
outline
disabled={!editable}
type={schema.fields[field]?.type}
required={schema.fields[field]?.required}
bind:value={fields[field]} />
<div class="horizontal">
<Label small>{field}</Label>
<Input
placeholder="Enter {field} name"
outline
disabled={!editable}
type={schema.fields[field]?.type}
required={schema.fields[field]?.required}
bind:value={fields[field]} />
</div>
{/if}
{/each}
</div>
@ -73,4 +74,10 @@
align-items: center;
}
.horizontal {
display: grid;
grid-template-columns: 20% 1fr;
grid-gap: var(--spacing-l);
align-items: center;
}
</style>

View File

@ -1,5 +1,5 @@
<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 {
readableToRuntimeBinding,
@ -30,7 +30,16 @@
</script>
<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 />
<div class="parameters" class:bindable>
{#each parameters as parameter, idx}
@ -59,9 +68,6 @@
{/if}
{/each}
</div>
{#if !bindable}
<Button secondary on:click={newQueryParameter}>Add Parameter</Button>
{/if}
</section>
<style>
@ -69,6 +75,11 @@
grid-template-columns: 1fr 1fr 1fr;
}
.controls {
display: flex;
justify-content: space-between;
}
.parameters {
display: grid;
grid-template-columns: 1fr 1fr 5%;

View File

@ -4,6 +4,7 @@
import {
Select,
Button,
Body,
Label,
Input,
TextArea,
@ -16,7 +17,7 @@
import { FIELDS } from "constants/backend"
import IntegrationQueryEditor from "components/integration/index.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"
const PREVIEW_HEADINGS = [
@ -60,8 +61,8 @@
$: datasourceType = datasource?.source
$: config = $backendUiStore.integrations[datasourceType]?.query
$: docsLink = $backendUiStore.integrations[datasourceType]?.docs
$: integrationInfo = $backendUiStore.integrations[datasourceType]
$: queryConfig = integrationInfo?.query
$: shouldShowQueryConfig = config && query.queryVerb
@ -130,63 +131,99 @@
}
</script>
<header>
<div class="input">
<section class="config">
<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>
<Input outline border bind:value={query.name} />
</div>
{#if config}
<div class="props">
<div class="query-type">
</div> -->
<!-- {#if config} -->
<!-- <div class="props"> -->
<!-- <div class="query-type">
Query type:
<span class="query-type-span">{config[query.queryVerb].type}</span>
</div>
<div class="select">
</div> -->
<!-- <div class="select">
<Select primary thin bind:value={query.queryVerb}>
{#each Object.keys(config) as queryVerb}
<option value={queryVerb}>{queryVerb}</option>
{/each}
</Select>
</div>
</div>
</div> -->
<!-- </div>
<EditQueryParamsPopover
bind:parameters={query.parameters}
bindable={false} />
{/if}
</header>
<Spacer extraLarge />
bindable={false} /> -->
<!-- {/if} -->
</section>
<Spacer large />
{#if shouldShowQueryConfig}
<section>
<div class="config">
<Heading small>Fields</Heading>
<Spacer medium />
<IntegrationQueryEditor
{query}
schema={config[query.queryVerb]}
bind:parameters />
<Spacer extraLarge />
<Spacer large />
<Spacer medium />
<hr />
<Spacer medium />
<div class="viewer-controls">
<Heading small>Query Results</Heading>
<Button
blue
secondary
thin
disabled={data.length === 0 || !query.name}
on:click={saveQuery}>
Save Query
</Button>
<Button primary on:click={previewQuery}>Run Query</Button>
<Button thin primary on:click={previewQuery}>Run Query</Button>
</div>
<Body small grey>
Below, you can preview the results from your query and change the
schema.
</Body>
<section class="viewer">
{#if data}
<Switcher headings={PREVIEW_HEADINGS} bind:value={tab}>
{#if tab === 'JSON'}
<pre class="preview">
<pre class="preview">
{#if !data[0]}
Please run your query to fetch some data.
{:else}
{JSON.stringify(data[0], undefined, 2)}
{/if}
{:else}
{JSON.stringify(data[0], undefined, 2)}
{/if}
</pre>
{:else if tab === 'PREVIEW'}
<ExternalDataSourceTable {query} {data} />
@ -222,28 +259,16 @@
{/if}
<style>
.input {
width: 500px;
display: flex;
.config-field {
display: grid;
grid-template-columns: 20% 1fr;
grid-gap: var(--spacing-l);
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 {
display: grid;
grid-template-columns: 1fr 1fr 50px;
grid-template-columns: 1fr 1fr 5%;
gap: var(--spacing-l);
}
@ -260,12 +285,6 @@
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;
}
@ -278,31 +297,18 @@
white-space: pre-wrap;
}
header {
display: flex;
align-items: center;
}
.viewer-controls {
display: flex;
flex-direction: row;
margin-left: auto;
direction: rtl;
z-index: 5;
/* margin-left: auto; */
/* direction: rtl; */
/* z-index: 5; */
gap: var(--spacing-m);
min-width: 150px;
}
.viewer {
margin-top: -28px;
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;
/* margin-top: -28px; */
/* z-index: -2; */
}
</style>

View File

@ -8,11 +8,11 @@
const tabs = [
{
title: "Tables",
title: "Internal",
key: "table",
},
{
title: "Data Sources",
title: "External",
key: "datasource",
},
]
@ -67,6 +67,7 @@
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-l);
background: var(--background);
}
.nav {

View File

@ -36,6 +36,8 @@
<style>
section {
overflow: scroll;
width: 800px;
margin: 0 auto;
}
::-webkit-scrollbar {
width: 0px;

View File

@ -1,9 +1,10 @@
<script>
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 { notifier } from "builderStore/store/notifications"
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
import ICONS from "components/backend/DatasourceNavigator/icons"
$: datasource = $backendUiStore.datasources.find(
ds => ds._id === $backendUiStore.selectedDatasourceId
@ -28,22 +29,35 @@
<section>
<Spacer medium />
<header>
<div class="datasource-icon">
<svelte:component
this={ICONS[datasource.source]}
height="30"
width="30" />
</div>
<h3 class="section-title">{datasource.name}</h3>
</header>
<Spacer extraLarge />
<div class="container">
<div class="config-header">
<h5>Configuration</h5>
<Heading small>Configuration</Heading>
<Button secondary on:click={saveDatasource}>Save</Button>
</div>
<Body small grey>
Connect your database to Budibase using the config below.
</Body>
<Spacer medium />
<IntegrationConfigForm integration={datasource.config} />
</div>
<Spacer extraLarge />
<div class="container">
<hr />
<Spacer extraLarge />
<div class="query-header">
<h5>Queries</h5>
<Button blue on:click={() => $goto('../new')}>Create Query</Button>
<Heading small>Queries</Heading>
<Button secondary on:click={() => $goto('../new')}>Add Query</Button>
</div>
<Spacer extraLarge />
<div class="query-list">
@ -54,7 +68,6 @@
<p></p>
</div>
{/each}
<Spacer medium />
</div>
</div>
</section>
@ -71,6 +84,8 @@
header {
margin: 0 0 var(--spacing-xs) 0;
display: flex;
gap: var(--spacing-m);
}
.section-title {
@ -85,13 +100,12 @@
.container {
border-radius: var(--border-radius-m);
background: var(--background);
padding: var(--layout-s);
margin: 0 auto;
}
h5 {
margin: 0 !important;
font-size: var(--font-size-l);
}
.query-header {
@ -115,7 +129,8 @@
display: grid;
grid-template-columns: 2fr 0.75fr 20px;
align-items: center;
padding: var(--spacing-m) var(--layout-xs);
padding-left: var(--spacing-m);
padding-right: var(--spacing-m);
gap: var(--layout-xs);
transition: 200ms background ease;
}

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://airtable.com/api",
friendlyName: "Airtable",
datasource: {
apiKey: {
type: FIELD_TYPES.STRING,

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://github.com/arangodb/arangojs",
friendlyName: "ArangoDB",
datasource: {
url: {
type: FIELD_TYPES.STRING,

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://docs.couchdb.org/en/stable/",
friendlyName: "CouchDB",
datasource: {
url: {
type: FIELD_TYPES.STRING,

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://github.com/dabit3/dynamodb-documentclient-cheat-sheet",
friendlyName: "DynamoDB",
datasource: {
region: {
type: FIELD_TYPES.STRING,

View File

@ -4,6 +4,7 @@ const { QUERY_TYPES, FIELD_TYPES } = require("./Integration")
const SCHEMA = {
docs:
"https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
friendlyName: "ElasticSearch",
datasource: {
url: {
type: "string",

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://github.com/tediousjs/node-mssql",
friendlyName: "MS SQL Server",
datasource: {
user: {
type: FIELD_TYPES.STRING,

View File

@ -3,6 +3,9 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
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: {
connectionString: {
type: FIELD_TYPES.STRING,

View File

@ -3,6 +3,7 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://github.com/mysqljs/mysql",
friendlyName: "MySQL",
datasource: {
host: {
type: FIELD_TYPES.STRING,

View File

@ -2,6 +2,7 @@ const { Client } = require("pg")
const SCHEMA = {
docs: "https://node-postgres.com",
friendlyName: "PostgreSQL",
datasource: {
host: {
type: "string",

View File

@ -3,15 +3,22 @@ const { FIELD_TYPES, QUERY_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://github.com/node-fetch/node-fetch",
friendlyName: "REST API",
datasource: {
url: {
type: FIELD_TYPES.STRING,
default: "localhost",
required: true,
},
defaultHeaders: {
type: FIELD_TYPES.OBJECT,
required: false,
default: {},
},
},
query: {
create: {
displayName: "POST",
type: QUERY_TYPES.FIELDS,
fields: {
path: {
@ -26,6 +33,7 @@ const SCHEMA = {
},
},
read: {
displayName: "GET",
type: QUERY_TYPES.FIELDS,
fields: {
path: {
@ -37,6 +45,7 @@ const SCHEMA = {
},
},
update: {
displayName: "PUT",
type: QUERY_TYPES.FIELDS,
fields: {
path: {
@ -51,6 +60,7 @@ const SCHEMA = {
},
},
delete: {
displayName: "DELETE",
type: QUERY_TYPES.FIELDS,
fields: {
path: {
@ -75,7 +85,10 @@ class RestIntegration {
async create({ path, headers = {}, json }) {
const response = await fetch(this.config.url + path, {
method: "POST",
headers,
headers: {
...this.config.defaultHeaders,
...headers,
},
body: JSON.stringify(json),
})
@ -84,7 +97,10 @@ class RestIntegration {
async read({ path, headers = {} }) {
const response = await fetch(this.config.url + path, {
headers,
headers: {
...this.config.defaultHeaders,
...headers,
},
})
return await response.json()
@ -93,7 +109,10 @@ class RestIntegration {
async update({ path, headers = {}, json }) {
const response = await fetch(this.config.url + path, {
method: "POST",
headers,
headers: {
...this.config.defaultHeaders,
...headers,
},
body: JSON.stringify(json),
})
@ -103,7 +122,10 @@ class RestIntegration {
async delete({ path, headers = {} }) {
const response = await fetch(this.config.url + path, {
method: "DELETE",
headers,
headers: {
...this.config.defaultHeaders,
...headers,
},
})
return await response.json()

View File

@ -2,6 +2,7 @@ const AWS = require("aws-sdk")
const SCHEMA = {
docs: "https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html",
friendlyName: "Amazon S3",
datasource: {
region: {
type: "string",