-
+
+
+
+ {#if !response && Object.keys(schema).length === 0}
+ Response
+
+
+
+ {"enter a url in the textbox above and click send to get a response".toUpperCase()}
-
-
-
-
-
-
-
-
- {#if response}
-
- {/if}
-
-
-
- Status: {response?.info.code}
-
-
- Time: {response?.info.time}
-
-
- Size: {response?.info.size}
-
- Save query
-
- {/if}
-
+ {:else}
+
+ {#if response}
+
+
+
+
+
+ {/if}
+ {#if schema || response}
+
+
+
+ {/if}
+ {#if response}
+
+
+
+
+
+
+
+
+ {#if response}
+
+ {/if}
+
+
+
+
+ Status: {response?.info.code}
+
+
+ Time: {response?.info.time}
+
+
+ Size: {response?.info.size}
+
+ Save query
+
+ {/if}
+
+ {/if}
+
+
{/if}
@@ -284,6 +332,9 @@
margin: 0 auto;
height: 100%;
}
+ .table {
+ width: 960px;
+ }
.url-block {
display: flex;
gap: var(--spacing-s);
@@ -297,6 +348,9 @@
.top {
min-height: 50%;
}
+ .bottom {
+ padding-bottom: 50px;
+ }
.stats {
display: flex;
gap: var(--spacing-xl);
@@ -319,4 +373,15 @@
gap: var(--spacing-m);
align-items: center;
}
+ .placeholder-internal {
+ display: flex;
+ flex-direction: column;
+ width: 200px;
+ gap: var(--spacing-l);
+ }
+ .placeholder {
+ display: flex;
+ margin-top: var(--spacing-xl);
+ justify-content: center;
+ }
diff --git a/packages/server/__mocks__/node-fetch.ts b/packages/server/__mocks__/node-fetch.ts
index dfb839fe85..8badbd0156 100644
--- a/packages/server/__mocks__/node-fetch.ts
+++ b/packages/server/__mocks__/node-fetch.ts
@@ -6,6 +6,9 @@ module FetchMock {
return {
status,
headers: {
+ raw: () => {
+ return { "content-type": ["application/json"] }
+ },
get: () => {
return ["application/json"]
},
diff --git a/packages/server/src/api/controllers/query/index.js b/packages/server/src/api/controllers/query/index.js
index b5c0c9bf92..e12b0039a5 100644
--- a/packages/server/src/api/controllers/query/index.js
+++ b/packages/server/src/api/controllers/query/index.js
@@ -123,7 +123,7 @@ async function enrichQueryFields(fields, parameters = {}) {
enrichedQuery.requestBody
)
} catch (err) {
- throw { message: `JSON Invalid - error: ${err}` }
+ // no json found, ignore
}
delete enrichedQuery.customData
}
@@ -151,7 +151,7 @@ exports.preview = async function (ctx) {
const enrichedQuery = await enrichQueryFields(fields, parameters)
try {
- const { rows, keys, info, raw } = await Runner.run({
+ const { rows, keys, info, extra } = await Runner.run({
datasource,
queryVerb,
query: enrichedQuery,
@@ -162,7 +162,7 @@ exports.preview = async function (ctx) {
rows,
schemaFields: [...new Set(keys)],
info,
- raw,
+ extra,
}
} catch (err) {
ctx.throw(400, err)
diff --git a/packages/server/src/api/controllers/query/validation.js b/packages/server/src/api/controllers/query/validation.js
index 2613e6806a..a17a752ca5 100644
--- a/packages/server/src/api/controllers/query/validation.js
+++ b/packages/server/src/api/controllers/query/validation.js
@@ -19,6 +19,7 @@ exports.queryValidation = () => {
extra: Joi.object().optional(),
schema: Joi.object({}).required().unknown(true),
transformer: Joi.string().optional(),
+ flags: Joi.object().optional(),
})
}
diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts
index 51310eef3b..fe1c3ca118 100644
--- a/packages/server/src/integrations/rest.ts
+++ b/packages/server/src/integrations/rest.ts
@@ -62,6 +62,18 @@ module RestModule {
const { formatBytes } = require("../utilities")
const { performance } = require("perf_hooks")
+ interface RestQuery {
+ path: string
+ queryString?: string
+ headers: { [key: string]: any }
+ enabledHeaders: { [key: string]: any }
+ requestBody: any
+ bodyType: string
+ json: object
+ method: string
+ authConfigId: string
+ }
+
interface RestConfig {
url: string
defaultHeaders: {
@@ -70,13 +82,6 @@ module RestModule {
authConfigs: AuthConfig[]
}
- interface Request {
- path: string
- queryString?: string
- headers?: string
- json?: any
- }
-
const SCHEMA: Integration = {
docs: "https://github.com/node-fetch/node-fetch",
description:
@@ -140,7 +145,7 @@ module RestModule {
}
async parseResponse(response: any) {
- let data, raw
+ let data, raw, headers
const contentType = response.headers.get("content-type")
if (contentType && contentType.indexOf("application/json") !== -1) {
data = await response.json()
@@ -149,8 +154,12 @@ module RestModule {
data = await response.text()
raw = data
}
- const size = formatBytes(response.headers.get("content-length") || 0)
+ const size = formatBytes(response.headers.get("content-length") || Buffer.byteLength(raw, "utf8"))
const time = `${Math.round(performance.now() - this.startTimeMs)}ms`
+ headers = response.headers.raw()
+ for (let [key, value] of Object.entries(headers)) {
+ headers[key] = Array.isArray(value) ? value[0] : value
+ }
return {
data,
info: {
@@ -158,55 +167,74 @@ module RestModule {
size,
time,
},
- raw,
+ extra: {
+ raw,
+ headers,
+ },
}
}
getUrl(path: string, queryString: string): string {
const main = `${path}?${queryString}`
- if (!this.config.url) {
- return main
- } else {
- return `${this.config.url}/${main}`
+ let complete = !this.config.url ? main : `${this.config.url}/${main}`
+ if (!complete.startsWith("http")) {
+ complete = `http://${complete}`
}
+ return complete
}
- processAuth(authConfigId: string) {
- if (!this.config.authConfigs || !authConfigId) {
- return
- }
- const authConfig = this.config.authConfigs.filter(
- c => c._id === authConfigId
- )[0]
- let config
- switch (authConfig.type) {
- case AuthType.BASIC:
- config = authConfig.config as BasicAuthConfig
- this.headers.Authorization = `Basic ${Buffer.from(
- `${config.username}:${config.password}`
- ).toString("base64")}`
- break
- case AuthType.BEARER:
- config = authConfig.config as BearerAuthConfig
- this.headers.Authorization = `Bearer ${config.token}`
- break
+ getAuthHeaders(authConfigId: string): { [key: string]: any }{
+ let headers: any = {}
+
+ if (this.config.authConfigs && authConfigId) {
+ const authConfig = this.config.authConfigs.filter(
+ c => c._id === authConfigId
+ )[0]
+ let config
+ switch (authConfig.type) {
+ case AuthType.BASIC:
+ config = authConfig.config as BasicAuthConfig
+ headers.Authorization = `Basic ${Buffer.from(
+ `${config.username}:${config.password}`
+ ).toString("base64")}`
+ break
+ case AuthType.BEARER:
+ config = authConfig.config as BearerAuthConfig
+ headers.Authorization = `Bearer ${config.token}`
+ break
+ }
}
+
+ return headers
}
- async _req({
- path = "",
- queryString = "",
- headers = {},
- json = {},
- method = "GET",
- authConfigId = "",
- }) {
+ async _req(query: RestQuery) {
+ const { path = "", queryString = "", headers = {}, method = "GET", enabledHeaders, bodyType, requestBody, authConfigId } = query
+
+ const authHeaders = this.getAuthHeaders(authConfigId)
+
this.headers = {
...this.config.defaultHeaders,
...headers,
+ ...authHeaders,
}
- this.processAuth(authConfigId)
+ if (enabledHeaders) {
+ for (let headerKey of Object.keys(this.headers)) {
+ if (!enabledHeaders[headerKey]) {
+ delete this.headers[headerKey]
+ }
+ }
+ }
+
+ let json
+ if (bodyType === BodyTypes.JSON && requestBody) {
+ try {
+ json = JSON.parse(requestBody)
+ } catch (err) {
+ throw "Invalid JSON for request body"
+ }
+ }
const input: any = { method, headers: this.headers }
if (json && typeof json === "object" && Object.keys(json).length > 0) {
@@ -218,23 +246,23 @@ module RestModule {
return await this.parseResponse(response)
}
- async create(opts: Request) {
+ async create(opts: RestQuery) {
return this._req({ ...opts, method: "POST" })
}
- async read(opts: Request) {
+ async read(opts: RestQuery) {
return this._req({ ...opts, method: "GET" })
}
- async update(opts: Request) {
+ async update(opts: RestQuery) {
return this._req({ ...opts, method: "PUT" })
}
- async patch(opts: Request) {
+ async patch(opts: RestQuery) {
return this._req({ ...opts, method: "PATCH" })
}
- async delete(opts: Request) {
+ async delete(opts: RestQuery) {
return this._req({ ...opts, method: "DELETE" })
}
}
diff --git a/packages/server/src/integrations/tests/rest.spec.js b/packages/server/src/integrations/tests/rest.spec.js
index 0523115ad8..6c1d989124 100644
--- a/packages/server/src/integrations/tests/rest.spec.js
+++ b/packages/server/src/integrations/tests/rest.spec.js
@@ -1,6 +1,9 @@
jest.mock("node-fetch", () =>
jest.fn(() => ({
headers: {
+ raw: () => {
+ return { "content-type": ["application/json"] }
+ },
get: () => ["application/json"]
},
json: jest.fn(),
@@ -25,6 +28,7 @@ describe("REST Integration", () => {
config = new TestConfiguration({
url: BASE_URL,
})
+ jest.clearAllMocks()
})
it("calls the create method with the correct params", async () => {
@@ -34,9 +38,10 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
- json: {
+ bodyType: "json",
+ requestBody: JSON.stringify({
name: "test",
- },
+ }),
}
const response = await config.integration.create(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
@@ -61,6 +66,7 @@ describe("REST Integration", () => {
headers: {
Accept: "text/html",
},
+ method: "GET",
})
})
@@ -71,13 +77,14 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
- json: {
+ bodyType: "json",
+ requestBody: JSON.stringify({
name: "test",
- },
+ }),
}
const response = await config.integration.update(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
- method: "POST",
+ method: "PUT",
body: '{"name":"test"}',
headers: {
Accept: "application/json",
@@ -92,9 +99,10 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
- json: {
+ bodyType: "json",
+ requestBody: JSON.stringify({
name: "test",
- },
+ }),
}
const response = await config.integration.delete(query)
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?test=1`, {
@@ -102,6 +110,7 @@ describe("REST Integration", () => {
headers: {
Accept: "application/json",
},
+ body: '{"name":"test"}',
})
})
diff --git a/packages/server/src/threads/query.js b/packages/server/src/threads/query.js
index d94c4e6c2c..9645bfcd47 100644
--- a/packages/server/src/threads/query.js
+++ b/packages/server/src/threads/query.js
@@ -19,8 +19,8 @@ function hasExtraData(response) {
return (
typeof response === "object" &&
!Array.isArray(response) &&
- response.data &&
- response.info
+ response.data != null &&
+ response.info != null
)
}
@@ -34,11 +34,11 @@ async function runAndTransform(datasource, queryVerb, query, transformer) {
let output = formatResponse(await integration[queryVerb](query))
let rows = output,
info = undefined,
- raw = undefined
+ extra = undefined
if (hasExtraData(output)) {
rows = output.data
info = output.info
- raw = output.raw
+ extra = output.extra
}
// transform as required
@@ -64,7 +64,7 @@ async function runAndTransform(datasource, queryVerb, query, transformer) {
integration.end()
}
- return { rows, keys, info, raw }
+ return { rows, keys, info, extra }
}
module.exports = (input, callback) => {