Merge branch 'feature/opinionated-sql' of github.com:Budibase/budibase into feature/opinionated-sql

This commit is contained in:
mike12345567 2021-06-17 14:42:41 +01:00
commit 1014260ad5
25 changed files with 166 additions and 136 deletions

View File

@ -2,21 +2,23 @@ import { store } from "./index"
import { get as svelteGet } from "svelte/store"
import { removeCookie, Cookies } from "./cookies"
const apiCall =
method =>
async (url, body, headers = { "Content-Type": "application/json" }) => {
headers["x-budibase-app-id"] = svelteGet(store).appId
const json = headers["Content-Type"] === "application/json"
const resp = await fetch(url, {
method: method,
body: json ? JSON.stringify(body) : body,
headers,
})
if (resp.status === 403) {
removeCookie(Cookies.Auth)
}
return resp
const apiCall = method => async (
url,
body,
headers = { "Content-Type": "application/json" }
) => {
headers["x-budibase-app-id"] = svelteGet(store).appId
const json = headers["Content-Type"] === "application/json"
const resp = await fetch(url, {
method: method,
body: json ? JSON.stringify(body) : body,
headers,
})
if (resp.status === 403) {
removeCookie(Cookies.Auth)
}
return resp
}
export const post = apiCall("POST")
export const get = apiCall("GET")

View File

@ -100,10 +100,9 @@ const automationActions = store => ({
},
deleteAutomationBlock: block => {
store.update(state => {
const idx =
state.selectedAutomation.automation.definition.steps.findIndex(
x => x.id === block.id
)
const idx = state.selectedAutomation.automation.definition.steps.findIndex(
x => x.id === block.id
)
state.selectedAutomation.deleteBlock(block.id)
// Select next closest step

View File

@ -53,7 +53,9 @@
bind:hideAutocolumns
{loading}
>
<CreateColumnButton />
{#if isInternal}
<CreateColumnButton />
{/if}
{#if schema && Object.keys(schema).length > 0}
{#if !isUsersTable}
<CreateRowButton

View File

@ -9,7 +9,11 @@
import CreateEditRow from "./modals/CreateEditRow.svelte"
import CreateEditUser from "./modals/CreateEditUser.svelte"
import CreateEditColumn from "./modals/CreateEditColumn.svelte"
import { TableNames, UNEDITABLE_USER_FIELDS, BUDIBASE_INTERNAL_DB } from "constants"
import {
TableNames,
UNEDITABLE_USER_FIELDS,
BUDIBASE_INTERNAL_DB,
} from "constants"
import RoleCell from "./cells/RoleCell.svelte"
export let schema = {}

View File

@ -15,9 +15,6 @@
}
function onClickQuery(query) {
if ($queries.selected === query._id) {
return
}
queries.select(query)
$goto(`./datasource/${query.datasourceId}/${query._id}`)
}

View File

@ -2,7 +2,7 @@
import { Icon } from "@budibase/bbui"
</script>
<a target="_blank" href="https://github.com/Budibase/budibase/discussions">
<a target="_blank" href="https://github.com/Budibase/budibase/discussions">
<Icon hoverable name="Help" size="XXL" />
</a>
@ -14,4 +14,4 @@
right: var(--spacing-m);
border-radius: 55%;
}
</style>
</style>

View File

@ -59,7 +59,9 @@
<section>
<Heading size="XS">Columns</Heading>
<ul>
{#each context.filter( context => context.readableBinding.match(searchRgx) ) as { readableBinding }}
{#each context.filter(context =>
context.readableBinding.match(searchRgx)
) as { readableBinding }}
<li
on:click={() => {
value = addToText(value, getCaretPosition(), readableBinding)
@ -75,7 +77,9 @@
<section>
<Heading size="XS">Components</Heading>
<ul>
{#each instance.filter( instance => instance.readableBinding.match(searchRgx) ) as { readableBinding }}
{#each instance.filter(instance =>
instance.readableBinding.match(searchRgx)
) as { readableBinding }}
<li on:click={() => addToText(readableBinding)}>
{readableBinding}
</li>

View File

@ -49,7 +49,9 @@
<div class="section">
{#each categories as [categoryName, bindings]}
<Heading size="XS">{categoryName}</Heading>
{#each bindings.filter( binding => binding.label.match(searchRgx) ) as binding}
{#each bindings.filter(binding =>
binding.label.match(searchRgx)
) as binding}
<div
class="binding"
on:click={() => {

View File

@ -103,9 +103,8 @@
}
function fetchQueryDefinition(query) {
const source = $datasources.list.find(
ds => ds._id === query.datasourceId
).source
const source = $datasources.list.find(ds => ds._id === query.datasourceId)
.source
return $integrations[source].query[query.queryVerb]
}
</script>

View File

@ -18,9 +18,8 @@
)
function fetchQueryDefinition(query) {
const source = $datasources.list.find(
ds => ds._id === query.datasourceId
).source
const source = $datasources.list.find(ds => ds._id === query.datasourceId)
.source
return $integrations[source].query[query.queryVerb]
}
</script>

View File

@ -1,12 +1,7 @@
<script>
import { goto, beforeUrlChange } from "@roxi/routify"
import { Button, Heading, Body, Divider, Layout } from "@budibase/bbui"
import {
datasources,
integrations,
queries,
tables,
} from "stores/backend"
import { datasources, integrations, queries, tables } from "stores/backend"
import { notifications } from "@budibase/bbui"
import IntegrationConfigForm from "components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte"
import ICONS from "components/backend/DatasourceNavigator/icons"
@ -100,11 +95,16 @@
>
</div>
<Body>
This datasource can determine tables automatically. Budibase can fetch your tables directly from the database and you can use them without having to write any queries at all.
This datasource can determine tables automatically. Budibase can fetch
your tables directly from the database and you can use them without
having to write any queries at all.
</Body>
<div class="query-list">
{#each Object.keys(datasource.entities) as entity}
<div class="query-list-item" on:click={() => onClickTable(datasource.entities[entity])}>
<div
class="query-list-item"
on:click={() => onClickTable(datasource.entities[entity])}
>
<p class="query-name">{entity}</p>
<p>Primary Key: {datasource.entities[entity].primary}</p>
<p></p>

View File

@ -2,9 +2,7 @@
import { Button, Heading, Body, Layout, Modal } from "@budibase/bbui"
import CreateTableModal from "components/backend/TableNavigator/modals/CreateTableModal.svelte"
let modal
let modal
</script>
<Modal bind:this={modal}>
@ -12,14 +10,9 @@
</Modal>
<Layout>
<Heading>
Budibase Internal DB
</Heading>
<Heading>Budibase Internal DB</Heading>
<Body>
Stuff about the internal table
</Body>
<Button cta on:click={modal.show}>Create new table</Button>
</Layout>
<div>
<Button cta on:click={modal.show}>Create new table</Button>
</div>
</Layout>

View File

@ -1,5 +1,5 @@
import { writable } from "svelte/store"
import { queries } from "./"
import { queries, tables, views } from "./"
import api from "../../builderStore/api"
export const INITIAL_DATASOURCE_VALUES = {
@ -26,7 +26,12 @@ export function createDatasourcesStore() {
},
select: async datasourceId => {
update(state => ({ ...state, selected: datasourceId }))
queries.update(state => ({ ...state, selected: null }))
queries.unselect()
tables.unselect()
views.unselect()
},
unselect: () => {
update(state => ({ ...state, selected: null }))
},
updateSchema: async datasource => {
let url = `/api/datasources/${datasource._id}/schema`

View File

@ -6,4 +6,4 @@ export { permissions } from "./permissions"
export { roles } from "./roles"
export { datasources } from "./datasources"
export { integrations } from "./integrations"
export { queries } from "./queries"
export { queries } from "./queries"

View File

@ -55,10 +55,6 @@ export function createQueriesStore() {
},
select: query => {
update(state => ({ ...state, selected: query._id }))
datasources.update(state => ({
...state,
selected: query.datasourceId,
}))
tables.update(state => ({
...state,
selected: null,
@ -66,10 +62,6 @@ export function createQueriesStore() {
},
unselect: () => {
update(state => ({ ...state, selected: null }))
datasources.update(state => ({
...state,
selected: null,
}))
},
delete: async query => {
const response = await api.delete(

View File

@ -1,5 +1,5 @@
import { writable, get } from "svelte/store"
import { views, queries } from "./"
import { views, queries, datasources } from "./"
import { cloneDeep } from "lodash/fp"
import api from "builderStore/api"
@ -25,8 +25,9 @@ export function createTablesStore() {
selected: table,
draft: cloneDeep(table),
}))
views.select({ name: table._id })
views.unselect()
queries.unselect()
datasources.unselect()
}
}
@ -70,6 +71,12 @@ export function createTablesStore() {
update,
fetch,
select,
unselect: () => {
update(state => ({
...state,
selected: null,
}))
},
save,
init: async () => {
const response = await api.get("/api/tables")

View File

@ -9,7 +9,8 @@ export const SOME_QUERY = {
queryVerb: "read",
schema: {},
name: "Speakers",
_id: "query_datasource_04b003a7b4a8428eadd3bb2f7eae0255_bcb8ffc6fcbc484e8d63121fc0bf986f",
_id:
"query_datasource_04b003a7b4a8428eadd3bb2f7eae0255_bcb8ffc6fcbc484e8d63121fc0bf986f",
_rev: "2-941f8699eb0adf995f8bd59c99203b26",
readable: true,
}
@ -74,7 +75,8 @@ export const SAVE_QUERY_RESPONSE = {
},
},
name: "Speakers",
_id: "query_datasource_04b003a7b4a8428eadd3bb2f7eae0255_bcb8ffc6fcbc484e8d63121fc0bf986f",
_id:
"query_datasource_04b003a7b4a8428eadd3bb2f7eae0255_bcb8ffc6fcbc484e8d63121fc0bf986f",
_rev: "3-5a64adef494b1e9c793dc91b51ce73c6",
readable: true,
}

View File

@ -1,5 +1,5 @@
import { writable, get } from "svelte/store"
import { tables } from "./"
import { tables, datasources, queries } from "./"
import api from "builderStore/api"
export function createViewsStore() {
@ -10,11 +10,20 @@ export function createViewsStore() {
return {
subscribe,
update,
select: async view => {
update(state => ({
...state,
selected: view,
}))
queries.unselect()
datasources.unselect()
},
unselect: () => {
update(state => ({
...state,
selected: null,
}))
},
delete: async view => {
await api.delete(`/api/views/${view}`)

View File

@ -2,7 +2,8 @@ const { Client } = require("@elastic/elasticsearch")
const { QUERY_TYPES, FIELD_TYPES } = require("./Integration")
const SCHEMA = {
docs: "https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
docs:
"https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/index.html",
description:
"Elasticsearch is a search engine based on the Lucene library. It provides a distributed, multitenant-capable full-text search engine with an HTTP web interface and schema-free JSON documents.",
friendlyName: "ElasticSearch",

View File

@ -14,52 +14,50 @@ const WEBHOOK_ENDPOINTS = new RegExp(
["webhooks/trigger", "webhooks/schema"].join("|")
)
module.exports =
(permType, permLevel = null) =>
async (ctx, next) => {
// webhooks don't need authentication, each webhook unique
if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
return next()
}
if (!ctx.user) {
return ctx.throw(403, "No user info found")
}
// check general builder stuff, this middleware is a good way
// to find API endpoints which are builder focused
await builderMiddleware(ctx, permType)
const isAuthed = ctx.isAuthenticated
const { basePermissions, permissions } = await getUserPermissions(
ctx.appId,
ctx.roleId
)
// builders for now have permission to do anything
// TODO: in future should consider separating permissions with an require("@budibase/auth").isClient check
let isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
const isBuilderApi = permType === PermissionTypes.BUILDER
if (isBuilder) {
return next()
} else if (isBuilderApi && !isBuilder) {
return ctx.throw(403, "Not Authorized")
}
if (
hasResource(ctx) &&
doesHaveResourcePermission(permissions, permLevel, ctx)
) {
return next()
}
if (!isAuthed) {
ctx.throw(403, "Session not authenticated")
}
if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
ctx.throw(403, "User does not have permission")
}
module.exports = (permType, permLevel = null) => async (ctx, next) => {
// webhooks don't need authentication, each webhook unique
if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
return next()
}
if (!ctx.user) {
return ctx.throw(403, "No user info found")
}
// check general builder stuff, this middleware is a good way
// to find API endpoints which are builder focused
await builderMiddleware(ctx, permType)
const isAuthed = ctx.isAuthenticated
const { basePermissions, permissions } = await getUserPermissions(
ctx.appId,
ctx.roleId
)
// builders for now have permission to do anything
// TODO: in future should consider separating permissions with an require("@budibase/auth").isClient check
let isBuilder = ctx.user && ctx.user.builder && ctx.user.builder.global
const isBuilderApi = permType === PermissionTypes.BUILDER
if (isBuilder) {
return next()
} else if (isBuilderApi && !isBuilder) {
return ctx.throw(403, "Not Authorized")
}
if (
hasResource(ctx) &&
doesHaveResourcePermission(permissions, permLevel, ctx)
) {
return next()
}
if (!isAuthed) {
ctx.throw(403, "Session not authenticated")
}
if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
ctx.throw(403, "User does not have permission")
}
return next()
}

View File

@ -1,5 +1,9 @@
const { getAppId, setCookie, getCookie, clearCookie } =
require("@budibase/auth").utils
const {
getAppId,
setCookie,
getCookie,
clearCookie,
} = require("@budibase/auth").utils
const { Cookies } = require("@budibase/auth").constants
const { getRole } = require("@budibase/auth/roles")
const { getGlobalSelf } = require("../utilities/workerRequests")

View File

@ -90,17 +90,15 @@ const numericalConstraint = (constraint, error) => value => {
return null
}
const inclusionConstraint =
(options = []) =>
value => {
if (value == null || value === "") {
return null
}
if (!options.includes(value)) {
return "Invalid value"
}
const inclusionConstraint = (options = []) => value => {
if (value == null || value === "") {
return null
}
if (!options.includes(value)) {
return "Invalid value"
}
return null
}
const dateConstraint = (dateString, isEarliest) => {
const dateLimit = Date.parse(dateString)

View File

@ -5,8 +5,15 @@ const authPkg = require("@budibase/auth")
const GLOBAL_DB = authPkg.StaticDatabases.GLOBAL.name
exports.sendEmail = async ctx => {
const { groupId, email, userId, purpose, contents, from, subject } =
ctx.request.body
const {
groupId,
email,
userId,
purpose,
contents,
from,
subject,
} = ctx.request.body
let user
if (userId) {
const db = new CouchDB(GLOBAL_DB)

View File

@ -1,6 +1,9 @@
const CouchDB = require("../../../db")
const { getGroupParams, generateGroupID, StaticDatabases } =
require("@budibase/auth").db
const {
getGroupParams,
generateGroupID,
StaticDatabases,
} = require("@budibase/auth").db
const GLOBAL_DB = StaticDatabases.GLOBAL.name

View File

@ -1,6 +1,9 @@
const CouchDB = require("../../../db")
const { generateGlobalUserID, getGlobalUserParams, StaticDatabases } =
require("@budibase/auth").db
const {
generateGlobalUserID,
getGlobalUserParams,
StaticDatabases,
} = require("@budibase/auth").db
const { hash, getGlobalUserByEmail } = require("@budibase/auth").utils
const { UserStatus, EmailTemplatePurpose } = require("../../../constants")
const { checkInviteCode } = require("../../../utilities/redis")