diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js
index 1d606901ec..507b4e3fe9 100644
--- a/packages/builder/src/builderStore/store/backend.js
+++ b/packages/builder/src/builderStore/store/backend.js
@@ -8,6 +8,7 @@ const INITIAL_BACKEND_UI_STATE = {
users: [],
roles: [],
datasources: [],
+ queries: [],
selectedDatabase: {},
selectedTable: {},
draftTable: {},
@@ -24,10 +25,13 @@ export const getBackendUiStore = () => {
const tables = await tablesResponse.json()
const datasourcesResponse = await api.get(`/api/datasources`)
const datasources = await datasourcesResponse.json()
+ const queriesResponse = await api.get(`/api/queries`)
+ const queries = await queriesResponse.json()
store.update(state => {
state.selectedDatabase = db
state.tables = tables
state.datasources = datasources
+ state.queries = queries
return state
})
},
@@ -98,35 +102,56 @@ export const getBackendUiStore = () => {
return state
})
},
- saveQuery: async (datasourceId, query) => {
- const response = await api.post(
- `/api/datasources/${datasourceId}/queries`,
- query
- )
+ },
+ queries: {
+ fetch: async () => {
+ const response = await api.get(`/api/queries`)
const json = await response.json()
store.update(state => {
- const currentIdx = state.datasources.findIndex(
- ds => ds._id === json._id
+ state.queries = json
+ return state
+ })
+ return json
+ },
+ save: async (datasourceId, query) => {
+ query.datasourceId = datasourceId
+ const response = await api.post(`/api/queries`, query)
+ const json = await response.json()
+ store.update(state => {
+ const currentIdx = state.queries.findIndex(
+ query => query._id === json._id
)
if (currentIdx >= 0) {
- state.datasources.splice(currentIdx, 1, json)
+ state.queries.splice(currentIdx, 1, json)
} else {
- state.datasources.push(json)
+ state.queries.push(json)
}
- state.datasources = state.datasources
+ state.queries = state.queries
+ state.selectedQueryId = json._id
return state
})
},
- },
- queries: {
select: queryId =>
store.update(state => {
state.selectedDatasourceId = null
state.selectedQueryId = queryId
return state
}),
+ delete: async queryId => {
+ await api.delete(`/api/queries/${queryId}`)
+ store.update(state => {
+ state.datasources = state.queries.filter(
+ existing => existing._id !== queryId
+ )
+ if (state.selectedQueryId === queryId) {
+ state.selectedQueryId = null
+ }
+
+ return state
+ })
+ },
},
tables: {
fetch: async () => {
diff --git a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte
index 85f23fe72b..a340f7c3c9 100644
--- a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte
+++ b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte
@@ -6,7 +6,6 @@
import Table from "./Table.svelte"
import CreateQueryButton from "components/backend/DataTable/buttons/CreateQueryButton.svelte"
- export let datasource
export let query = {}
let data = []
@@ -23,7 +22,6 @@
data = response.rows || []
error = false
} catch (err) {
- console.log(err)
error = `${query}: Query error. (${err.message}). This could be a problem with your datasource configuration.`
notifier.danger(error)
} finally {
@@ -32,14 +30,14 @@
}
// Fetch rows for specified query
- $: fetchData()
+ $: query && fetchData()
{#if error}
{error}
{/if}
diff --git a/packages/builder/src/components/integration/index.svelte b/packages/builder/src/components/integration/index.svelte
index 15021fe489..da301e3458 100644
--- a/packages/builder/src/components/integration/index.svelte
+++ b/packages/builder/src/components/integration/index.svelte
@@ -1,17 +1,28 @@
-{#if queryType === QueryTypes.SQL}
+
+
+{#if type === QueryTypes.SQL}
{/if}
diff --git a/packages/builder/src/components/userInterface/EventsEditor/actions/ExecuteQuery.svelte b/packages/builder/src/components/userInterface/EventsEditor/actions/ExecuteQuery.svelte
index 95b92835e6..8195e16258 100644
--- a/packages/builder/src/components/userInterface/EventsEditor/actions/ExecuteQuery.svelte
+++ b/packages/builder/src/components/userInterface/EventsEditor/actions/ExecuteQuery.svelte
@@ -24,7 +24,7 @@
diff --git a/packages/builder/src/components/userInterface/TableViewSelect.svelte b/packages/builder/src/components/userInterface/TableViewSelect.svelte
index bcc5d05030..fed621b818 100644
--- a/packages/builder/src/components/userInterface/TableViewSelect.svelte
+++ b/packages/builder/src/components/userInterface/TableViewSelect.svelte
@@ -31,17 +31,13 @@
return [...acc, ...viewsArr]
}, [])
- $: queries = $backendUiStore.datasources.reduce((acc, cur) => {
- let queriesArr = Object.entries(cur.queries).map(([key, value]) => ({
- label: value.name,
- name: value.name,
- datasourceId: cur._id,
- queryId: key,
- schema: value.schema,
+ $: queries = $backendUiStore.queries.map(query => ({
+ label: query.name,
+ name: query.name,
+ ...query,
+ schema: query.schema,
type: "query",
- }))
- return [...acc, ...queriesArr]
- }, [])
+ }))
$: bindableProperties = fetchBindableProperties({
componentInstanceId: $store.selectedComponentId,
diff --git a/packages/builder/src/pages/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte b/packages/builder/src/pages/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte
index 32c53a0c2d..27e3845584 100644
--- a/packages/builder/src/pages/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte
+++ b/packages/builder/src/pages/[application]/data/datasource/[selectedDatasource]/[query]/index.svelte
@@ -3,13 +3,10 @@
import { backendUiStore } from "builderStore"
import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte"
- // TODO: refactor
- $: datasource = $backendUiStore.datasources.find(
- ds => ds._id === $params.selectedDatasource
- )
- $: query = datasource && datasource.queries[$params.query]
+ $: query = $backendUiStore.queries.find(query => query._id === $params.query)
+ $: console.log("updated", query)
-{#if $backendUiStore.selectedDatabase._id && datasource && query}
-
+{#if $backendUiStore.selectedDatabase._id && query}
+
{/if}
diff --git a/packages/client/src/api/queries.js b/packages/client/src/api/queries.js
index cfc92262a0..e3a4749f95 100644
--- a/packages/client/src/api/queries.js
+++ b/packages/client/src/api/queries.js
@@ -3,9 +3,9 @@ import API from "./api"
/**
* Fetches all rows from a query.
*/
-export const fetchQueryData = async ({ datasourceId, queryId }) => {
+export const fetchQueryData = async ({ _id }) => {
const response = await API.get({
- url: `/api/datasources/${datasourceId}/queries/${queryId}`,
+ url: `/api/queries/${_id}`,
})
return response.rows
}
@@ -13,9 +13,9 @@ export const fetchQueryData = async ({ datasourceId, queryId }) => {
/**
* Executes a query against an external data connector.
*/
-export const executeQuery = async ({ datasourceId, queryId }) => {
+export const executeQuery = async ({ _id }) => {
const response = await API.post({
- url: `/api/datasources/${datasourceId}/queries/${queryId}`,
+ url: `/api/queries/${_id}`,
// body: params,
})
return response.rows
diff --git a/packages/server/src/api/controllers/datasource.js b/packages/server/src/api/controllers/datasource.js
index d2a963d89a..87e22d2e8e 100644
--- a/packages/server/src/api/controllers/datasource.js
+++ b/packages/server/src/api/controllers/datasource.js
@@ -1,11 +1,6 @@
const CouchDB = require("../../db")
const bcrypt = require("../../utilities/bcrypt")
-const {
- generateDatasourceID,
- getDatasourceParams,
- generateQueryID,
-} = require("../../db/utils")
-const { integrations } = require("../../integrations")
+const { generateDatasourceID, getDatasourceParams } = require("../../db/utils")
exports.fetch = async function(ctx) {
const database = new CouchDB(ctx.user.appId)
@@ -30,7 +25,6 @@ exports.save = async function(ctx) {
const datasource = {
_id: generateDatasourceID(),
type: "datasource",
- queries: {},
...ctx.request.body,
}
@@ -77,90 +71,3 @@ exports.find = async function(ctx) {
const datasource = await database.get(ctx.params.datasourceId)
ctx.body = datasource
}
-
-exports.saveQuery = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
- const query = ctx.request.body
-
- //
- // {
- // type: "",
- // query: "",
- // otherStuff: ""
- // }
-
- const datasource = await db.get(ctx.params.datasourceId)
-
- const queryId = generateQueryID()
-
- datasource.queries[queryId] = query
-
- const response = await db.put(datasource)
- datasource._rev = response.rev
-
- ctx.body = datasource
- ctx.message = `Query ${query.name} saved successfully.`
-}
-
-exports.previewQuery = async function(ctx) {
- const { type, config, query } = ctx.request.body
-
- const Integration = integrations[type]
-
- if (!Integration) {
- ctx.throw(400, "Integration type does not exist.")
- return
- }
-
- ctx.body = await new Integration(config, query).query()
-}
-
-exports.fetchQuery = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
-
- const datasource = await db.get(ctx.params.datasourceId)
-
- const query = datasource.queries[ctx.params.queryId]
-
- const Integration = integrations[datasource.source]
-
- if (!Integration) {
- ctx.throw(400, "Integration type does not exist.")
- return
- }
-
- const rows = await new Integration(
- datasource.config,
- query.queryString
- ).query()
-
- ctx.body = {
- schema: query.schema,
- rows,
- }
-}
-
-exports.executeQuery = async function(ctx) {
- const db = new CouchDB(ctx.user.appId)
-
- const datasource = await db.get(ctx.params.datasourceId)
-
- const query = datasource.queries[ctx.params.queryId]
-
- const Integration = integrations[datasource.source]
-
- if (!Integration) {
- ctx.throw(400, "Integration type does not exist.")
- return
- }
-
- // TODO: allow the ability to POST parameters down when executing the query
- // const customParams = ctx.request.body
-
- const response = await new Integration(
- datasource.config,
- query.queryString
- ).query()
-
- ctx.body = response
-}
diff --git a/packages/server/src/api/controllers/query.js b/packages/server/src/api/controllers/query.js
index 8cdd1f1567..229897debb 100644
--- a/packages/server/src/api/controllers/query.js
+++ b/packages/server/src/api/controllers/query.js
@@ -1,39 +1,128 @@
-// const CouchDB = require("../../../db")
-// const { generateQueryID } = require("../../db/utils")
-// const viewTemplate = require("./viewBuilder")
+const handlebars = require("handlebars")
+const Joi = require("joi")
+const CouchDB = require("../../db")
+const bcrypt = require("../../utilities/bcrypt")
+const { generateQueryID, getQueryParams } = require("../../db/utils")
+const { integrations } = require("../../integrations")
+const joiValidator = require("../../middleware/joi-validator")
-// exports.save = async ctx => {
-// const db = new CouchDB(ctx.user.appId)
-// const { datasourceId, query } = ctx.request.body
+function generateQueryValidation() {
+ // prettier-ignore
+ return joiValidator.body(Joi.object({
+ name: Joi.string().required(),
+ queryString: Joi.string().required(),
+ datasourceId: Joi.string().required(),
+ queryType: Joi.string().required(),
+ schema: Joi.object({}).required().unknown(true)
+ }))
+}
-// const datasource = await db.get(datasourceId)
+exports.fetch = async function(ctx) {
+ const db = new CouchDB(ctx.user.appId)
-// const queryId = generateQueryID()
+ const body = await db.allDocs(
+ getQueryParams(null, {
+ include_docs: true,
+ })
+ )
+ ctx.body = body.rows.map(row => row.doc)
+}
-// datasource.queries[queryId] = query
+exports.save = async function(ctx) {
+ const db = new CouchDB(ctx.user.appId)
+ const query = ctx.request.body
-// const response = await db.put(datasource)
+ //
+ // {
+ // type: "",
+ // query: "",
+ // otherStuff: ""
+ // }
-// ctx.body = query
-// ctx.message = `View ${viewToSave.name} saved successfully.`
-// }
+ if (!query._id) {
+ query._id = generateQueryID(query.datasourceId)
+ }
-// exports.destroy = async ctx => {
-// const db = new CouchDB(ctx.user.appId)
-// const designDoc = await db.get("_design/database")
+ const response = await db.put(query)
+ query._rev = response.rev
-// const viewName = decodeURI(ctx.params.viewName)
+ ctx.body = query
+ ctx.message = `Query ${query.name} saved successfully.`
+}
-// const view = designDoc.views[viewName]
+exports.preview = async function(ctx) {
+ const { query, datasourceId } = ctx.request.body
-// delete designDoc.views[viewName]
+ const db = new CouchDB(ctx.user.appId)
-// await db.put(designDoc)
+ const datasource = await db.get(datasourceId)
-// const table = await db.get(view.meta.tableId)
-// delete table.views[viewName]
-// await db.put(table)
+ const Integration = integrations[datasource.source]
-// ctx.body = view
-// ctx.message = `View ${ctx.params.viewName} saved successfully.`
-// }
+ if (!Integration) {
+ ctx.throw(400, "Integration type does not exist.")
+ return
+ }
+
+ ctx.body = await new Integration(datasource.config, query).query()
+}
+
+exports.execute = async function(ctx) {
+ const db = new CouchDB(ctx.user.appId)
+
+ const datasource = await db.get(ctx.params.datasourceId)
+
+ const query = datasource.queries[ctx.params.queryId]
+
+ const Integration = integrations[datasource.source]
+
+ if (!Integration) {
+ ctx.throw(400, "Integration type does not exist.")
+ return
+ }
+
+ // TODO: allow the ability to POST parameters down when executing the query
+ // const customParams = ctx.request.body
+ const queryTemplate = handlebars.compile(query.queryString)
+
+ const response = await new Integration(
+ datasource.config,
+ queryTemplate({
+ // pass the params here from the UI and backend contexts
+ })
+ ).query()
+
+ ctx.body = response
+}
+
+exports.fetchQuery = async function(ctx) {
+ const db = new CouchDB(ctx.user.appId)
+
+ const query = await db.get(ctx.params.queryId)
+
+ const datasource = await db.get(query.datasourceId)
+
+ const Integration = integrations[datasource.source]
+
+ if (!Integration) {
+ ctx.throw(400, "Integration type does not exist.")
+ return
+ }
+
+ const rows = await new Integration(
+ datasource.config,
+ query.queryString
+ ).query()
+
+ ctx.body = {
+ schema: query.schema,
+ rows,
+ }
+}
+
+exports.destroy = async function(ctx) {
+ const db = new CouchDB(ctx.user.appId)
+ await db.destroy(ctx.params.queryId)
+ ctx.message = `Query deleted.`
+ ctx.status = 200
+}
diff --git a/packages/server/src/api/routes/datasource.js b/packages/server/src/api/routes/datasource.js
index 19930e6f48..2441d64399 100644
--- a/packages/server/src/api/routes/datasource.js
+++ b/packages/server/src/api/routes/datasource.js
@@ -17,26 +17,26 @@ router
datasourceController.find
)
.post("/api/datasources", authorized(BUILDER), datasourceController.save)
- .post(
- "/api/datasources/:datasourceId/queries",
- authorized(BUILDER),
- datasourceController.saveQuery
- )
- .post(
- "/api/datasources/queries/preview",
- authorized(BUILDER),
- datasourceController.previewQuery
- )
- .get(
- "/api/datasources/:datasourceId/queries/:queryId",
- authorized(BUILDER),
- datasourceController.fetchQuery
- )
- .post(
- "/api/datasources/:datasourceId/queries/:queryId",
- authorized(BUILDER),
- datasourceController.executeQuery
- )
+ // .post(
+ // "/api/datasources/:datasourceId/queries",
+ // authorized(BUILDER),
+ // datasourceController.saveQuery
+ // )
+ // .post(
+ // "/api/datasources/queries/preview",
+ // authorized(BUILDER),
+ // datasourceController.previewQuery
+ // )
+ // .get(
+ // "/api/datasources/:datasourceId/queries/:queryId",
+ // authorized(BUILDER),
+ // datasourceController.fetchQuery
+ // )
+ // .post(
+ // "/api/datasources/:datasourceId/queries/:queryId",
+ // authorized(BUILDER),
+ // datasourceController.executeQuery
+ // )
.delete(
"/api/datasources/:datasourceId/:revId",
authorized(BUILDER),
diff --git a/packages/server/src/api/routes/index.js b/packages/server/src/api/routes/index.js
index 468c0a7b53..de5ce0eb0a 100644
--- a/packages/server/src/api/routes/index.js
+++ b/packages/server/src/api/routes/index.js
@@ -19,7 +19,7 @@ const routingRoutes = require("./routing")
const integrationRoutes = require("./integration")
const permissionRoutes = require("./permission")
const datasourceRoutes = require("./datasource")
-// const queryRoutes = require("./query")
+const queryRoutes = require("./query")
exports.mainRoutes = [
deployRoutes,
@@ -39,7 +39,7 @@ exports.mainRoutes = [
integrationRoutes,
permissionRoutes,
datasourceRoutes,
- // queryRoutes,
+ queryRoutes,
// these need to be handled last as they still use /api/:tableId
// this could be breaking as koa may recognise other routes as this
tableRoutes,
diff --git a/packages/server/src/api/routes/query.js b/packages/server/src/api/routes/query.js
index b0da295703..06597e52cf 100644
--- a/packages/server/src/api/routes/query.js
+++ b/packages/server/src/api/routes/query.js
@@ -1,28 +1,17 @@
-// const Router = require("@koa/router")
-// const queryController = require("../controllers/query")
-// const authorized = require("../../middleware/authorized")
-// const { BUILDER } = require("../../utilities/security/permissions")
+const Router = require("@koa/router")
+const queryController = require("../controllers/query")
+const authorized = require("../../middleware/authorized")
+const { BUILDER } = require("../../utilities/security/permissions")
-// const router = Router()
+const router = Router()
-// // TODO: send down the datasource ID as well
+// TODO: sort out auth so apps have the right permissions
+router
+ .get("/api/queries", authorized(BUILDER), queryController.fetch)
+ .get("/api/queries/:queryId", authorized(BUILDER), queryController.fetchQuery)
+ .post("/api/queries", authorized(BUILDER), queryController.save)
+ .post("/api/queries/preview", authorized(BUILDER), queryController.preview)
+ .post("/api/queries/:queryId", authorized(BUILDER), queryController.execute)
+ .delete("/api/queries/:queryId", authorized(BUILDER), queryController.destroy)
-// router
-// // .get("/api/queries", authorized(BUILDER), queryController.fetch)
-// // .get(
-// // "/api/datasources/:datasourceId/queries/:id",
-// // authorized(PermissionTypes.TABLE, PermissionLevels.READ),
-// // queryController.find
-// // )
-// .post(
-// "/api/datasources/:datasourceId/queries",
-// authorized(BUILDER),
-// queryController.save
-// )
-// .delete(
-// "/api/datasources/:datasourceId/queries/:queryId/:revId",
-// authorized(BUILDER),
-// queryController.destroy
-// )
-
-// module.exports = router
+module.exports = router
diff --git a/packages/server/src/db/client.js b/packages/server/src/db/client.js
index 1477ee4315..1d025a1402 100644
--- a/packages/server/src/db/client.js
+++ b/packages/server/src/db/client.js
@@ -38,6 +38,6 @@ function replicateLocal() {
})
}
-// replicateLocal()
+replicateLocal()
module.exports = Pouch
diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js
index 946ce26dd2..4ebd95ca98 100644
--- a/packages/server/src/db/utils.js
+++ b/packages/server/src/db/utils.js
@@ -245,8 +245,10 @@ exports.getDatasourceParams = (datasourceId = null, otherProps = {}) => {
* Generates a new query ID.
* @returns {string} The new query ID which the query doc can be stored under.
*/
-exports.generateQueryID = () => {
- return `${DocumentTypes.QUERY}${SEPARATOR}${newid()}`
+exports.generateQueryID = datasourceId => {
+ return `${
+ DocumentTypes.QUERY
+ }${SEPARATOR}${datasourceId}${SEPARATOR}${newid()}`
}
/**
diff --git a/packages/server/src/utilities/security/permissions.js b/packages/server/src/utilities/security/permissions.js
index 0836b29412..008145cc99 100644
--- a/packages/server/src/utilities/security/permissions.js
+++ b/packages/server/src/utilities/security/permissions.js
@@ -14,6 +14,7 @@ const PermissionTypes = {
WEBHOOK: "webhook",
BUILDER: "builder",
VIEW: "view",
+ QUERY: "query",
}
function Permission(type, level) {