Merge pull request #15744 from Budibase/BUDI-9127/fix-save-button-state

Fix save button state on rest
This commit is contained in:
Adria Navarro 2025-03-14 12:11:53 +01:00 committed by GitHub
commit 9d20f33a40
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 62 deletions

View File

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

View File

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

View File

@ -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">
{#if query._id}
<ConnectedQueryScreens sourceId={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
on:change={paramsChanged} bind:object={breakQs}
object={breakQs}
name="param" name="param"
headings headings
bindings={mergedBindings} bindings={mergedBindings}
bindingDrawerLeft="260px" 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>