Merge pull request #3932 from Budibase/feature/query-variables
Fixes for new rest datasource
This commit is contained in:
commit
10d9928679
|
@ -10,6 +10,8 @@
|
|||
export let noHorizPadding = false
|
||||
export let quiet = false
|
||||
export let emphasized = false
|
||||
// overlay content from the tab bar onto tabs e.g. for a dropdown
|
||||
export let onTop = false
|
||||
|
||||
let thisSelected = undefined
|
||||
|
||||
|
@ -78,6 +80,7 @@
|
|||
'spectrum-Tabs--quiet'} spectrum-Tabs--{vertical
|
||||
? 'vertical'
|
||||
: 'horizontal'}"
|
||||
class:onTop
|
||||
>
|
||||
<slot />
|
||||
{#if $tab.info}
|
||||
|
@ -98,7 +101,9 @@
|
|||
.quiet {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
.onTop {
|
||||
z-index: 20;
|
||||
}
|
||||
.spectrum-Tabs {
|
||||
padding-left: var(--spacing-xl);
|
||||
padding-right: var(--spacing-xl);
|
||||
|
|
|
@ -137,7 +137,7 @@
|
|||
selected={$queries.selected === query._id}
|
||||
on:click={() => onClickQuery(query)}
|
||||
>
|
||||
<EditQueryPopover {query} />
|
||||
<EditQueryPopover {query} {onClickQuery} />
|
||||
</NavItem>
|
||||
{/each}
|
||||
{/if}
|
||||
|
|
|
@ -58,7 +58,7 @@
|
|||
/>
|
||||
{/if}
|
||||
<div>
|
||||
<ActionButton on:click={() => openConfigModal()} con="Add"
|
||||
<ActionButton on:click={() => openConfigModal()} icon="Add"
|
||||
>Add authentication</ActionButton
|
||||
>
|
||||
</div>
|
||||
|
|
|
@ -5,22 +5,29 @@
|
|||
import { datasources, queries } from "stores/backend"
|
||||
|
||||
export let query
|
||||
export let onClickQuery
|
||||
|
||||
let confirmDeleteDialog
|
||||
|
||||
async function deleteQuery() {
|
||||
const wasSelectedQuery = $queries.selected
|
||||
const selectedDatasource = $datasources.selected
|
||||
// need to calculate this before the query is deleted
|
||||
const navigateToDatasource = wasSelectedQuery === query._id
|
||||
|
||||
await queries.delete(query)
|
||||
if (wasSelectedQuery === query._id) {
|
||||
$goto(`./datasource/${selectedDatasource}`)
|
||||
await datasources.fetch()
|
||||
|
||||
if (navigateToDatasource) {
|
||||
await datasources.select(query.datasourceId)
|
||||
$goto(`./datasource/${query.datasourceId}`)
|
||||
}
|
||||
notifications.success("Query deleted")
|
||||
}
|
||||
|
||||
async function duplicateQuery() {
|
||||
try {
|
||||
await queries.duplicate(query)
|
||||
const newQuery = await queries.duplicate(query)
|
||||
onClickQuery(newQuery)
|
||||
} catch (e) {
|
||||
notifications.error(e.message)
|
||||
}
|
||||
|
|
|
@ -232,8 +232,12 @@
|
|||
const datasourceUrl = datasource?.config.url
|
||||
const qs = query?.fields.queryString
|
||||
breakQs = restUtils.breakQueryString(qs)
|
||||
if (datasourceUrl && !query.fields.path?.startsWith(datasourceUrl)) {
|
||||
const path = query.fields.path
|
||||
const path = query.fields.path
|
||||
if (
|
||||
datasourceUrl &&
|
||||
!path?.startsWith("http") &&
|
||||
!path?.startsWith("{{") // don't substitute the datasource url when query starts with a variable e.g. the upgrade path
|
||||
) {
|
||||
query.fields.path = `${datasource.config.url}/${path ? path : ""}`
|
||||
}
|
||||
url = buildUrl(query.fields.path, breakQs)
|
||||
|
@ -306,7 +310,7 @@
|
|||
</div>
|
||||
<Button cta disabled={!url} on:click={runQuery}>Send</Button>
|
||||
</div>
|
||||
<Tabs selected="Bindings" quiet noPadding noHorizPadding>
|
||||
<Tabs selected="Bindings" quiet noPadding noHorizPadding onTop>
|
||||
<Tab title="Bindings">
|
||||
<KeyValueBuilder
|
||||
bind:object={bindings}
|
||||
|
@ -450,7 +454,7 @@
|
|||
<Layout noPadding gap="S">
|
||||
<Body size="S">
|
||||
Create dynamic variables based on response body or headers
|
||||
from other queries.
|
||||
from this query.
|
||||
</Body>
|
||||
<KeyValueBuilder
|
||||
bind:object={dynamicVariables}
|
||||
|
|
|
@ -134,7 +134,7 @@ export function createQueriesStore() {
|
|||
list.map(q => q.name)
|
||||
)
|
||||
|
||||
actions.save(datasourceId, newQuery)
|
||||
return actions.save(datasourceId, newQuery)
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Query, QueryParameter } from "../../../../../../definitions/datasource"
|
||||
import { URL } from "url"
|
||||
|
||||
export interface ImportInfo {
|
||||
url: string
|
||||
name: string
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ export abstract class ImportSource {
|
|||
name: string,
|
||||
method: string,
|
||||
path: string,
|
||||
url: URL,
|
||||
queryString: string,
|
||||
headers: object = {},
|
||||
parameters: QueryParameter[] = [],
|
||||
|
@ -33,6 +34,7 @@ export abstract class ImportSource {
|
|||
const transformer = "return data"
|
||||
const schema = {}
|
||||
path = this.processPath(path)
|
||||
path = `${url.origin}/${path}`
|
||||
queryString = this.processQuery(queryString)
|
||||
const requestBody = JSON.stringify(body, null, 2)
|
||||
|
||||
|
|
|
@ -60,16 +60,19 @@ export class Curl extends ImportSource {
|
|||
return true
|
||||
}
|
||||
|
||||
getUrl = (): URL => {
|
||||
return new URL(this.curl.raw_url)
|
||||
}
|
||||
|
||||
getInfo = async (): Promise<ImportInfo> => {
|
||||
const url = new URL(this.curl.url)
|
||||
const url = this.getUrl()
|
||||
return {
|
||||
url: url.origin,
|
||||
name: url.hostname,
|
||||
}
|
||||
}
|
||||
|
||||
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
||||
const url = new URL(this.curl.raw_url)
|
||||
const url = this.getUrl()
|
||||
const name = url.pathname
|
||||
const path = url.pathname
|
||||
const method = this.curl.method
|
||||
|
@ -87,6 +90,7 @@ export class Curl extends ImportSource {
|
|||
name,
|
||||
method,
|
||||
path,
|
||||
url,
|
||||
queryString,
|
||||
headers,
|
||||
[],
|
||||
|
|
|
@ -2,6 +2,7 @@ import { ImportInfo } from "./base"
|
|||
import { Query, QueryParameter } from "../../../../../definitions/datasource"
|
||||
import { OpenAPIV2 } from "openapi-types"
|
||||
import { OpenAPISource } from "./base/openapi"
|
||||
import { URL } from "url"
|
||||
|
||||
const parameterNotRef = (
|
||||
param: OpenAPIV2.Parameter | OpenAPIV2.ReferenceObject
|
||||
|
@ -55,20 +56,22 @@ export class OpenAPI2 extends OpenAPISource {
|
|||
}
|
||||
}
|
||||
|
||||
getInfo = async (): Promise<ImportInfo> => {
|
||||
getUrl = (): URL => {
|
||||
const scheme = this.document.schemes?.includes("https") ? "https" : "http"
|
||||
const basePath = this.document.basePath || ""
|
||||
const host = this.document.host || "<host>"
|
||||
const url = `${scheme}://${host}${basePath}`
|
||||
const name = this.document.info.title || "Swagger Import"
|
||||
return new URL(`${scheme}://${host}${basePath}`)
|
||||
}
|
||||
|
||||
getInfo = async (): Promise<ImportInfo> => {
|
||||
const name = this.document.info.title || "Swagger Import"
|
||||
return {
|
||||
url: url,
|
||||
name: name,
|
||||
name,
|
||||
}
|
||||
}
|
||||
|
||||
getQueries = async (datasourceId: string): Promise<Query[]> => {
|
||||
const url = this.getUrl()
|
||||
const queries = []
|
||||
|
||||
for (let [path, pathItem] of Object.entries(this.document.paths)) {
|
||||
|
@ -145,6 +148,7 @@ export class OpenAPI2 extends OpenAPISource {
|
|||
name,
|
||||
methodName,
|
||||
path,
|
||||
url,
|
||||
queryString,
|
||||
headers,
|
||||
parameters,
|
||||
|
|
|
@ -35,7 +35,6 @@ describe("Curl Import", () => {
|
|||
it("returns import info", async () => {
|
||||
await init("get")
|
||||
const info = await curl.getInfo()
|
||||
expect(info.url).toBe("http://example.com")
|
||||
expect(info.name).toBe("example.com")
|
||||
})
|
||||
|
||||
|
@ -67,8 +66,8 @@ describe("Curl Import", () => {
|
|||
}
|
||||
|
||||
it("populates path", async () => {
|
||||
await testPath("get", "")
|
||||
await testPath("path", "paths/abc")
|
||||
await testPath("get", "http://example.com/")
|
||||
await testPath("path", "http://example.com/paths/abc")
|
||||
})
|
||||
|
||||
const testHeaders = async (file, headers) => {
|
||||
|
|
|
@ -41,7 +41,6 @@ describe("OpenAPI2 Import", () => {
|
|||
const testImportInfo = async (file, extension) => {
|
||||
await init(file, extension)
|
||||
const info = await openapi2.getInfo()
|
||||
expect(info.url).toBe("https://petstore.swagger.io/v2")
|
||||
expect(info.name).toBe("Swagger Petstore")
|
||||
}
|
||||
|
||||
|
@ -92,12 +91,12 @@ describe("OpenAPI2 Import", () => {
|
|||
|
||||
it("populates path", async () => {
|
||||
const assertions = {
|
||||
"createEntity" : "entities",
|
||||
"getEntities" : "entities",
|
||||
"getEntity" : "entities/{{entityId}}",
|
||||
"updateEntity" : "entities/{{entityId}}",
|
||||
"patchEntity" : "entities/{{entityId}}",
|
||||
"deleteEntity" : "entities/{{entityId}}"
|
||||
"createEntity" : "http://example.com/entities",
|
||||
"getEntities" : "http://example.com/entities",
|
||||
"getEntity" : "http://example.com/entities/{{entityId}}",
|
||||
"updateEntity" : "http://example.com/entities/{{entityId}}",
|
||||
"patchEntity" : "http://example.com/entities/{{entityId}}",
|
||||
"deleteEntity" : "http://example.com/entities/{{entityId}}"
|
||||
}
|
||||
await runTests("crud", testPath, assertions)
|
||||
})
|
||||
|
|
|
@ -51,30 +51,24 @@ describe("Rest Importer", () => {
|
|||
await init(data)
|
||||
const info = await restImporter.getInfo()
|
||||
expect(info.name).toBe(assertions[key].name)
|
||||
expect(info.url).toBe(assertions[key].url)
|
||||
}
|
||||
|
||||
it("gets info", async () => {
|
||||
const assertions = {
|
||||
"oapi2CrudJson" : {
|
||||
name: "CRUD",
|
||||
url: "http://example.com"
|
||||
},
|
||||
"oapi2CrudYaml" : {
|
||||
name: "CRUD",
|
||||
url: "http://example.com"
|
||||
},
|
||||
"oapi2PetstoreJson" : {
|
||||
name: "Swagger Petstore",
|
||||
url: "https://petstore.swagger.io/v2"
|
||||
},
|
||||
"oapi2PetstoreYaml" :{
|
||||
name: "Swagger Petstore",
|
||||
url: "https://petstore.swagger.io/v2"
|
||||
},
|
||||
"curl": {
|
||||
name: "example.com",
|
||||
url: "http://example.com"
|
||||
}
|
||||
}
|
||||
await runTest(testGetInfo, assertions)
|
||||
|
|
|
@ -8,6 +8,7 @@ const { BaseQueryVerbs } = require("../../../constants")
|
|||
const { Thread, ThreadType } = require("../../../threads")
|
||||
const { save: saveDatasource } = require("../datasource")
|
||||
const { RestImporter } = require("./import")
|
||||
const { invalidateDynamicVariables } = require("../../../threads/utils")
|
||||
|
||||
const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
|
||||
|
||||
|
@ -166,8 +167,28 @@ exports.executeV2 = async function (ctx) {
|
|||
return execute(ctx, { rowsOnly: false })
|
||||
}
|
||||
|
||||
const removeDynamicVariables = async (db, queryId) => {
|
||||
const query = await db.get(queryId)
|
||||
const datasource = await db.get(query.datasourceId)
|
||||
const dynamicVariables = datasource.config.dynamicVariables
|
||||
|
||||
if (dynamicVariables) {
|
||||
// delete dynamic variables from the datasource
|
||||
const newVariables = dynamicVariables.filter(dv => dv.queryId !== queryId)
|
||||
datasource.config.dynamicVariables = newVariables
|
||||
await db.put(datasource)
|
||||
|
||||
// invalidate the deleted variables
|
||||
const variablesToDelete = dynamicVariables.filter(
|
||||
dv => dv.queryId === queryId
|
||||
)
|
||||
await invalidateDynamicVariables(variablesToDelete)
|
||||
}
|
||||
}
|
||||
|
||||
exports.destroy = async function (ctx) {
|
||||
const db = new CouchDB(ctx.appId)
|
||||
await removeDynamicVariables(db, ctx.params.queryId)
|
||||
await db.remove(ctx.params.queryId, ctx.params.revId)
|
||||
ctx.message = `Query deleted.`
|
||||
ctx.status = 200
|
||||
|
|
|
@ -171,7 +171,7 @@ module RestModule {
|
|||
getUrl(path: string, queryString: string): string {
|
||||
const main = `${path}?${queryString}`
|
||||
let complete = main
|
||||
if (this.config.url && !main.startsWith(this.config.url)) {
|
||||
if (this.config.url && !main.startsWith("http")) {
|
||||
complete = !this.config.url ? main : `${this.config.url}/${main}`
|
||||
}
|
||||
if (!complete.startsWith("http")) {
|
||||
|
|
|
@ -42,10 +42,11 @@ exports.checkCacheForDynamicVariable = async (queryId, variable) => {
|
|||
}
|
||||
|
||||
exports.invalidateDynamicVariables = async cachedVars => {
|
||||
const cache = await getClient()
|
||||
let promises = []
|
||||
for (let variable of cachedVars) {
|
||||
promises.push(
|
||||
client.delete(makeVariableKey(variable.queryId, variable.name))
|
||||
cache.delete(makeVariableKey(variable.queryId, variable.name))
|
||||
)
|
||||
}
|
||||
await Promise.all(promises)
|
||||
|
|
Loading…
Reference in New Issue