console: integrate useCreateTableRelationships and useSuggestedRelationships hooks in the relationships tab

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9939
GitOrigin-RevId: 71391115fe7403663c53dfbc0bec6e7b62019b50
This commit is contained in:
Vijay Prasanna 2023-07-26 09:51:07 +05:30 committed by hasura-bot
parent 4d86cd3a62
commit 1f11bd388f
17 changed files with 266 additions and 624 deletions

View File

@ -58,7 +58,7 @@ export const BasicDisplayTest: StoryObj<typeof BrowseRows> = {
const albumRows = await canvas.findAllByTestId(/^@table-row-.*$/);
expect(albumRows.length).toBe(10);
},
{ timeout: 5000 }
{ timeout: 10000 }
);
/**

View File

@ -38,12 +38,20 @@ export const Testing: StoryObj<typeof DataGrid> = {
await waitFor(
async () => {
await userEvent.click(await canvas.findByTestId('@nextPageBtn'));
await canvas.findAllByTestId(/^@table-cell-0-.*$/);
},
{ timeout: 10000 }
);
await userEvent.click(await canvas.findByTestId('@nextPageBtn'));
await waitFor(
async () => {
const firstRow = await canvas.findAllByTestId(/^@table-cell-0-.*$/);
expect(firstRow.length).toBe(5);
expect(firstRow[0]).toHaveTextContent('11'); // AlbumId
},
{ timeout: 5000 }
{ timeout: 10000 }
);
const firstRow = await canvas.findAllByTestId(/^@table-cell-0-.*$/);

View File

@ -12,15 +12,12 @@ export const ManageSuggestedRelationships = ({
}: ManageDatabaseProps) => {
const [tab, setTab] = React.useState<TabState>('untracked');
const {
data: { tracked = [], untracked = [] } = {},
isLoading,
invalidateQuery,
} = useSuggestedRelationships({
dataSourceName,
which: 'all',
schema,
});
const { data: { tracked = [], untracked = [] } = {}, isLoading } =
useSuggestedRelationships({
dataSourceName,
which: 'all',
schema,
});
return (
<TrackableResourceTabs
@ -36,9 +33,6 @@ export const ManageSuggestedRelationships = ({
<TrackedSuggestedRelationships
dataSourceName={dataSourceName}
trackedRelationships={tracked}
onChange={() => {
invalidateQuery();
}}
/>
),
},
@ -48,9 +42,6 @@ export const ManageSuggestedRelationships = ({
<UntrackedRelationships
untrackedRelationships={untracked}
dataSourceName={dataSourceName}
onTrack={() => {
invalidateQuery();
}}
/>
),
},

View File

@ -81,10 +81,12 @@ export const useSuggestedRelationships = ({
}) => {
const httpClient = useHttpClient();
const { data: { source, fkRels = [] } = {} } = useMetadata(m => ({
fkRels: MetadataSelectors.getForeignKeyRelationships(dataSourceName)(m),
source: MetadataSelectors.findSource(dataSourceName)(m),
}));
const { data: { source, fkRels = [] } = {}, isFetching } = useMetadata(m => {
return {
fkRels: MetadataSelectors.getForeignKeyRelationships(dataSourceName)(m),
source: MetadataSelectors.findSource(dataSourceName)(m),
};
});
const selector = useCallback(
(data: QueryReturnType) => {
@ -125,6 +127,7 @@ export const useSuggestedRelationships = ({
const query = useConsoleQuery<QueryReturnType, SelectReturnType>({
queryKey: [dataSourceName, QUERY_KEY],
select: selector,
enabled: !isFetching,
queryFn: async () => {
if (!source)
throw Error(`Unable to find source, "${dataSourceName}" in metadata`);

View File

@ -1,60 +0,0 @@
import { useQuery } from 'react-query';
import { useAllSuggestedRelationships } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import { getTableLocalRelationships } from '../../../../DatabaseRelationships/utils/tableRelationships';
import {
MetadataSelectors,
useMetadata,
} from '../../../../hasura-metadata-api';
export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [
'tracked_relationships',
dataSourceName,
];
export const useTrackedRelationships = (dataSourceName: string) => {
const { suggestedRelationships } = useAllSuggestedRelationships({
dataSourceName,
isEnabled: true,
omitTracked: false,
});
const { data: currentMetadataSource } = useMetadata(
MetadataSelectors.findSource(dataSourceName)
);
const fetchLocalRelationships = async () => {
const metadataTables = currentMetadataSource?.tables || [];
const _tableRelationships = [];
if (metadataTables) {
for (const metadataTable of metadataTables) {
const tableRelationships = getTableLocalRelationships(
metadataTable,
dataSourceName,
suggestedRelationships
);
_tableRelationships.push(...tableRelationships);
}
}
return _tableRelationships;
};
const {
data: relationships,
isLoading: isLoadingRelationships,
isFetching: isFetchingRelationships,
refetch: refetchRelationships,
error,
} = useQuery({
queryFn: fetchLocalRelationships,
queryKey: getTrackedRelationshipsCacheKey(dataSourceName),
});
return {
data: relationships || [],
isFetching: isFetchingRelationships,
isLoading: isLoadingRelationships,
error: [error],
refetchRelationships,
};
};

View File

@ -1,5 +1,5 @@
import { expect } from '@storybook/jest';
import { StoryFn, Meta, StoryObj } from '@storybook/react';
import { Meta, StoryObj } from '@storybook/react';
import { waitFor, within, userEvent } from '@storybook/testing-library';
import { ReactQueryDecorator } from '../../storybook/decorators/react-query';
import { DatabaseRelationships } from './DatabaseRelationships';
@ -16,12 +16,17 @@ export default {
},
} as Meta<typeof DatabaseRelationships>;
export const Basic: StoryFn<typeof DatabaseRelationships> = () => (
<DatabaseRelationships
dataSourceName="bikes"
table={{ name: 'products', schema: 'production' }}
/>
);
export const Basic: StoryObj<typeof DatabaseRelationships> = {
render: () => (
<DatabaseRelationships
dataSourceName="aPostgres"
table={{ name: 'Album', schema: 'public' }}
/>
),
parameters: {
msw: trackedArrayRelationshipsHandlers(),
},
};
export const Testing: StoryObj<typeof DatabaseRelationships> = {
name: '🧪 Test - Tracked array relationships',
@ -37,7 +42,12 @@ export const Testing: StoryObj<typeof DatabaseRelationships> = {
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(await canvas.findByText('NAME')).toBeVisible();
await waitFor(
async () => {
expect(await canvas.findByText('NAME')).toBeVisible();
},
{ timeout: 10000 }
);
const firstRelationship = await canvas.findByText('albumAlbumCovers');
expect(firstRelationship).toBeVisible();

View File

@ -2,7 +2,11 @@ import { useState } from 'react';
import { FaPlusCircle } from 'react-icons/fa';
import { Button } from '../../new-components/Button';
import { useFireNotification } from '../../new-components/Notifications';
import { useSyncResourceVersionOnMount } from '../hasura-metadata-api';
import {
MetadataSelectors,
useMetadata,
useSyncResourceVersionOnMount,
} from '../hasura-metadata-api';
import { Table } from '../hasura-metadata-types';
import { AvailableRelationshipsList } from './components/AvailableRelationshipsList/AvailableRelationshipsList';
import Legend from './components/Legend';
@ -10,6 +14,11 @@ import { RenderWidget } from './components/RenderWidget/RenderWidget';
import { SuggestedRelationships } from './components/SuggestedRelationships/SuggestedRelationships';
import { NOTIFICATIONS } from './components/constants';
import { MODE, Relationship } from './types';
import { useDriverCapabilities } from '../Data/hooks/useDriverCapabilities';
import { Feature } from '../DataSource';
import Skeleton from 'react-loading-skeleton';
import { useAppDispatch } from '../../storeHooks';
import { updateSchemaInfo } from '../../components/Services/Data/DataActions';
export interface DatabaseRelationshipsProps {
dataSourceName: string;
@ -29,6 +38,22 @@ export const DatabaseRelationships = ({
});
const { fireNotification } = useFireNotification();
const { data: driver } = useMetadata(
m => MetadataSelectors.findSource(dataSourceName)(m)?.kind
);
const dispatch = useAppDispatch();
const isLoadSchemaRequired = driver === 'mssql' || driver === 'postgres';
const { data: areForeignKeysSupported, isLoading } = useDriverCapabilities({
dataSourceName,
select: data => {
if (data === Feature.NotImplemented) return false;
return data.data_schema?.supports_foreign_keys;
},
});
const onCancel = () => {
setTabState({
mode: undefined,
@ -41,21 +66,29 @@ export const DatabaseRelationships = ({
});
const onError = (err: Error) => {
if (mode)
if (mode) {
fireNotification({
type: 'error',
title: NOTIFICATIONS.onError[mode],
message: err?.message ?? '',
});
if (isLoadSchemaRequired) {
dispatch(updateSchemaInfo());
}
}
};
const onSuccess = () => {
if (mode)
if (mode) {
fireNotification({
type: 'success',
title: 'Success!',
message: NOTIFICATIONS.onSuccess[mode],
});
if (isLoadSchemaRequired) {
dispatch(updateSchemaInfo());
}
}
setTabState({
mode: undefined,
@ -63,6 +96,8 @@ export const DatabaseRelationships = ({
});
};
if (isLoading) return <Skeleton count={10} height={20} />;
return (
<div className="my-2">
<div>
@ -77,7 +112,12 @@ export const DatabaseRelationships = ({
}}
/>
<SuggestedRelationships dataSourceName={dataSourceName} table={table} />
{areForeignKeysSupported && (
<SuggestedRelationships
dataSourceName={dataSourceName}
table={table}
/>
)}
<Legend />
</div>

View File

@ -3,8 +3,8 @@ import { z } from 'zod';
import { Dialog } from '../../../../new-components/Dialog';
import { InputField, SimpleForm } from '../../../../new-components/Form';
import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { useManageLocalRelationship } from '../../hooks/useManageLocalRelationship';
import { Relationship } from '../../types';
import { useCreateTableRelationships } from '../../hooks/useCreateTableRelationships/useCreateTableRelationships';
interface RenameRelationshipProps {
relationship: Relationship;
@ -15,12 +15,10 @@ interface RenameRelationshipProps {
export const RenameRelationship = (props: RenameRelationshipProps) => {
const { relationship, onCancel, onSuccess, onError } = props;
const { renameRelationship } = useManageLocalRelationship({
dataSourceName: relationship.fromSource,
table: relationship.fromTable,
onSuccess,
onError,
});
const { renameRelationships } = useCreateTableRelationships(
relationship.fromSource,
{ onSuccess, onError }
);
if (
relationship.type === 'remoteDatabaseRelationship' ||
@ -56,7 +54,16 @@ export const RenameRelationship = (props: RenameRelationshipProps) => {
updatedName: z.string().min(1, 'Updated name cannot be empty!'),
})}
onSubmit={data => {
renameRelationship(relationship, data.updatedName);
renameRelationships({
data: [
{
name: relationship.name,
new_name: data.updatedName,
source: relationship.fromSource,
table: relationship.fromTable,
},
],
});
}}
>
<>

View File

@ -1,9 +1,7 @@
import { Dialog } from '../../../../../new-components/Dialog';
import React from 'react';
import { Relationship } from '../../../types';
import { useManageLocalRelationship } from '../../../hooks/useManageLocalRelationship';
import { useManageRemoteDatabaseRelationship } from '../../../hooks/useManageRemoteDatabaseRelationship';
import { useManageRemoteSchemaRelationship } from '../../../hooks/useManageRemoteSchemaRelationship';
import { useCreateTableRelationships } from '../../../hooks/useCreateTableRelationships/useCreateTableRelationships';
interface ConfirmDeleteRelationshipPopupProps {
relationship: Relationship;
@ -17,33 +15,13 @@ export const ConfirmDeleteRelationshipPopup = (
) => {
const { relationship, onCancel, onSuccess, onError } = props;
const {
deleteRelationship: deleteLocalRelationship,
isLoading: isDeleteLocalRelationshipLoading,
} = useManageLocalRelationship({
dataSourceName: relationship.fromSource,
table: relationship.fromTable,
onSuccess,
onError,
});
const {
deleteRelationship: deleteRemoteDatabaseRelationship,
isLoading: isDeleteRemoteDatabaseRelationshipLoading,
} = useManageRemoteDatabaseRelationship({
dataSourceName: relationship.fromSource,
onSuccess,
onError,
});
const {
deleteRelationship: deleteRemoteSchemaRelationship,
isLoading: isDeleteRemoteSchemaRelationshipLoading,
} = useManageRemoteSchemaRelationship({
dataSourceName: relationship.fromSource,
onSuccess,
onError,
});
const { deleteRelationships, isLoading } = useCreateTableRelationships(
relationship.fromSource,
{
onSuccess,
onError,
}
);
return (
<Dialog
@ -55,21 +33,20 @@ export const ConfirmDeleteRelationshipPopup = (
footer={
<Dialog.Footer
onSubmit={() => {
if (relationship.type === 'localRelationship') {
deleteLocalRelationship(relationship);
} else if (relationship.type === 'remoteDatabaseRelationship') {
deleteRemoteDatabaseRelationship(relationship);
} else if (relationship.type === 'remoteSchemaRelationship')
deleteRemoteSchemaRelationship(relationship);
deleteRelationships({
data: [
{
name: relationship.name,
source: relationship.fromSource,
table: relationship.fromTable,
},
],
});
}}
onClose={onCancel}
callToDeny="Cancel"
callToAction="Drop Relationship"
isLoading={
isDeleteLocalRelationshipLoading ||
isDeleteRemoteDatabaseRelationshipLoading ||
isDeleteRemoteSchemaRelationshipLoading
}
isLoading={isLoading}
/>
}
>

View File

@ -6,12 +6,12 @@ import {
GraphQLSanitizedInputField,
} from '../../../../new-components/Form';
import { hasuraToast } from '../../../../new-components/Toasts';
import {
SuggestedRelationshipWithName,
useSuggestedRelationships,
} from '../SuggestedRelationships/hooks/useSuggestedRelationships';
import { SuggestedRelationshipWithName } from '../SuggestedRelationships/hooks/useSuggestedRelationships';
import { useCreateTableRelationships } from '../../hooks/useCreateTableRelationships/useCreateTableRelationships';
import { DisplayToastErrorMessage } from '../../../Data/components/DisplayErrorMessage';
import { useAppDispatch } from '../../../../storeHooks';
import { updateSchemaInfo } from '../../../../components/Services/Data/DataActions';
import { MetadataSelectors, useMetadata } from '../../../hasura-metadata-api';
type SuggestedRelationshipTrackModalProps = {
relationship: SuggestedRelationshipWithName;
@ -22,14 +22,23 @@ type SuggestedRelationshipTrackModalProps = {
export const SuggestedRelationshipTrackModal: React.VFC<
SuggestedRelationshipTrackModalProps
> = ({ relationship, dataSourceName, onClose }) => {
const { refetchSuggestedRelationships } = useSuggestedRelationships({
dataSourceName,
table: relationship.from.table,
isEnabled: true,
});
const dispatch = useAppDispatch();
const { data: driver } = useMetadata(
m => MetadataSelectors.findSource(dataSourceName)(m)?.kind
);
const { createTableRelationships, isLoading } =
useCreateTableRelationships(dataSourceName);
const isLoadSchemaRequired = driver === 'mssql' || driver === 'postgres';
const { createTableRelationships, isLoading } = useCreateTableRelationships(
dataSourceName,
{
onSuccess: () => {
if (isLoadSchemaRequired) {
dispatch(updateSchemaInfo());
}
},
}
);
const onTrackRelationship = async (relationshipName: string) => {
createTableRelationships({
@ -62,7 +71,6 @@ export const SuggestedRelationshipTrackModal: React.VFC<
type: 'success',
title: 'Tracked Successfully',
});
refetchSuggestedRelationships();
onClose();
},
onError: err => {

View File

@ -10,15 +10,12 @@ import {
FaMagic,
FaTable,
} from 'react-icons/fa';
import { MetadataSelectors, useMetadata } from '../../../hasura-metadata-api';
import { getSupportsForeignKeys } from '../../../hasura-metadata-api/utils';
import { areTablesEqual } from '../../../hasura-metadata-api';
import { getTableDisplayName } from '../../utils/helpers';
import {
SuggestedRelationshipWithName,
useSuggestedRelationships,
} from './hooks/useSuggestedRelationships';
import { SuggestedRelationshipWithName } from './hooks/useSuggestedRelationships';
import Skeleton from 'react-loading-skeleton';
import { SuggestedRelationshipTrackModal } from '../SuggestedRelationshipTrackModal/SuggestedRelationshipTrackModal';
import { useSuggestedRelationships } from '../../../Data/TrackResources/TrackRelationships/hooks/useSuggestedRelationships';
type SuggestedRelationshipsProps = {
dataSourceName: string;
@ -29,27 +26,23 @@ export const SuggestedRelationships = ({
dataSourceName,
table,
}: SuggestedRelationshipsProps) => {
const { data: source } = useMetadata(
MetadataSelectors.findSource(dataSourceName)
);
const supportsForeignKeys = getSupportsForeignKeys(source);
const { suggestedRelationships, isLoadingSuggestedRelationships } =
const { data: { untracked = [] } = {}, isLoading } =
useSuggestedRelationships({
dataSourceName,
table,
isEnabled: supportsForeignKeys,
which: 'all',
});
const untrackedSuggestedRelationships = untracked.filter(rel =>
areTablesEqual(rel.from.table, table)
);
const [isModalVisible, setModalVisible] = useState(false);
const [selectedRelationship, setSelectedRelationship] =
useState<SuggestedRelationshipWithName | null>(null);
if (isLoadingSuggestedRelationships)
return <Skeleton count={4} height={30} />;
if (isLoading) return <Skeleton count={4} height={30} />;
return suggestedRelationships.length > 0 ? (
return untrackedSuggestedRelationships.length > 0 ? (
<>
<CardedTable.Table>
<CardedTable.Header
@ -64,7 +57,7 @@ export const SuggestedRelationships = ({
/>
<CardedTable.TableBody>
{suggestedRelationships.map(relationship => (
{untrackedSuggestedRelationships.map(relationship => (
<CardedTable.TableBodyRow key={relationship.constraintName}>
<CardedTable.TableBodyCell>
<div className="flex flex-row items-center">

View File

@ -1,23 +1,9 @@
import { useEffect } from 'react';
import inflection from 'inflection';
import camelCase from 'lodash/camelCase';
import { SuggestedRelationship } from '../../../types';
import { getTableDisplayName } from '../../../utils/helpers';
import { getDriverPrefix, runMetadataQuery } from '../../../../DataSource';
import {
areTablesEqual,
MetadataSelectors,
} from '../../../../hasura-metadata-api';
import { useMetadata } from '../../../../hasura-metadata-api/useMetadata';
import { NamingConvention, Table } from '../../../../hasura-metadata-types';
import { useHttpClient } from '../../../../Network';
import { useQuery } from 'react-query';
type UseSuggestedRelationshipsArgs = {
dataSourceName: string;
table?: Table;
isEnabled: boolean;
};
import { areTablesEqual } from '../../../../hasura-metadata-api';
export type SuggestedRelationshipsResponse = {
relationships: SuggestedRelationship[];
@ -87,89 +73,3 @@ export const addConstraintName = (
constraintName,
};
});
export const getSuggestedRelationshipsCacheQuery = (
dataSourceName: string,
table: Table
) => ['suggested_relationships', dataSourceName, table];
export const useSuggestedRelationships = ({
dataSourceName,
table,
isEnabled,
}: UseSuggestedRelationshipsArgs) => {
const { data: metadataSource, isFetching } = useMetadata(
MetadataSelectors.findSource(dataSourceName)
);
const namingConvention: NamingConvention =
metadataSource?.customization?.naming_convention || 'hasura-default';
const dataSourcePrefix = metadataSource?.kind
? getDriverPrefix(metadataSource?.kind)
: undefined;
const httpClient = useHttpClient();
const {
data,
refetch: refetchSuggestedRelationships,
isLoading: isLoadingSuggestedRelationships,
} = useQuery({
queryKey: getSuggestedRelationshipsCacheQuery(dataSourceName, table),
queryFn: async () => {
const body = {
type: `${dataSourcePrefix}_suggest_relationships`,
args: {
omit_tracked: true,
source: dataSourceName,
...(table ? { tables: [table] } : {}),
},
};
const result = await runMetadataQuery<SuggestedRelationshipsResponse>({
httpClient,
body,
});
return result;
},
enabled: isEnabled && !isFetching,
refetchOnWindowFocus: false,
});
useEffect(() => {
if (dataSourcePrefix) {
refetchSuggestedRelationships();
}
}, [dataSourcePrefix, refetchSuggestedRelationships]);
const suggestedRelationships = data?.relationships || [];
/**
* This is needed because the suggested_relationships metadata API returns Foreign Keys
*
* from current table -> to other table
* but also
*
* from other table -> to current table
*
* After the tracking, the second type of Foreign Keys would not be shown in the current table UI
*/
const tableFilteredRelationships = table
? filterTableRelationships({
table,
relationships: suggestedRelationships,
})
: suggestedRelationships;
const relationshipsWithConstraintName = addConstraintName(
tableFilteredRelationships,
namingConvention
);
return {
suggestedRelationships: relationshipsWithConstraintName,
isLoadingSuggestedRelationships,
refetchSuggestedRelationships,
};
};

