console: refactor modify tab metadata hooks [GDC-666]

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/7165
GitOrigin-RevId: b46408b3395198ac84f62eeae31a6bde3133c65f
This commit is contained in:
Matthew Goodwin 2022-12-12 12:46:03 -06:00 committed by hasura-bot
parent ed190d6cb6
commit bffdc2f930
13 changed files with 131 additions and 163 deletions

View File

@ -16,7 +16,7 @@ import {
} from './LegacyRunQueryContainer.utils';
import { LegacyRunQuery } from './LegacyRunQuery';
import { FiltersAndSortFormValues, UserQuery } from '../types';
import { useTableColumns } from '../hooks/useTableColumns';
import { useLegacyTableColumns } from '../hooks/useLegacyTableColumns';
import { useTableName } from '../hooks/useTableName';
import { useDatabaseOperators } from '../hooks/useDatabaseOperators';
import { useTableSchema } from '../hooks/useTableSchema';
@ -69,7 +69,7 @@ export const LegacyRunQueryContainer = ({
const curFilter = useAppSelector(state => state.tables.view.curFilter);
const limit = curFilter.limit;
const tableColumns = useTableColumns({ dataSourceName, table });
const tableColumns = useLegacyTableColumns({ dataSourceName, table });
const tableOperators = useDatabaseOperators({ dataSourceName });
const tableSchema = useTableSchema(table);

View File

@ -1,14 +1,14 @@
import { useState, useEffect } from 'react';
import { useIsUnmounted } from '@/components/Services/Data';
import { DataSource, TableColumn } from '@/features/DataSource';
import { useHttpClient } from '@/features/Network';
import { useIsUnmounted } from '@/components/Services/Data';
import { useEffect, useState } from 'react';
type UseTableColumnsProps = {
dataSourceName: string;
table: unknown;
};
export const useTableColumns = ({
export const useLegacyTableColumns = ({
dataSourceName,
table,
}: UseTableColumnsProps) => {

View File

@ -10,4 +10,4 @@ export {
convertUserQueryToFiltersAndSortFormValues,
} from './components/RunQuery/LegacyRunQueryContainer/LegacyRunQueryContainer.utils';
export { UserQuery } from './components/RunQuery/types';
export { useTableColumns } from './components/RunQuery/hooks/useTableColumns';
export { useTableColumns } from './hooks';

View File

@ -1,13 +1,11 @@
import { MetadataSelectors, useMetadata } from '@/features/hasura-metadata-api';
import { Button } from '@/new-components/Button';
import { Dialog } from '@/new-components/Dialog';
import { InputField, UpdatedForm } from '@/new-components/Form';
import { sanitizeGraphQLFieldNames } from '@/utils';
import { SanitizeTips } from '@/utils/sanitizeGraphQLFieldNames';
import React from 'react';
import {
useMetadataTable,
useUpdateTableConfiguration,
} from '../../../ModifyTable/hooks';
import { useUpdateTableConfiguration } from '../../../ModifyTable/hooks';
import { ModifyTableProps } from '../../ModifyTable';
import { ModifyTableColumn } from '../../types';
import { schema, Schema } from './schema';
@ -20,7 +18,9 @@ interface EditTableColumnDialogProps extends ModifyTableProps {
export const EditTableColumnDialog = (props: EditTableColumnDialogProps) => {
const { onClose, column, table, dataSourceName } = props;
const { metadataTable } = useMetadataTable(dataSourceName, table);
const { data: metadataTable } = useMetadata(
MetadataSelectors.findTable(dataSourceName, table)
);
const { isLoading: isSaveInProgress, updateTableConfiguration } =
useUpdateTableConfiguration(dataSourceName, table);

View File

@ -10,11 +10,10 @@ import { ModifyTableProps } from '../ModifyTable';
interface TableColumnProps extends ModifyTableProps {}
export const TableColumns: React.VFC<TableColumnProps> = props => {
const { dataSourceName, table } = props;
const {
data: columns,
isLoading,
isError,
} = useListAllTableColumns(dataSourceName, table);
const { columns, isLoading, isError } = useListAllTableColumns(
dataSourceName,
table
);
const [isEditColumnFormActive, setIsEditColumnFormActive] = useState(false);
const [selectedColumn, setSelectedColumn] = useState<ModifyTableColumn>();

View File

@ -1,12 +1,14 @@
import { MetadataUtils, useMetadata } from '@/features/hasura-metadata-api';
import { Table } from '@/features/hasura-metadata-types';
import useUpdateEffect from '@/hooks/useUpdateEffect';
import { Button } from '@/new-components/Button';
import { IndicatorCard } from '@/new-components/IndicatorCard';
import { Nullable } from '@/types';
import clsx from 'clsx';
import React, { useMemo } from 'react';
import React from 'react';
import TextareaAutosize from 'react-autosize-textarea';
import { FaEdit, FaRegComment } from 'react-icons/fa';
import Skeleton from 'react-loading-skeleton';
import { useUpdateTableConfiguration } from '../hooks';
import { ModifyTableProps } from '../ModifyTable';
@ -19,7 +21,11 @@ export interface TableCommentsProps extends ModifyTableProps {
export const TableComments: React.VFC<TableCommentsProps> = props => {
const { dataSourceName, table } = props;
const { isLoading, data: savedComment } = useMetadata(
const {
isLoading,
data: savedComment,
isError,
} = useMetadata(
m =>
MetadataUtils.findMetadataTable(dataSourceName, table, m)?.configuration
?.comment
@ -27,15 +33,25 @@ export const TableComments: React.VFC<TableCommentsProps> = props => {
const [comment, setComment] = React.useState<Nullable<string>>(null);
const saveNeeded = useMemo(
() => comment != null && comment !== savedComment,
[comment, savedComment]
);
useUpdateEffect(() => {
// this resets the state so we aren't carrying over the comment from a previous table
// prevents the saveNeeded from becoming inaccurate
setComment(null);
}, [table]);
const saveNeeded = comment != null && comment !== savedComment;
const { updateTableConfiguration, isLoading: savingComment } =
useUpdateTableConfiguration(dataSourceName, table);
if (isLoading) return <IndicatorCard status="info">Loading...</IndicatorCard>;
if (isLoading) return <Skeleton count={5} height={20} />;
if (isError)
return (
<IndicatorCard status="negative" headline="Error">
Unable to fetch table comments.
</IndicatorCard>
);
return (
<div className="mb-lg flex items-center">
@ -43,11 +59,11 @@ export const TableComments: React.VFC<TableCommentsProps> = props => {
<TextareaAutosize
style={{ minWidth: '24rem', outline: 'none' }}
rows={1}
key={savedComment}
className={clsx(
'bg-secondary-light border border-gray-300 border-l-4 border-l-secondary p-sm peer',
'focus:bg-white rounded focus:[box-shadow:none] focus:border-secondary',
'placeholder-shown:italic pl-9',
saveNeeded && 'border-l-red-500 '
'placeholder-shown:italic pl-9'
)}
name="comments"
defaultValue={savedComment}

View File

@ -1,7 +1,10 @@
import { TableTrackingCustomizationModal } from '@/components/Services/Data/Schema/tableTrackCustomization/TableTrackingCustomizationModal';
import { MetadataSelectors, useMetadata } from '@/features/hasura-metadata-api';
import { Button } from '@/new-components/Button';
import { IndicatorCard } from '@/new-components/IndicatorCard';
import React from 'react';
import { useMetadataTable, useUpdateTableConfiguration } from '../hooks';
import Skeleton from 'react-loading-skeleton';
import { useUpdateTableConfiguration } from '../hooks';
import { ModifyTableProps } from '../ModifyTable';
const RootField: React.VFC<{ property: string; value: string }> = ({
@ -18,14 +21,24 @@ const RootField: React.VFC<{ property: string; value: string }> = ({
export const TableRootFields: React.VFC<ModifyTableProps> = props => {
const { dataSourceName, table, tableName } = props;
const [showCustomModal, setShowCustomModal] = React.useState(false);
const { metadataTable, isLoading } = useMetadataTable(dataSourceName, table);
const {
data: metadataTable,
isLoading,
isError,
} = useMetadata(MetadataSelectors.findTable(dataSourceName, table));
const { updateCustomRootFields, isLoading: saving } =
useUpdateTableConfiguration(dataSourceName, table);
if (isLoading) {
return null;
}
if (isLoading) return <Skeleton count={5} height={20} />;
if (isError)
return (
<IndicatorCard status="negative" headline="Error">
Unable to fetch table data.
</IndicatorCard>
);
const isEmpty =
!metadataTable?.configuration?.custom_name &&

View File

@ -1,7 +1,2 @@
export {
getTableFromMetadata,
manageTableMetadataQueryKey,
useMetadataTable,
useListAllTableColumns,
} from './useMetadataForManageTable';
export { useListAllTableColumns } from './useListAllTableColumns';
export { useUpdateTableConfiguration } from './useUpdateTableConfiguration';

View File

@ -0,0 +1,26 @@
import { useTableColumns } from '@/features/BrowseRows';
import { MetadataSelectors, useMetadata } from '@/features/hasura-metadata-api';
export const useListAllTableColumns = (
dataSourceName: string,
table: unknown
) => {
const { data: tableColumns } = useTableColumns({
table,
dataSourceName,
});
const { data: metadataTable, ...rest } = useMetadata(
MetadataSelectors.findTable(dataSourceName, table)
);
const tableConfig = metadataTable?.configuration?.column_config;
return {
columns: (tableColumns?.columns ?? []).map(tableColumn => ({
...tableColumn,
config: tableConfig?.[tableColumn.name],
})),
...rest,
};
};

View File

@ -1,106 +0,0 @@
import { exportMetadata } from '@/features/DataSource';
import { Source } from '@/features/hasura-metadata-types';
import { useHttpClient } from '@/features/Network';
import { AxiosError } from 'axios';
import isEqual from 'lodash.isequal';
import React from 'react';
import { useQuery } from 'react-query';
// eslint-disable-next-line no-restricted-imports
import { useTableColumns } from '@/features/BrowseRows/hooks/useTableColumns';
export const getTableFromMetadata = (
metadata: Source | undefined,
table: unknown
) => {
if (!table) return null;
return metadata?.tables.find(t => isEqual(t.table, table));
};
export const manageTableMetadataQueryKey = (dataSourceName: string) => [
'manage-table-metadata',
dataSourceName,
];
// adding underscore to prevent conflicts or confusion as this is only mean to be for this scope
export const useMetadataForManageTable = (
dataSourceName: string,
queryEnabled = true
) => {
const httpClient = useHttpClient();
return useQuery<
{ metadata: Source; resource_version: number } | undefined,
AxiosError
>({
queryKey: manageTableMetadataQueryKey(dataSourceName),
queryFn: async () => {
const { metadata, resource_version } = await exportMetadata({
httpClient,
});
if (!metadata) throw Error('Unable to fetch sources from metadata');
const metadataSource = metadata.sources.find(
source => source.name === dataSourceName
);
if (!metadataSource) throw Error('Unable to find the source in metadata');
const data = {
metadata: metadataSource,
resource_version,
};
return data;
},
enabled: queryEnabled,
refetchOnWindowFocus: false,
});
};
export const useMetadataTable = (dataSourceName: string, table: unknown) => {
const { data, ...rest } = useMetadataForManageTable(dataSourceName);
const metadataTable = React.useMemo(
() => getTableFromMetadata(data?.metadata, table),
[table, data?.metadata]
);
return {
metadata: data?.metadata,
resource_version: data?.resource_version,
metadataTable,
...rest,
};
};
export const useListAllTableColumns = (
dataSourceName: string,
table: unknown
) => {
const { data: tableColumns, isFetching: isIntrospectionReady } =
useTableColumns({
table,
dataSourceName,
});
const { data, ...rest } = useMetadataForManageTable(
dataSourceName,
!isIntrospectionReady
);
const metadataTable = React.useMemo(
() => getTableFromMetadata(data?.metadata, table),
[table, data?.metadata]
);
const tableConfig = metadataTable?.configuration?.column_config;
return {
data: (tableColumns?.columns ?? []).map(tableColumn => ({
...tableColumn,
config: tableConfig?.[tableColumn.name],
})),
...rest,
};
};

View File

@ -1,9 +1,12 @@
import {
MetadataUtils,
useInvalidateMetata,
useMetadata,
} from '@/features/hasura-metadata-api';
import { MetadataTable } from '@/features/hasura-metadata-types';
import { useMetadataMigration } from '@/features/MetadataAPI';
import { useFireNotification } from '@/new-components/Notifications';
import { useCallback } from 'react';
import { useQueryClient } from 'react-query';
import { manageTableMetadataQueryKey, useMetadataTable } from '.';
export const useUpdateTableConfiguration = (
dataSourceName: string,
@ -13,20 +16,23 @@ export const useUpdateTableConfiguration = (
const { fireNotification } = useFireNotification();
const queryClient = useQueryClient();
const invalidateMetadata = useInvalidateMetata();
const { metadata, resource_version, metadataTable } = useMetadataTable(
dataSourceName,
table
);
const { data } = useMetadata(m => ({
source: MetadataUtils.findMetadataSource(dataSourceName, m),
resource_version: m.resource_version,
metadataTable: MetadataUtils.findMetadataTable(dataSourceName, table, m),
}));
const { source, metadataTable, resource_version } = data || {};
const updateTableConfiguration = useCallback(
(config: MetadataTable['configuration']) => {
const driver = metadata?.kind;
const driver = source?.kind;
return new Promise<void>((resolve, reject) => {
if (!metadata) {
throw Error('Metadata not found!');
if (!source) {
throw Error('Data Source not found!');
}
mutate(
@ -46,10 +52,8 @@ export const useUpdateTableConfiguration = (
},
{
onSuccess: () => {
queryClient.invalidateQueries(
manageTableMetadataQueryKey(dataSourceName)
);
queryClient.invalidateQueries('export_metadata');
invalidateMetadata();
fireNotification({
type: 'success',
title: 'Success!',
@ -72,10 +76,11 @@ export const useUpdateTableConfiguration = (
[
dataSourceName,
fireNotification,
metadata,
mutate,
queryClient,
invalidateMetadata,
metadataTable,
resource_version,
source,
table,
]
);
@ -98,7 +103,7 @@ export const useUpdateTableConfiguration = (
return {
updateTableConfiguration,
updateCustomRootFields,
metadata,
source,
resource_version,
...rest,
};

View File

@ -1,7 +1,7 @@
import * as MetadataSelectors from './selectors';
import * as MetadataUtils from './utils';
export { useMetadata } from './useMetadata';
export { useMetadata, useInvalidateMetata } from './useMetadata';
export { areTablesEqual } from './areTablesEqual';
export { MetadataSelectors };
export { MetadataUtils };

View File

@ -1,7 +1,8 @@
import { exportMetadata } from '@/features/DataSource';
import { Metadata } from '@/features/hasura-metadata-types';
import { useHttpClient } from '@/features/Network';
import { useQuery } from 'react-query';
import { useCallback } from 'react';
import { useQuery, useQueryClient } from 'react-query';
export const DEFAULT_STALE_TIME = 5 * 60000; // 5 minutes as default stale time
@ -11,13 +12,27 @@ export const DEFAULT_STALE_TIME = 5 * 60000; // 5 minutes as default stale time
Default stale time is 5 minutes, but can be adjusted using the staleTime arg
*/
const METADATA_QUERY_KEY = 'export_metadata';
export const useInvalidateMetata = () => {
const queryClient = useQueryClient();
const invalidate = useCallback(
() => queryClient.invalidateQueries([METADATA_QUERY_KEY]),
[queryClient]
);
return invalidate;
};
export const useMetadata = <T = Metadata>(
selector?: (m: Metadata) => T,
staleTime: number = DEFAULT_STALE_TIME
) => {
const httpClient = useHttpClient();
return useQuery({
queryKey: ['export_metadata'],
const invalidateMetadata = useInvalidateMetata();
const queryReturn = useQuery({
queryKey: [METADATA_QUERY_KEY],
queryFn: async () => {
const result = await exportMetadata({ httpClient });
return result;
@ -26,4 +41,9 @@ export const useMetadata = <T = Metadata>(
refetchOnWindowFocus: false,
select: selector,
});
return {
...queryReturn,
invalidateMetadata,
};
};