forked from tsai/budibase
20 changed files with 339 additions and 238 deletions
@ -0,0 +1,85 @@ |
|||||
|
<script> |
||||
|
import { backendUiStore, store, allScreens } from "builderStore" |
||||
|
import { notifier } from "builderStore/store/notifications" |
||||
|
import { DropdownMenu, Button, Input } from "@budibase/bbui" |
||||
|
import ConfirmDialog from "components/common/ConfirmDialog.svelte" |
||||
|
import IntegrationConfigForm from "../TableIntegrationMenu//IntegrationConfigForm.svelte" |
||||
|
import { DropdownContainer, DropdownItem } from "components/common/Dropdowns" |
||||
|
|
||||
|
export let query |
||||
|
|
||||
|
let anchor |
||||
|
let dropdown |
||||
|
let confirmDeleteDialog |
||||
|
let error = "" |
||||
|
let willBeDeleted |
||||
|
|
||||
|
function hideEditor() { |
||||
|
dropdown?.hide() |
||||
|
} |
||||
|
|
||||
|
function showModal() { |
||||
|
hideEditor() |
||||
|
confirmDeleteDialog.show() |
||||
|
} |
||||
|
|
||||
|
async function deleteQuery() { |
||||
|
await backendUiStore.actions.queries.delete(query._id) |
||||
|
notifier.success("Query deleted") |
||||
|
hideEditor() |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<div on:click|stopPropagation> |
||||
|
<div bind:this={anchor} class="icon" on:click={dropdown.show}> |
||||
|
<i class="ri-more-line" /> |
||||
|
</div> |
||||
|
<DropdownMenu align="left" {anchor} bind:this={dropdown}> |
||||
|
<DropdownContainer> |
||||
|
<DropdownItem |
||||
|
icon="ri-delete-bin-line" |
||||
|
title="Delete" |
||||
|
on:click={showModal} |
||||
|
data-cy="delete-datasource" /> |
||||
|
</DropdownContainer> |
||||
|
</DropdownMenu> |
||||
|
</div> |
||||
|
<ConfirmDialog |
||||
|
bind:this={confirmDeleteDialog} |
||||
|
okText="Delete Query" |
||||
|
onOk={deleteQuery} |
||||
|
title="Confirm Deletion"> |
||||
|
Are you sure you wish to delete this query? |
||||
|
This action cannot be undone. |
||||
|
</ConfirmDialog> |
||||
|
|
||||
|
<style> |
||||
|
div.icon { |
||||
|
display: flex; |
||||
|
flex-direction: row; |
||||
|
justify-content: flex-end; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
div.icon i { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
|
||||
|
.actions { |
||||
|
padding: var(--spacing-xl); |
||||
|
display: grid; |
||||
|
grid-gap: var(--spacing-xl); |
||||
|
min-width: 400px; |
||||
|
} |
||||
|
|
||||
|
h5 { |
||||
|
margin: 0; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
footer { |
||||
|
display: flex; |
||||
|
justify-content: flex-end; |
||||
|
gap: var(--spacing-m); |
||||
|
} |
||||
|
</style> |
||||
@ -1,17 +1,28 @@ |
|||||
<script> |
<script> |
||||
import { TextArea } from "@budibase/bbui" |
import { TextArea, Label, Input } from "@budibase/bbui" |
||||
import Editor from "./Editor.svelte" |
import Editor from "./Editor.svelte" |
||||
|
|
||||
|
const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g |
||||
|
|
||||
const QueryTypes = { |
const QueryTypes = { |
||||
SQL: "sql", |
SQL: "sql", |
||||
} |
} |
||||
|
|
||||
export let queryType |
export let type |
||||
export let query |
export let query |
||||
|
|
||||
$: console.log(query) |
// $: parameters = Array.from( |
||||
|
// query |
||||
|
// .matchAll(CAPTURE_VAR_INSIDE_MUSTACHE) |
||||
|
// .map(([_, paramName]) => paramName) |
||||
|
// ) |
||||
</script> |
</script> |
||||
|
|
||||
{#if queryType === QueryTypes.SQL} |
<!-- {#each parameters as param} |
||||
|
<Label grey extraSmall>{param}</Label> |
||||
|
<Input thin bind:value={query.params[param]} /> |
||||
|
{/each} --> |
||||
|
|
||||
|
{#if type === QueryTypes.SQL} |
||||
<Editor label="Query" bind:value={query} /> |
<Editor label="Query" bind:value={query} /> |
||||
{/if} |
{/if} |
||||
|
|||||
@ -1,39 +1,128 @@ |
|||||
// const CouchDB = require("../../../db")
|
const handlebars = require("handlebars") |
||||
// const { generateQueryID } = require("../../db/utils")
|
const Joi = require("joi") |
||||
// const viewTemplate = require("./viewBuilder")
|
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 => {
|
function generateQueryValidation() { |
||||
// const db = new CouchDB(ctx.user.appId)
|
// prettier-ignore
|
||||
// const { datasourceId, query } = ctx.request.body
|
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
|
if (!query._id) { |
||||
// ctx.message = `View ${viewToSave.name} saved successfully.`
|
query._id = generateQueryID(query.datasourceId) |
||||
// }
|
} |
||||
|
|
||||
// exports.destroy = async ctx => {
|
const response = await db.put(query) |
||||
// const db = new CouchDB(ctx.user.appId)
|
query._rev = response.rev |
||||
// const designDoc = await db.get("_design/database")
|
|
||||
|
|
||||
// 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)
|
const Integration = integrations[datasource.source] |
||||
// delete table.views[viewName]
|
|
||||
// await db.put(table)
|
|
||||
|
|
||||
// ctx.body = view
|
if (!Integration) { |
||||
// ctx.message = `View ${ctx.params.viewName} saved successfully.`
|
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 |
||||
|
} |
||||
|
|||||
@ -1,28 +1,17 @@ |
|||||
// const Router = require("@koa/router")
|
const Router = require("@koa/router") |
||||
// const queryController = require("../controllers/query")
|
const queryController = require("../controllers/query") |
||||
// const authorized = require("../../middleware/authorized")
|
const authorized = require("../../middleware/authorized") |
||||
// const { BUILDER } = require("../../utilities/security/permissions")
|
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
|
module.exports = 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
|
|
||||
|
|||||
Loading…
Reference in new issue