diff --git a/bun.lock b/bun.lock index 05bdf932d..c2793e6bf 100644 --- a/bun.lock +++ b/bun.lock @@ -6,7 +6,7 @@ "name": "@appwrite/console", "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@8f00f95", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@341620a", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", @@ -124,7 +124,7 @@ "@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="], - "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@8f00f95", { "dependencies": { "json-bigint": "1.0.0" } }], + "@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@341620a", { "dependencies": { "json-bigint": "1.0.0" } }], "@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="], diff --git a/package.json b/package.json index 0eb4abad8..b0fe8b089 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ }, "dependencies": { "@ai-sdk/svelte": "^1.1.24", - "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@8f00f95", + "@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@341620a", "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bfe7ce3", "@appwrite.io/pink-legacy": "^1.0.3", diff --git a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte index 1df280dce..017e1a03a 100644 --- a/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte +++ b/src/routes/(console)/project-[region]-[project]/databases/database-[database]/collection-[collection]/+page.svelte @@ -5,7 +5,15 @@ import type { Column, ColumnType } from '$lib/helpers/types'; import { Container } from '$lib/layout'; import { preferences } from '$lib/stores/preferences'; - import { Icon, Layout, Divider, Tooltip } from '@appwrite.io/pink-svelte'; + import { + Icon, + Layout, + Divider, + Tooltip, + Selector, + Typography, + Dialog + } from '@appwrite.io/pink-svelte'; import type { PageProps } from './$types'; import FilePicker from '$lib/components/filePicker.svelte'; import { page } from '$app/state'; @@ -21,7 +29,7 @@ IconUpload, IconDownload } from '@appwrite.io/pink-icons-svelte'; - import { type Models } from '@appwrite.io/console'; + import { OnDuplicate, type Models } from '@appwrite.io/console'; import { sdk } from '$lib/stores/sdk'; import { goto } from '$app/navigation'; import { resolve } from '$app/paths'; @@ -49,7 +57,11 @@ let isRefreshing = $state(false); let showImportJson = $state(false); + let showImportOptions = $state(false); let showCustomColumnsModal = $state(false); + let importOnDuplicate: OnDuplicate = $state(OnDuplicate.Fail); + let pendingFile: Models.File | null = $state(null); + let pendingLocalFile = $state(false); let columnsError: string = $state(null); let spreadsheet: SpreadSheet | null = $state(null); @@ -74,17 +86,28 @@ return queryParam ? `${url}?query=${encodeURIComponent(queryParam)}` : url; } - async function onSelect(file: Models.File, localFile = false) { + function onSelect(file: Models.File, localFile = false) { + pendingFile = file; + pendingLocalFile = localFile; + importOnDuplicate = OnDuplicate.Fail; + showImportOptions = true; + } + + async function startImport() { + if (!pendingFile) return; + + showImportOptions = false; $isCollectionsJsonImportInProgress = true; try { await sdk .forProject(page.params.region, page.params.project) .migrations.createJSONImport({ - bucketId: file.bucketId, - fileId: file.$id, + bucketId: pendingFile.bucketId, + fileId: pendingFile.$id, resourceId: `${page.params.database}:${page.params.collection}`, - internalFile: localFile + internalFile: pendingLocalFile, + onDuplicate: importOnDuplicate }); addNotification({ @@ -101,6 +124,7 @@ }); } finally { $isCollectionsJsonImportInProgress = false; + pendingFile = null; } } @@ -343,6 +367,52 @@ }} /> {/if} + + + + Choose how to handle documents that already exist in this collection. + + + + + Import aborts on the first document with a matching ID. + + + + + Documents with matching IDs will be silently skipped. + + + + + Documents with matching IDs will be updated with the imported data. + + + + + + + + + + + + - { - importOverwrite = e.detail; - if (e.detail) importSkip = false; - }} - label="Overwrite existing documents" - description="Documents with matching IDs will be updated with the imported data." /> - + + Migration aborts on the first row with a matching ID. + + + { - importSkip = e.detail; - if (e.detail) importOverwrite = false; - }} - label="Skip existing documents" - description="Documents with matching IDs will be silently skipped." /> + bind:group={importOnDuplicate} + name="importOnDuplicate" + value={OnDuplicate.Skip} + label="Skip existing documents"> + + Documents with matching IDs will be silently skipped. + + + + + Documents with matching IDs will be updated with the imported data. + + diff --git a/src/routes/(console)/project-[region]-[project]/settings/migrations/(import)/wizard.svelte b/src/routes/(console)/project-[region]-[project]/settings/migrations/(import)/wizard.svelte index 67043f5d6..033332ca4 100644 --- a/src/routes/(console)/project-[region]-[project]/settings/migrations/(import)/wizard.svelte +++ b/src/routes/(console)/project-[region]-[project]/settings/migrations/(import)/wizard.svelte @@ -11,6 +11,7 @@ AppwriteMigrationResource, FirebaseMigrationResource, NHostMigrationResource, + OnDuplicate, SupabaseMigrationResource } from '@appwrite.io/console'; import { started } from '../stores'; @@ -39,8 +40,7 @@ import { capitalize } from '$lib/helpers/string'; import { page } from '$app/state'; - let importOverwrite = false; - let importSkip = false; + let importOnDuplicate: OnDuplicate = OnDuplicate.Fail; const onExit = () => { resetImportStores(); @@ -51,8 +51,7 @@ const resources = migrationFormToResources($formData, $provider.provider); const importOptions = { - overwrite: importOverwrite, - skip: importSkip + onDuplicate: importOnDuplicate }; switch ($provider.provider) { @@ -213,24 +212,39 @@ {#if $formData.databases.root}
- { - importOverwrite = e.detail; - if (e.detail) importSkip = false; - }} - label="Overwrite existing documents" - description="Documents with matching IDs will be updated with the imported data." /> - + + Migration aborts on the first existing resource (database, + table, column, index, or row). + + + { - importSkip = e.detail; - if (e.detail) importOverwrite = false; - }} - label="Skip existing documents" - description="Documents with matching IDs will be silently skipped." /> + bind:group={importOnDuplicate} + name="importOnDuplicate" + value={OnDuplicate.Skip} + label="Skip existing resources"> + + Existing resources are left untouched. Only resources missing on + the destination are created. + + + + + Existing resources are updated to match the source. Schema drift + and row data are both reconciled. + +
{/if}