Merge branch 'master' into feature/sql-attachments
This commit is contained in:
commit
14506c3b75
|
@ -9,6 +9,8 @@
|
||||||
export let type = "label"
|
export let type = "label"
|
||||||
export let size = "M"
|
export let size = "M"
|
||||||
|
|
||||||
|
export const disableEditingState = () => setEditing(false)
|
||||||
|
|
||||||
let editing = false
|
let editing = false
|
||||||
|
|
||||||
function setEditing(state) {
|
function setEditing(state) {
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
import { lowercase } from "@/helpers"
|
import { lowercase } from "@/helpers"
|
||||||
import DrawerBindableInput from "@/components/common/bindings/DrawerBindableInput.svelte"
|
import DrawerBindableInput from "@/components/common/bindings/DrawerBindableInput.svelte"
|
||||||
|
|
||||||
let dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
export let defaults
|
export let defaults
|
||||||
export let object = defaults || {}
|
export let object = defaults || {}
|
||||||
|
@ -47,10 +47,17 @@
|
||||||
}))
|
}))
|
||||||
let fieldActivity = buildFieldActivity(activity)
|
let fieldActivity = buildFieldActivity(activity)
|
||||||
|
|
||||||
$: object = fields.reduce(
|
$: fullObject = fields.reduce((acc, next) => {
|
||||||
(acc, next) => ({ ...acc, [next.name]: next.value }),
|
acc[next.name] = next.value
|
||||||
{}
|
return acc
|
||||||
)
|
}, {})
|
||||||
|
|
||||||
|
$: object = Object.entries(fullObject).reduce((acc, [key, next]) => {
|
||||||
|
if (key) {
|
||||||
|
acc[key] = next
|
||||||
|
}
|
||||||
|
return acc
|
||||||
|
}, {})
|
||||||
|
|
||||||
function buildFieldActivity(obj) {
|
function buildFieldActivity(obj) {
|
||||||
if (!obj || typeof obj !== "object") {
|
if (!obj || typeof obj !== "object") {
|
||||||
|
@ -78,16 +85,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function changed() {
|
function changed() {
|
||||||
|
// Required for reactivity
|
||||||
fields = fields
|
fields = fields
|
||||||
const newActivity = {}
|
const newActivity = {}
|
||||||
|
const trimmedFields = []
|
||||||
for (let idx = 0; idx < fields.length; idx++) {
|
for (let idx = 0; idx < fields.length; idx++) {
|
||||||
const fieldName = fields[idx].name
|
const fieldName = fields[idx].name
|
||||||
if (fieldName) {
|
if (fieldName) {
|
||||||
newActivity[fieldName] = fieldActivity[idx]
|
newActivity[fieldName] = fieldActivity[idx]
|
||||||
|
trimmedFields.push(fields[idx])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
activity = newActivity
|
activity = newActivity
|
||||||
dispatch("change", fields)
|
dispatch("change", trimmedFields)
|
||||||
}
|
}
|
||||||
|
|
||||||
function isJsonArray(value) {
|
function isJsonArray(value) {
|
||||||
|
@ -102,7 +112,7 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. -->
|
<!-- Builds Objects with Key Value Pairs. Useful for building things like Request Headers. -->
|
||||||
{#if Object.keys(object || {}).length > 0}
|
{#if Object.keys(fullObject || {}).length > 0}
|
||||||
{#if headings}
|
{#if headings}
|
||||||
<div class="container" class:container-active={toggle}>
|
<div class="container" class:container-active={toggle}>
|
||||||
<Label {tooltip}>{keyHeading || keyPlaceholder}</Label>
|
<Label {tooltip}>{keyHeading || keyPlaceholder}</Label>
|
||||||
|
|
|
@ -56,12 +56,13 @@
|
||||||
let query, datasource
|
let query, datasource
|
||||||
let breakQs = {},
|
let breakQs = {},
|
||||||
requestBindings = {}
|
requestBindings = {}
|
||||||
let saveId, url
|
let saveId
|
||||||
let response, schema, enabledHeaders
|
let response, schema, enabledHeaders
|
||||||
let authConfigId
|
|
||||||
let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings
|
let dynamicVariables, addVariableModal, varBinding, globalDynamicBindings
|
||||||
let restBindings = getRestBindings()
|
let restBindings = getRestBindings()
|
||||||
let nestedSchemaFields = {}
|
let nestedSchemaFields = {}
|
||||||
|
let saving
|
||||||
|
let queryNameLabel
|
||||||
|
|
||||||
$: staticVariables = datasource?.config?.staticVariables || {}
|
$: staticVariables = datasource?.config?.staticVariables || {}
|
||||||
|
|
||||||
|
@ -91,7 +92,7 @@
|
||||||
$: datasourceType = datasource?.source
|
$: datasourceType = datasource?.source
|
||||||
$: integrationInfo = $integrations[datasourceType]
|
$: integrationInfo = $integrations[datasourceType]
|
||||||
$: queryConfig = integrationInfo?.query
|
$: queryConfig = integrationInfo?.query
|
||||||
$: url = buildUrl(url, breakQs)
|
$: url = buildUrl(query?.fields?.path, breakQs)
|
||||||
$: checkQueryName(url)
|
$: checkQueryName(url)
|
||||||
$: responseSuccess = response?.info?.code >= 200 && response?.info?.code < 400
|
$: responseSuccess = response?.info?.code >= 200 && response?.info?.code < 400
|
||||||
$: isGet = query?.queryVerb === "read"
|
$: isGet = query?.queryVerb === "read"
|
||||||
|
@ -103,6 +104,10 @@
|
||||||
|
|
||||||
$: runtimeUrlQueries = readableToRuntimeMap(mergedBindings, breakQs)
|
$: runtimeUrlQueries = readableToRuntimeMap(mergedBindings, breakQs)
|
||||||
|
|
||||||
|
$: originalQuery = originalQuery ?? cloneDeep(query)
|
||||||
|
$: builtQuery = buildQuery(query, runtimeUrlQueries, requestBindings)
|
||||||
|
$: isModified = JSON.stringify(originalQuery) !== JSON.stringify(builtQuery)
|
||||||
|
|
||||||
function getSelectedQuery() {
|
function getSelectedQuery() {
|
||||||
return cloneDeep(
|
return cloneDeep(
|
||||||
$queries.list.find(q => q._id === queryId) || {
|
$queries.list.find(q => q._id === queryId) || {
|
||||||
|
@ -126,7 +131,8 @@
|
||||||
?.trim() || inputUrl
|
?.trim() || inputUrl
|
||||||
|
|
||||||
function checkQueryName(inputUrl = null) {
|
function checkQueryName(inputUrl = null) {
|
||||||
if (query && (!query.name || query.flags.urlName)) {
|
if (query && (!query.name || query.flags?.urlName)) {
|
||||||
|
query.flags ??= {}
|
||||||
query.flags.urlName = true
|
query.flags.urlName = true
|
||||||
query.name = cleanUrl(inputUrl)
|
query.name = cleanUrl(inputUrl)
|
||||||
}
|
}
|
||||||
|
@ -147,9 +153,12 @@
|
||||||
return qs.length === 0 ? newUrl : `${newUrl}?${qs}`
|
return qs.length === 0 ? newUrl : `${newUrl}?${qs}`
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildQuery() {
|
function buildQuery(fromQuery, urlQueries, requestBindings) {
|
||||||
const newQuery = cloneDeep(query)
|
if (!fromQuery) {
|
||||||
const queryString = restUtils.buildQueryString(runtimeUrlQueries)
|
return
|
||||||
|
}
|
||||||
|
const newQuery = cloneDeep(fromQuery)
|
||||||
|
const queryString = restUtils.buildQueryString(urlQueries)
|
||||||
|
|
||||||
newQuery.parameters = restUtils.keyValueToQueryParameters(requestBindings)
|
newQuery.parameters = restUtils.keyValueToQueryParameters(requestBindings)
|
||||||
newQuery.fields.requestBody =
|
newQuery.fields.requestBody =
|
||||||
|
@ -157,9 +166,8 @@
|
||||||
? readableToRuntimeMap(mergedBindings, newQuery.fields.requestBody)
|
? readableToRuntimeMap(mergedBindings, newQuery.fields.requestBody)
|
||||||
: readableToRuntimeBinding(mergedBindings, newQuery.fields.requestBody)
|
: readableToRuntimeBinding(mergedBindings, newQuery.fields.requestBody)
|
||||||
|
|
||||||
newQuery.fields.path = url.split("?")[0]
|
newQuery.fields.path = url?.split("?")[0]
|
||||||
newQuery.fields.queryString = queryString
|
newQuery.fields.queryString = queryString
|
||||||
newQuery.fields.authConfigId = authConfigId
|
|
||||||
newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders)
|
newQuery.fields.disabledHeaders = restUtils.flipHeaderState(enabledHeaders)
|
||||||
newQuery.schema = schema || {}
|
newQuery.schema = schema || {}
|
||||||
newQuery.nestedSchemaFields = nestedSchemaFields || {}
|
newQuery.nestedSchemaFields = nestedSchemaFields || {}
|
||||||
|
@ -168,13 +176,12 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
async function saveQuery() {
|
async function saveQuery() {
|
||||||
const toSave = buildQuery()
|
const toSave = builtQuery
|
||||||
|
saving = true
|
||||||
try {
|
try {
|
||||||
const isNew = !query._rev
|
const isNew = !query._rev
|
||||||
const { _id } = await queries.save(toSave.datasourceId, toSave)
|
const { _id } = await queries.save(toSave.datasourceId, toSave)
|
||||||
saveId = _id
|
saveId = _id
|
||||||
query = getSelectedQuery()
|
|
||||||
notifications.success(`Request saved successfully`)
|
|
||||||
if (dynamicVariables) {
|
if (dynamicVariables) {
|
||||||
datasource.config.dynamicVariables = rebuildVariables(saveId)
|
datasource.config.dynamicVariables = rebuildVariables(saveId)
|
||||||
datasource = await datasources.save({
|
datasource = await datasources.save({
|
||||||
|
@ -182,6 +189,13 @@
|
||||||
datasource,
|
datasource,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifications.success(`Request saved successfully`)
|
||||||
|
if (isNew) {
|
||||||
|
$goto(`../../${_id}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
query = getSelectedQuery()
|
||||||
prettifyQueryRequestBody(
|
prettifyQueryRequestBody(
|
||||||
query,
|
query,
|
||||||
requestBindings,
|
requestBindings,
|
||||||
|
@ -189,11 +203,15 @@
|
||||||
staticVariables,
|
staticVariables,
|
||||||
restBindings
|
restBindings
|
||||||
)
|
)
|
||||||
if (isNew) {
|
|
||||||
$goto(`../../${_id}`)
|
// Force rebuilding original query
|
||||||
}
|
originalQuery = null
|
||||||
|
|
||||||
|
queryNameLabel.disableEditingState()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
notifications.error(`Error saving query`)
|
notifications.error(`Error saving query`)
|
||||||
|
} finally {
|
||||||
|
saving = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +245,7 @@
|
||||||
async function runQuery() {
|
async function runQuery() {
|
||||||
try {
|
try {
|
||||||
await validateQuery()
|
await validateQuery()
|
||||||
response = await queries.preview(buildQuery())
|
response = await queries.preview(builtQuery)
|
||||||
if (response.rows.length === 0) {
|
if (response.rows.length === 0) {
|
||||||
notifications.info("Request did not return any data")
|
notifications.info("Request did not return any data")
|
||||||
} else {
|
} else {
|
||||||
|
@ -249,22 +267,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getAuthConfigId = () => {
|
|
||||||
let id = query.fields.authConfigId
|
|
||||||
if (id) {
|
|
||||||
// find the matching config on the datasource
|
|
||||||
const matchedConfig = datasource?.config?.authConfigs?.filter(
|
|
||||||
c => c._id === id
|
|
||||||
)[0]
|
|
||||||
// clear the id if the config is not found (deleted)
|
|
||||||
// i.e. just show 'None' in the dropdown
|
|
||||||
if (!matchedConfig) {
|
|
||||||
id = undefined
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
|
|
||||||
const buildAuthConfigs = datasource => {
|
const buildAuthConfigs = datasource => {
|
||||||
if (datasource?.config?.authConfigs) {
|
if (datasource?.config?.authConfigs) {
|
||||||
return datasource.config.authConfigs.map(c => ({
|
return datasource.config.authConfigs.map(c => ({
|
||||||
|
@ -375,13 +377,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const paramsChanged = evt => {
|
|
||||||
breakQs = {}
|
|
||||||
for (let param of evt.detail) {
|
|
||||||
breakQs[param.name] = param.value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const urlChanged = evt => {
|
const urlChanged = evt => {
|
||||||
breakQs = {}
|
breakQs = {}
|
||||||
const qs = evt.target.value.split("?")[1]
|
const qs = evt.target.value.split("?")[1]
|
||||||
|
@ -426,9 +421,7 @@
|
||||||
) {
|
) {
|
||||||
query.fields.path = `${datasource.config.url}/${path ? path : ""}`
|
query.fields.path = `${datasource.config.url}/${path ? path : ""}`
|
||||||
}
|
}
|
||||||
url = buildUrl(query.fields.path, breakQs)
|
|
||||||
requestBindings = restUtils.queryParametersToKeyValue(query.parameters)
|
requestBindings = restUtils.queryParametersToKeyValue(query.parameters)
|
||||||
authConfigId = getAuthConfigId()
|
|
||||||
if (!query.fields.disabledHeaders) {
|
if (!query.fields.disabledHeaders) {
|
||||||
query.fields.disabledHeaders = {}
|
query.fields.disabledHeaders = {}
|
||||||
}
|
}
|
||||||
|
@ -497,6 +490,7 @@
|
||||||
<Layout gap="S">
|
<Layout gap="S">
|
||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<EditableLabel
|
<EditableLabel
|
||||||
|
bind:this={queryNameLabel}
|
||||||
type="heading"
|
type="heading"
|
||||||
bind:value={query.name}
|
bind:value={query.name}
|
||||||
defaultValue="Untitled"
|
defaultValue="Untitled"
|
||||||
|
@ -504,7 +498,9 @@
|
||||||
on:save={saveQuery}
|
on:save={saveQuery}
|
||||||
/>
|
/>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
<ConnectedQueryScreens sourceId={query._id} />
|
{#if query._id}
|
||||||
|
<ConnectedQueryScreens sourceId={query._id} />
|
||||||
|
{/if}
|
||||||
<div class="access">
|
<div class="access">
|
||||||
<Label>Access</Label>
|
<Label>Access</Label>
|
||||||
<AccessLevelSelect {query} {saveId} />
|
<AccessLevelSelect {query} {saveId} />
|
||||||
|
@ -524,13 +520,13 @@
|
||||||
<div class="url">
|
<div class="url">
|
||||||
<Input
|
<Input
|
||||||
on:blur={urlChanged}
|
on:blur={urlChanged}
|
||||||
bind:value={url}
|
bind:value={query.fields.path}
|
||||||
placeholder="http://www.api.com/endpoint"
|
placeholder="http://www.api.com/endpoint"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
|
<Button primary disabled={!url} on:click={runQuery}>Send</Button>
|
||||||
<Button
|
<Button
|
||||||
disabled={!query.name}
|
disabled={!query.name || !isModified || saving}
|
||||||
cta
|
cta
|
||||||
on:click={saveQuery}
|
on:click={saveQuery}
|
||||||
tooltip={!hasSchema
|
tooltip={!hasSchema
|
||||||
|
@ -557,16 +553,13 @@
|
||||||
/>
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Params">
|
<Tab title="Params">
|
||||||
{#key breakQs}
|
<KeyValueBuilder
|
||||||
<KeyValueBuilder
|
bind:object={breakQs}
|
||||||
on:change={paramsChanged}
|
name="param"
|
||||||
object={breakQs}
|
headings
|
||||||
name="param"
|
bindings={mergedBindings}
|
||||||
headings
|
bindingDrawerLeft="260px"
|
||||||
bindings={mergedBindings}
|
/>
|
||||||
bindingDrawerLeft="260px"
|
|
||||||
/>
|
|
||||||
{/key}
|
|
||||||
</Tab>
|
</Tab>
|
||||||
<Tab title="Headers">
|
<Tab title="Headers">
|
||||||
<KeyValueBuilder
|
<KeyValueBuilder
|
||||||
|
@ -654,7 +647,7 @@
|
||||||
label="Auth"
|
label="Auth"
|
||||||
labelPosition="left"
|
labelPosition="left"
|
||||||
placeholder="None"
|
placeholder="None"
|
||||||
bind:value={authConfigId}
|
bind:value={query.fields.authConfigId}
|
||||||
options={authConfigs}
|
options={authConfigs}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,20 +1,29 @@
|
||||||
<script>
|
<script>
|
||||||
import RestAuthenticationBuilder from "./RestAuthenticationBuilder.svelte"
|
import RestAuthenticationBuilder from "./RestAuthenticationBuilder.svelte"
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import SaveDatasourceButton from "../SaveDatasourceButton.svelte"
|
|
||||||
import Panel from "../Panel.svelte"
|
import Panel from "../Panel.svelte"
|
||||||
import Tooltip from "../Tooltip.svelte"
|
import Tooltip from "../Tooltip.svelte"
|
||||||
|
import { integrations } from "@/stores/builder"
|
||||||
|
import { notifications } from "@budibase/bbui"
|
||||||
|
|
||||||
export let datasource
|
export let datasource
|
||||||
$: updatedDatasource = cloneDeep(datasource)
|
$: updatedDatasource = cloneDeep(datasource)
|
||||||
|
|
||||||
const updateAuthConfigs = newAuthConfigs => {
|
const updateAuthConfigs = async newAuthConfigs => {
|
||||||
updatedDatasource.config.authConfigs = newAuthConfigs
|
updatedDatasource.config.authConfigs = newAuthConfigs
|
||||||
|
|
||||||
|
try {
|
||||||
|
await integrations.saveDatasource(updatedDatasource)
|
||||||
|
notifications.success(
|
||||||
|
`Datasource ${updatedDatasource.name} updated successfully`
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(`Error saving datasource: ${error.message}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Panel>
|
<Panel>
|
||||||
<SaveDatasourceButton slot="controls" {datasource} {updatedDatasource} />
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
slot="tooltip"
|
slot="tooltip"
|
||||||
title="REST Authentication"
|
title="REST Authentication"
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
<script>
|
<script>
|
||||||
import { get } from "svelte/store"
|
|
||||||
import { isEqual } from "lodash"
|
import { isEqual } from "lodash"
|
||||||
import { integrationForDatasource } from "@/stores/selectors"
|
import { integrations } from "@/stores/builder"
|
||||||
import { integrations, datasources } from "@/stores/builder"
|
|
||||||
import { notifications, Button } from "@budibase/bbui"
|
import { notifications, Button } from "@budibase/bbui"
|
||||||
|
|
||||||
export let datasource
|
export let datasource
|
||||||
|
@ -12,11 +10,7 @@
|
||||||
|
|
||||||
const save = async () => {
|
const save = async () => {
|
||||||
try {
|
try {
|
||||||
const integration = integrationForDatasource(
|
await integrations.saveDatasource(updatedDatasource)
|
||||||
get(integrations),
|
|
||||||
updatedDatasource
|
|
||||||
)
|
|
||||||
await datasources.save({ datasource: updatedDatasource, integration })
|
|
||||||
notifications.success(
|
notifications.success(
|
||||||
`Datasource ${updatedDatasource.name} updated successfully`
|
`Datasource ${updatedDatasource.name} updated successfully`
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import { writable, type Writable } from "svelte/store"
|
import { get, writable, type Writable } from "svelte/store"
|
||||||
import { API } from "@/api"
|
import { API } from "@/api"
|
||||||
import { Integration } from "@budibase/types"
|
import { Datasource, Integration } from "@budibase/types"
|
||||||
|
import { integrationForDatasource } from "@/stores/selectors"
|
||||||
|
import { datasources } from "./datasources"
|
||||||
|
|
||||||
type IntegrationsState = Record<string, Integration>
|
type IntegrationsState = Record<string, Integration>
|
||||||
|
|
||||||
|
@ -25,9 +27,15 @@ const createIntegrationsStore = () => {
|
||||||
store.set(integrations)
|
store.set(integrations)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveDatasource = async (datasource: Datasource) => {
|
||||||
|
const integration = integrationForDatasource(get(store), datasource)
|
||||||
|
await datasources.save({ datasource, integration })
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...store,
|
...store,
|
||||||
init,
|
init,
|
||||||
|
saveDatasource,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue