Skip to content
Open
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
4 changes: 2 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@8e7decc",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@297fbee",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc",
"@appwrite.io/pink-legacy": "^1.0.3",
Expand Down
153 changes: 98 additions & 55 deletions src/lib/stores/migration.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
import { writable } from 'svelte/store';
import { Resources } from '@appwrite.io/console';
import {
AppwriteMigrationResource,
FirebaseMigrationResource,
NHostMigrationResource,
SupabaseMigrationResource
} from '@appwrite.io/console';
import { includesAll } from '$lib/helpers/array';

export type MigrationResource =
| AppwriteMigrationResource
| FirebaseMigrationResource
| NHostMigrationResource
| SupabaseMigrationResource;

// Appwrite enum is the superset of all provider resources — used as a
// provider-agnostic reference. The addResource guard filters by provider.
const ResourceType = AppwriteMigrationResource;

type ProviderResourceMap = {
appwrite: AppwriteMigrationResource[];
supabase: SupabaseMigrationResource[];
nhost: NHostMigrationResource[];
firebase: FirebaseMigrationResource[];
};

const initialFormData = {
users: {
root: false,
Expand All @@ -13,11 +35,14 @@ const initialFormData = {
},
functions: {
root: false,
env: false,
inactive: false
},
storage: {
root: false
},
sites: {
root: false,
inactive: false
}
};

Expand Down Expand Up @@ -48,72 +73,67 @@ export const ResourcesFriendly = {
table: { singular: 'Table', plural: 'Tables' },
index: { singular: 'Index', plural: 'Indexes' },
column: { singular: 'Column', plural: 'Columns' },
row: { singular: 'Row', plural: 'Rows' }
row: { singular: 'Row', plural: 'Rows' },
site: { singular: 'Site', plural: 'Sites' },
'site-deployment': { singular: 'Site Deployment', plural: 'Site Deployments' },
'site-variable': { singular: 'Site Variable', plural: 'Site Variables' }
};

// @todo: @itznotabug - check if other resources are correct and work fine!
export const providerResources: Record<Provider, Resources[]> = {
appwrite: Object.values(Resources),
supabase: [
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Index,
Resources.Document,
Resources.Bucket,
Resources.File
],
nhost: [
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Index,
Resources.Document,
Resources.Bucket,
Resources.File
],
firebase: [
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Document,
Resources.Bucket,
Resources.File
]
export const providerResources: ProviderResourceMap = {
appwrite: Object.values(AppwriteMigrationResource),
supabase: Object.values(SupabaseMigrationResource),
nhost: Object.values(NHostMigrationResource),
firebase: Object.values(FirebaseMigrationResource)
};

export const migrationFormToResources = (
export const migrationFormToResources = <P extends Provider>(
formData: MigrationFormData,
provider: Provider
): Resources[] => {
const resources: Resources[] = [];
const addResource = (resource: Resources) => {
if (providerResources[provider].includes(resource)) {
provider: P
): ProviderResourceMap[P] => {
const resources: MigrationResource[] = [];
const providerValues = providerResources[provider] as MigrationResource[];
const addResource = (resource: MigrationResource) => {
if (providerValues.includes(resource)) {
resources.push(resource);
}
};

if (formData.users.root) {
addResource(Resources.User);
addResource(ResourceType.User);
if (formData.users.teams) {
addResource(ResourceType.Team);
addResource(ResourceType.Membership);
}
}
if (formData.databases.root) {
addResource(Resources.Database);
addResource(Resources.Table);
addResource(Resources.Column);
addResource(Resources.Index);
addResource(ResourceType.Database);
addResource(ResourceType.Table);
addResource(ResourceType.Column);
addResource(ResourceType.Index);
}
if (formData.databases.rows) {
addResource(Resources.Row);
addResource(ResourceType.Row);
}
if (formData.storage.root) {
addResource(Resources.Bucket);
addResource(Resources.File);
addResource(ResourceType.Bucket);
addResource(ResourceType.File);
}
if (formData.functions.root) {
addResource(ResourceType.Function);
addResource(ResourceType.Environmentvariable);
if (formData.functions.inactive) {
addResource(ResourceType.Deployment);
}
}
if (formData.sites.root) {
addResource(ResourceType.Site);
addResource(ResourceType.Sitevariable);
if (formData.sites.inactive) {
addResource(ResourceType.Sitedeployment);
}
}

return resources;
return resources as ProviderResourceMap[P];
};

const compareVersions = (a: string, b: string) => {
Expand All @@ -137,20 +157,43 @@ export const isVersionAtLeast = (version: string, atLeast: string) => {
return compareVersions(version, atLeast) >= 0;
};

export const resourcesToMigrationForm = (resources: Resources[]): MigrationFormData => {
export const resourcesToMigrationForm = (resources: MigrationResource[]): MigrationFormData => {
const formData = { ...initialFormData };
if (resources.includes(Resources.User)) {
if (resources.includes(ResourceType.User)) {
formData.users.root = true;
}
if (resources.includes(Resources.Database)) {
if (
includesAll(resources, [ResourceType.Team, ResourceType.Membership] as MigrationResource[])
) {
formData.users.teams = true;
}
if (resources.includes(ResourceType.Database)) {
formData.databases.root = true;
}
if (includesAll(resources, [Resources.Table, Resources.Column, Resources.Row] as Resources[])) {
if (
includesAll(resources, [
ResourceType.Table,
ResourceType.Column,
ResourceType.Row
] as MigrationResource[])
) {
formData.databases.rows = true;
}
if (includesAll(resources, [Resources.Bucket, Resources.File] as Resources[])) {
if (includesAll(resources, [ResourceType.Bucket, ResourceType.File] as MigrationResource[])) {
formData.storage.root = true;
}
if (resources.includes(ResourceType.Function)) {
formData.functions.root = true;
}
if (resources.includes(ResourceType.Deployment)) {
formData.functions.inactive = true;
}
if (resources.includes(ResourceType.Site)) {
formData.sites.root = true;
}
if (resources.includes(ResourceType.Sitedeployment)) {
formData.sites.inactive = true;
}

return formData;
};
Expand Down
29 changes: 18 additions & 11 deletions src/routes/(console)/(migration-wizard)/resource-form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
createMigrationFormStore,
createMigrationProviderStore,
type MigrationFormData,
type MigrationResource,
providerResources,
resourcesToMigrationForm
} from '$lib/stores/migration';
import { Button } from '$lib/elements/forms';
import { wizard } from '$lib/stores/wizard';
import { Resources, type Models } from '@appwrite.io/console';
import { AppwriteMigrationResource, type Models } from '@appwrite.io/console';
import type { sdk } from '$lib/stores/sdk';
import ImportReport from '$routes/(console)/project-[region]-[project]/settings/migrations/(import)/importReport.svelte';

Expand Down Expand Up @@ -91,22 +92,27 @@

$: errorInResources = error;
$: wizard.setNextDisabled(!report);
$: resources = providerResources[$provider.provider];
$: resources = providerResources[$provider.provider] as MigrationResource[];

const shouldRenderGroup = (groupKey: string): boolean => {
if (groupKey === 'storage') {
return (
resources.includes(AppwriteMigrationResource.Bucket) &&
resources.includes(AppwriteMigrationResource.File)
);
}

if (groupKey === 'functions') {
// Functions not in SDK Resources enum, skip
return false;
return resources.includes(AppwriteMigrationResource.Function);
}

if (groupKey === 'storage') {
return resources.includes(Resources.Bucket) && resources.includes(Resources.File);
if (groupKey === 'sites') {
return resources.includes(AppwriteMigrationResource.Site);
}

// Map groupKey to Resources enum
const groupToResource: Record<string, Resources> = {
users: Resources.User,
databases: Resources.Database
const groupToResource: Record<string, MigrationResource> = {
users: AppwriteMigrationResource.User,
databases: AppwriteMigrationResource.Database
};
const resource = groupToResource[groupKey];
return resource ? resources.includes(resource) : false;
Expand All @@ -122,7 +128,8 @@
users: 'user',
databases: 'database',
functions: 'function',
storage: 'bucket'
storage: 'bucket',
sites: 'site'
};
return map[groupKey] || groupKey;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
databases: { root: 'Databases', rows: 'Include rows' },
functions: {
root: 'Functions',
env: 'Include environment variables',
inactive: 'Include inactive deployments'
},
storage: { root: 'Storage' }
storage: { root: 'Storage' },
sites: {
root: 'Sites',
inactive: 'Include inactive deployments'
}
};

const descriptionMap = {
Expand All @@ -40,7 +43,10 @@
},
functions: {
root: 'Import all functions and their active deployment',
env: 'Import all environment variables',
inactive: 'Import all deployments that are not currently active'
},
sites: {
root: 'Import all sites and their active deployment',
inactive: 'Import all deployments that are not currently active'
}
};
Expand All @@ -49,24 +55,16 @@

let parentState: boolean | 'indeterminate';

$: {
const total = formGroupChildren.length;
const checked = formGroupChildren.filter((key) => formGroup[key]).length;

if (checked === 0) {
parentState = false;
} else if (checked === total) {
parentState = true;
} else {
parentState = 'indeterminate';
}
}
$: parentState = formGroup.root;

function onParentChange(event: CustomEvent<boolean | 'indeterminate'>) {
if (event.detail === 'indeterminate') return;
const updated = { ...formGroup };
for (const key of Object.keys(formGroup)) {
updated[key] = event.detail;
updated.root = event.detail;
if (!event.detail) {
for (const key of formGroupChildren) {
updated[key] = false;
}
}

dispatch('updateFormGroup', updated);
Expand Down Expand Up @@ -114,7 +112,11 @@
<Selector.Checkbox
size="s"
id={key}
bind:checked={formGroup[key]}
checked={formGroup[key]}
on:change={(event) => {
const updated = { ...formGroup, [key]: event.detail };
dispatch('updateFormGroup', updated);
}}
label={labelMap[groupKey]?.[key] ?? key}
description={descriptionMap[groupKey]?.[key]} />
</div>
Expand Down
Loading