mirror of https://github.com/Budibase/budibase.git
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> |
|||
import { TextArea } from "@budibase/bbui" |
|||
import { TextArea, Label, Input } from "@budibase/bbui" |
|||
import Editor from "./Editor.svelte" |
|||
|
|||
const CAPTURE_VAR_INSIDE_MUSTACHE = /{{([^}]+)}}/g |
|||
|
|||
const QueryTypes = { |
|||
SQL: "sql", |
|||
} |
|||
|
|||
export let queryType |
|||
export let type |
|||
export let query |
|||
|
|||
$: console.log(query) |
|||
// $: parameters = Array.from( |
|||
// query |
|||
// .matchAll(CAPTURE_VAR_INSIDE_MUSTACHE) |
|||
// .map(([_, paramName]) => paramName) |
|||
// ) |
|||
</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} /> |
|||
{/if} |
|||
|
|||
@ -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 |
|||
} |
|||
|
|||
@ -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 |
|||
|
|||
Loading…
Reference in new issue