diff --git a/packages/server/src/api/controllers/query.js b/packages/server/src/api/controllers/query.js index cf6f03f00f..b24513ba79 100644 --- a/packages/server/src/api/controllers/query.js +++ b/packages/server/src/api/controllers/query.js @@ -109,7 +109,7 @@ exports.preview = async function (ctx) { const enrichedQuery = await enrichQueryFields(fields, parameters) try { - const { rows, keys } = await Runner.run({ + const { rows, keys, info } = await Runner.run({ datasource, queryVerb, query: enrichedQuery, @@ -119,6 +119,7 @@ exports.preview = async function (ctx) { ctx.body = { rows, schemaFields: [...new Set(keys)], + info, } } catch (err) { ctx.throw(400, err) diff --git a/packages/server/src/integrations/base/IntegrationBase.ts b/packages/server/src/integrations/base/IntegrationBase.ts index a48713997a..d87a98c73b 100644 --- a/packages/server/src/integrations/base/IntegrationBase.ts +++ b/packages/server/src/integrations/base/IntegrationBase.ts @@ -1,6 +1,6 @@ export interface IntegrationBase { - create?(query: any): Promise - read?(query: any): Promise - update?(query: any): Promise - delete?(query: any): Promise + create?(query: any): Promise + read?(query: any): Promise + update?(query: any): Promise + delete?(query: any): Promise } diff --git a/packages/server/src/integrations/rest.ts b/packages/server/src/integrations/rest.ts index 89ba40b077..5228dc83f5 100644 --- a/packages/server/src/integrations/rest.ts +++ b/packages/server/src/integrations/rest.ts @@ -38,6 +38,8 @@ const coreFields = { module RestModule { const fetch = require("node-fetch") + const { formatBytes } = require("../utilities") + const { performance } = require("perf_hooks") interface RestConfig { url: string @@ -46,6 +48,13 @@ module RestModule { } } + interface Request { + path: string + queryString?: string + headers?: string + json?: any + } + const SCHEMA: Integration = { docs: "https://github.com/node-fetch/node-fetch", description: @@ -102,17 +111,29 @@ module RestModule { private headers: { [key: string]: string } = {} + private startTimeMs: number = performance.now() constructor(config: RestConfig) { this.config = config } async parseResponse(response: any) { + let data const contentType = response.headers.get("content-type") if (contentType && contentType.indexOf("application/json") !== -1) { - return await response.json() + data = await response.json() } else { - return await response.text() + data = await response.text() + } + const size = formatBytes(response.headers.get("content-length") || 0) + const time = `${Math.round(performance.now() - this.startTimeMs)}ms` + return { + data, + info: { + code: response.status, + size, + time, + }, } } @@ -125,76 +146,40 @@ module RestModule { } } - async create({ path = "", queryString = "", headers = {}, json = {} }) { + async _req({ path = "", queryString = "", headers = {}, json = {}, method = "GET" }) { this.headers = { ...this.config.defaultHeaders, ...headers, } - const response = await fetch(this.getUrl(path, queryString), { - method: "POST", - headers: this.headers, - body: JSON.stringify(json), - }) + const input: any = { method, headers: this.headers } + if (json && typeof json === "object" && Object.keys(json).length > 0) { + input.body = JSON.stringify(json) + } + this.startTimeMs = performance.now() + const response = await fetch(this.getUrl(path, queryString), input) return await this.parseResponse(response) } - async read({ path = "", queryString = "", headers = {} }) { - this.headers = { - ...this.config.defaultHeaders, - ...headers, - } - - const response = await fetch(this.getUrl(path, queryString), { - headers: this.headers, - }) - - return await this.parseResponse(response) + async create(opts: Request) { + return this._req({ ...opts, method: "POST" }) } - async update({ path = "", queryString = "", headers = {}, json = {} }) { - this.headers = { - ...this.config.defaultHeaders, - ...headers, - } - - const response = await fetch(this.getUrl(path, queryString), { - method: "PUT", - headers: this.headers, - body: JSON.stringify(json), - }) - - return await this.parseResponse(response) + async read(opts: Request) { + return this._req({ ...opts, method: "GET" }) } - async patch({ path = "", queryString = "", headers = {}, json = {} }) { - this.headers = { - ...this.config.defaultHeaders, - ...headers, - } - - const response = await fetch(this.getUrl(path, queryString), { - method: "PATCH", - headers: this.headers, - body: JSON.stringify(json), - }) - - return await this.parseResponse(response) + async update(opts: Request) { + return this._req({ ...opts, method: "PUT" }) } - async delete({ path = "", queryString = "", headers = {} }) { - this.headers = { - ...this.config.defaultHeaders, - ...headers, - } + async patch(opts: Request) { + return this._req({ ...opts, method: "PATCH" }) + } - const response = await fetch(this.getUrl(path, queryString), { - method: "DELETE", - headers: this.headers, - }) - - return await this.parseResponse(response) + async delete(opts: Request) { + return this._req({ ...opts, method: "DELETE" }) } } diff --git a/packages/server/src/threads/query.js b/packages/server/src/threads/query.js index 6b0d021e94..9995116fdc 100644 --- a/packages/server/src/threads/query.js +++ b/packages/server/src/threads/query.js @@ -15,6 +15,15 @@ function formatResponse(resp) { return resp } +function hasExtraData(response) { + return ( + typeof response === "object" && + !Array.isArray(response) && + response.data && + response.info + ) +} + async function runAndTransform(datasource, queryVerb, query, transformer) { const Integration = integrations[datasource.source] if (!Integration) { @@ -22,7 +31,13 @@ async function runAndTransform(datasource, queryVerb, query, transformer) { } const integration = new Integration(datasource.config) - let rows = formatResponse(await integration[queryVerb](query)) + let output = formatResponse(await integration[queryVerb](query)) + let rows = output, + info = undefined + if (hasExtraData(output)) { + rows = output.data + info = output.info + } // transform as required if (transformer) { @@ -47,7 +62,7 @@ async function runAndTransform(datasource, queryVerb, query, transformer) { integration.end() } - return { rows, keys } + return { rows, keys, info } } module.exports = (input, callback) => { diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index eacf9708e2..389514cc4b 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -150,3 +150,14 @@ exports.doesDatabaseExist = async dbName => { return false } } + +exports.formatBytes = bytes => { + const units = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"] + const byteIncrements = 1024 + let unit = 0 + let size = parseInt(bytes, 10) || 0 + while (size >= byteIncrements && ++unit) { + size /= byteIncrements + } + return `${size.toFixed(size < 10 && unit > 0 ? 1 : 0)}${units[unit]}` +}