Fixing an issue with deleting large apps with a lot of internal table data and adding back the export modal which allows picking whether an export includes internal table rows - #7583.

This commit is contained in:
mike12345567 2022-09-09 20:06:29 +01:00
parent 2686d50523
commit 3d66e71d7d
7 changed files with 45 additions and 24 deletions

View File

@ -1,16 +1,24 @@
<script>
import { ModalContent, Toggle } from "@budibase/bbui"
import { ModalContent, Toggle, Body } from "@budibase/bbui"
export let app
export let published
let excludeRows = false
$: title = published ? "Export published app" : "Export latest app"
$: confirmText = published ? "Export published" : "Export latest"
const exportApp = () => {
const id = app.deployed ? app.prodId : app.devId
const id = published ? app.prodId : app.devId
const appName = encodeURIComponent(app.name)
window.location = `/api/backups/export?appId=${id}&appname=${appName}&excludeRows=${excludeRows}`
}
</script>
<ModalContent title={"Export"} confirmText={"Export"} onConfirm={exportApp}>
<ModalContent {title} {confirmText} onConfirm={exportApp}>
<Body
>Apps can be exported with or without data that is within internal tables -
select this below.</Body
>
<Toggle text="Exclude Rows" bind:value={excludeRows} />
</ModalContent>

View File

@ -15,7 +15,6 @@
import Spinner from "components/common/Spinner.svelte"
import CreateAppModal from "components/start/CreateAppModal.svelte"
import UpdateAppModal from "components/start/UpdateAppModal.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte"
import { store, automationStore } from "builderStore"
import { API } from "api"
@ -33,7 +32,6 @@
let selectedApp
let creationModal
let updatingModal
let exportModal
let creatingApp = false
let loaded = $apps?.length || $templates?.length
let searchTerm = ""
@ -41,6 +39,9 @@
let creatingFromTemplate = false
let automationErrors
let accessFilterList = null
let welcomeHeader, welcomeBody
let createAppButtonText
let enrichedApps, filteredApps, lockedApps, unlocked
const resolveWelcomeMessage = (auth, apps) => {
const userWelcome = auth?.user?.firstName
@ -407,10 +408,6 @@
<UpdateAppModal app={selectedApp} />
</Modal>
<Modal bind:this={exportModal} padding={false} width="600px">
<ExportAppModal app={selectedApp} />
</Modal>
<style>
.appTable {
border-top: var(--border-light);

View File

@ -16,6 +16,7 @@
MenuItem,
Icon,
Helpers,
Modal,
} from "@budibase/bbui"
import OverviewTab from "../_components/OverviewTab.svelte"
import SettingsTab from "../_components/SettingsTab.svelte"
@ -29,6 +30,7 @@
import EditableIcon from "components/common/EditableIcon.svelte"
import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import HistoryTab from "components/portal/overview/automation/HistoryTab.svelte"
import ExportAppModal from "components/start/ExportAppModal.svelte"
import { checkIncomingDeploymentStatus } from "components/deploy/utils"
import { onDestroy, onMount } from "svelte"
@ -38,7 +40,9 @@
let loaded = false
let deletionModal
let unpublishModal
let exportModal
let appName = ""
let published
// App
$: filteredApps = $apps.filter(app => app.devId === application)
@ -140,11 +144,9 @@
notifications.success("App ID copied to clipboard.")
}
const exportApp = (app, opts = { published: false }) => {
const appName = encodeURIComponent(app.name)
const id = opts?.published ? app.prodId : app.devId
// always export the development version
window.location = `/api/backups/export?appId=${id}&appname=${appName}`
const exportApp = opts => {
published = opts.published
exportModal.show()
}
const unpublishApp = app => {
@ -206,6 +208,10 @@
})
</script>
<Modal bind:this={exportModal} padding={false} width="600px">
<ExportAppModal app={selectedApp} {published} />
</Modal>
<span class="overview-wrap">
<Page wide noPadding>
{#await promise}
@ -269,14 +275,14 @@
<Icon hoverable name="More" />
</span>
<MenuItem
on:click={() => exportApp(selectedApp, { published: false })}
on:click={() => exportApp({ published: false })}
icon="DownloadFromCloud"
>
Export latest
</MenuItem>
{#if isPublished}
<MenuItem
on:click={() => exportApp(selectedApp, { published: true })}
on:click={() => exportApp({ published: true })}
icon="DownloadFromCloudOutline"
>
Export published

View File

@ -1,11 +1,12 @@
const { streamBackup } = require("../../utilities/fileSystem")
const { events, context } = require("@budibase/backend-core")
const { DocumentType } = require("../../db/utils")
const { isQsTrue } = require("../../utilities")
exports.exportAppDump = async function (ctx) {
let { appId, excludeRows } = ctx.query
const appName = decodeURI(ctx.query.appname)
excludeRows = excludeRows === "true"
excludeRows = isQsTrue(excludeRows)
const backupIdentifier = `${appName}-export-${new Date().getTime()}.txt`
ctx.attachment(backupIdentifier)
ctx.body = await streamBackup(appId, excludeRows)

View File

@ -125,13 +125,13 @@ exports.defineFilter = excludeRows => {
* data or user relationships.
* @param {string} appId The app to backup
* @param {object} config Config to send to export DB
* @param {boolean} includeRows Flag to state whether the export should include data.
* @param {boolean} excludeRows Flag to state whether the export should include data.
* @returns {*} either a string or a stream of the backup
*/
const backupAppData = async (appId, config, includeRows) => {
const backupAppData = async (appId, config, excludeRows) => {
return await exports.exportDB(appId, {
...config,
filter: exports.defineFilter(includeRows),
filter: exports.defineFilter(excludeRows),
})
}
@ -148,11 +148,11 @@ exports.performBackup = async (appId, backupName) => {
/**
* Streams a backup of the database state for an app
* @param {string} appId The ID of the app which is to be backed up.
* @param {boolean} includeRows Flag to state whether the export should include data.
* @param {boolean} excludeRows Flag to state whether the export should include data.
* @returns {*} a readable stream of the backup which is written in real time
*/
exports.streamBackup = async (appId, includeRows) => {
return await backupAppData(appId, { stream: true }, includeRows)
exports.streamBackup = async (appId, excludeRows) => {
return await backupAppData(appId, { stream: true }, excludeRows)
}
/**

View File

@ -162,3 +162,11 @@ exports.convertBookmark = bookmark => {
}
return bookmark
}
exports.isQsTrue = param => {
if (typeof param === "string") {
return param.toLowerCase() === "true"
} else {
return param === true
}
}

View File

@ -72,7 +72,8 @@ exports.getUniqueRows = async appIds => {
// ensure uniqueness on a per app pair basis
// this can't be done on all rows because app import results in
// duplicate row ids across apps
uniqueRows = uniqueRows.concat(...new Set(appRows))
// the array pre-concat is important to avoid stack overflow
uniqueRows = uniqueRows.concat([...new Set(appRows)])
}
return uniqueRows