View File

@ -20,6 +20,7 @@ import {
deleteTableRelationshipRequestBody,
renameRelationshipRequestBody,
} from './utils';
import { useInvalidateSuggestedRelationships } from '../../../Data/TrackResources/TrackRelationships/hooks/useSuggestedRelationships';
type AllowedRelationshipDefinitions =
| Omit<LocalTableRelationshipDefinition, 'capabilities'>
@ -72,6 +73,11 @@ export const useCreateTableRelationships = (
) => {
// get these capabilities
const { invalidateSuggestedRelationships } =
useInvalidateSuggestedRelationships({
dataSourceName,
});
const { data: driverCapabilties = [] } = useAllDriverCapabilities({
select: data => {
const result = data.map(item => {
@ -129,6 +135,8 @@ export const useCreateTableRelationships = (
errorTransform: transformErrorResponse,
onSuccess: (data, variable, ctx) => {
globalMutateOptions?.onSuccess?.(data, variable, ctx);
console.log('invalidate');
invalidateSuggestedRelationships();
},
});

View File

@ -1,7 +1,24 @@
import { Table } from '../../hasura-metadata-types';
import { useMetadata, MetadataSelectors } from '../../hasura-metadata-api';
import { getAllTableRelationships } from '../utils/tableRelationships';
import { useAllSuggestedRelationships } from '../components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import {
useMetadata,
MetadataSelectors,
areTablesEqual,
} from '../../hasura-metadata-api';
import { useSuggestedRelationships } from '../../Data/TrackResources/TrackRelationships/hooks/useSuggestedRelationships';
import {
isLegacyRemoteSchemaRelationship,
isManualArrayRelationship,
isManualObjectRelationship,
isRemoteSchemaRelationship,
} from '../../DataSource';
import {
adaptLegacyRemoteSchemaRelationship,
adaptLocalArrayRelationshipWithManualConfiguration,
adaptLocalObjectRelationshipWithManualConfiguration,
adaptRemoteDatabaseRelationship,
adaptRemoteSchemaRelationship,
} from '../utils/adaptResponse';
import { LocalRelationship } from '../types';
export const useListAllDatabaseRelationships = ({
dataSourceName,
@ -11,31 +28,96 @@ export const useListAllDatabaseRelationships = ({
table: Table;
}) => {
const {
data: metadataTable,
isFetching: isMetadataPending,
isLoading: isMetadataLoading,
error: metadataError,
} = useMetadata(MetadataSelectors.findTable(dataSourceName, table));
data: { tracked = [] } = {},
isLoading: isSuggestedRelationshipsLoading,
isFetching: isSuggestedRelationshipsFetching,
error: suggestedRelationshipsError,
} = useSuggestedRelationships({
dataSourceName,
which: 'all',
});
const filteredTrackedFkRels: LocalRelationship[] = tracked
.filter(rel => areTablesEqual(rel.fromTable, table))
.map(rel => ({
name: rel.name,
fromSource: dataSourceName,
fromTable: rel.fromTable,
type: 'localRelationship',
relationshipType: rel.type === 'object' ? 'Object' : 'Array',
definition: {
toTable: rel.toTable,
mapping: rel.columnMapping,
},
}));
const {
suggestedRelationships,
isLoadingSuggestedRelationships,
isFetchingSuggestedRelationships,
error,
} = useAllSuggestedRelationships({
dataSourceName,
isEnabled: true,
omitTracked: false,
data: relationshipsWithManualConfigs = [],
isLoading: isMetadataLoading,
isFetching: isMetadataFetching,
error: metadataError,
} = useMetadata(m => {
const metadataTable = MetadataSelectors.findTable(dataSourceName, table)(m);
const localArrayRelationshipsWithManualConfig = (
metadataTable?.array_relationships ?? []
)
.filter(isManualArrayRelationship)
.map(relationship =>
adaptLocalArrayRelationshipWithManualConfiguration({
table,
dataSourceName,
relationship,
})
);
const localObjectRelationshipsWithManualConfig = (
metadataTable?.object_relationships ?? []
)
.filter(isManualObjectRelationship)
.map(relationship =>
adaptLocalObjectRelationshipWithManualConfiguration({
table,
dataSourceName,
relationship,
})
);
const remoteRels = (metadataTable?.remote_relationships ?? []).map(
relationship => {
if (isRemoteSchemaRelationship(relationship))
return adaptRemoteSchemaRelationship({
table,
dataSourceName,
relationship,
});
if (isLegacyRemoteSchemaRelationship(relationship))
return adaptLegacyRemoteSchemaRelationship({
table,
dataSourceName,
relationship,
});
return adaptRemoteDatabaseRelationship({
table,
dataSourceName,
relationship,
});
}
);
return [
...remoteRels,
...localArrayRelationshipsWithManualConfig,
...localObjectRelationshipsWithManualConfig,
];
});
return {
data: getAllTableRelationships(
metadataTable,
dataSourceName,
suggestedRelationships
),
isFetching: isMetadataPending || isFetchingSuggestedRelationships,
isLoading: isMetadataLoading || isLoadingSuggestedRelationships,
error: [metadataError, error],
data: [...filteredTrackedFkRels, ...relationshipsWithManualConfigs],
isLoading: isSuggestedRelationshipsLoading || isMetadataLoading,
isFetching: isSuggestedRelationshipsFetching || isMetadataFetching,
error: [metadataError, suggestedRelationshipsError],
};
};

View File

@ -1,114 +0,0 @@
import { useMetadataMigration } from '../../MetadataAPI';
import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query';
import { useMetadata } from '../../hasura-metadata-api';
import { LocalRelationship } from '../types';
import {
generateCreateLocalRelationshipWithManualConfigurationRequest,
generateDeleteLocalRelationshipRequest,
generateRenameLocalRelationshipRequest,
} from '../utils/generateRequest';
import { generateQueryKeys } from '../utils/queryClientUtils';
import { Table } from '../../hasura-metadata-types';
export const useManageLocalRelationship = ({
dataSourceName,
table,
onSuccess,
onError,
}: {
dataSourceName: string;
table: Table;
onSuccess?: () => void;
onError?: (err: Error) => void;
}) => {
const { data } = useMetadata(m => {
return {
resource_version: m.resource_version,
source: m.metadata.sources.find(s => s.name === dataSourceName),
};
});
const queryClient = useQueryClient();
const { mutate, ...rest } = useMetadataMigration();
const mutationOptions = useMemo(
() => ({
onSuccess: () => {
queryClient.invalidateQueries(
generateQueryKeys.suggestedRelationships({ dataSourceName, table })
);
onSuccess?.();
},
onError: (err: Error) => {
onError?.(err);
},
}),
[onError, onSuccess, queryClient]
);
const metadataSource = data?.source;
const resource_version = data?.resource_version;
const driver = metadataSource?.kind;
const renameRelationship = useCallback(
async (relationship: LocalRelationship, newName: string) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRenameLocalRelationshipRequest({
resource_version,
relationship,
driver,
newName,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const createRelationship = useCallback(
async (relationship: LocalRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateCreateLocalRelationshipWithManualConfigurationRequest({
resource_version,
relationship,
driver,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const deleteRelationship = useCallback(
async (relationship: LocalRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateDeleteLocalRelationshipRequest({
driver,
resource_version,
relationship,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
return {
renameRelationship,
deleteRelationship,
createRelationship,
...rest,
};
};

View File

@ -1,106 +0,0 @@
import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query';
import { useMetadataMigration } from '../../MetadataAPI';
import { useMetadata } from '../../hasura-metadata-api';
import { RemoteDatabaseRelationship } from '../types';
import {
generateRemoteRelationshipCreateRequest,
generateRemoteRelationshipDeleteRequest,
generateRemoteRelationshipEditRequest,
} from '../utils/generateRequest';
export const useManageRemoteDatabaseRelationship = ({
dataSourceName,
onSuccess,
onError,
}: {
dataSourceName: string;
onSuccess?: () => void;
onError?: (err: Error) => void;
}) => {
const { data } = useMetadata(m => {
return {
resource_version: m.resource_version,
source: m.metadata.sources.find(s => s.name === dataSourceName),
};
});
const queryClient = useQueryClient();
const { mutate, ...rest } = useMetadataMigration();
const mutationOptions = useMemo(
() => ({
onSuccess: () => {
onSuccess?.();
},
onError: (err: Error) => {
onError?.(err);
},
}),
[onError, onSuccess, queryClient]
);
const metadataSource = data?.source;
const resource_version = data?.resource_version;
const driver = metadataSource?.kind;
const createRelationship = useCallback(
async (relationship: RemoteDatabaseRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipCreateRequest({
resource_version,
relationship,
driver,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const editRelationship = useCallback(
async (relationship: RemoteDatabaseRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipEditRequest({
resource_version,
relationship,
driver,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const deleteRelationship = useCallback(
async (relationship: RemoteDatabaseRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipDeleteRequest({
driver,
resource_version,
relationship,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
return {
editRelationship,
deleteRelationship,
createRelationship,
...rest,
};
};

View File

@ -1,105 +0,0 @@
import { useCallback, useMemo } from 'react';
import { useQueryClient } from 'react-query';
import { useMetadataMigration } from '../../MetadataAPI';
import { useMetadata } from '../../hasura-metadata-api';
import { RemoteSchemaRelationship } from '../types';
import {
generateRemoteRelationshipCreateRequest,
generateRemoteRelationshipDeleteRequest,
generateRemoteRelationshipEditRequest,
} from '../utils/generateRequest';
export const useManageRemoteSchemaRelationship = ({
dataSourceName,
onSuccess,
onError,
}: {
dataSourceName: string;
onSuccess?: () => void;
onError?: (err: Error) => void;
}) => {
const { data } = useMetadata(m => {
return {
resource_version: m.resource_version,
source: m.metadata.sources.find(s => s.name === dataSourceName),
};
});
const queryClient = useQueryClient();
const { mutate, ...rest } = useMetadataMigration();
const mutationOptions = useMemo(
() => ({
onSuccess: () => {
onSuccess?.();
},
onError: (err: Error) => {
onError?.(err);
},
}),
[onError, onSuccess, queryClient]
);
const metadataSource = data?.source;
const resource_version = data?.resource_version;
const driver = metadataSource?.kind;
const createRelationship = useCallback(
async (relationship: RemoteSchemaRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipCreateRequest({
resource_version,
relationship,
driver,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const editRelationship = useCallback(
async (relationship: RemoteSchemaRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipEditRequest({
resource_version,
relationship,
driver,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
const deleteRelationship = useCallback(
async (relationship: RemoteSchemaRelationship) => {
if (!resource_version || !driver) throw Error('Metadata not ready');
mutate(
{
query: generateRemoteRelationshipDeleteRequest({
driver,
resource_version,
relationship,
}),
},
mutationOptions
);
},
[driver, mutate, mutationOptions, resource_version]
);
return {
editRelationship,
deleteRelationship,
createRelationship,
...rest,
};
};