Skip to content

Commit

Permalink
feat: snippets for table (under tree dots in navigation tree) (#1476)
Browse files Browse the repository at this point in the history
  • Loading branch information
astandrik authored Oct 18, 2024
1 parent 82531a5 commit 39d86c9
Show file tree
Hide file tree
Showing 6 changed files with 151 additions and 17 deletions.
23 changes: 23 additions & 0 deletions src/containers/Tenant/Schema/SchemaTree/SchemaTree.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import {NavigationTree} from 'ydb-ui-components';

import {useCreateDirectoryFeatureAvailable} from '../../../../store/reducers/capabilities/hooks';
import {schemaApi} from '../../../../store/reducers/schema/schema';
import {tableSchemaDataApi} from '../../../../store/reducers/tableSchemaData';
import type {GetTableSchemaDataParams} from '../../../../store/reducers/tableSchemaData';
import type {EPathType, TEvDescribeSchemeResult} from '../../../../types/api/schema';
import {wait} from '../../../../utils';
import {SECOND_IN_MS} from '../../../../utils/constants';
import {useQueryExecutionSettings, useTypedDispatch} from '../../../../utils/hooks';
import {getSchemaControls} from '../../utils/controls';
import {isChildlessPathType, mapPathTypeToNavigationTreeType} from '../../utils/schema';
Expand All @@ -22,10 +26,28 @@ interface SchemaTreeProps {
onActivePathUpdate: (path: string) => void;
}

const TABLE_SCHEMA_TIMEOUT = SECOND_IN_MS * 2;

export function SchemaTree(props: SchemaTreeProps) {
const createDirectoryFeatureAvailable = useCreateDirectoryFeatureAvailable();
const {rootPath, rootName, rootType, currentPath, onActivePathUpdate} = props;
const dispatch = useTypedDispatch();
const [getTableSchemaDataMutation] = tableSchemaDataApi.useGetTableSchemaDataMutation();

const getTableSchemaDataPromise = React.useCallback(
async (args: GetTableSchemaDataParams) => {
try {
const result = await Promise.race([
getTableSchemaDataMutation(args).unwrap(),
wait<undefined>(TABLE_SCHEMA_TIMEOUT),
]);
return result;
} catch (e) {
return undefined;
}
},
[getTableSchemaDataMutation],
);

const [querySettings, setQueryExecutionSettings] = useQueryExecutionSettings();
const [createDirectoryOpen, setCreateDirectoryOpen] = React.useState(false);
Expand Down Expand Up @@ -119,6 +141,7 @@ export function SchemaTree(props: SchemaTreeProps) {
showCreateDirectoryDialog: createDirectoryFeatureAvailable
? handleOpenCreateDirectoryDialog
: undefined,
getTableSchemaDataPromise,
},
rootPath,
)}
Expand Down
8 changes: 8 additions & 0 deletions src/containers/Tenant/utils/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ const pathTypeToNodeType: Record<EPathType, NavigationTreeNodeType | undefined>
[EPathType.EPathTypeReplication]: 'async_replication',
};

export const nodeTableTypeToPathType: Partial<Record<NavigationTreeNodeType, EPathType>> = {
table: EPathType.EPathTypeTable,
index: EPathType.EPathTypeTableIndex,
column_table: EPathType.EPathTypeColumnTable,
external_table: EPathType.EPathTypeExternalTable,
view: EPathType.EPathTypeView,
};

