Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
<script lang="ts">
import { Body, Input, ModalContent, Select, Toggle } from "@budibase/bbui"
import type { ProjectResponse } from "@budibase/types"
import {
Body,
InlineAlert,
Input,
ModalContent,
Select,
Toggle,
} from "@budibase/bbui"
import type { ExportProjectRequest, ProjectResponse } from "@budibase/types"

interface ConfirmPayload {
interface ConfirmPayload extends ExportProjectRequest {
id: string
encryptPassword?: string
}

interface Props {
Expand All @@ -22,6 +28,7 @@
}: Props = $props()

let projectId = $state("")
let includeRows = $state(false)
let encrypted = $state(false)
let encryptPassword = $state("")
let projectIsEmpty = $derived(
Expand Down Expand Up @@ -66,12 +73,12 @@
onConfirm={() =>
onConfirm({
id: projectId,
includeRows,
encryptPassword: encrypted ? encryptPassword.trim() : undefined,
})}
>
<Body size="S">
Export a portable Project package for use in another workspace. Rows and
attachments are not included yet.
Export a portable Project package for use in another workspace.
</Body>

<Select
Expand All @@ -83,6 +90,12 @@
getOptionColour={project => project.color}
/>

<Toggle text="Include rows and attachments" bind:value={includeRows} />
<Body size="S">
Includes all rows from included Budibase DB tables, including shared tables,
and their referenced attachments.
</Body>

<Toggle text="Encrypt export" bind:value={encrypted} />

{#if encrypted}
Expand All @@ -93,5 +106,8 @@
autocomplete="new-password"
bind:value={encryptPassword}
/>
{#if includeRows}
<InlineAlert header="Attachments are not encrypted in the export." />
{/if}
{/if}
</ModalContent>
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ vi.mock("@budibase/bbui", () => ({
import ImportProjectResultModal from "./ImportProjectResultModal.svelte"

describe("ImportProjectResultModal", () => {
it("groups setup reasons by resource and explains omitted content", async () => {
it("groups setup reasons by resource and shows omitted content and data counts", async () => {
const response: ImportProjectResponse = {
project: {
_id: "project_1",
Expand All @@ -24,6 +24,7 @@ describe("ImportProjectResultModal", () => {
updatedAt: "2026-01-01T00:00:00.000Z",
},
resources: { [ResourceType.AGENT]: ["agent_1"] },
dataImport: { tables: 2, rows: 3, relationships: 1, attachments: 1 },
requirements: [
{
type: "agent_secrets",
Expand Down Expand Up @@ -58,6 +59,11 @@ describe("ImportProjectResultModal", () => {
expect(screen.getByText("Choose an AI model.")).toBeTruthy()
expect(screen.getByText(response.unsupportedContent[0].reason)).toBeTruthy()
expect(screen.queryByText(/agent_linked_content/)).toBeNull()
expect(
screen.getByText(
"Tables: 2 · Rows: 3 · Relationships: 1 · Attachments: 1"
)
).toBeTruthy()

await fireEvent.click(buttons[0])
expect(onOpenResource).toHaveBeenCalledExactlyOnceWith(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@
publishing the imported resources.
</Body>

{#if response.dataImport}
<Body size="S">
Tables: {response.dataImport.tables.toLocaleString()} · Rows: {response.dataImport.rows.toLocaleString()}
· Relationships:
{response.dataImport.relationships.toLocaleString()} · Attachments:
{response.dataImport.attachments.toLocaleString()}
</Body>
{/if}

<div class="import-details">
{#if resourceRequirements.length}
<section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
type ProjectResponse,
type ImportProjectResponse,
type ProjectImportRequirement,
type ExportProjectRequest,
PublishResourceState,
type Agent,
type Table,
Expand Down Expand Up @@ -490,12 +491,10 @@
const handleExportProject = async ({
id,
encryptPassword,
}: {
id: string
encryptPassword?: string
}) => {
includeRows,
}: ExportProjectRequest & { id: string }) => {
try {
await projectsStore.exportProject(id, { encryptPassword })
await projectsStore.exportProject({ id, encryptPassword, includeRows })
exportProjectModal?.hide()
} catch (error) {
console.error(error)
Expand Down Expand Up @@ -550,7 +549,11 @@
"Project imported, but some resources could not be refreshed. Reload the workspace to see all imported resources."
)
}
if (response.requirements.length || response.unsupportedContent.length) {
if (
response.requirements.length ||
response.unsupportedContent.length ||
response.dataImport
) {
importProjectResultModal?.show()
}
} catch (error) {
Expand Down
11 changes: 5 additions & 6 deletions packages/builder/src/stores/portal/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { API } from "@/api"
import { downloadStream } from "@budibase/frontend-core"
import type {
CreateProjectRequest,
ExportProjectRequest,
ImportProjectRequest,
ImportProjectResponse,
PreviewProjectAssignmentRequest,
Expand Down Expand Up @@ -86,12 +87,10 @@ export class ProjectsStore extends BudiStore<ProjectResponse[]> {
return response.project
}

exportProject = async (
id: string,
body?: {
encryptPassword?: string
}
) => {
exportProject = async ({
id,
...body
}: ExportProjectRequest & { id: string }) => {
const response = await API.projects.exportBundle(id, body)
await downloadStream(response)
}
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/api/controllers/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export async function exportBundle(
const exportStream = await sdk.projects.streamExportProject({
projectId: id,
encryptPassword,
includeRows: ctx.request.body?.includeRows,
Comment thread
andz-bb marked this conversation as resolved.
})
ctx.attachment(identifier)
ctx.body = exportStream
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/api/routes/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ builderRoutes
middleware.joiValidator.body(
Joi.object({
encryptPassword: Joi.string().max(1024).optional().allow(""),
includeRows: Joi.boolean().strict().optional(),
}),
{
allowUnknown: false,
Expand Down
Loading
Loading