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

This commit is contained in:
mike12345567 2021-06-16 18:39:15 +01:00
commit 7b8d448d78
26 changed files with 260 additions and 195 deletions

View File

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

View File

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

View File

@ -2,81 +2,125 @@
export let width = "100" export let width = "100"
export let height = "100" export let height = "100"
</script> </script>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve" {height} {width}> <svg
<style type="text/css"> version="1.1"
.st0{fill:#393C44;} id="Layer_1"
.st1{fill:#FFFFFF;} xmlns="http://www.w3.org/2000/svg"
.st2{fill:#4285F4;} xmlns:xlink="http://www.w3.org/1999/xlink"
</style> x="0px"
<rect x="-152.17" y="-24.17" class="st0" width="96.17" height="96.17"/> y="0px"
<path class="st1" d="M-83.19,48h-41.79c-1.76,0-3.19-1.43-3.19-3.19V3.02c0-1.76,1.43-3.19,3.19-3.19h41.79 viewBox="0 0 48 48"
c1.76,0,3.19,1.43,3.19,3.19v41.79C-80,46.57-81.43,48-83.19,48z"/> style="enable-background:new 0 0 48 48;"
<g> xml:space="preserve"
<g> {height}
<path class="st0" d="M-99.62,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57 {width}
>
<style type="text/css">
.st0 {
fill: #393c44;
}
.st1 {
fill: #ffffff;
}
.st2 {
fill: #4285f4;
}
</style>
<rect x="-152.17" y="-24.17" class="st0" width="96.17" height="96.17" />
<path
class="st1"
d="M-83.19,48h-41.79c-1.76,0-3.19-1.43-3.19-3.19V3.02c0-1.76,1.43-3.19,3.19-3.19h41.79
c1.76,0,3.19,1.43,3.19,3.19v41.79C-80,46.57-81.43,48-83.19,48z"
/>
<g>
<g>
<path
class="st0"
d="M-99.62,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35 c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35h-4.89V12.57H-99.62z c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35h-4.89V12.57H-99.62z
M-93.46,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69 M-93.46,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01 c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68 c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1 c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C-93.55,28.92-93.46,28.52-93.46,28.11z"/> C-93.55,28.92-93.46,28.52-93.46,28.11z"
</g> />
<g> </g>
<path class="st0" d="M-114.76,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58 <g>
<path
class="st0"
d="M-114.76,12.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58
c0.86,0.39,1.59,0.91,2.19,1.57c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89 c0.86,0.39,1.59,0.91,2.19,1.57c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89
c-0.35,0.9-0.84,1.68-1.47,2.35c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35 c-0.35,0.9-0.84,1.68-1.47,2.35c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V35
h-4.89V12.57H-114.76z M-108.6,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69 h-4.89V12.57H-114.76z M-108.6,28.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01 c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68 c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1 c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C-108.68,28.92-108.6,28.52-108.6,28.11z"/> C-108.68,28.92-108.6,28.52-108.6,28.11z"
</g> />
</g> </g>
<path class="st2" d="M44.81,159H3.02c-1.76,0-3.19-1.43-3.19-3.19v-41.79c0-1.76,1.43-3.19,3.19-3.19h41.79 </g>
c1.76,0,3.19,1.43,3.19,3.19v41.79C48,157.57,46.57,159,44.81,159z"/> <path
<g> class="st2"
<g> d="M44.81,159H3.02c-1.76,0-3.19-1.43-3.19-3.19v-41.79c0-1.76,1.43-3.19,3.19-3.19h41.79
<path class="st1" d="M28.38,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57 c1.76,0,3.19,1.43,3.19,3.19v41.79C48,157.57,46.57,159,44.81,159z"
/>
<g>
<g>
<path
class="st1"
d="M28.38,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35 c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146h-4.89v-22.43H28.38z c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146h-4.89v-22.43H28.38z
M34.54,139.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69 M34.54,139.11c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69
c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01 c-0.38-0.17-0.79-0.26-1.24-0.26c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01
c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68 c-0.17,0.39-0.26,0.8-0.26,1.23c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68
c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1 c0.39,0.17,0.8,0.26,1.23,0.26c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1
C34.45,139.92,34.54,139.52,34.54,139.11z"/> C34.45,139.92,34.54,139.52,34.54,139.11z"
</g> />
<g> </g>
<path class="st1" d="M13.24,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57 <g>
<path
class="st1"
d="M13.24,123.57v9.94c1.15-1.21,2.59-1.81,4.32-1.81c1.03,0,1.97,0.19,2.82,0.58c0.86,0.39,1.59,0.91,2.19,1.57
c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35 c0.6,0.66,1.08,1.43,1.42,2.32c0.34,0.89,0.51,1.84,0.51,2.85c0,1.03-0.18,1.99-0.53,2.89c-0.35,0.9-0.84,1.68-1.47,2.35
c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146H8.35v-22.43H13.24z M19.4,139.11 c-0.63,0.67-1.37,1.19-2.23,1.58c-0.86,0.39-1.78,0.58-2.77,0.58c-1.8,0-3.22-0.66-4.27-1.97V146H8.35v-22.43H13.24z M19.4,139.11
c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69c-0.38-0.17-0.79-0.26-1.24-0.26 c0-0.43-0.08-0.84-0.24-1.23c-0.16-0.39-0.39-0.72-0.68-1.01c-0.29-0.29-0.62-0.52-1-0.69c-0.38-0.17-0.79-0.26-1.24-0.26
c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01c-0.17,0.39-0.26,0.8-0.26,1.23 c-0.43,0-0.84,0.08-1.22,0.24c-0.38,0.16-0.71,0.39-0.99,0.68c-0.28,0.29-0.5,0.63-0.68,1.01c-0.17,0.39-0.26,0.8-0.26,1.23
c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68c0.39,0.17,0.8,0.26,1.23,0.26 c0,0.43,0.08,0.84,0.24,1.22c0.16,0.38,0.39,0.71,0.68,0.99c0.29,0.28,0.63,0.5,1.01,0.68c0.39,0.17,0.8,0.26,1.23,0.26
c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1C19.32,139.92,19.4,139.52,19.4,139.11z"/> c0.43,0,0.84-0.08,1.22-0.24c0.38-0.16,0.71-0.39,0.99-0.68c0.28-0.29,0.5-0.62,0.68-1C19.32,139.92,19.4,139.52,19.4,139.11z"
</g> />
</g> </g>
<g> </g>
<path class="st0" d="M44,48H4c-2.21,0-4-1.79-4-4V4c0-2.21,1.79-4,4-4h40c2.21,0,4,1.79,4,4v40C48,46.21,46.21,48,44,48z"/> <g>
<g> <path
<path class="st1" d="M28.48,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65 class="st0"
d="M44,48H4c-2.21,0-4-1.79-4-4V4c0-2.21,1.79-4,4-4h40c2.21,0,4,1.79,4,4v40C48,46.21,46.21,48,44,48z"
/>
<g>
<path
class="st1"
d="M28.48,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65
c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47 c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47
c-0.64,0.7-1.4,1.25-2.28,1.66C34.8,35.8,33.86,36,32.84,36c-1.84,0-3.3-0.69-4.37-2.07v1.62h-5V12H28.48z M34.78,28.31 c-0.64,0.7-1.4,1.25-2.28,1.66C34.8,35.8,33.86,36,32.84,36c-1.84,0-3.3-0.69-4.37-2.07v1.62h-5V12H28.48z M34.78,28.31
c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27 c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27
c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29 c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29
s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27 s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27
c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C34.69,29.16,34.78,28.75,34.78,28.31z" c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C34.69,29.16,34.78,28.75,34.78,28.31z"
/> />
</g> </g>
<g> <g>
<path class="st1" d="M13,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65 <path
class="st1"
d="M13,12v10.44c1.18-1.27,2.65-1.9,4.42-1.9c1.05,0,2.01,0.2,2.89,0.61c0.87,0.41,1.62,0.96,2.24,1.65
c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47 c0.62,0.69,1.1,1.5,1.45,2.44c0.35,0.94,0.52,1.93,0.52,2.99c0,1.08-0.18,2.09-0.54,3.04c-0.36,0.95-0.86,1.77-1.51,2.47
c-0.64,0.7-1.4,1.25-2.28,1.66C19.32,35.8,18.38,36,17.37,36c-1.84,0-3.3-0.69-4.37-2.07v1.62H8V12H13z M19.3,28.31 c-0.64,0.7-1.4,1.25-2.28,1.66C19.32,35.8,18.38,36,17.37,36c-1.84,0-3.3-0.69-4.37-2.07v1.62H8V12H13z M19.3,28.31
c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27 c0-0.45-0.08-0.88-0.25-1.29c-0.17-0.41-0.4-0.76-0.69-1.06c-0.3-0.3-0.64-0.54-1.02-0.72c-0.39-0.18-0.81-0.27-1.27-0.27
c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29 c-0.44,0-0.86,0.09-1.24,0.26c-0.39,0.17-0.72,0.41-1.01,0.71c-0.29,0.3-0.52,0.66-0.69,1.06c-0.18,0.41-0.26,0.84-0.26,1.29
s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27 s0.08,0.88,0.25,1.28c0.17,0.4,0.4,0.74,0.69,1.04c0.29,0.29,0.64,0.53,1.04,0.71c0.4,0.18,0.82,0.27,1.26,0.27
c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C19.21,29.16,19.3,28.75,19.3,28.31z"/> c0.44,0,0.86-0.09,1.24-0.26c0.39-0.17,0.72-0.41,1.01-0.71c0.29-0.3,0.52-0.65,0.69-1.05C19.21,29.16,19.3,28.75,19.3,28.31z"
</g> />
</g> </g>
</g>
</svg> </svg>

View File

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

View File

@ -49,7 +49,9 @@
<div class="section"> <div class="section">
{#each categories as [categoryName, bindings]} {#each categories as [categoryName, bindings]}
<Heading size="XS">{categoryName}</Heading> <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 <div
class="binding" class="binding"
on:click={() => { on:click={() => {

View File

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

View File

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

View File

@ -81,22 +81,24 @@
on:change={setUnsaved} on:change={setUnsaved}
/> />
</div> </div>
<Divider /> <Divider />
<div class="query-header"> <div class="query-header">
<Heading size="S">Queries</Heading> <Heading size="S">Queries</Heading>
<Button secondary on:click={() => $goto("./new")}>Add Query</Button> <Button secondary on:click={() => $goto("./new")}>Add Query</Button>
</div> </div>
<div class="query-list"> <div class="query-list">
{#each $queries.list.filter(query => query.datasourceId === datasource._id) as query} {#each $queries.list.filter(query => query.datasourceId === datasource._id) as query}
<div class="query-list-item" on:click={() => onClickQuery(query)}> <div class="query-list-item" on:click={() => onClickQuery(query)}>
<p class="query-name">{query.name}</p> <p class="query-name">{query.name}</p>
<p>{capitalise(query.queryVerb)}</p> <p>{capitalise(query.queryVerb)}</p>
<p></p> <p></p>
</div> </div>
{/each} {/each}
</div> </div>
{#if datasource.plus} {#if datasource.plus}
<Button cta on:click={updateDatasourceSchema}>Fetch Tables From Database</Button> <Button cta on:click={updateDatasourceSchema}
>Fetch Tables From Database</Button
>
{/if} {/if}
</Layout> </Layout>
</section> </section>

View File

@ -28,7 +28,7 @@ export function createDatasourcesStore() {
update(state => ({ ...state, selected: datasourceId })) update(state => ({ ...state, selected: datasourceId }))
queries.update(state => ({ ...state, selected: null })) queries.update(state => ({ ...state, selected: null }))
}, },
updateSchema: async (datasource) => { updateSchema: async datasource => {
let url = `/api/datasources/${datasource._id}/schema` let url = `/api/datasources/${datasource._id}/schema`
const response = await api.post(url) const response = await api.post(url)
@ -53,7 +53,7 @@ export function createDatasourcesStore() {
}) })
return json return json
}, },
save: async (datasource) => { save: async datasource => {
let url = "/api/datasources" let url = "/api/datasources"
const response = await api.post(url, datasource) const response = await api.post(url, datasource)

View File

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

View File

@ -160,6 +160,8 @@ exports.execute = async function (ctx) {
) )
const integration = new Integration(datasource.config) const integration = new Integration(datasource.config)
console.log(query)
// ctx.body = {}
// call the relevant CRUD method on the integration class // call the relevant CRUD method on the integration class
ctx.body = formatResponse(await integration[query.queryVerb](enrichedQuery)) ctx.body = formatResponse(await integration[query.queryVerb](enrichedQuery))
// cleanup // cleanup

View File

@ -86,8 +86,11 @@ async function handleRequest(
// clean up row on ingress using schema // clean up row on ingress using schema
filters = buildFilters(id, filters, table) filters = buildFilters(id, filters, table)
row = inputProcessing(row, table) row = inputProcessing(row, table)
if (operation === DataSourceOperation.DELETE && Object.keys(filters).length === 0) { if (
throw "Deletion must be filtered in someway" operation === DataSourceOperation.DELETE &&
Object.keys(filters).length === 0
) {
throw "Deletion must be filtered"
} }
let json = { let json = {
endpoint: { endpoint: {

View File

@ -18,8 +18,8 @@ validateJs.extend(validateJs.validators.datetime, {
exports.makeExternalQuery = async (appId, json) => { exports.makeExternalQuery = async (appId, json) => {
const datasourceId = json.endpoint.datasourceId const datasourceId = json.endpoint.datasourceId
const database = new CouchDB(appId) const db = new CouchDB(appId)
const datasource = await database.get(datasourceId) const datasource = await db.get(datasourceId)
const Integration = integrations[datasource.source] const Integration = integrations[datasource.source]
// query is the opinionated function // query is the opinionated function
if (Integration.prototype.query) { if (Integration.prototype.query) {

View File

@ -9,13 +9,10 @@ const {
BudibaseInternalDB, BudibaseInternalDB,
} = require("../../../db/utils") } = require("../../../db/utils")
const { FieldTypes } = require("../../../constants") const { FieldTypes } = require("../../../constants")
const { const { TableSaveFunctions, getExternalTable } = require("./utils")
TableSaveFunctions,
getExternalTable
} = require("./utils")
const { const {
isExternalTable, isExternalTable,
breakExternalTableId breakExternalTableId,
} = require("../../../integrations/utils") } = require("../../../integrations/utils")
exports.fetch = async function (ctx) { exports.fetch = async function (ctx) {

View File

@ -2,7 +2,8 @@ const { Client } = require("@elastic/elasticsearch")
const { QUERY_TYPES, FIELD_TYPES } = require("./Integration") const { QUERY_TYPES, FIELD_TYPES } = require("./Integration")
const SCHEMA = { 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: 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.", "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", friendlyName: "ElasticSearch",

View File

@ -62,11 +62,11 @@
// "select * from information_schema.columns where table_schema = 'public'" // "select * from information_schema.columns where table_schema = 'public'"
// PRIMARY_KEYS_SQL = ` // PRIMARY_KEYS_SQL = `
// select tc.table_schema, tc.table_name, kc.column_name as primary_key // select tc.table_schema, tc.table_name, kc.column_name as primary_key
// from information_schema.table_constraints tc // from information_schema.table_constraints tc
// join // join
// information_schema.key_column_usage kc on kc.table_name = tc.table_name // information_schema.key_column_usage kc on kc.table_name = tc.table_name
// and kc.table_schema = tc.table_schema // and kc.table_schema = tc.table_schema
// and kc.constraint_name = tc.constraint_name // and kc.constraint_name = tc.constraint_name
// where tc.constraint_type = 'PRIMARY KEY'; // where tc.constraint_type = 'PRIMARY KEY';
// ` // `

View File

@ -153,17 +153,17 @@ class PostgresIntegration extends Sql {
this.tables = tables this.tables = tables
} }
async create({ sql }) { async create(sql) {
const response = await internalQuery(this.client, sql) const response = await internalQuery(this.client, sql)
return response.rows.length ? response.rows : [{ created: true }] return response.rows.length ? response.rows : [{ created: true }]
} }
async read({ sql }) { async read(sql) {
const response = await internalQuery(this.client, sql) const response = await internalQuery(this.client, sql)
return response.rows return response.rows
} }
async update({ sql }) { async update(sql) {
const response = await internalQuery(this.client, sql) const response = await internalQuery(this.client, sql)
return response.rows.length ? response.rows : [{ updated: true }] return response.rows.length ? response.rows : [{ updated: true }]
} }

View File

@ -1,19 +1,17 @@
const { DocumentTypes, SEPARATOR } = require("../db/utils") const { DocumentTypes, SEPARATOR } = require("../db/utils")
const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
exports.isExternalTable = tableId => { exports.isExternalTable = tableId => {
return tableId.includes(DocumentTypes.DATASOURCE) return tableId.includes(DocumentTypes.DATASOURCE)
} }
exports.buildExternalTableId = (datasourceId, tableName) => { exports.buildExternalTableId = (datasourceId, tableName) => {
return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}` return `${datasourceId}${SEPARATOR}${tableName}`
} }
exports.breakExternalTableId = tableId => { exports.breakExternalTableId = tableId => {
const parts = tableId.split(DOUBLE_SEPARATOR) const parts = tableId.split(SEPARATOR)
let tableName = parts.pop() let tableName = parts.pop()
let datasourceId = parts.join(DOUBLE_SEPARATOR) let datasourceId = parts.join(SEPARATOR)
return { datasourceId, tableName } return { datasourceId, tableName }
} }

View File

@ -14,52 +14,50 @@ const WEBHOOK_ENDPOINTS = new RegExp(
["webhooks/trigger", "webhooks/schema"].join("|") ["webhooks/trigger", "webhooks/schema"].join("|")
) )
module.exports = module.exports = (permType, permLevel = null) => async (ctx, next) => {
(permType, permLevel = null) => // webhooks don't need authentication, each webhook unique
async (ctx, next) => { if (WEBHOOK_ENDPOINTS.test(ctx.request.url)) {
// 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() 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 } = const {
require("@budibase/auth").utils getAppId,
setCookie,
getCookie,
clearCookie,
} = require("@budibase/auth").utils
const { Cookies } = require("@budibase/auth").constants const { Cookies } = require("@budibase/auth").constants
const { getRole } = require("@budibase/auth/roles") const { getRole } = require("@budibase/auth/roles")
const { getGlobalSelf } = require("../utilities/workerRequests") const { getGlobalSelf } = require("../utilities/workerRequests")

View File

@ -135,6 +135,20 @@
"label": "Text", "label": "Text",
"key": "text" "key": "text"
}, },
{
"type": "select",
"label": "Button Type",
"key": "type",
"options": ["primary", "secondary", "cta", "warning"],
"defaultValue": "primary"
},
{
"type": "select",
"label": "Size",
"key": "size",
"options": ["S", "M", "L", "XL"],
"defaultValue": "M"
},
{ {
"type": "boolean", "type": "boolean",
"label": "Disabled", "label": "Disabled",

View File

@ -7,31 +7,15 @@
export let disabled = false export let disabled = false
export let text = "" export let text = ""
export let onClick export let onClick
export let size = "M"
export let type = "primary"
</script> </script>
<button <button
class="default" class={`spectrum-Button spectrum-Button--size${size} spectrum-Button--${type}`}
disabled={disabled || false} disabled={disabled || false}
use:styleable={$component.styles} use:styleable={$component.styles}
on:click={onClick} on:click={onClick}
> >
{text || ""} {text || ""}
</button> </button>
<style>
.default {
align-items: center;
padding: var(--spacing-s) var(--spacing-l);
box-sizing: border-box;
border-radius: 4px;
outline: none;
cursor: pointer;
transition: all 0.2s ease 0s;
overflow: hidden;
outline: none;
user-select: none;
white-space: nowrap;
text-align: center;
font-family: var(--font-sans);
}
</style>

View File

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

View File

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

View File

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

View File

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