export const mapPathTypeToNavigationTreeType = (
type: EPathType = EPathType.EPathTypeDir,
subType?: EPathSubType,
Expand Down
63 changes: 50 additions & 13 deletions src/containers/Tenant/utils/schemaActions.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
import copy from 'copy-to-clipboard';
import type {NavigationTreeNodeType, NavigationTreeProps} from 'ydb-ui-components';

import type {AppDispatch} from '../../../store';
import {changeUserInput} from '../../../store/reducers/executeQuery';
import type {GetTableSchemaDataParams} from '../../../store/reducers/tableSchemaData';
import {TENANT_PAGES_IDS, TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
import {setQueryTab, setTenantPage} from '../../../store/reducers/tenant/tenant';
import type {QueryMode, QuerySettings} from '../../../types/store/query';
import createToast from '../../../utils/createToast';
import {transformPath} from '../ObjectSummary/transformPath';
import type {SchemaData} from '../Schema/SchemaViewer/types';
import i18n from '../i18n';

import type {SchemaQueryParams} from './schemaQueryTemplates';
import {nodeTableTypeToPathType} from './schema';
import type {TemplateFn} from './schemaQueryTemplates';
import {
addTableIndex,
alterAsyncReplicationTemplate,
Expand All @@ -34,31 +38,60 @@ interface ActionsAdditionalEffects {
updateQueryExecutionSettings: (settings?: Partial<QuerySettings>) => void;
setActivePath: (path: string) => void;
showCreateDirectoryDialog?: (path: string) => void;
getTableSchemaDataPromise?: (
params: GetTableSchemaDataParams,
) => Promise<SchemaData[] | undefined>;
}

interface BindActionParams {
tenantName: string;
type: NavigationTreeNodeType;
path: string;
relativePath: string;
}

const bindActions = (
schemaQueryParams: SchemaQueryParams,
dispatch: React.Dispatch<any>,
params: BindActionParams,
dispatch: AppDispatch,
additionalEffects: ActionsAdditionalEffects,
) => {
const {setActivePath, updateQueryExecutionSettings, showCreateDirectoryDialog} =
additionalEffects;

const inputQuery = (tmpl: (params?: SchemaQueryParams) => string, mode?: QueryMode) => () => {
const {
setActivePath,
updateQueryExecutionSettings,
showCreateDirectoryDialog,
getTableSchemaDataPromise,
} = additionalEffects;

const inputQuery = (tmpl: TemplateFn, mode?: QueryMode) => () => {
if (mode) {
updateQueryExecutionSettings({queryMode: mode});
}

dispatch(changeUserInput({input: tmpl(schemaQueryParams)}));
const pathType = nodeTableTypeToPathType[params.type];
const withTableData = [selectQueryTemplate, upsertQueryTemplate].includes(tmpl);

const userInputDataPromise =
withTableData && pathType && getTableSchemaDataPromise
? getTableSchemaDataPromise({
path: params.path,
tenantName: params.tenantName,
type: pathType,
})
: Promise.resolve(undefined);

userInputDataPromise.then((tableData) => {
dispatch(changeUserInput({input: tmpl({...params, tableData})}));
});

dispatch(setTenantPage(TENANT_PAGES_IDS.query));
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
setActivePath(schemaQueryParams.path);
setActivePath(params.path);
};

return {
createDirectory: showCreateDirectoryDialog
? () => {
showCreateDirectoryDialog(schemaQueryParams.path);
showCreateDirectoryDialog(params.path);
}
: undefined,
createTable: inputQuery(createTableTemplate, 'script'),
Expand All @@ -81,7 +114,7 @@ const bindActions = (
addTableIndex: inputQuery(addTableIndex, 'script'),
copyPath: () => {
try {
copy(schemaQueryParams.relativePath);
copy(params.relativePath);
createToast({
name: 'Copied',
title: i18n('actions.copied'),
Expand All @@ -101,10 +134,14 @@ const bindActions = (
type ActionsSet = ReturnType<Required<NavigationTreeProps>['getActions']>;

export const getActions =
(dispatch: React.Dispatch<any>, additionalEffects: ActionsAdditionalEffects, rootPath = '') =>
(dispatch: AppDispatch, additionalEffects: ActionsAdditionalEffects, rootPath = '') =>
(path: string, type: NavigationTreeNodeType) => {
const relativePath = transformPath(path, rootPath);
const actions = bindActions({path, relativePath}, dispatch, additionalEffects);
const actions = bindActions(
{path, relativePath, tenantName: rootPath, type},
dispatch,
additionalEffects,
);
const copyItem = {text: i18n('actions.copyPath'), action: actions.copyPath};

const DIR_SET: ActionsSet = [
Expand Down
14 changes: 12 additions & 2 deletions src/containers/Tenant/utils/schemaQueryTemplates.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import type {SchemaData} from '../Schema/SchemaViewer/types';

export interface SchemaQueryParams {
path: string;
relativePath: string;
tableData?: SchemaData[];
}

export type TemplateFn = (params?: SchemaQueryParams) => string;

export const createTableTemplate = (params?: SchemaQueryParams) => {
return `-- docs: https://ydb.tech/en/docs/yql/reference/syntax/create_table
CREATE TABLE \`${params?.relativePath || '$path'}/ydb_row_table\` (
Expand Down Expand Up @@ -67,13 +72,18 @@ export const alterTableTemplate = (params?: SchemaQueryParams) => {
ADD COLUMN numeric_column Int32;`;
};
export const selectQueryTemplate = (params?: SchemaQueryParams) => {
return `SELECT *
const columns = params?.tableData?.map((column) => '`' + column.name + '`').join(', ') || '*';

return `SELECT ${columns}
FROM \`${params?.relativePath || '$path'}\`
LIMIT 10;`;
};
export const upsertQueryTemplate = (params?: SchemaQueryParams) => {
const columns =
params?.tableData?.map((column) => `\`${column.name}\``).join(', ') || `\`id\`, \`name\``;

return `UPSERT INTO \`${params?.relativePath || '$path'}\`
( \`id\`, \`name\` )
( ${columns} )
VALUES ( );`;
};

Expand Down
56 changes: 56 additions & 0 deletions src/store/reducers/tableSchemaData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
prepareSchemaData,
prepareViewSchema,
} from '../../containers/Tenant/Schema/SchemaViewer/prepareData';
import type {SchemaData} from '../../containers/Tenant/Schema/SchemaViewer/types';
import {isViewType} from '../../containers/Tenant/utils/schema';
import type {EPathType} from '../../types/api/schema';
import {isQueryErrorResponse} from '../../utils/query';

import {api} from './api';
import {overviewApi} from './overview/overview';
import {viewSchemaApi} from './viewSchema/viewSchema';

export interface GetTableSchemaDataParams {
path: string;
tenantName: string;
type: EPathType;
}

export const tableSchemaDataApi = api.injectEndpoints({
endpoints: (build) => ({
getTableSchemaData: build.mutation<SchemaData[], GetTableSchemaDataParams>({
queryFn: async ({path, tenantName, type}, {dispatch}) => {
try {
if (isViewType(type)) {
const response = await dispatch(
viewSchemaApi.endpoints.getViewSchema.initiate({
database: tenantName,
path,
}),
);

if (isQueryErrorResponse(response)) {
return {error: response};
}

const result = prepareViewSchema(response.data);
return {data: result};
}

const schemaData = await dispatch(
overviewApi.endpoints.getOverview.initiate({
path,
database: tenantName,
}),
);
const result = prepareSchemaData(type, schemaData.data);

return {data: result};
} catch (error) {
return {error};
}
},
}),
}),
});
4 changes: 2 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ export function valueIsDefined<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}

export async function wait(time: number) {
export async function wait<T = unknown>(time: number, value?: T): Promise<T | undefined> {
return new Promise((resolve) => {
setTimeout(resolve, time);
setTimeout(() => resolve(value), time);
});
}

0 comments on commit 39d86c9

Please sign in to comment.