add different clients support

This commit is contained in:
Guy Ben-Aharon
2024-09-02 18:46:15 +03:00
committed by Guy Ben-Aharon
parent 1fbf7cc677
commit eee9832fe1
11 changed files with 194 additions and 22 deletions

31
package-lock.json generated
View File

@@ -25,6 +25,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",
@@ -2192,6 +2193,36 @@
}
}
},
"node_modules/@radix-ui/react-tabs": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
"integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.0",
"@radix-ui/react-context": "1.1.0",
"@radix-ui/react-direction": "1.1.0",
"@radix-ui/react-id": "1.1.0",
"@radix-ui/react-presence": "1.1.0",
"@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-roving-focus": "1.1.0",
"@radix-ui/react-use-controllable-state": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-toast": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.1.tgz",

View File

@@ -29,6 +29,7 @@
"@radix-ui/react-select": "^2.1.1",
"@radix-ui/react-separator": "^1.1.0",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
"@radix-ui/react-toast": "^1.2.1",
"@radix-ui/react-toggle": "^1.1.0",
"@radix-ui/react-toggle-group": "^1.1.0",

View File

@@ -7,17 +7,19 @@ export interface CodeSnippetProps {
className?: string;
codeProps?: CodeBlockProps;
code: string;
language?: 'sql' | 'bash';
}
export const CodeSnippet: React.FC<CodeSnippetProps> = ({
className,
codeProps,
code,
language = 'sql',
}) => {
return (
<div className={cn('flex flex-1', className)}>
<CopyBlock
language="sql"
language={language}
text={code}
theme={atomOneDark}
customStyle={{

View File

@@ -0,0 +1,53 @@
import * as React from 'react';
import * as TabsPrimitive from '@radix-ui/react-tabs';
import { cn } from '@/lib/utils';
const Tabs = TabsPrimitive.Root;
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
>(({ className, ...props }, ref) => (
<TabsPrimitive.List
ref={ref}
className={cn(
'inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground',
className
)}
{...props}
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Trigger
ref={ref}
className={cn(
'inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow',
className
)}
{...props}
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
>(({ className, ...props }, ref) => (
<TabsPrimitive.Content
ref={ref}
className={cn(
'mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2',
className
)}
{...props}
/>
));
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent };

View File

@@ -15,9 +15,9 @@ import { generateId } from '@/lib/utils';
import { useChartDB } from '@/hooks/use-chartdb';
import { useDialog } from '@/hooks/use-dialog';
import { DatabaseEdition } from '@/lib/domain/database-edition';
import { CreateDiagramDialogSelectDatabase } from './create-diagram-dialog-select-database/create-diagram-dialog-select-database';
import { SelectDatabaseStep } from './select-database-step/select-database-step';
import { CreateDiagramDialogStep } from './create-diagram-dialog-step';
import { CreateDiagramDialogImportDatabase } from './create-diagram-dialog-import-database/create-diagram-dialog-import-database';
import { ImportDatabaseStep } from './import-database-step/import-database-step';
const errorScriptOutputMessage =
'Invalid JSON. Please correct it or contact us at chartdb.io@gmail.com for help.';
@@ -142,11 +142,11 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
}}
>
<DialogContent
className="flex w-[90vw] flex-col overflow-visible xl:min-w-[45vw]"
className="flex max-h-[90vh] w-[90vw] flex-col overflow-y-auto md:overflow-visible xl:min-w-[45vw]"
showClose={hasExistingDiagram}
>
{step === CreateDiagramDialogStep.SELECT_DATABASE ? (
<CreateDiagramDialogSelectDatabase
<SelectDatabaseStep
createNewDiagram={createNewDiagram}
databaseType={databaseType}
hasExistingDiagram={hasExistingDiagram}
@@ -154,7 +154,7 @@ export const CreateDiagramDialog: React.FC<CreateDiagramDialogProps> = ({
setStep={setStep}
/>
) : (
<CreateDiagramDialogImportDatabase
<ImportDatabaseStep
createNewDiagram={createNewDiagram}
databaseEdition={databaseEdition}
databaseType={databaseType}

View File

@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import { Button } from '@/components/button/button';
import {
DialogClose,
@@ -26,8 +26,14 @@ import {
import { CreateDiagramDialogStep } from '../create-diagram-dialog-step';
import { SSMSInfo } from './ssms-info/ssms-info';
import { useTranslation } from 'react-i18next';
import { Tabs, TabsList, TabsTrigger } from '@/components/tabs/tabs';
import {
DatabaseClient,
databaseClientToLabelMap,
databaseTypeToClientsMap,
} from '@/lib/domain/database-clients';
export interface CreateDiagramDialogImportDatabaseProps {
export interface ImportDatabaseStepProps {
setStep: React.Dispatch<React.SetStateAction<CreateDiagramDialogStep>>;
createNewDiagram: () => void;
scriptResult: string;
@@ -40,9 +46,7 @@ export interface CreateDiagramDialogImportDatabaseProps {
errorMessage: string;
}
export const CreateDiagramDialogImportDatabase: React.FC<
CreateDiagramDialogImportDatabaseProps
> = ({
export const ImportDatabaseStep: React.FC<ImportDatabaseStepProps> = ({
setScriptResult,
setStep,
scriptResult,
@@ -52,6 +56,10 @@ export const CreateDiagramDialogImportDatabase: React.FC<
setDatabaseEdition,
errorMessage,
}) => {
const databaseClients = databaseTypeToClientsMap[databaseType];
const [databaseClient, setDatabaseClient] = useState<
DatabaseClient | undefined
>();
const { t } = useTranslation();
const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
@@ -158,12 +166,56 @@ export const CreateDiagramDialogImportDatabase: React.FC<
<SSMSInfo />
)}
</div>
{databaseTypeToClientsMap[databaseType].length > 0 ? (
<Tabs
value={
!databaseClient ? 'dbclient' : databaseClient
}
onValueChange={(value) => {
setDatabaseClient(
value === 'dbclient'
? undefined
: (value as DatabaseClient)
);
}}
>
<div className="flex flex-1">
<TabsList className="h-8 justify-start rounded-none rounded-t-sm ">
<TabsTrigger
value="dbclient"
className="h-6 w-20"
>
DB Client
</TabsTrigger>
{databaseClients?.map((client) => (
<TabsTrigger
key={client}
value={client}
className="h-6 !w-20"
>
{databaseClientToLabelMap[client]}
</TabsTrigger>
)) ?? []}
</TabsList>
</div>
<CodeSnippet
className="max-h-40 w-full"
code={importMetadataScripts[databaseType]({
databaseEdition,
databaseClient,
})}
language={databaseClient ? 'bash' : 'sql'}
/>
</Tabs>
) : (
<CodeSnippet
className="max-h-40 w-full"
code={importMetadataScripts[databaseType]({
databaseEdition,
})}
/>
)}
</div>
<div className="flex h-48 flex-col gap-1">
<p className="text-sm text-muted-foreground">
@@ -192,6 +244,8 @@ export const CreateDiagramDialogImportDatabase: React.FC<
handleInputChange,
scriptResult,
setDatabaseEdition,
databaseClients,
databaseClient,
t,
]);

View File

@@ -16,7 +16,7 @@ import { LayoutGrid } from 'lucide-react';
import { CreateDiagramDialogStep } from '../create-diagram-dialog-step';
import { useTranslation } from 'react-i18next';
export interface CreateDiagramDialogSelectDatabaseProps {
export interface SelectDatabaseStepProps {
setStep: React.Dispatch<React.SetStateAction<CreateDiagramDialogStep>>;
databaseType: DatabaseType;
setDatabaseType: React.Dispatch<React.SetStateAction<DatabaseType>>;
@@ -24,9 +24,7 @@ export interface CreateDiagramDialogSelectDatabaseProps {
createNewDiagram: () => void;
}
export const CreateDiagramDialogSelectDatabase: React.FC<
CreateDiagramDialogSelectDatabaseProps
> = ({
export const SelectDatabaseStep: React.FC<SelectDatabaseStepProps> = ({
setStep,
databaseType,
setDatabaseType,

View File

@@ -1,3 +1,4 @@
import { DatabaseClient } from '@/lib/domain/database-clients';
import {
DatabaseEdition,
databaseEditionToLabelMap,
@@ -6,6 +7,7 @@ import {
export const getPostgresQuery = (
options: {
databaseEdition?: DatabaseEdition;
databaseClient?: DatabaseClient;
} = {}
): string => {
const databaseEdition: DatabaseEdition | undefined =
@@ -227,5 +229,11 @@ SELECT CONCAT('{ "fk_info": [', COALESCE(fk_metadata, ''),
FROM fk_info${databaseEdition ? '_' + databaseEdition : ''}, pk_info, cols, indexes_metadata, tbls, config, views;
`;
if (options.databaseClient === DatabaseClient.POSTGRESQL_PSQL) {
return `psql -h HOST_NAME -p PORT -U USER_NAME -d DATABASE_NAME -c "
${query}
" -t -A | pbcopy; LG='\\033[0;32m'; NC='\\033[0m'; echo "You got the resultset ($(pbpaste | wc -c | xargs) characters) in Copy/Paste. \${LG}Go back & paste in ChartDB :)\${NC}";`;
}
return query;
};

View File

@@ -5,10 +5,14 @@ import { sqliteQuery } from './sqlite-script';
import { sqlServerQuery } from './sqlserver-script';
import { mariaDBQuery } from './maria-script';
import { DatabaseEdition } from '@/lib/domain/database-edition';
import { DatabaseClient } from '@/lib/domain/database-clients';
export const importMetadataScripts: Record<
DatabaseType,
(options?: { databaseEdition?: DatabaseEdition }) => string
(options?: {
databaseEdition?: DatabaseEdition;
databaseClient?: DatabaseClient;
}) => string
> = {
[DatabaseType.GENERIC]: () => '',
[DatabaseType.POSTGRESQL]: getPostgresQuery,

View File

@@ -0,0 +1,21 @@
import { DatabaseType } from './database-type';
export enum DatabaseClient {
// PostgreSQL
POSTGRESQL_PSQL = 'psql',
}
export const databaseClientToLabelMap: Record<DatabaseClient, string> = {
// PostgreSQL
[DatabaseClient.POSTGRESQL_PSQL]: 'PSQL',
};
export const databaseTypeToClientsMap: Record<DatabaseType, DatabaseClient[]> =
{
[DatabaseType.POSTGRESQL]: [DatabaseClient.POSTGRESQL_PSQL],
[DatabaseType.MYSQL]: [],
[DatabaseType.SQLITE]: [],
[DatabaseType.GENERIC]: [],
[DatabaseType.SQL_SERVER]: [],
[DatabaseType.MARIADB]: [],
};