Browse Source

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

pull/1732/head
mike12345567 5 years ago
parent
commit
3929663ff9
  1. 30
      packages/builder/src/builderStore/api.js
  2. 7
      packages/builder/src/builderStore/store/automation/index.js
  3. 4
      packages/builder/src/components/backend/DataTable/DataTable.svelte
  4. 6
      packages/builder/src/components/backend/DataTable/Table.svelte
  5. 3
      packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte
  6. 4
      packages/builder/src/components/common/HelpIcon.svelte
  7. 8
      packages/builder/src/components/common/bindings/BindingPanel.svelte
  8. 4
      packages/builder/src/components/common/bindings/ServerBindingPanel.svelte
  9. 5
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte
  10. 5
      packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte
  11. 16
      packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte
  12. 19
      packages/builder/src/pages/builder/app/[application]/data/datasource/bb_internal/index.svelte
  13. 9
      packages/builder/src/stores/backend/datasources.js
  14. 2
      packages/builder/src/stores/backend/index.js
  15. 8
      packages/builder/src/stores/backend/queries.js
  16. 11
      packages/builder/src/stores/backend/tables.js
  17. 6
      packages/builder/src/stores/backend/tests/fixtures/queries.js
  18. 11
      packages/builder/src/stores/backend/views.js
  19. 3
      packages/server/src/integrations/elasticsearch.js
  20. 84
      packages/server/src/middleware/authorized.js
  21. 8
      packages/server/src/middleware/currentapp.js
  22. 16
      packages/standard-components/src/forms/validation.js
  23. 11
      packages/worker/src/api/controllers/admin/email.js
  24. 7
      packages/worker/src/api/controllers/admin/groups.js
  25. 7
      packages/worker/src/api/controllers/admin/users.js

30
packages/builder/src/builderStore/api.js

@ -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")

7
packages/builder/src/builderStore/store/automation/index.js

@ -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

4
packages/builder/src/components/backend/DataTable/DataTable.svelte

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

6
packages/builder/src/components/backend/DataTable/Table.svelte

@ -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 = {}

3
packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte

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

4
packages/builder/src/components/common/HelpIcon.svelte

@ -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>

8
packages/builder/src/components/common/bindings/BindingPanel.svelte

@ -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>

4
packages/builder/src/components/common/bindings/ServerBindingPanel.svelte

@ -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={() => {

5
packages/builder/src/components/design/PropertiesPanel/PropertyControls/DataSourceSelect.svelte

@ -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>

5
packages/builder/src/components/design/PropertiesPanel/PropertyControls/EventsEditor/actions/ExecuteQuery.svelte

@ -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>

16
packages/builder/src/pages/builder/app/[application]/data/datasource/[selectedDatasource]/index.svelte

@ -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>

19
packages/builder/src/pages/builder/app/[application]/data/datasource/bb_internal/index.svelte

@ -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>
<Body>
Stuff about the internal table
</Body>
<Button cta on:click={modal.show}>Create new table</Button>
<Heading>Budibase Internal DB</Heading>
</Layout>
<div>
<Button cta on:click={modal.show}>Create new table</Button>
</div>
</Layout>

9
packages/builder/src/stores/backend/datasources.js

@ -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`

2
packages/builder/src/stores/backend/index.js

@ -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"

8
packages/builder/src/stores/backend/queries.js

@ -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(

11
packages/builder/src/stores/backend/tables.js

@ -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")

6
packages/builder/src/stores/backend/tests/fixtures/queries.js

@ -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,
}

11
packages/builder/src/stores/backend/views.js

@ -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}`)

3
packages/server/src/integrations/elasticsearch.js

@ -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",

84
packages/server/src/middleware/authorized.js

@ -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
)
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()
}
// 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 (!ctx.user) {
return ctx.throw(403, "No user info found")
}
if (
hasResource(ctx) &&
doesHaveResourcePermission(permissions, permLevel, ctx)
) {
return next()
}
// 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 (!isAuthed) {
ctx.throw(403, "Session not authenticated")
}
if (
hasResource(ctx) &&
doesHaveResourcePermission(permissions, permLevel, ctx)
) {
return next()
}
if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
ctx.throw(403, "User does not have permission")
}
if (!isAuthed) {
ctx.throw(403, "Session not authenticated")
}
return next()
if (!doesHaveBasePermission(permType, permLevel, basePermissions)) {
ctx.throw(403, "User does not have permission")
}
return next()
}

8
packages/server/src/middleware/currentapp.js

@ -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")

16
packages/standard-components/src/forms/validation.js

@ -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)

11
packages/worker/src/api/controllers/admin/email.js

@ -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)

7
packages/worker/src/api/controllers/admin/groups.js

@ -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

7
packages/worker/src/api/controllers/admin/users.js

@ -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")

Loading…
Cancel
Save