diff --git a/packages/bbui/src/Tabs/Tabs.svelte b/packages/bbui/src/Tabs/Tabs.svelte
index c68f715de0..40e02058c1 100644
--- a/packages/bbui/src/Tabs/Tabs.svelte
+++ b/packages/bbui/src/Tabs/Tabs.svelte
@@ -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
>
{#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);
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
index 18143c2071..af345ddcdf 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
@@ -137,7 +137,7 @@
selected={$queries.selected === query._id}
on:click={() => onClickQuery(query)}
>
-
+
{/each}
{/if}
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/auth/RestAuthenticationBuilder.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/auth/RestAuthenticationBuilder.svelte
index 6c68e055e4..7bbd2402f0 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/auth/RestAuthenticationBuilder.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/rest/auth/RestAuthenticationBuilder.svelte
@@ -58,7 +58,7 @@
/>
{/if}
-
openConfigModal()} con="Add"
+ openConfigModal()} icon="Add"
>Add authentication
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte
index 4e38dba11d..b15746735b 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte
+++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte
@@ -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)
}
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 43037242f3..c0e3bcea5e 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
@@ -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 @@
-
+
Create dynamic variables based on response body or headers
- from other queries.
+ from this query.
q.name)
)
- actions.save(datasourceId, newQuery)
+ return actions.save(datasourceId, newQuery)
},
}
diff --git a/packages/server/src/api/controllers/query/import/sources/base/index.ts b/packages/server/src/api/controllers/query/import/sources/base/index.ts
index 06e8dcfeff..e666fdc193 100644
--- a/packages/server/src/api/controllers/query/import/sources/base/index.ts
+++ b/packages/server/src/api/controllers/query/import/sources/base/index.ts
@@ -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)
diff --git a/packages/server/src/api/controllers/query/import/sources/curl.ts b/packages/server/src/api/controllers/query/import/sources/curl.ts
index b55d24403b..d72441ab12 100644
--- a/packages/server/src/api/controllers/query/import/sources/curl.ts
+++ b/packages/server/src/api/controllers/query/import/sources/curl.ts
@@ -60,16 +60,19 @@ export class Curl extends ImportSource {
return true
}
+ getUrl = (): URL => {
+ return new URL(this.curl.raw_url)
+ }
+
getInfo = async (): Promise => {
- const url = new URL(this.curl.url)
+ const url = this.getUrl()
return {
- url: url.origin,
name: url.hostname,
}
}
getQueries = async (datasourceId: string): Promise => {
- 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,
[],
diff --git a/packages/server/src/api/controllers/query/import/sources/openapi2.ts b/packages/server/src/api/controllers/query/import/sources/openapi2.ts
index 35dab163f6..c193654909 100644
--- a/packages/server/src/api/controllers/query/import/sources/openapi2.ts
+++ b/packages/server/src/api/controllers/query/import/sources/openapi2.ts
@@ -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 => {
+ getUrl = (): URL => {
const scheme = this.document.schemes?.includes("https") ? "https" : "http"
const basePath = this.document.basePath || ""
const host = this.document.host || ""
- const url = `${scheme}://${host}${basePath}`
- const name = this.document.info.title || "Swagger Import"
+ return new URL(`${scheme}://${host}${basePath}`)
+ }
+ getInfo = async (): Promise => {
+ const name = this.document.info.title || "Swagger Import"
return {
- url: url,
- name: name,
+ name,
}
}
getQueries = async (datasourceId: string): Promise => {
+ 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,
diff --git a/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js
index 11869862f7..2b17685f24 100644
--- a/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js
+++ b/packages/server/src/api/controllers/query/import/sources/tests/curl/curl.spec.js
@@ -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) => {
diff --git a/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js b/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js
index 845c4f38f6..3c5aa89e3c 100644
--- a/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js
+++ b/packages/server/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js
@@ -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)
})
diff --git a/packages/server/src/api/controllers/query/import/tests/index.spec.js b/packages/server/src/api/controllers/query/import/tests/index.spec.js
index 32f3b43b44..5a509d2258 100644
--- a/packages/server/src/api/controllers/query/import/tests/index.spec.js
+++ b/packages/server/src/api/controllers/query/import/tests/index.spec.js
@@ -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)
diff --git a/packages/server/src/api/controllers/query/index.js b/packages/server/src/api/controllers/query/index.js
index 6001b81aa9..6e5fdfb356 100644
--- a/packages/server/src/api/controllers/query/index.js
+++ b/packages/server/src/api/controllers/query/index.js
@@ -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
diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts
index 63d7795a6d..06b4d327da 100644
--- a/packages/server/src/integrations/rest.ts
+++ b/packages/server/src/integrations/rest.ts
@@ -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")) {
diff --git a/packages/server/src/threads/utils.js b/packages/server/src/threads/utils.js
index ffea596abd..e989d5cb63 100644
--- a/packages/server/src/threads/utils.js
+++ b/packages/server/src/threads/utils.js
@@ -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)