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 ab01d4224e..f236eaaf33 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
@@ -96,10 +96,13 @@
}
if (cloneQuery?.fields?.requestBody) {
- cloneQuery.fields.requestBody = runtimeToReadableBinding(
- restBindings,
- cloneQuery.fields.requestBody
- )
+ cloneQuery.fields.requestBody =
+ typeof cloneQuery.fields.requestBody === "object"
+ ? runtimeToReadableMap(restBindings, cloneQuery.fields.requestBody)
+ : runtimeToReadableBinding(
+ restBindings,
+ cloneQuery.fields.requestBody
+ )
}
if (cloneQuery?.parameters) {
@@ -141,10 +144,11 @@
restBindings,
newQuery.fields.headers
)
- newQuery.fields.requestBody = readableToRuntimeBinding(
- restBindings,
- newQuery.fields.requestBody
- )
+ newQuery.fields.requestBody =
+ typeof newQuery.fields.requestBody === "object"
+ ? readableToRuntimeMap(restBindings, newQuery.fields.requestBody)
+ : readableToRuntimeBinding(restBindings, newQuery.fields.requestBody)
+
newQuery.fields.path = url.split("?")[0]
newQuery.fields.queryString = queryString
newQuery.fields.authConfigId = authConfigId
diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts
index 350680691b..1a7015fa52 100644
--- a/packages/server/__mocks__/node-fetch.ts
+++ b/packages/server/__mocks__/node-fetch.ts
@@ -15,6 +15,15 @@ module FetchMock {
},
},
json: async () => {
+ //x-www-form-encoded body is a URLSearchParams
+ //The call to stringify it leaves it blank
+ if (body?.opts?.body instanceof URLSearchParams) {
+ const paramArray = Array.from(body.opts.body.entries())
+ body.opts.body = paramArray.reduce((acc: any, pair: any) => {
+ acc[pair[0]] = pair[1]
+ return acc
+ }, {})
+ }
return body
},
}
diff --git a/packages/server/src/api/routes/tests/query.spec.js b/packages/server/src/api/routes/tests/query.spec.js
index 5dacda3505..273bdb9993 100644
--- a/packages/server/src/api/routes/tests/query.spec.js
+++ b/packages/server/src/api/routes/tests/query.spec.js
@@ -346,4 +346,170 @@ describe("/queries", () => {
expect(contents).toBe(null)
})
})
+
+ describe("Current User Request Mapping", () => {
+
+ async function previewGet(datasource, fields, params) {
+ return config.previewQuery(request, config, datasource, fields, params)
+ }
+
+ async function previewPost(datasource, fields, params) {
+ return config.previewQuery(request, config, datasource, fields, params, "create")
+ }
+
+ it("should parse global and query level header mappings", async () => {
+ const userDetails = config.getUserDetails()
+
+ const datasource = await config.restDatasource({
+ defaultHeaders: {
+ "test": "headerVal",
+ "emailHdr": "{{[user].[email]}}"
+ }
+ })
+ const res = await previewGet(datasource, {
+ path: "www.google.com",
+ queryString: "email={{[user].[email]}}",
+ headers: {
+ queryHdr : "{{[user].[firstName]}}",
+ secondHdr : "1234"
+ }
+ })
+
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+ expect(parsedRequest.opts.headers).toEqual({
+ "test": "headerVal",
+ "emailHdr": userDetails.email,
+ "queryHdr": userDetails.firstName,
+ "secondHdr" : "1234"
+ })
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?email=" + userDetails.email)
+ })
+
+ it("should bind the current user to query parameters", async () => {
+ const userDetails = config.getUserDetails()
+
+ const datasource = await config.restDatasource()
+
+ const res = await previewGet(datasource, {
+ path: "www.google.com",
+ queryString: "test={{myEmail}}&testName={{myName}}&testParam={{testParam}}",
+ }, {
+ "myEmail" : "{{[user].[email]}}",
+ "myName" : "{{[user].[firstName]}}",
+ "testParam" : "1234"
+ })
+
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?test=" + userDetails.email +
+ "&testName=" + userDetails.firstName + "&testParam=1234")
+ })
+
+ it("should bind the current user the request body - plain text", async () => {
+ const userDetails = config.getUserDetails()
+ const datasource = await config.restDatasource()
+
+ const res = await previewPost(datasource, {
+ path: "www.google.com",
+ queryString: "testParam={{testParam}}",
+ requestBody: "This is plain text and this is my email: {{[user].[email]}}. This is a test param: {{testParam}}",
+ bodyType: "text"
+ }, {
+ "testParam" : "1234"
+ })
+
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+ expect(parsedRequest.opts.body).toEqual(`This is plain text and this is my email: ${userDetails.email}. This is a test param: 1234`)
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
+ })
+
+ it("should bind the current user the request body - json", async () => {
+ const userDetails = config.getUserDetails()
+ const datasource = await config.restDatasource()
+
+ const res = await previewPost(datasource, {
+ path: "www.google.com",
+ queryString: "testParam={{testParam}}",
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
+ bodyType: "json"
+ }, {
+ "testParam" : "1234",
+ "userRef" : "{{[user].[firstName]}}"
+ })
+
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+ const test = `{"email":"${userDetails.email}","queryCode":1234,"userRef":"${userDetails.firstName}"}`
+ expect(parsedRequest.opts.body).toEqual(test)
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
+ })
+
+ it("should bind the current user the request body - xml", async () => {
+ const userDetails = config.getUserDetails()
+ const datasource = await config.restDatasource()
+
+ const res = await previewPost(datasource, {
+ path: "www.google.com",
+ queryString: "testParam={{testParam}}",
+ requestBody: " {{[user].[email]}} {{testParam}}
" +
+ "[{{userId}}] testing ",
+ bodyType: "xml"
+ }, {
+ "testParam" : "1234",
+ "userId" : "{{[user].[firstName]}}"
+ })
+
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+ const test = ` ${userDetails.email} 1234
[${userDetails.firstName}] testing `
+
+ expect(parsedRequest.opts.body).toEqual(test)
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
+ })
+
+ it("should bind the current user the request body - form-data", async () => {
+ const userDetails = config.getUserDetails()
+ const datasource = await config.restDatasource()
+
+ const res = await previewPost(datasource, {
+ path: "www.google.com",
+ queryString: "testParam={{testParam}}",
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
+ bodyType: "form"
+ }, {
+ "testParam" : "1234",
+ "userRef" : "{{[user].[firstName]}}"
+ })
+
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+
+ const emailData = parsedRequest.opts.body._streams[1]
+ expect(emailData).toEqual(userDetails.email)
+
+ const queryCodeData = parsedRequest.opts.body._streams[4]
+ expect(queryCodeData).toEqual("1234")
+
+ const userRef = parsedRequest.opts.body._streams[7]
+ expect(userRef).toEqual(userDetails.firstName)
+
+ expect(res.body.rows[0].url).toEqual("http://www.google.com?testParam=1234")
+ })
+
+ it("should bind the current user the request body - encoded", async () => {
+ const userDetails = config.getUserDetails()
+ const datasource = await config.restDatasource()
+
+ const res = await previewPost(datasource, {
+ path: "www.google.com",
+ queryString: "testParam={{testParam}}",
+ requestBody: "{\"email\":\"{{[user].[email]}}\",\"queryCode\":{{testParam}},\"userRef\":\"{{userRef}}\"}",
+ bodyType: "encoded"
+ }, {
+ "testParam" : "1234",
+ "userRef" : "{{[user].[firstName]}}"
+ })
+ const parsedRequest = JSON.parse(res.body.extra.raw)
+
+ expect(parsedRequest.opts.body.email).toEqual(userDetails.email)
+ expect(parsedRequest.opts.body.queryCode).toEqual("1234")
+ expect(parsedRequest.opts.body.userRef).toEqual(userDetails.firstName)
+ })
+
+ });
})
diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts
index 6174613fc8..e9c5f59fad 100644
--- a/packages/server/src/integrations/rest.ts
+++ b/packages/server/src/integrations/rest.ts
@@ -286,7 +286,7 @@ module RestModule {
input.body = form
break
case BodyTypes.XML:
- if (object != null) {
+ if (object != null && Object.keys(object).length) {
string = new XmlBuilder().buildObject(object)
}
input.body = string
diff --git a/packages/server/src/integrations/tests/rest.spec.js b/packages/server/src/integrations/tests/rest.spec.js
index 8f3c7f7f58..0bb1e3a75d 100644
--- a/packages/server/src/integrations/tests/rest.spec.js
+++ b/packages/server/src/integrations/tests/rest.spec.js
@@ -155,12 +155,27 @@ describe("REST Integration", () => {
expect(output.headers["Content-Type"]).toEqual("application/json")
})
- it("should allow XML", () => {
+ it("should allow raw XML", () => {
+ const output = config.integration.addBody("xml", "12", {})
+ expect(output.body.includes("1")).toEqual(true)
+ expect(output.body.includes("2")).toEqual(true)
+ expect(output.headers["Content-Type"]).toEqual("application/xml")
+ })
+
+ it("should allow a valid js object and parse the contents to xml", () => {
const output = config.integration.addBody("xml", input, {})
expect(output.body.includes("1")).toEqual(true)
expect(output.body.includes("2")).toEqual(true)
expect(output.headers["Content-Type"]).toEqual("application/xml")
})
+
+ it("should allow a valid json string and parse the contents to xml", () => {
+ const output = config.integration.addBody("xml", JSON.stringify(input), {})
+ expect(output.body.includes("1")).toEqual(true)
+ expect(output.body.includes("2")).toEqual(true)
+ expect(output.headers["Content-Type"]).toEqual("application/xml")
+ })
+
})
describe("response", () => {
diff --git a/packages/server/src/migrations/tests/index.spec.ts b/packages/server/src/migrations/tests/index.spec.ts
index ca30fbca06..84400f3df3 100644
--- a/packages/server/src/migrations/tests/index.spec.ts
+++ b/packages/server/src/migrations/tests/index.spec.ts
@@ -91,8 +91,24 @@ describe("migrations", () => {
await clearMigrations()
const appId = config.prodAppId
const roles = { [appId]: "role_12345" }
- await config.createUser(undefined, undefined, false, true, roles) // admin only
- await config.createUser(undefined, undefined, false, false, roles) // non admin non builder
+ await config.createUser(
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ false,
+ true,
+ roles
+ ) // admin only
+ await config.createUser(
+ undefined,
+ undefined,
+ undefined,
+ undefined,
+ false,
+ false,
+ roles
+ ) // non admin non builder
await config.createTable()
await config.createRow()
await config.createRow()
diff --git a/packages/server/src/tests/utilities/TestConfiguration.js b/packages/server/src/tests/utilities/TestConfiguration.js
index fc4d302c63..baa4ec13b8 100644
--- a/packages/server/src/tests/utilities/TestConfiguration.js
+++ b/packages/server/src/tests/utilities/TestConfiguration.js
@@ -28,6 +28,8 @@ const { encrypt } = require("@budibase/backend-core/encryption")
const GLOBAL_USER_ID = "us_uuid1"
const EMAIL = "babs@babs.com"
+const FIRSTNAME = "Barbara"
+const LASTNAME = "Barbington"
const CSRF_TOKEN = "e3727778-7af0-4226-b5eb-f43cbe60a306"
class TestConfiguration {
@@ -59,6 +61,15 @@ class TestConfiguration {
return this.prodAppId
}
+ getUserDetails() {
+ return {
+ globalId: GLOBAL_USER_ID,
+ email: EMAIL,
+ firstName: FIRSTNAME,
+ lastName: LASTNAME,
+ }
+ }
+
async doInContext(appId, task) {
if (!appId) {
appId = this.appId
@@ -118,6 +129,8 @@ class TestConfiguration {
// USER / AUTH
async globalUser({
id = GLOBAL_USER_ID,
+ firstName = FIRSTNAME,
+ lastName = LASTNAME,
builder = true,
admin = false,
email = EMAIL,
@@ -135,6 +148,8 @@ class TestConfiguration {
...existing,
roles: roles || {},
tenantId: TENANT_ID,
+ firstName,
+ lastName,
}
await createASession(id, {
sessionId: "sessionid",
@@ -161,6 +176,8 @@ class TestConfiguration {
async createUser(
id = null,
+ firstName = FIRSTNAME,
+ lastName = LASTNAME,
email = EMAIL,
builder = true,
admin = false,
@@ -169,6 +186,8 @@ class TestConfiguration {
const globalId = !id ? `us_${Math.random()}` : `us_${id}`
const resp = await this.globalUser({
id: globalId,
+ firstName,
+ lastName,
email,
builder,
admin,
@@ -520,14 +539,14 @@ class TestConfiguration {
// QUERY
- async previewQuery(request, config, datasource, fields) {
+ async previewQuery(request, config, datasource, fields, params, verb) {
return request
.post(`/api/queries/preview`)
.send({
datasourceId: datasource._id,
- parameters: {},
+ parameters: params || {},
fields,
- queryVerb: "read",
+ queryVerb: verb || "read",
name: datasource.name,
})
.set(config.defaultHeaders())