Merge pull request #10900 from Budibase/budi-7010/export_controller_as_post
BUDI-7010 - Export controller as post
This commit is contained in:
commit
672804c150
|
@ -1,5 +1,11 @@
|
||||||
<script>
|
<script>
|
||||||
import { ModalContent, Toggle, Body, InlineAlert } from "@budibase/bbui"
|
import {
|
||||||
|
ModalContent,
|
||||||
|
Toggle,
|
||||||
|
Body,
|
||||||
|
InlineAlert,
|
||||||
|
notifications,
|
||||||
|
} from "@budibase/bbui"
|
||||||
|
|
||||||
export let app
|
export let app
|
||||||
export let published
|
export let published
|
||||||
|
@ -8,10 +14,45 @@
|
||||||
$: title = published ? "Export published app" : "Export latest app"
|
$: title = published ? "Export published app" : "Export latest app"
|
||||||
$: confirmText = published ? "Export published" : "Export latest"
|
$: confirmText = published ? "Export published" : "Export latest"
|
||||||
|
|
||||||
const exportApp = () => {
|
const exportApp = async () => {
|
||||||
const id = published ? app.prodId : app.devId
|
const id = published ? app.prodId : app.devId
|
||||||
const appName = encodeURIComponent(app.name)
|
const url = `/api/backups/export?appId=${id}`
|
||||||
window.location = `/api/backups/export?appId=${id}&appname=${appName}&excludeRows=${excludeRows}`
|
await downloadFile(url, { excludeRows })
|
||||||
|
}
|
||||||
|
|
||||||
|
async function downloadFile(url, body) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify(body),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const contentDisposition = response.headers.get("Content-Disposition")
|
||||||
|
|
||||||
|
const matches = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(
|
||||||
|
contentDisposition
|
||||||
|
)
|
||||||
|
|
||||||
|
const filename = matches[1].replace(/['"]/g, "")
|
||||||
|
|
||||||
|
const url = URL.createObjectURL(await response.blob())
|
||||||
|
|
||||||
|
const link = document.createElement("a")
|
||||||
|
link.href = url
|
||||||
|
link.download = filename
|
||||||
|
link.click()
|
||||||
|
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
} else {
|
||||||
|
notifications.error("Error exporting the app.")
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notifications.error(error.message || "Error downloading the exported app")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,22 @@
|
||||||
import sdk from "../../sdk"
|
import sdk from "../../sdk"
|
||||||
import { events, context } from "@budibase/backend-core"
|
import { events, context, db } from "@budibase/backend-core"
|
||||||
import { DocumentType } from "../../db/utils"
|
import { DocumentType } from "../../db/utils"
|
||||||
import { isQsTrue } from "../../utilities"
|
import { Ctx } from "@budibase/types"
|
||||||
|
|
||||||
|
interface ExportAppDumpRequest {
|
||||||
|
excludeRows: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function exportAppDump(ctx: Ctx<ExportAppDumpRequest>) {
|
||||||
|
const { appId } = ctx.query as any
|
||||||
|
const { excludeRows } = ctx.request.body
|
||||||
|
|
||||||
|
const [app] = await db.getAppsByIDs([appId])
|
||||||
|
const appName = app.name
|
||||||
|
|
||||||
export async function exportAppDump(ctx: any) {
|
|
||||||
let { appId, excludeRows } = ctx.query
|
|
||||||
// remove the 120 second limit for the request
|
// remove the 120 second limit for the request
|
||||||
ctx.req.setTimeout(0)
|
ctx.req.setTimeout(0)
|
||||||
const appName = decodeURI(ctx.query.appname)
|
|
||||||
excludeRows = isQsTrue(excludeRows)
|
|
||||||
const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz`
|
const backupIdentifier = `${appName}-export-${new Date().getTime()}.tar.gz`
|
||||||
ctx.attachment(backupIdentifier)
|
ctx.attachment(backupIdentifier)
|
||||||
ctx.body = await sdk.backups.streamExportApp(appId, excludeRows)
|
ctx.body = await sdk.backups.streamExportApp(appId, excludeRows)
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { permissions } from "@budibase/backend-core"
|
||||||
|
|
||||||
const router: Router = new Router()
|
const router: Router = new Router()
|
||||||
|
|
||||||
router.get(
|
router.post(
|
||||||
"/api/backups/export",
|
"/api/backups/export",
|
||||||
authorized(permissions.BUILDER),
|
authorized(permissions.BUILDER),
|
||||||
controller.exportAppDump
|
controller.exportAppDump
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import tk from "timekeeper"
|
||||||
import * as setup from "./utilities"
|
import * as setup from "./utilities"
|
||||||
import { events } from "@budibase/backend-core"
|
import { events } from "@budibase/backend-core"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
import { checkBuilderEndpoint } from "./utilities/TestFunctions"
|
||||||
|
import { mocks } from "@budibase/backend-core/tests"
|
||||||
|
|
||||||
describe("/backups", () => {
|
describe("/backups", () => {
|
||||||
let request = setup.getRequest()
|
let request = setup.getRequest()
|
||||||
|
@ -16,7 +18,7 @@ describe("/backups", () => {
|
||||||
describe("exportAppDump", () => {
|
describe("exportAppDump", () => {
|
||||||
it("should be able to export app", async () => {
|
it("should be able to export app", async () => {
|
||||||
const res = await request
|
const res = await request
|
||||||
.get(`/api/backups/export?appId=${config.getAppId()}&appname=test`)
|
.post(`/api/backups/export?appId=${config.getAppId()}`)
|
||||||
.set(config.defaultHeaders())
|
.set(config.defaultHeaders())
|
||||||
.expect(200)
|
.expect(200)
|
||||||
expect(res.headers["content-type"]).toEqual("application/gzip")
|
expect(res.headers["content-type"]).toEqual("application/gzip")
|
||||||
|
@ -26,10 +28,24 @@ describe("/backups", () => {
|
||||||
it("should apply authorization to endpoint", async () => {
|
it("should apply authorization to endpoint", async () => {
|
||||||
await checkBuilderEndpoint({
|
await checkBuilderEndpoint({
|
||||||
config,
|
config,
|
||||||
method: "GET",
|
method: "POST",
|
||||||
url: `/api/backups/export?appId=${config.getAppId()}`,
|
url: `/api/backups/export?appId=${config.getAppId()}`,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should infer the app name from the app", async () => {
|
||||||
|
tk.freeze(mocks.date.MOCK_DATE)
|
||||||
|
|
||||||
|
const res = await request
|
||||||
|
.post(`/api/backups/export?appId=${config.getAppId()}`)
|
||||||
|
.set(config.defaultHeaders())
|
||||||
|
|
||||||
|
expect(res.headers["content-disposition"]).toEqual(
|
||||||
|
`attachment; filename="${
|
||||||
|
config.getApp()!.name
|
||||||
|
}-export-${mocks.date.MOCK_DATE.getTime()}.tar.gz"`
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("calculateBackupStats", () => {
|
describe("calculateBackupStats", () => {
|
||||||
|
|
Loading…
Reference in New Issue