Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI for Snowflake Peer #474

Merged
merged 14 commits into from
Oct 4, 2023
13 changes: 12 additions & 1 deletion ui/app/api/peers/route.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { PeerConfig } from '@/app/peers/create/configuration/types';
import { DBType, Peer, PostgresConfig } from '@/grpc_generated/peers';
import {
DBType,
Peer,
PostgresConfig,
SnowflakeConfig,
} from '@/grpc_generated/peers';
import {
CreatePeerRequest,
CreatePeerResponse,
Expand All @@ -22,6 +27,12 @@ const constructPeer = (
type: DBType.POSTGRES,
postgresConfig: config as PostgresConfig,
};
case 'SNOWFLAKE':
return {
name,
type: DBType.SNOWFLAKE,
snowflakeConfig: config as SnowflakeConfig,
};
default:
return;
}
Expand Down
40 changes: 0 additions & 40 deletions ui/app/peers/create/configuration/configForm.tsx

This file was deleted.

7 changes: 6 additions & 1 deletion ui/app/peers/create/configuration/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { AppRouterInstance } from 'next/dist/shared/lib/app-router-context';
import { Dispatch, SetStateAction } from 'react';
import { pgSchema } from './schema';
import { pgSchema, sfSchema } from './schema';
import { PeerConfig } from './types';

// Frontend form validation
Expand All @@ -15,11 +15,16 @@ const validateFields = (
return false;
}
let validationErr: string | undefined;
console.log(config);
switch (type) {
case 'POSTGRES':
const pgConfig = pgSchema.safeParse(config);
if (!pgConfig.success) validationErr = pgConfig.error.issues[0].message;
break;
case 'SNOWFLAKE':
const sfConfig = sfSchema.safeParse(config);
if (!sfConfig.success) validationErr = sfConfig.error.issues[0].message;
break;
default:
validationErr = 'Unsupported peer type ' + type;
}
Expand Down
12 changes: 11 additions & 1 deletion ui/app/peers/create/configuration/helpers/common.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
import { PeerConfig, PeerSetter } from '../types';
import { blankPostgresSetting } from './pg';
import { blankSnowflakeSetting } from './sf';

export const getBlankSetting = (dbType: string) => {
export interface Setting {
label: string;
stateHandler: (value: string, setter: PeerSetter) => void;
type?: string;
}

export const getBlankSetting = (dbType: string): PeerConfig => {
switch (dbType) {
case 'POSTGRES':
return blankPostgresSetting;
case 'SNOWFLAKE':
return blankSnowflakeSetting;
default:
return blankPostgresSetting;
}
Expand Down
19 changes: 10 additions & 9 deletions ui/app/peers/create/configuration/helpers/pg.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,42 @@
import { PeerSetter } from '../types';
import { PostgresConfig } from '@/grpc_generated/peers';
import { Setting } from './common';

export const postgresSetting = [
export const postgresSetting: Setting[] = [
{
label: 'Host',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, host: value })),
},
{
label: 'Port',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, port: parseInt(value, 10) })),
type: 'number', // type for textfield
},
{
label: 'User',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, user: value })),
},
{
label: 'Password',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, password: value })),
type: 'password',
},
{
label: 'Database',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, database: value })),
},
{
label: 'Transaction Snapshot',
stateHandler: (value: string, setter: PeerSetter) =>
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, transactionSnapshot: value })),
},
];

export const blankPostgresSetting = {
export const blankPostgresSetting: PostgresConfig = {
host: '',
port: 5432,
user: '',
Expand Down
69 changes: 69 additions & 0 deletions ui/app/peers/create/configuration/helpers/sf.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { SnowflakeConfig } from '@/grpc_generated/peers';
import { Setting } from './common';

export const snowflakeSetting: Setting[] = [
{
label: 'Account ID',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, accountId: value })),
},
{
label: 'Username',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, username: value })),
},
{
label: 'Private Key',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, privateKey: value })),
type: 'file',
},
{
label: 'Warehouse',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, warehouse: value })),
},
{
label: 'Database',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, database: value })),
},
{
label: 'Role',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, role: value })),
},
{
label: 'Query Timeout',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, queryTimeout: parseInt(value, 10) || 0 })),
},
{
label: 'S3 Integration',
stateHandler: (value, setter) =>
setter((curr) => ({ ...curr, s3Integration: value })),
},
{
label: 'Password',
stateHandler: (value, setter) => {
if (!value.length) {
// remove password key from state if empty
setter((curr) => {
delete curr['password'];
return curr;
});
} else setter((curr) => ({ ...curr, password: value }));
},
},
];

