Merge remote-tracking branch 'origin/develop' into feature/map-component
This commit is contained in:
commit
c672a439c0
|
@ -49,7 +49,7 @@ http {
|
|||
add_header X-Frame-Options SAMEORIGIN always;
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io; font-src 'self' data https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com; frame-src 'self' https:; img-src http: https: data; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
||||
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.budi.live https://js.intercomcdn.com https://widget.intercom.io; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net https://fonts.googleapis.com https://rsms.me https://maxcdn.bootstrapcdn.com; object-src 'none'; base-uri 'self'; connect-src 'self' https://api-iam.intercom.io https://app.posthog.com wss://nexus-websocket-a.intercom.io ; font-src 'self' data https://cdn.jsdelivr.net https://fonts.gstatic.com https://rsms.me https://maxcdn.bootstrapcdn.com; frame-src 'self' https:; img-src http: https: data; manifest-src 'self'; media-src 'self'; worker-src 'none';" always;
|
||||
|
||||
# upstreams
|
||||
set $apps {{ apps }};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"npmClient": "yarn",
|
||||
"packages": [
|
||||
"packages/*"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/backend-core",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Budibase backend core libraries used in server and worker",
|
||||
"main": "src/index.js",
|
||||
"author": "Budibase",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/bbui",
|
||||
"description": "A UI solution used in the different Budibase projects.",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"module": "dist/bbui.es.js",
|
||||
|
@ -38,7 +38,7 @@
|
|||
],
|
||||
"dependencies": {
|
||||
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.3",
|
||||
"@budibase/string-templates": "^1.0.81-alpha.4",
|
||||
"@spectrum-css/actionbutton": "^1.0.1",
|
||||
"@spectrum-css/actiongroup": "^1.0.1",
|
||||
"@spectrum-css/avatar": "^3.0.2",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/builder",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"license": "GPL-3.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
@ -65,10 +65,10 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.0.80-alpha.3",
|
||||
"@budibase/client": "^1.0.80-alpha.3",
|
||||
"@budibase/frontend-core": "^1.0.80-alpha.3",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.3",
|
||||
"@budibase/bbui": "^1.0.81-alpha.4",
|
||||
"@budibase/client": "^1.0.81-alpha.4",
|
||||
"@budibase/frontend-core": "^1.0.81-alpha.4",
|
||||
"@budibase/string-templates": "^1.0.81-alpha.4",
|
||||
"@sentry/browser": "5.19.1",
|
||||
"@spectrum-css/page": "^3.0.1",
|
||||
"@spectrum-css/vars": "^3.0.1",
|
||||
|
|
|
@ -331,7 +331,9 @@ const getSelectedRowsBindings = asset => {
|
|||
bindings = bindings.concat(
|
||||
tables.map(table => ({
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe(table._id)}`,
|
||||
runtimeBinding: `${safeState}.${makePropSafe(table._id)}.${makePropSafe(
|
||||
"selectedRows"
|
||||
)}`,
|
||||
readableBinding: `${table._instanceName}.Selected rows`,
|
||||
}))
|
||||
)
|
||||
|
@ -343,7 +345,9 @@ const getSelectedRowsBindings = asset => {
|
|||
bindings = bindings.concat(
|
||||
tableBlocks.map(block => ({
|
||||
type: "context",
|
||||
runtimeBinding: `${safeState}.${makePropSafe(block._id + "-table")}`,
|
||||
runtimeBinding: `${safeState}.${makePropSafe(
|
||||
block._id + "-table"
|
||||
)}.${makePropSafe("selectedRows")}`,
|
||||
readableBinding: `${block._instanceName}.Selected rows`,
|
||||
}))
|
||||
)
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
$goto(`./datasource/${resp._id}`)
|
||||
notifications.success(`Datasource updated successfully.`)
|
||||
} catch (err) {
|
||||
notifications.error("Error saving datasource")
|
||||
notifications.error(err?.message ?? "Error saving datasource")
|
||||
// prevent the modal from closing
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,8 +134,9 @@
|
|||
|
||||
// Remove all iframe event listeners on component destroy
|
||||
onDestroy(() => {
|
||||
window.removeEventListener("message", receiveMessage)
|
||||
|
||||
if (iframe.contentWindow) {
|
||||
window.removeEventListener("message", receiveMessage)
|
||||
if (!$store.clientFeatures.messagePassing) {
|
||||
// Legacy - remove in later versions of BB
|
||||
iframe.contentWindow.removeEventListener(
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
<script>
|
||||
import { Label, Select, Body } from "@budibase/bbui"
|
||||
import { findAllMatchingComponents } from "builderStore/componentUtils"
|
||||
import { currentAsset } from "builderStore"
|
||||
import { onMount } from "svelte"
|
||||
|
||||
export let parameters
|
||||
|
||||
$: tables = findAllMatchingComponents($currentAsset?.props, component =>
|
||||
component._component.endsWith("table")
|
||||
).map(table => ({
|
||||
label: table._instanceName,
|
||||
value: table._id,
|
||||
}))
|
||||
|
||||
$: tableBlocks = findAllMatchingComponents($currentAsset?.props, component =>
|
||||
component._component.endsWith("tableblock")
|
||||
).map(block => ({
|
||||
label: block._instanceName,
|
||||
value: `${block._id}-table`,
|
||||
}))
|
||||
|
||||
$: componentOptions = tables.concat(tableBlocks)
|
||||
|
||||
const FORMATS = [
|
||||
{
|
||||
label: "CSV",
|
||||
value: "csv",
|
||||
},
|
||||
{
|
||||
label: "JSON",
|
||||
value: "json",
|
||||
},
|
||||
]
|
||||
|
||||
onMount(() => {
|
||||
if (!parameters.type) {
|
||||
parameters.type = "csv"
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="root">
|
||||
<Body size="S">
|
||||
Choose the table component that you would like to export your row selection
|
||||
from.
|
||||
<br />
|
||||
Please ensure you have enabled row selection in the table settings.
|
||||
</Body>
|
||||
|
||||
<div class="params">
|
||||
<Label small>Table</Label>
|
||||
<Select
|
||||
bind:value={parameters.tableComponentId}
|
||||
options={componentOptions}
|
||||
/>
|
||||
|
||||
<Label small>Export as</Label>
|
||||
<Select bind:value={parameters.type} options={FORMATS} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.root {
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: stretch;
|
||||
gap: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.root :global(p) {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.params {
|
||||
display: grid;
|
||||
column-gap: var(--spacing-xs);
|
||||
row-gap: var(--spacing-s);
|
||||
grid-template-columns: 70px 1fr;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
|
@ -12,3 +12,4 @@ export { default as UpdateState } from "./UpdateState.svelte"
|
|||
export { default as RefreshDataProvider } from "./RefreshDataProvider.svelte"
|
||||
export { default as DuplicateRow } from "./DuplicateRow.svelte"
|
||||
export { default as S3Upload } from "./S3Upload.svelte"
|
||||
export { default as ExportData } from "./ExportData.svelte"
|
||||
|
|
|
@ -80,6 +80,10 @@
|
|||
"value": "publicUrl"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Export Data",
|
||||
"component": "ExportData"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/cli",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Budibase CLI, for developers, self hosting and migrations.",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/client",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"license": "MPL-2.0",
|
||||
"module": "dist/budibase-client.js",
|
||||
"main": "dist/budibase-client.js",
|
||||
|
@ -19,9 +19,9 @@
|
|||
"dev:builder": "rollup -cw"
|
||||
},
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.0.80-alpha.3",
|
||||
"@budibase/frontend-core": "^1.0.80-alpha.3",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.3",
|
||||
"@budibase/bbui": "^1.0.80-alpha.6",
|
||||
"@budibase/frontend-core": "^1.0.80-alpha.6",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.6",
|
||||
"@spectrum-css/button": "^3.0.3",
|
||||
"@spectrum-css/card": "^3.0.3",
|
||||
"@spectrum-css/divider": "^1.0.3",
|
||||
|
@ -33,6 +33,7 @@
|
|||
"apexcharts": "^3.22.1",
|
||||
"dayjs": "^1.10.5",
|
||||
"leaflet": "^1.7.1",
|
||||
"downloadjs": "1.4.7",
|
||||
"regexparam": "^1.3.0",
|
||||
"rollup-plugin-polyfill-node": "^0.8.0",
|
||||
"screenfull": "^6.0.1",
|
||||
|
|
|
@ -81,7 +81,10 @@
|
|||
loading = false
|
||||
return res
|
||||
} catch (error) {
|
||||
notificationStore.actions.error(`Error uploading file: ${error}`)
|
||||
notificationStore.actions.error(
|
||||
`Error uploading file: ${error?.message || error}`
|
||||
)
|
||||
loading = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@
|
|||
$: {
|
||||
rowSelectionStore.actions.updateSelection(
|
||||
$component.id,
|
||||
selectedRows.length ? selectedRows[0].tableId : "",
|
||||
selectedRows.map(row => row._id)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,20 +1,29 @@
|
|||
import { writable } from "svelte/store"
|
||||
import { get, writable } from "svelte/store"
|
||||
|
||||
const createRowSelectionStore = () => {
|
||||
const store = writable({})
|
||||
|
||||
function updateSelection(componentId, selectedRows) {
|
||||
function updateSelection(componentId, tableId, selectedRows) {
|
||||
store.update(state => {
|
||||
state[componentId] = [...selectedRows]
|
||||
state[componentId] = { tableId: tableId, selectedRows: selectedRows }
|
||||
return state
|
||||
})
|
||||
}
|
||||
|
||||
function getSelection(tableComponentId) {
|
||||
const selection = get(store)
|
||||
const componentId = Object.keys(selection).find(
|
||||
componentId => componentId === tableComponentId
|
||||
)
|
||||
return selection[componentId] || {}
|
||||
}
|
||||
|
||||
return {
|
||||
subscribe: store.subscribe,
|
||||
set: store.set,
|
||||
actions: {
|
||||
updateSelection,
|
||||
getSelection,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { get } from "svelte/store"
|
||||
import download from "downloadjs"
|
||||
import {
|
||||
routeStore,
|
||||
builderStore,
|
||||
|
@ -8,6 +9,7 @@ import {
|
|||
notificationStore,
|
||||
dataSourceStore,
|
||||
uploadStore,
|
||||
rowSelectionStore,
|
||||
} from "stores"
|
||||
import { API } from "api"
|
||||
import { ActionTypes } from "constants"
|
||||
|
@ -239,6 +241,26 @@ const s3UploadHandler = async action => {
|
|||
}
|
||||
}
|
||||
|
||||
const exportDataHandler = async action => {
|
||||
let selection = rowSelectionStore.actions.getSelection(
|
||||
action.parameters.tableComponentId
|
||||
)
|
||||
if (selection.selectedRows && selection.selectedRows.length > 0) {
|
||||
try {
|
||||
const data = await API.exportRows({
|
||||
tableId: selection.tableId,
|
||||
rows: selection.selectedRows,
|
||||
})
|
||||
|
||||
download(JSON.stringify(data), `export.${action.parameters.type}`)
|
||||
} catch (error) {
|
||||
notificationStore.actions.error("There was an error exporting the data")
|
||||
}
|
||||
} else {
|
||||
notificationStore.actions.error("Please select at least one row")
|
||||
}
|
||||
}
|
||||
|
||||
const handlerMap = {
|
||||
["Save Row"]: saveRowHandler,
|
||||
["Duplicate Row"]: duplicateRowHandler,
|
||||
|
@ -254,6 +276,7 @@ const handlerMap = {
|
|||
["Change Form Step"]: changeFormStepHandler,
|
||||
["Update State"]: updateStateHandler,
|
||||
["Upload File to S3"]: s3UploadHandler,
|
||||
["Export Data"]: exportDataHandler,
|
||||
}
|
||||
|
||||
const confirmTextMap = {
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
{
|
||||
"name": "@budibase/frontend-core",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Budibase frontend core libraries used in builder and client",
|
||||
"author": "Budibase",
|
||||
"license": "MPL-2.0",
|
||||
"svelte": "src/index.js",
|
||||
"dependencies": {
|
||||
"@budibase/bbui": "^1.0.80-alpha.3",
|
||||
"@budibase/bbui": "^1.0.81-alpha.4",
|
||||
"lodash": "^4.17.21",
|
||||
"svelte": "^3.46.2"
|
||||
}
|
||||
|
|
|
@ -1,61 +1,66 @@
|
|||
export const buildAttachmentEndpoints = API => ({
|
||||
/**
|
||||
* Uploads an attachment to the server.
|
||||
* @param data the attachment to upload
|
||||
* @param tableId the table ID to upload to
|
||||
*/
|
||||
uploadAttachment: async ({ data, tableId }) => {
|
||||
return await API.post({
|
||||
url: `/api/attachments/${tableId}/upload`,
|
||||
body: data,
|
||||
json: false,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads an attachment to the server as a builder user from the builder.
|
||||
* @param data the data to upload
|
||||
*/
|
||||
uploadBuilderAttachment: async data => {
|
||||
return await API.post({
|
||||
url: "/api/attachments/process",
|
||||
body: data,
|
||||
json: false,
|
||||
})
|
||||
},
|
||||
|
||||
export const buildAttachmentEndpoints = API => {
|
||||
/**
|
||||
* Generates a signed URL to upload a file to an external datasource.
|
||||
* @param datasourceId the ID of the datasource to upload to
|
||||
* @param bucket the name of the bucket to upload to
|
||||
* @param key the name of the file to upload to
|
||||
*/
|
||||
getSignedDatasourceURL: async ({ datasourceId, bucket, key }) => {
|
||||
const getSignedDatasourceURL = async ({ datasourceId, bucket, key }) => {
|
||||
return await API.post({
|
||||
url: `/api/attachments/${datasourceId}/url`,
|
||||
body: { bucket, key },
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file to an external datasource.
|
||||
* @param datasourceId the ID of the datasource to upload to
|
||||
* @param bucket the name of the bucket to upload to
|
||||
* @param key the name of the file to upload to
|
||||
* @param data the file to upload
|
||||
*/
|
||||
externalUpload: async ({ datasourceId, bucket, key, data }) => {
|
||||
const { signedUrl, publicUrl } = await API.getSignedDatasourceURL({
|
||||
datasourceId,
|
||||
bucket,
|
||||
key,
|
||||
})
|
||||
await API.put({
|
||||
url: signedUrl,
|
||||
body: data,
|
||||
json: false,
|
||||
external: true,
|
||||
})
|
||||
return { publicUrl }
|
||||
},
|
||||
})
|
||||
return {
|
||||
getSignedDatasourceURL,
|
||||
|
||||
/**
|
||||
* Uploads an attachment to the server.
|
||||
* @param data the attachment to upload
|
||||
* @param tableId the table ID to upload to
|
||||
*/
|
||||
uploadAttachment: async ({ data, tableId }) => {
|
||||
return await API.post({
|
||||
url: `/api/attachments/${tableId}/upload`,
|
||||
body: data,
|
||||
json: false,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads an attachment to the server as a builder user from the builder.
|
||||
* @param data the data to upload
|
||||
*/
|
||||
uploadBuilderAttachment: async data => {
|
||||
return await API.post({
|
||||
url: "/api/attachments/process",
|
||||
body: data,
|
||||
json: false,
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads a file to an external datasource.
|
||||
* @param datasourceId the ID of the datasource to upload to
|
||||
* @param bucket the name of the bucket to upload to
|
||||
* @param key the name of the file to upload to
|
||||
* @param data the file to upload
|
||||
*/
|
||||
externalUpload: async ({ datasourceId, bucket, key, data }) => {
|
||||
console.log(API)
|
||||
const { signedUrl, publicUrl } = await getSignedDatasourceURL({
|
||||
datasourceId,
|
||||
bucket,
|
||||
key,
|
||||
})
|
||||
await API.put({
|
||||
url: signedUrl,
|
||||
body: data,
|
||||
json: false,
|
||||
external: true,
|
||||
})
|
||||
return { publicUrl }
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,4 +60,18 @@ export const buildRowEndpoints = API => ({
|
|||
},
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
* Exports rows.
|
||||
* @param tableId the table ID to export the rows from
|
||||
* @param rows the array of rows to export
|
||||
*/
|
||||
exportRows: async ({ tableId, rows }) => {
|
||||
return await API.post({
|
||||
url: `/api/${tableId}/rows/exportRows`,
|
||||
body: {
|
||||
rows,
|
||||
},
|
||||
})
|
||||
},
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/server",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Budibase Web Server",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -71,9 +71,9 @@
|
|||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@apidevtools/swagger-parser": "^10.0.3",
|
||||
"@budibase/backend-core": "^1.0.80-alpha.3",
|
||||
"@budibase/client": "^1.0.80-alpha.3",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.3",
|
||||
"@budibase/backend-core": "^1.0.81-alpha.4",
|
||||
"@budibase/client": "^1.0.81-alpha.4",
|
||||
"@budibase/string-templates": "^1.0.81-alpha.4",
|
||||
"@bull-board/api": "^3.7.0",
|
||||
"@bull-board/koa": "^3.7.0",
|
||||
"@elastic/elasticsearch": "7.10.0",
|
||||
|
|
|
@ -152,6 +152,27 @@ exports.validate = async () => {
|
|||
return { valid: true }
|
||||
}
|
||||
|
||||
exports.exportRows = async ctx => {
|
||||
const { datasourceId, tableName } = breakExternalTableId(ctx.params.tableId)
|
||||
const db = getAppDB()
|
||||
const datasource = await db.get(datasourceId)
|
||||
if (!datasource || !datasource.entities) {
|
||||
ctx.throw(400, "Datasource has not been configured for plus API.")
|
||||
}
|
||||
const tables = datasource.entities
|
||||
const table = tables[tableName]
|
||||
ctx.request.body = {
|
||||
query: {
|
||||
oneOf: {
|
||||
[table.primaryDisplay]: ctx.request.body.map(
|
||||
id => breakRowIdField(id)[0]
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
return exports.search(ctx)
|
||||
}
|
||||
|
||||
exports.fetchEnrichedRow = async ctx => {
|
||||
const id = ctx.params.rowId
|
||||
const tableId = ctx.params.tableId
|
||||
|
|
|
@ -137,3 +137,12 @@ exports.fetchEnrichedRow = async function (ctx) {
|
|||
ctx.throw(400, err)
|
||||
}
|
||||
}
|
||||
|
||||
exports.export = async function (ctx) {
|
||||
const tableId = getTableId(ctx)
|
||||
try {
|
||||
ctx.body = await pickApi(tableId).exportRows(ctx)
|
||||
} catch (err) {
|
||||
ctx.throw(400, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -362,6 +362,22 @@ exports.validate = async ctx => {
|
|||
})
|
||||
}
|
||||
|
||||
exports.exportRows = async ctx => {
|
||||
const db = getAppDB()
|
||||
const table = await db.get(ctx.params.tableId)
|
||||
const rowIds = ctx.request.body.rows
|
||||
let response = (
|
||||
await db.allDocs({
|
||||
include_docs: true,
|
||||
keys: rowIds,
|
||||
})
|
||||
).rows.map(row => row.doc)
|
||||
|
||||
let rows = await outputProcessing(table, response)
|
||||
|
||||
return rows
|
||||
}
|
||||
|
||||
exports.fetchEnrichedRow = async ctx => {
|
||||
const db = getAppDB()
|
||||
const tableId = ctx.params.tableId
|
||||
|
|
|
@ -25,7 +25,8 @@ async function makeTableRequest(
|
|||
operation,
|
||||
table,
|
||||
tables,
|
||||
oldTable = null
|
||||
oldTable = null,
|
||||
renamed = null
|
||||
) {
|
||||
const json = {
|
||||
endpoint: {
|
||||
|
@ -41,6 +42,9 @@ async function makeTableRequest(
|
|||
if (oldTable) {
|
||||
json.meta.table = oldTable
|
||||
}
|
||||
if (renamed) {
|
||||
json.meta.renamed = renamed
|
||||
}
|
||||
return makeExternalQuery(datasource, json)
|
||||
}
|
||||
|
||||
|
@ -160,6 +164,7 @@ function isRelationshipSetup(column) {
|
|||
|
||||
exports.save = async function (ctx) {
|
||||
const table = ctx.request.body
|
||||
const { _rename: renamed } = table
|
||||
// can't do this right now
|
||||
delete table.dataImport
|
||||
const datasourceId = getDatasourceId(ctx.request.body)
|
||||
|
@ -241,7 +246,14 @@ exports.save = async function (ctx) {
|
|||
const operation = oldTable
|
||||
? DataSourceOperation.UPDATE_TABLE
|
||||
: DataSourceOperation.CREATE_TABLE
|
||||
await makeTableRequest(datasource, operation, tableToSave, tables, oldTable)
|
||||
await makeTableRequest(
|
||||
datasource,
|
||||
operation,
|
||||
tableToSave,
|
||||
tables,
|
||||
oldTable,
|
||||
renamed
|
||||
)
|
||||
// update any extra tables (like foreign keys in other tables)
|
||||
for (let extraTable of extraTablesToUpdate) {
|
||||
const oldExtraTable = oldTables[extraTable.name]
|
||||
|
@ -258,6 +270,8 @@ exports.save = async function (ctx) {
|
|||
)
|
||||
}
|
||||
|
||||
// remove the rename prop
|
||||
delete tableToSave._rename
|
||||
// store it into couch now for budibase reference
|
||||
datasource.entities[tableToSave.name] = tableToSave
|
||||
await db.put(datasource)
|
||||
|
|
|
@ -252,4 +252,25 @@ router
|
|||
rowController.destroy
|
||||
)
|
||||
|
||||
/**
|
||||
* @api {post} /api/:tableId/rows/exportRows Export Rows
|
||||
* @apiName Export rows
|
||||
* @apiGroup rows
|
||||
* @apiPermission table write access
|
||||
* @apiDescription This API can export a number of provided rows
|
||||
*
|
||||
* @apiParam {string} tableId The ID of the table the row is to be deleted from.
|
||||
*
|
||||
* @apiParam (Body) {object[]} [rows] The row IDs which are to be exported
|
||||
*
|
||||
* @apiSuccess {object[]|object}
|
||||
*/
|
||||
.post(
|
||||
"/api/:tableId/rows/exportRows",
|
||||
paramResource("tableId"),
|
||||
authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
|
||||
usage,
|
||||
rowController.export
|
||||
)
|
||||
|
||||
module.exports = router
|
||||
|
|
|
@ -138,6 +138,11 @@ export interface PaginationJson {
|
|||
page?: string | number
|
||||
}
|
||||
|
||||
export interface RenameColumn {
|
||||
old: string
|
||||
updated: string
|
||||
}
|
||||
|
||||
export interface RelationshipsJson {
|
||||
through?: string
|
||||
from?: string
|
||||
|
@ -166,6 +171,7 @@ export interface QueryJson {
|
|||
meta?: {
|
||||
table?: Table
|
||||
tables?: Record<string, Table>
|
||||
renamed: RenameColumn
|
||||
}
|
||||
extra?: {
|
||||
idFilter?: SearchFilters
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
import { Knex, knex } from "knex"
|
||||
import { Table } from "../../definitions/common"
|
||||
import { Operation, QueryJson } from "../../definitions/datasource"
|
||||
import {
|
||||
Operation,
|
||||
QueryJson,
|
||||
RenameColumn,
|
||||
} from "../../definitions/datasource"
|
||||
import { breakExternalTableId } from "../utils"
|
||||
import SchemaBuilder = Knex.SchemaBuilder
|
||||
import CreateTableBuilder = Knex.CreateTableBuilder
|
||||
|
@ -10,7 +14,8 @@ function generateSchema(
|
|||
schema: CreateTableBuilder,
|
||||
table: Table,
|
||||
tables: Record<string, Table>,
|
||||
oldTable: null | Table = null
|
||||
oldTable: null | Table = null,
|
||||
renamed?: RenameColumn
|
||||
) {
|
||||
let primaryKey = table && table.primary ? table.primary[0] : null
|
||||
const columns = Object.values(table.schema)
|
||||
|
@ -29,7 +34,11 @@ function generateSchema(
|
|||
for (let [key, column] of Object.entries(table.schema)) {
|
||||
// skip things that are already correct
|
||||
const oldColumn = oldTable ? oldTable.schema[key] : null
|
||||
if ((oldColumn && oldColumn.type) || (primaryKey === key && !isJunction)) {
|
||||
if (
|
||||
(oldColumn && oldColumn.type) ||
|
||||
(primaryKey === key && !isJunction) ||
|
||||
renamed?.updated === key
|
||||
) {
|
||||
continue
|
||||
}
|
||||
switch (column.type) {
|
||||
|
@ -81,6 +90,10 @@ function generateSchema(
|
|||
}
|
||||
}
|
||||
|
||||
if (renamed) {
|
||||
schema.renameColumn(renamed.old, renamed.updated)
|
||||
}
|
||||
|
||||
// need to check if any columns have been deleted
|
||||
if (oldTable) {
|
||||
const deletedColumns = Object.entries(oldTable.schema)
|
||||
|
@ -90,6 +103,9 @@ function generateSchema(
|
|||
)
|
||||
.map(([key]) => key)
|
||||
deletedColumns.forEach(key => {
|
||||
if (renamed?.old === key) {
|
||||
return
|
||||
}
|
||||
if (oldTable.constrained && oldTable.constrained.indexOf(key) !== -1) {
|
||||
schema.dropForeign(key)
|
||||
}
|
||||
|
@ -114,10 +130,11 @@ function buildUpdateTable(
|
|||
knex: SchemaBuilder,
|
||||
table: Table,
|
||||
tables: Record<string, Table>,
|
||||
oldTable: Table
|
||||
oldTable: Table,
|
||||
renamed: RenameColumn
|
||||
): SchemaBuilder {
|
||||
return knex.alterTable(table.name, schema => {
|
||||
generateSchema(schema, table, tables, oldTable)
|
||||
generateSchema(schema, table, tables, oldTable, renamed)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -167,7 +184,8 @@ class SqlTableQueryBuilder {
|
|||
client,
|
||||
json.table,
|
||||
json.meta.tables,
|
||||
json.meta.table
|
||||
json.meta.table,
|
||||
json.meta.renamed
|
||||
)
|
||||
break
|
||||
case Operation.DELETE_TABLE:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@budibase/string-templates",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Handlebars wrapper for Budibase templating.",
|
||||
"main": "src/index.cjs",
|
||||
"module": "dist/bundle.mjs",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "@budibase/worker",
|
||||
"email": "hi@budibase.com",
|
||||
"version": "1.0.80-alpha.3",
|
||||
"version": "1.0.81-alpha.4",
|
||||
"description": "Budibase background service",
|
||||
"main": "src/index.ts",
|
||||
"repository": {
|
||||
|
@ -34,8 +34,8 @@
|
|||
"author": "Budibase",
|
||||
"license": "GPL-3.0",
|
||||
"dependencies": {
|
||||
"@budibase/backend-core": "^1.0.80-alpha.3",
|
||||
"@budibase/string-templates": "^1.0.80-alpha.3",
|
||||
"@budibase/backend-core": "^1.0.81-alpha.4",
|
||||
"@budibase/string-templates": "^1.0.81-alpha.4",
|
||||
"@koa/router": "^8.0.0",
|
||||
"@sentry/node": "^6.0.0",
|
||||
"@techpass/passport-openidconnect": "^0.3.0",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue