diff --git a/packages/builder/package.json b/packages/builder/package.json index 623c19321e..781210041a 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -70,6 +70,7 @@ "@sentry/browser": "5.19.1", "@svelteschool/svelte-forms": "^0.7.0", "britecharts": "^2.16.0", + "codemirror": "^5.59.0", "d3-selection": "^1.4.1", "deepmerge": "^4.2.2", "fast-sort": "^2.2.0", diff --git a/packages/builder/src/builderStore/fetchBindableProperties.js b/packages/builder/src/builderStore/fetchBindableProperties.js index 962fb80ebf..9302f3c0f0 100644 --- a/packages/builder/src/builderStore/fetchBindableProperties.js +++ b/packages/builder/src/builderStore/fetchBindableProperties.js @@ -23,13 +23,20 @@ import { cloneDeep, difference } from "lodash/fp" * @param {fetchBindablePropertiesParameter} param * @returns {Array.} */ -export default function({ componentInstanceId, screen, components, tables }) { +export default function({ + componentInstanceId, + screen, + components, + tables, + queries, +}) { const result = walk({ // cloning so we are free to mutate props (e.g. by adding _contexts) instance: cloneDeep(screen.props), targetId: componentInstanceId, components, tables, + queries, }) return [ @@ -37,6 +44,9 @@ export default function({ componentInstanceId, screen, components, tables }) { .filter(isInstanceInSharedContext(result)) .map(componentInstanceToBindable), ...(result.target?._contexts.map(contextToBindables(tables)).flat() ?? []), + ...(result.target?._contexts + .map(context => queriesToBindables(queries, context)) + .flat() ?? []), ] } @@ -61,9 +71,36 @@ const componentInstanceToBindable = i => { } } +const queriesToBindables = (queries, context) => { + let queryId = context.table._id + + const query = queries.find(query => query._id === queryId) + let schema = query?.schema + + // Avoid crashing whenever no data source has been selected + if (!schema) { + return [] + } + + const queryBindings = Object.entries(schema).map(([key, value]) => ({ + type: "context", + fieldSchema: value, + instance: context.instance, + // how the binding expression persists, and is used in the app at runtime + runtimeBinding: `${context.instance._id}.${key}`, + // how the binding expressions looks to the user of the builder + readableBinding: `${context.instance._instanceName}.${query.name}.${key}`, + // table / view info + table: context.table, + })) + + return queryBindings +} + const contextToBindables = tables => context => { - const tableId = context.table?.tableId ?? context.table - const table = tables.find(table => table._id === tableId) + let tableId = context.table?.tableId ?? context.table + + const table = tables.find(table => table._id === tableId || context.table._id) let schema = context.table?.type === "view" ? table?.views?.[context.table.name]?.schema @@ -152,8 +189,8 @@ const walk = ({ instance, targetId, components, tables, result }) => { const currentContexts = [...result.currentContexts] for (let child of instance._children || []) { - // attaching _contexts of components, for eas comparison later - // these have been deep cloned above, so shouln't modify the + // attaching _contexts of components, for easy comparison later + // these have been deep cloned above, so shouldn't modify the // original component instances child._contexts = currentContexts walk({ instance: child, targetId, components, tables, result }) diff --git a/packages/builder/src/builderStore/store/backend.js b/packages/builder/src/builderStore/store/backend.js index 5592488590..0a235dd9a0 100644 --- a/packages/builder/src/builderStore/store/backend.js +++ b/packages/builder/src/builderStore/store/backend.js @@ -7,6 +7,9 @@ const INITIAL_BACKEND_UI_STATE = { views: [], users: [], roles: [], + datasources: [], + queries: [], + integrations: {}, selectedDatabase: {}, selectedTable: {}, draftTable: {}, @@ -21,9 +24,19 @@ export const getBackendUiStore = () => { select: async db => { const tablesResponse = await api.get(`/api/tables`) const tables = await tablesResponse.json() + const datasourcesResponse = await api.get(`/api/datasources`) + const datasources = await datasourcesResponse.json() + const queriesResponse = await api.get(`/api/queries`) + const queries = await queriesResponse.json() + const integrationsResponse = await api.get("/api/integrations") + const integrations = await integrationsResponse.json() + store.update(state => { state.selectedDatabase = db state.tables = tables + state.datasources = datasources + state.queries = queries + state.integrations = integrations return state }) }, @@ -45,6 +58,107 @@ export const getBackendUiStore = () => { return state }), }, + datasources: { + fetch: async () => { + const response = await api.get(`/api/datasources`) + const json = await response.json() + store.update(state => { + state.datasources = json + return state + }) + return json + }, + select: async datasourceId => { + store.update(state => { + state.selectedDatasourceId = datasourceId + state.selectedQueryId = null + return state + }) + }, + save: async datasource => { + const response = await api.post("/api/datasources", datasource) + const json = await response.json() + store.update(state => { + const currentIdx = state.datasources.findIndex( + ds => ds._id === json._id + ) + + if (currentIdx >= 0) { + state.datasources.splice(currentIdx, 1, json) + } else { + state.datasources.push(json) + } + + state.datasources = state.datasources + state.selectedDatasourceId = json._id + return state + }) + return json + }, + delete: async datasource => { + await api.delete( + `/api/datasources/${datasource._id}/${datasource._rev}` + ) + store.update(state => { + state.datasources = state.datasources.filter( + existing => existing._id !== datasource._id + ) + state.selectedDatasourceId = null + return state + }) + }, + }, + queries: { + fetch: async () => { + const response = await api.get(`/api/queries`) + const json = await response.json() + store.update(state => { + state.queries = json + return state + }) + return json + }, + save: async (datasourceId, query) => { + query.datasourceId = datasourceId + const response = await api.post(`/api/queries`, query) + const json = await response.json() + store.update(state => { + const currentIdx = state.queries.findIndex( + query => query._id === json._id + ) + + if (currentIdx >= 0) { + state.queries.splice(currentIdx, 1, json) + } else { + state.queries.push(json) + } + + state.queries = state.queries + state.selectedQueryId = json._id + return state + }) + return json + }, + select: query => + store.update(state => { + state.selectedDatasourceId = query.datasourceId + state.selectedQueryId = query._id + return state + }), + delete: async query => { + await api.delete(`/api/queries/${query._id}/${query._rev}`) + store.update(state => { + state.queries = state.queries.filter( + existing => existing._id !== query._id + ) + if (state.selectedQueryId === query._id) { + state.selectedQueryId = null + } + + return state + }) + }, + }, tables: { fetch: async () => { const tablesResponse = await api.get(`/api/tables`) diff --git a/packages/builder/src/builderStore/store/frontend.js b/packages/builder/src/builderStore/store/frontend.js index 2082a3e49b..13a5cbc824 100644 --- a/packages/builder/src/builderStore/store/frontend.js +++ b/packages/builder/src/builderStore/store/frontend.js @@ -42,6 +42,7 @@ const INITIAL_FRONTEND_STATE = { libraries: null, appId: "", routes: {}, + bottomDrawerVisible: false, } export const getFrontendStore = () => { diff --git a/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte new file mode 100644 index 0000000000..9be0481c58 --- /dev/null +++ b/packages/builder/src/components/backend/DataTable/ExternalDataSourceTable.svelte @@ -0,0 +1,28 @@ + + +{#if error} +
{error}
+{/if} + + + diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 1e6c53bd11..070ebec080 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -199,7 +199,6 @@ align-items: stretch; } .grid-wrapper :global(> *) { - height: auto; flex: 1 1 auto; } :global(.grid-wrapper) { @@ -236,6 +235,7 @@ } :global(.ag-filter) { + background: var(--background); padding: var(--spacing-s); outline: none; box-sizing: border-box; diff --git a/packages/builder/src/components/backend/DataTable/api.js b/packages/builder/src/components/backend/DataTable/api.js index 629405a9fc..91ebc19b26 100644 --- a/packages/builder/src/components/backend/DataTable/api.js +++ b/packages/builder/src/components/backend/DataTable/api.js @@ -23,5 +23,10 @@ export async function fetchDataForView(view) { const FETCH_ROWS_URL = `/api/views/${view.name}` const response = await api.get(FETCH_ROWS_URL) - return await response.json() + const json = await response.json() + + if (response.status !== 200) { + throw new Error(json.message) + } + return json } diff --git a/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte new file mode 100644 index 0000000000..aa8954cb8d --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/DatasourceNavigator.svelte @@ -0,0 +1,63 @@ + + +{#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id} +
+ {#each $backendUiStore.datasources as datasource, idx} + 0} + text={datasource.name} + selected={$backendUiStore.selectedDatasourceId === datasource._id} + on:click={() => selectDatasource(datasource)}> +
+ +
+ +
+ {#each $backendUiStore.queries.filter(query => query.datasourceId === datasource._id) as query} + onClickQuery(query)}> + + + {/each} + {/each} +
+{/if} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte new file mode 100644 index 0000000000..f148eaf8b2 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/ListItem.svelte @@ -0,0 +1,55 @@ + + +
+ + {title} + +
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte new file mode 100644 index 0000000000..79bb951c01 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/IntegrationConfigForm.svelte @@ -0,0 +1,16 @@ + + +
+ {#each Object.keys(integration) as configKey} + + + {/each} + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte new file mode 100644 index 0000000000..dbe289caba --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/TableIntegrationMenu/index.svelte @@ -0,0 +1,97 @@ + + +
+
+ {#each Object.keys(integrations) as integrationType} +
selectIntegration(integrationType)}> + + {integrationType} +
+ {/each} +
+ + {#if schema} + {#each Object.keys(schema) as configKey} + + + {/each} + {/if} +
+ + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte new file mode 100644 index 0000000000..ce689df205 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Airtable.svelte @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte new file mode 100644 index 0000000000..1ed737fc81 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/CouchDB.svelte @@ -0,0 +1,35 @@ + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte new file mode 100644 index 0000000000..1ec061ca20 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/DynamoDB.svelte @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte new file mode 100644 index 0000000000..20df3bd620 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Elasticsearch.svelte @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte new file mode 100644 index 0000000000..823ec3a2fa --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/MongoDB.svelte @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte new file mode 100644 index 0000000000..56268f75b3 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Postgres.svelte @@ -0,0 +1,149 @@ + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte new file mode 100644 index 0000000000..23168a3501 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/S3.svelte @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte new file mode 100644 index 0000000000..6aa2fcafca --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/SQLServer.svelte @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js new file mode 100644 index 0000000000..afedb9e78f --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js @@ -0,0 +1,19 @@ +import Postgres from "./Postgres.svelte" +import DynamoDB from "./DynamoDB.svelte" +import Elasticsearch from "./Elasticsearch.svelte" +import MongoDB from "./MongoDB.svelte" +import CouchDB from "./CouchDB.svelte" +import S3 from "./S3.svelte" +import Airtable from "./Airtable.svelte" +import SqlServer from "./SQLServer.svelte" + +export default { + POSTGRES: Postgres, + DYNAMODB: DynamoDB, + MONGODB: MongoDB, + ELASTICSEARCH: Elasticsearch, + COUCHDB: CouchDB, + SQL_SERVER: SqlServer, + S3: S3, + AIRTABLE: Airtable, +} diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte new file mode 100644 index 0000000000..a41cbd2466 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateDatasourceModal.svelte @@ -0,0 +1,61 @@ + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte new file mode 100644 index 0000000000..6a98313335 --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/modals/CreateQueryModal.svelte @@ -0,0 +1,61 @@ + + + + + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte new file mode 100644 index 0000000000..1bbaddf5ef --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditDatasourcePopover.svelte @@ -0,0 +1,87 @@ + + +
+
+ +
+ + + + + +
+ + Are you sure you wish to delete the datasource + {datasource.name}? + This action cannot be undone. + + + diff --git a/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte new file mode 100644 index 0000000000..92a541ac1d --- /dev/null +++ b/packages/builder/src/components/backend/DatasourceNavigator/popovers/EditQueryPopover.svelte @@ -0,0 +1,84 @@ + + +
+
+ +
+ + + + + +
+ + Are you sure you wish to delete this query? This action cannot be undone. + + + diff --git a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte index 810d7960f5..293f52b487 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableDataImport.svelte @@ -5,7 +5,7 @@ import api from "builderStore/api" const BYTES_IN_MB = 1000000 - const FILE_SIZE_LIMIT = BYTES_IN_MB * 1 + const FILE_SIZE_LIMIT = BYTES_IN_MB * 5 export let files = [] export let dataImport = { diff --git a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte index 30a0568e8e..3719d62c62 100644 --- a/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte +++ b/packages/builder/src/components/backend/TableNavigator/TableNavigator.svelte @@ -2,13 +2,11 @@ import { goto } from "@sveltech/routify" import { backendUiStore } from "builderStore" import { TableNames } from "constants" - import CreateTableModal from "./modals/CreateTableModal.svelte" import EditTablePopover from "./popovers/EditTablePopover.svelte" import EditViewPopover from "./popovers/EditViewPopover.svelte" - import { Modal } from "@budibase/bbui" + import { Switcher } from "@budibase/bbui" import NavItem from "components/common/NavItem.svelte" - let modal $: selectedView = $backendUiStore.selectedView && $backendUiStore.selectedView.name @@ -34,10 +32,6 @@ {#if $backendUiStore.selectedDatabase && $backendUiStore.selectedDatabase._id} -
-

Tables

- -
{#each $backendUiStore.tables as table, idx} {/if} - - - - - diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 3338072ec2..faadbdeb49 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -2,7 +2,14 @@ import { goto } from "@sveltech/routify" import { backendUiStore, store } from "builderStore" import { notifier } from "builderStore/store/notifications" - import { Input, Label, ModalContent, Toggle } from "@budibase/bbui" + import { + Input, + Label, + ModalContent, + Button, + Spacer, + Toggle, + } from "@budibase/bbui" import TableDataImport from "../TableDataImport.svelte" import analytics from "analytics" import screenTemplates from "builderStore/store/screenTemplates" diff --git a/packages/builder/src/components/common/BottomDrawer.svelte b/packages/builder/src/components/common/BottomDrawer.svelte new file mode 100644 index 0000000000..dc442bc2c3 --- /dev/null +++ b/packages/builder/src/components/common/BottomDrawer.svelte @@ -0,0 +1,52 @@ + + + +
+
+ {title} +
+ + +
+
+ +
+
+ + diff --git a/packages/builder/src/components/common/NavItem.svelte b/packages/builder/src/components/common/NavItem.svelte index 8eca07b1b0..972427328b 100644 --- a/packages/builder/src/components/common/NavItem.svelte +++ b/packages/builder/src/components/common/NavItem.svelte @@ -29,6 +29,8 @@
{/if} + + {#if icon}
{/if} diff --git a/packages/builder/src/components/integration/QueryEditor.svelte b/packages/builder/src/components/integration/QueryEditor.svelte new file mode 100644 index 0000000000..22cddb5606 --- /dev/null +++ b/packages/builder/src/components/integration/QueryEditor.svelte @@ -0,0 +1,162 @@ + + +