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