diff --git a/packages/bbui/src/Icon/Icon.svelte b/packages/bbui/src/Icon/Icon.svelte index 2f39fde0d9..fbaac7098c 100644 --- a/packages/bbui/src/Icon/Icon.svelte +++ b/packages/bbui/src/Icon/Icon.svelte @@ -9,6 +9,7 @@ export let size = "M" export let hoverable = false export let disabled = false + export let color $: rotation = getRotation(direction) @@ -25,7 +26,9 @@ focusable="false" aria-hidden={hidden} aria-label={name} - style={`transform: rotate(${rotation}deg)`} + style={`transform: rotate(${rotation}deg); ${ + color ? `color: ${color};` : "" + }`} > diff --git a/packages/bbui/src/Label/Label.svelte b/packages/bbui/src/Label/Label.svelte index a3a94b2836..7ec378fc29 100644 --- a/packages/bbui/src/Label/Label.svelte +++ b/packages/bbui/src/Label/Label.svelte @@ -19,6 +19,7 @@
(showTooltip = true)} on:mouseleave={() => (showTooltip = false)} > @@ -44,6 +45,7 @@ } .container { display: flex; + align-items: center; } .icon-container { position: relative; @@ -64,4 +66,8 @@ .icon { transform: scale(0.75); } + .icon-small { + margin-top: -2px; + margin-bottom: -5px; + } diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte index c801ff44fd..235e103bb1 100644 --- a/packages/bbui/src/Tabs/Tabs.svelte +++ b/packages/bbui/src/Tabs/Tabs.svelte @@ -11,6 +11,8 @@ export let quiet = false export let emphasized = false + let thisSelected = undefined + let _id = id() const tab = writable({ title: selected, id: _id, emphasized }) setContext("tab", tab) @@ -20,9 +22,18 @@ const dispatch = createEventDispatcher() $: { - if ($tab.title !== selected) { - selected = $tab.title - dispatch("select", selected) + if (thisSelected !== selected) { + thisSelected = selected + dispatch("select", thisSelected) + } else if ($tab.title !== thisSelected) { + thisSelected = $tab.title + dispatch("select", thisSelected) + } + if ($tab.title !== thisSelected) { + tab.update(state => { + state.title = thisSelected + return state + }) } } diff --git a/packages/builder/assets/bb-spaceship.svg b/packages/builder/assets/bb-spaceship.svg new file mode 100755 index 0000000000..a0bc5a49cd --- /dev/null +++ b/packages/builder/assets/bb-spaceship.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte index 796f70b43b..18143c2071 100644 --- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte @@ -8,6 +8,7 @@ import EditQueryPopover from "./popovers/EditQueryPopover.svelte" import NavItem from "components/common/NavItem.svelte" import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte" + import { customQueryIconText, customQueryIconColor } from "helpers/data/utils" import ICONS from "./icons" let openDataSources = [] @@ -129,6 +130,8 @@ - import { Icon } from "@budibase/bbui" + import { Icon, Body } from "@budibase/bbui" - +
+ + +
diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index ec7994be84..2d7dc745d1 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -11,6 +11,8 @@ export let selected = false export let opened = false export let draggable = false + export let iconText + export let iconColor const dispatch = createEventDispatcher() @@ -42,9 +44,13 @@ {/if} - {#if icon} + {#if iconText} +
+ {iconText} +
+ {:else if icon}
- +
{/if}
{text}
@@ -123,4 +129,9 @@ justify-content: center; align-items: center; } + + .iconText { + margin-top: 1px; + font-size: var(--spectrum-global-dimension-font-size-50); + } diff --git a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte index 220bd41205..62a89af5b4 100644 --- a/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte +++ b/packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte @@ -17,7 +17,7 @@ queries as queriesStore, } from "stores/backend" import { datasources, integrations } from "stores/backend" - import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" + import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" import { makePropSafe as safe } from "@budibase/string-templates" @@ -148,15 +148,15 @@ /> {#if value?.type === "query"} - + - + {#if getQueryParams(value).length > 0} - {/if} import { Select, Layout, Input, Checkbox } from "@budibase/bbui" import { datasources, integrations, queries } from "stores/backend" - import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" + import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" export let parameters @@ -53,10 +53,10 @@ {#if query?.parameters?.length > 0}
- ({ name, value })) let fieldActivity = [] @@ -76,8 +79,8 @@ {#if Object.keys(object || {}).length > 0} {#if headings}
- - + + {#if toggle} {/if} @@ -85,12 +88,16 @@ {/if}
{#each fields as field, idx} - + {#if options} @@ -127,4 +134,7 @@ .container-active { grid-template-columns: 1fr 1fr 50px 20px; } + .readOnly { + grid-template-columns: 1fr 1fr; + } diff --git a/packages/builder/src/components/integration/QueryParameterBuilder.svelte b/packages/builder/src/components/integration/QueryBindingBuilder.svelte similarity index 52% rename from packages/builder/src/components/integration/QueryParameterBuilder.svelte rename to packages/builder/src/components/integration/QueryBindingBuilder.svelte index 618cb2b442..dd688dceb6 100644 --- a/packages/builder/src/components/integration/QueryParameterBuilder.svelte +++ b/packages/builder/src/components/integration/QueryBindingBuilder.svelte @@ -1,5 +1,5 @@ - -
- Parameters + +
+ Bindings {#if !bindable} - + {/if}
{#if !bindable} - Parameters come in two parts: the parameter name, and a default/fallback - value. + Bindings come in two parts: the binding name, and a default/fallback + value. These bindings can be used as Handlebars expressions throughout the + query. {:else} - Enter a value for each parameter. The default values will be used for any + Enter a value for each binding. The default values will be used for any values left blank. {/if} -
- {#each parameters as parameter, idx} +
+ {#each bindings as binding, idx} {#if bindable} onBindingChange(parameter.name, evt.detail)} + on:change={evt => onBindingChange(binding.name, evt.detail)} value={runtimeToReadableBinding( - bindings, - customParams?.[parameter.name] + bindableOptions, + customParams?.[binding.name] )} - {bindings} + {bindableOptions} /> {:else} - deleteQueryParameter(idx)} - /> + deleteQueryBinding(idx)} /> {/if} {/each}
diff --git a/packages/builder/src/components/integration/QueryViewer.svelte b/packages/builder/src/components/integration/QueryViewer.svelte index aed3c4730d..f14d1d2b88 100644 --- a/packages/builder/src/components/integration/QueryViewer.svelte +++ b/packages/builder/src/components/integration/QueryViewer.svelte @@ -17,7 +17,7 @@ import ExtraQueryConfig from "./ExtraQueryConfig.svelte" import IntegrationQueryEditor from "components/integration/index.svelte" import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte" - import ParameterBuilder from "components/integration/QueryParameterBuilder.svelte" + import BindingBuilder from "components/integration/QueryBindingBuilder.svelte" import { datasources, integrations, queries } from "stores/backend" import { capitalise } from "../../helpers" import CodeMirrorEditor from "components/common/CodeMirrorEditor.svelte" @@ -120,7 +120,7 @@ config={integrationInfo.extra} /> {/if} - + {/if}
{#if shouldShowQueryConfig} diff --git a/packages/builder/src/helpers/data/utils.js b/packages/builder/src/helpers/data/utils.js index b1c4139d52..64cb4df5c2 100644 --- a/packages/builder/src/helpers/data/utils.js +++ b/packages/builder/src/helpers/data/utils.js @@ -1,3 +1,5 @@ +import { IntegrationTypes } from "constants/backend" + export function schemaToFields(schema) { const response = {} if (schema && typeof schema === "object") { @@ -49,3 +51,60 @@ export function buildQueryString(obj) { } return str } + +export function keyValueToQueryParameters(obj) { + let array = [] + if (obj && typeof obj === "object") { + for (let [key, value] of Object.entries(obj)) { + array.push({ name: key, default: value }) + } + } + return array +} + +export function queryParametersToKeyValue(array) { + let obj = {} + if (Array.isArray(array)) { + for (let param of array) { + obj[param.name] = param.default + } + } + return obj +} + +export function customQueryIconText(datasource, query) { + if (datasource.source !== IntegrationTypes.REST) { + return + } + switch (query.queryVerb) { + case "create": + return "POST" + case "update": + return "PUT" + case "read": + return "GET" + case "delete": + return "DELETE" + case "patch": + return "PATCH" + } +} + +export function customQueryIconColor(datasource, query) { + if (datasource.source !== IntegrationTypes.REST) { + return + } + switch (query.queryVerb) { + case "create": + return "#dcc339" + case "update": + return "#5197ec" + case "read": + return "#53a761" + case "delete": + return "#ea7d82" + case "patch": + default: + return + } +} diff --git a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte index 7d30b8a0a0..64572280ca 100644 --- a/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte +++ b/packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/rest/[query]/index.svelte @@ -13,6 +13,7 @@ Heading, RadioGroup, Label, + Body, TextArea, Table, notifications, @@ -30,6 +31,8 @@ schemaToFields, breakQueryString, buildQueryString, + keyValueToQueryParameters, + queryParametersToKeyValue, } from "helpers/data/utils" import { RestBodyTypes as bodyTypes, @@ -37,9 +40,12 @@ } from "constants/backend" import JSONPreview from "components/integration/JSONPreview.svelte" import AccessLevelSelect from "components/integration/AccessLevelSelect.svelte" + import Placeholder from "assets/bb-spaceship.svg" + import { cloneDeep } from "lodash/fp" let query, datasource - let breakQs = {} + let breakQs = {}, + bindings = {} let url = "" let saveId let response, schema, isGet @@ -56,7 +62,7 @@ response?.info?.code >= 200 && response?.info?.code <= 206 function getSelectedQuery() { - return ( + return cloneDeep( $queries.list.find(q => q._id === $queries.selected) || { datasourceId: $params.selectedDatasource, parameters: [], @@ -85,7 +91,9 @@ return qs.length > 0 ? `${newUrl}?${qs}` : newUrl } - function learnMoreBanner() {} + function learnMoreBanner() { + window.open("https://docs.budibase.com/building-apps/data/transformers") + } function buildQuery() { const newQuery = { ...query } @@ -93,6 +101,7 @@ newQuery.fields.path = url.split("?")[0] newQuery.fields.queryString = queryString newQuery.schema = fieldsToSchema(schema) + newQuery.parameters = keyValueToQueryParameters(bindings) return newQuery } @@ -101,6 +110,7 @@ try { const { _id } = await queries.save(toSave.datasourceId, toSave) saveId = _id + query = getSelectedQuery() notifications.success(`Request saved successfully.`) } catch (err) { notifications.error(`Error creating query. ${err.message}`) @@ -114,6 +124,7 @@ notifications.info("Request did not return any data.") } else { response.info = response.info || { code: 200 } + schema = response.schema notifications.success("Request sent successfully.") } } catch (err) { @@ -127,6 +138,7 @@ breakQs = breakQueryString(qs) url = buildUrl(query.fields.path, breakQs) schema = schemaToFields(query.schema) + bindings = queryParametersToKeyValue(query.parameters) if (query && !query.transformer) { query.transformer = "return data" } @@ -168,11 +180,21 @@ />
- +
- + + + + @@ -219,62 +241,88 @@
- - - {#if !response} - Response - {:else} - - -
- +
+ + + {#if !response && Object.keys(schema).length === 0} + Response +
+
+ placeholder + {"enter a url in the textbox above and click send to get a response".toUpperCase()}
- - - - - -