From 979d5294826b98cef8ef4c611a67be1fa93a725b Mon Sep 17 00:00:00 2001 From: Mathieu Stefani Date: Wed, 25 Sep 2024 13:23:34 +0200 Subject: [PATCH] fix: properly display datasources in table creation dialog --- .../dialog/table/TableDialog.svelte | 283 +++++++++--------- 1 file changed, 140 insertions(+), 143 deletions(-) diff --git a/src/lib/components/dialog/table/TableDialog.svelte b/src/lib/components/dialog/table/TableDialog.svelte index 76bdd08..cc1c454 100644 --- a/src/lib/components/dialog/table/TableDialog.svelte +++ b/src/lib/components/dialog/table/TableDialog.svelte @@ -24,7 +24,7 @@ import type { Database, DatasourceConfig, FileType } from '$lib/lens/types'; import Icon from '@iconify/svelte'; - import type { Component } from 'svelte'; + import type { Component, SvelteComponent } from 'svelte'; import CsvOptions from './CsvOptions.svelte'; import ParquetOptions from './ParquetOptions.svelte'; import JsonOptions from './JsonOptions.svelte'; @@ -56,24 +56,28 @@ }; }); - const datasourceItems: Array<{ value: DatasourceItem | undefined; label: string }> = [ + const FILE_DATASOURCE: DatasourceItem = { + kind: 'file', + url: 'file://' + }; + + const DIR_DATASOURCE: DatasourceItem = { + kind: 'dir', + url: 'file://' + }; + + const datasourceItems: Array<{ value: DatasourceItem; label: string }> = [ { - value: { - kind: 'file', - url: 'file://' - }, - label: 'file://' + value: FILE_DATASOURCE, + label: FILE_DATASOURCE.url }, { - value: { - kind: 'dir', - url: 'file://' - }, + value: DIR_DATASOURCE, label: 'dir://' }, ...datasources.map((ds) => { return { - value: toDatasourceItem(ds), + value: { kind: ds.store.kind, url: ds.url }, label: ds.url }; }) @@ -94,118 +98,119 @@ dir: 'mdi:folder' }; - let database = $state(info?.database ?? ''); - let schema = $state(info?.schema ?? ''); - let name = $state(info?.name ?? ''); - let fileType: FileType = $state(info?.fileType ?? 'csv'); - let partitions = $state< - { - get name(): string; - set name(val: string); - get type(): string | undefined; - set type(val: string); - }[] - >([]); - let dataSource: DatasourceItem = $state({ kind: 'file', url: 'file://' }); - let locationPath = $state(info?.location ?? ''); - let optionsComponent = $state(undefined); + class Partition { + name = $state(''); + type = $state(''); + } - let schemas = $derived( - databases - .filter((db) => db.name == database) - .flatMap((db) => db.schemas.map((schema) => schema.name)) - ); - let schemaItems = $derived( - schemas.map((schema: string) => { - return { - value: schema, - label: schema - }; - }) - ); + class Table { + private readonly fileTypeOptions: Record = { + csv: CsvOptions, + arrow: undefined, + parquet: ParquetOptions, + avro: undefined, + json: JsonOptions + }; - let open = $state(false); + database = $state(''); + schema = $state(''); + name = $state(''); + fileType = $state('csv'); - let browsable = $derived.by(() => { - const browsable = dataSource.kind == 'file' || dataSource.kind == 'dir'; + partitions = $state([]); - if (browsable) { - const dir = dataSource.kind == 'dir'; - return { dir }; - } + dataSource = $state({ kind: 'file', url: 'file://' }); + locationPath = $state(''); - return undefined; - }); + browsable = $derived.by(() => { + const browsable = this.dataSource.kind == 'file' || this.dataSource.kind == 'dir'; - let fileTypeOpts: Record = { - csv: CsvOptions, - arrow: undefined, - parquet: ParquetOptions, - avro: undefined, - json: JsonOptions - }; + if (browsable) { + const dir = this.dataSource.kind == 'dir'; + return { dir }; + } - const dataTypes = ['Bool', 'Double', 'Int', 'Date', 'String']; + return undefined; + }); - let accept_: ((info: TableInfo) => void) | undefined = undefined; - let reject_: ((reason?: any) => void) | undefined = undefined; + public static fromInfo(info: Partial): Table { + let table = new Table(); - function deletePartition(index: number) { - partitions = [...partitions.slice(0, index), ...partitions.slice(index + 1)]; - } + const { database, schema, name, fileType } = info; + + table.database = database ?? ''; + table.schema = schema ?? ''; + table.name = name ?? ''; + table.fileType = fileType ?? 'csv'; - function createPartition() { - let name = $state(''); - let type = $state(undefined); - - const partition = { - get name(): string { - return name; - }, - set name(val: string) { - name = val; - }, - - get type(): string | undefined { - return type; - }, - set type(val: string) { - type = val; + return table; + } + + addPartition() { + this.partitions.push(new Partition()); + } + + deletePartition(index: number) { + this.partitions = [...this.partitions.slice(0, index), ...this.partitions.slice(index + 1)]; + } + + get optionsComponent(): OptionsComponent | undefined { + return this.fileTypeOptions[this.fileType]; + } + + getLocationPath(): string { + if (this.dataSource?.kind === 'dir') { + // DataFusion requires that table path that represent a directory structure + // ends with with a '/' delimiter + if (!this.locationPath.endsWith('/')) return this.locationPath + '/'; } - }; - partitions.push(partition); - } + return this.locationPath; + } - async function openFileBrowser(directory: boolean) { - const selected = await dialogOpen({ - multiple: false, - directory - }); - if (Array.isArray(selected)) { - locationPath = selected[0]; - } else if (selected !== null) { - locationPath = selected; + async openFileBrowser() { + if (!this.browsable) return; + + const directory = this.browsable.dir; + + const selected = await dialogOpen({ + multiple: false, + directory + }); + if (Array.isArray(selected)) { + this.locationPath = selected[0]; + } else if (selected !== null) { + this.locationPath = selected; + } } } - function toDatasourceItem({ url, store }: DatasourceConfig): DatasourceItem | undefined { - if ('s3' in store) { - return { - kind: 's3', - url - }; - } + const table = info ? Table.fromInfo(info) : new Table(); - if ('gcs' in store) { + let optionsComponent = $state< + SvelteComponent<{}, { getOptions: () => Record }> | undefined + >(undefined); + + let schemas = $derived( + databases + .filter((db) => db.name == table.database) + .flatMap((db) => db.schemas.map((schema) => schema.name)) + ); + let schemaItems = $derived( + schemas.map((schema: string) => { return { - kind: 'gcs', - url + value: schema, + label: schema }; - } + }) + ); - return undefined; - } + let open = $state(false); + + const dataTypes = ['Bool', 'Double', 'Int', 'Date', 'String']; + + let accept_: ((info: TableInfo) => void) | undefined = undefined; + let reject_: ((reason?: any) => void) | undefined = undefined; function createOptions( options: Record @@ -227,27 +232,13 @@ return opts; } - function getLocationPath(): string { - if (dataSource?.kind === 'dir') { - // DataFusion requires that table path that represent a directory structure - // ends with with a '/' delimiter - if (!locationPath.endsWith('/')) return locationPath + '/'; - } - - return locationPath; - } - function closeDialog() { - const getOptions = (): Record => { - if (typeof optionsComponent === 'undefined') return {}; - - return createOptions(optionsComponent.getOptions()); - }; - open = false; if (accept_) { - const location = `${dataSource?.url}${getLocationPath()}`; - const options = getOptions(); + const { database, schema, name, fileType, partitions, dataSource } = table; + const locationPath = table.getLocationPath(); + const location = `${dataSource?.url}${locationPath}`; + const options = createOptions(optionsComponent?.getOptions() ?? {}); accept_({ database, schema, name, fileType, partitions, options, location }); } } @@ -270,9 +261,9 @@ v && (database = v.value)} + onSelectedChange={(v) => v && (table.database = v.value)} > @@ -288,9 +279,9 @@ v && (schema = v.value)} + onSelectedChange={(v) => v && (table.schema = v.value)} > @@ -305,11 +296,11 @@ - + - + {#each Object.entries(fileTypeIcons) as [fileType, icon]} - {#if fileTypeOpts[fileType]} + {#if table.optionsComponent}
- +
{/if}
-
- {#each partitions as partition, i} + {#each table.partitions as partition, i} { return { label: dt, value: dt }; })} - onSelectedChange={(v) => v && (partitions[i].type = v.value)} + onSelectedChange={(v) => v && (partition.type = v.value)} > @@ -359,7 +355,12 @@ - {/each} @@ -369,9 +370,9 @@
(dataSource = s?.value)} + selected={{ value: FILE_DATASOURCE, label: FILE_DATASOURCE.url }} + onSelectedChange={(s) => s && (table.dataSource = s.value)} > @@ -388,14 +389,10 @@
- + - {#if browsable} - {/if}