export const blankSnowflakeSetting: SnowflakeConfig = {
accountId: '',
privateKey: '',
username: '',
warehouse: '',
database: '',
role: '',
queryTimeout: 0,
s3Integration: '',
};
12 changes: 9 additions & 3 deletions ui/app/peers/create/configuration/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import { TextField } from '@/lib/TextField';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { useState } from 'react';
import ConfigForm from './configForm';
import ConfigForm from '../../../../components/ConfigForm';
import { handleCreate, handleValidate } from './handlers';
import { getBlankSetting } from './helpers/common';
import { Setting, getBlankSetting } from './helpers/common';
import { postgresSetting } from './helpers/pg';
import { snowflakeSetting } from './helpers/sf';
import { PeerConfig } from './types';
export default function CreateConfig() {
const searchParams = useSearchParams();
Expand All @@ -26,9 +27,14 @@ export default function CreateConfig() {
});
const [loading, setLoading] = useState<boolean>(false);
const configComponentMap = (dbType: string) => {
const configForm = (settingList: Setting[]) => (
<ConfigForm settings={settingList} setter={setConfig} />
);
switch (dbType) {
case 'POSTGRES':
return <ConfigForm settings={postgresSetting} setter={setConfig} />;
return configForm(postgresSetting);
case 'SNOWFLAKE':
return configForm(snowflakeSetting);
default:
return <></>;
}
Expand Down
64 changes: 64 additions & 0 deletions ui/app/peers/create/configuration/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,67 @@ export const pgSchema = z.object({
.max(100, 'Transaction snapshot too long (100 char limit)')
.optional(),
});

export const sfSchema = z.object({
accountId: z
.string({
required_error: 'Account ID is required',
invalid_type_error: 'Account ID must be a string',
})
.nonempty({ message: 'Account ID must be non-empty' })
.max(255, 'Account ID must be less than 255 characters'),
privateKey: z
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you create an issue to upload pkey file and link it here?

.string({
required_error: 'Private Key is required',
invalid_type_error: 'Private Key must be a string',
})
.nonempty({ message: 'Private Key must be non-empty' }),
username: z
.string({
required_error: 'Username is required',
invalid_type_error: 'Username must be a number',
})
.nonempty({ message: 'Username must be non-empty' })
.max(255, 'Port must be below 65535'),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is for username

database: z
.string({
required_error: 'Database is required',
invalid_type_error: 'Database must be a string',
})
.nonempty({ message: 'Database must be non-empty' })
.max(255, 'Database must be less than 100 characters'),
warehouse: z
.string({
required_error: 'Warehouse is required',
invalid_type_error: 'Warehouse must be a string',
})
.nonempty({ message: 'Warehouse must be non-empty' })
.max(255, 'Warehouse must be less than 64 characters'),
role: z
.string({
invalid_type_error: 'Role must be a string',
})
.nonempty({ message: 'Role must be non-empty' })
.max(255, 'Role must be below 255 characters'),
queryTimeout: z
.number({
invalid_type_error: 'Query timeout must be a number',
})
.int()
.min(0, 'Query timeout must be a positive integer')
.max(65535, 'Query timeout must be below 65535 seconds')
.optional(),
password: z
.string({
invalid_type_error: 'Password must be a string',
})
.max(255, 'Password must be less than 255 characters')
.optional()
.transform((e) => (e === '' ? undefined : e)),
s3Integration: z
.string({
invalid_type_error: 's3Integration must be a string',
})
.max(255, 's3Integration must be less than 255 characters')
.optional(),
});
4 changes: 2 additions & 2 deletions ui/app/peers/create/configuration/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PostgresConfig } from '@/grpc_generated/peers';
import { PostgresConfig, SnowflakeConfig } from '@/grpc_generated/peers';
import { Dispatch, SetStateAction } from 'react';

export type PeerConfig = PostgresConfig;
export type PeerConfig = PostgresConfig | SnowflakeConfig;
export type PeerSetter = Dispatch<SetStateAction<PeerConfig>>;
Loading
Loading