fix: Too many API calls fired from the suggested relationships component

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8708
GitOrigin-RevId: 9d917b9015053e5653d2277f3dcdf69168bd1eff
This commit is contained in:
Luca Restagno 2023-04-17 11:19:18 +02:00 committed by hasura-bot
parent 1ad1e08959
commit ed8be7dd69
12 changed files with 317 additions and 194 deletions

View File

@ -102,12 +102,12 @@ export const ManageTrackedRelationships: React.VFC<
</div> </div>
) : ( ) : (
<> <>
<Tabs.Content value="tracked" className="px-md">
<TrackedRelationshipsContainer dataSourceName={dataSourceName} />
</Tabs.Content>
<Tabs.Content value="untracked" className="px-md"> <Tabs.Content value="untracked" className="px-md">
<UntrackedRelationships dataSourceName={dataSourceName} /> <UntrackedRelationships dataSourceName={dataSourceName} />
</Tabs.Content> </Tabs.Content>
<Tabs.Content value="tracked" className="px-md">
<TrackedRelationshipsContainer dataSourceName={dataSourceName} />
</Tabs.Content>
</> </>
)} )}
</Tabs.Root> </Tabs.Root>

View File

@ -1,4 +1,4 @@
import { useSuggestedRelationships } from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships'; import { useAllSuggestedRelationships } from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import { useTrackedRelationships } from './hooks/useTrackedRelationships'; import { useTrackedRelationships } from './hooks/useTrackedRelationships';
import { ManageTrackedRelationships } from './ManageTrackedRelationships'; import { ManageTrackedRelationships } from './ManageTrackedRelationships';
@ -13,9 +13,9 @@ export const ManageTrackedRelationshipsContainer = ({
} = useTrackedRelationships(dataSourceName); } = useTrackedRelationships(dataSourceName);
const { suggestedRelationships, isLoadingSuggestedRelationships } = const { suggestedRelationships, isLoadingSuggestedRelationships } =
useSuggestedRelationships({ useAllSuggestedRelationships({
dataSourceName, dataSourceName,
existingRelationships: [], omitTracked: true,
isEnabled: true, isEnabled: true,
}); });

View File

@ -23,14 +23,13 @@ import {
generateDeleteLocalRelationshipRequest, generateDeleteLocalRelationshipRequest,
generateRemoteRelationshipDeleteRequest, generateRemoteRelationshipDeleteRequest,
} from '../../../DatabaseRelationships/utils/generateRequest'; } from '../../../DatabaseRelationships/utils/generateRequest';
import { generateQueryKeys } from '../../../DatabaseRelationships/utils/queryClientUtils';
import { useQueryClient } from 'react-query';
import { exportMetadata } from '../../../DataSource'; import { exportMetadata } from '../../../DataSource';
import { useHttpClient } from '../../../Network'; import { useHttpClient } from '../../../Network';
import { IndicatorCard } from '../../../../new-components/IndicatorCard'; import { IndicatorCard } from '../../../../new-components/IndicatorCard';
import { MetadataDataSource } from '../../../../metadata/types'; import { MetadataDataSource } from '../../../../metadata/types';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
import { getSuggestedRelationshipsCacheQuery } from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships'; import { generateQueryKeys } from '../../../DatabaseRelationships/utils/queryClientUtils';
import { useQueryClient } from 'react-query';
import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows'; import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows';
const getQueryFunction = (relationship: Relationship) => { const getQueryFunction = (relationship: Relationship) => {
@ -59,7 +58,7 @@ interface TrackedRelationshipsProps {
dataSourceName: string; dataSourceName: string;
driver?: MetadataDataSource['kind']; driver?: MetadataDataSource['kind'];
isLoading: boolean; isLoading: boolean;
onRefetchMetadata: () => void; onUpdate: () => void;
relationships: Relationship[]; relationships: Relationship[];
} }
@ -67,12 +66,13 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
dataSourceName, dataSourceName,
driver, driver,
isLoading, isLoading,
onRefetchMetadata, onUpdate,
relationships, relationships,
}) => { }) => {
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const { mutateAsync } = useMetadataMigration(); const { mutateAsync } = useMetadataMigration();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] = const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] =
useState(false); useState(false);
@ -118,50 +118,41 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
); );
if (driver) { if (driver) {
for (let i = 0; i < selectedRelationships.length; i++) { const recentMetadata = await exportMetadata({ httpClient });
const selectedRelationship = selectedRelationships[i];
const mutationOptions = { const queries = selectedRelationships.map(relationship => {
const queryFunction = getQueryFunction(relationship);
const query = queryFunction
? queryFunction({
driver,
resource_version: recentMetadata.resource_version,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
relationship,
})
: {};
return query;
});
await mutateAsync(
{
query: {
type: 'bulk',
args: queries,
},
},
{
onSuccess: () => { onSuccess: () => {
queryClient.invalidateQueries(generateQueryKeys.metadata()); queryClient.invalidateQueries(generateQueryKeys.metadata());
queryClient.invalidateQueries(
generateQueryKeys.suggestedRelationships({
dataSourceName,
table: selectedRelationship.fromTable,
})
);
}, },
};
const queryFunction = getQueryFunction(selectedRelationship);
if (queryFunction) {
const recentMetadata = await exportMetadata({ httpClient });
await mutateAsync(
{
query: queryFunction({
driver,
resource_version: recentMetadata.resource_version,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
relationship: selectedRelationship,
}),
},
mutationOptions
);
queryClient.invalidateQueries(
getSuggestedRelationshipsCacheQuery(
dataSourceName,
selectedRelationship.fromTable
)
);
} }
} );
onRefetchMetadata(); onUpdate();
const relationshipLabel = const plural = selectedRelationships.length > 1 ? 's' : '';
selectedRelationships.length > 1 ? 'Relationships' : 'Relationship'; const toastMessage = `${selectedRelationships.length} relationship${plural} untracked`;
const toastMessage = `${selectedRelationships.length} ${relationshipLabel} untracked`;
hasuraToast({ hasuraToast({
title: 'Success', title: 'Success',
@ -170,6 +161,7 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
}); });
} }
} catch (err) { } catch (err) {
console.error(err);
setTrackingSelectedRelationships(false); setTrackingSelectedRelationships(false);
} }
reset(); reset();
@ -200,6 +192,7 @@ export const TrackedRelationships: React.VFC<TrackedRelationshipsProps> = ({
}; };
const onRelationshipActionSuccess = () => { const onRelationshipActionSuccess = () => {
onUpdate();
if (mode) if (mode)
fireNotification({ fireNotification({
type: 'success', type: 'success',

View File

@ -47,10 +47,9 @@ export const TrackedRelationshipsContainer: React.VFC<
dataSourceName={dataSourceName} dataSourceName={dataSourceName}
isLoading={isLoadingRelationships || isLoadingMetadata} isLoading={isLoadingRelationships || isLoadingMetadata}
relationships={relationships} relationships={relationships}
onRefetchMetadata={() => { onUpdate={async () => {
refetchMetadata().then(() => { await refetchMetadata();
refetchRelationships(); await refetchRelationships();
});
}} }}
driver={driver} driver={driver}
/> />

View File

@ -9,27 +9,39 @@ import { DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE } from '../constants';
import { paginate } from '../utils'; import { paginate } from '../utils';
import { SearchBar } from './SearchBar'; import { SearchBar } from './SearchBar';
import { Badge } from '../../../../new-components/Badge'; import { Badge } from '../../../../new-components/Badge';
import { import { SuggestedRelationshipWithName } from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships';
SuggestedRelationshipWithName,
useSuggestedRelationships,
} from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships';
import { RelationshipRow } from './RelationshipRow'; import { RelationshipRow } from './RelationshipRow';
import { SuggestedRelationshipTrackModal } from '../../../DatabaseRelationships/components/SuggestedRelationshipTrackModal/SuggestedRelationshipTrackModal'; import { SuggestedRelationshipTrackModal } from '../../../DatabaseRelationships/components/SuggestedRelationshipTrackModal/SuggestedRelationshipTrackModal';
import { hasuraToast } from '../../../../new-components/Toasts'; import { hasuraToast } from '../../../../new-components/Toasts';
import Skeleton from 'react-loading-skeleton'; import Skeleton from 'react-loading-skeleton';
import { useQueryClient } from 'react-query'; import {
import { getTrackedRelationshipsCacheKey } from './hooks/useTrackedRelationships'; AddSuggestedRelationship,
useAllSuggestedRelationships,
} from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows'; import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows';
interface UntrackedRelationshipsProps { interface UntrackedRelationshipsProps {
dataSourceName: string; dataSourceName: string;
} }
const adaptTrackRelationship = (
relationship: SuggestedRelationshipWithName
): AddSuggestedRelationship => {
const isObjectRelationship = !!relationship.from?.constraint_name;
return {
name: relationship.constraintName,
columnNames: isObjectRelationship
? relationship.from.columns
: relationship.to.columns,
relationshipType: isObjectRelationship ? 'object' : 'array',
toTable: isObjectRelationship ? undefined : relationship.to.table,
fromTable: relationship.from.table,
};
};
export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
dataSourceName, dataSourceName,
}) => { }) => {
const queryClient = useQueryClient();
const [pageNumber, setPageNumber] = useState(DEFAULT_PAGE_NUMBER); const [pageNumber, setPageNumber] = useState(DEFAULT_PAGE_NUMBER);
const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE); const [pageSize, setPageSize] = useState(DEFAULT_PAGE_SIZE);
const [searchText, setSearchText] = useState(''); const [searchText, setSearchText] = useState('');
@ -41,12 +53,11 @@ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
const { const {
suggestedRelationships, suggestedRelationships,
isLoadingSuggestedRelationships, isLoadingSuggestedRelationships,
onAddSuggestedRelationship, onAddMultipleSuggestedRelationships,
refetchSuggestedRelationships, } = useAllSuggestedRelationships({
} = useSuggestedRelationships({
dataSourceName, dataSourceName,
existingRelationships: [],
isEnabled: true, isEnabled: true,
omitTracked: true,
}); });
const checkboxRef = React.useRef<HTMLInputElement>(null); const checkboxRef = React.useRef<HTMLInputElement>(null);
@ -82,19 +93,9 @@ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
}, [inputStatus]); }, [inputStatus]);
const onTrackRelationship = (relationship: SuggestedRelationshipWithName) => { const onTrackRelationship = (relationship: SuggestedRelationshipWithName) => {
const isObjectRelationship = !!relationship.from?.constraint_name; return onAddMultipleSuggestedRelationships([
adaptTrackRelationship(relationship),
return onAddSuggestedRelationship({ ]);
name: relationship.constraintName,
columnNames: isObjectRelationship
? relationship.from.columns
: relationship.to.columns,
relationshipType: isObjectRelationship ? 'object' : 'array',
toTable: isObjectRelationship ? undefined : relationship.to.table,
fromTable: relationship.from.table,
}).then(() => {
refetchSuggestedRelationships();
});
}; };
const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] = const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] =
@ -106,24 +107,21 @@ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
checkedIds.includes(rel.constraintName) checkedIds.includes(rel.constraintName)
); );
for (const selectedRelationship of selectedRelationships) { const trackRelationships: AddSuggestedRelationship[] =
await onTrackRelationship(selectedRelationship); selectedRelationships.map(adaptTrackRelationship);
}
queryClient.invalidateQueries({ await onAddMultipleSuggestedRelationships(trackRelationships);
queryKey: getTrackedRelationshipsCacheKey(dataSourceName),
});
const plural = selectedRelationships.length > 1 ? 's' : '';
hasuraToast({ hasuraToast({
title: 'Success', title: 'Success',
message: 'Relationships tracked', message: `${selectedRelationships.length} relationship${plural} tracked`,
type: 'success', type: 'success',
}); });
} catch (err) { } catch (err) {
setTrackingSelectedRelationships(false); setTrackingSelectedRelationships(false);
} }
reset(); reset();
refetchSuggestedRelationships();
setTrackingSelectedRelationships(false); setTrackingSelectedRelationships(false);
}; };

View File

@ -1,10 +1,7 @@
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { useAllSuggestedRelationships } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
import { tableRelationships as getTableRelationships } from '../../../../DatabaseRelationships/utils/tableRelationships'; import { tableRelationships as getTableRelationships } from '../../../../DatabaseRelationships/utils/tableRelationships';
import { DataSource } from '../../../../DataSource'; import { exportMetadata } from '../../../../DataSource';
import {
useMetadata,
MetadataSelectors,
} from '../../../../hasura-metadata-api';
import { useHttpClient } from '../../../../Network'; import { useHttpClient } from '../../../../Network';
export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [ export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [
@ -15,29 +12,28 @@ export const getTrackedRelationshipsCacheKey = (dataSourceName: string) => [
export const useTrackedRelationships = (dataSourceName: string) => { export const useTrackedRelationships = (dataSourceName: string) => {
const httpClient = useHttpClient(); const httpClient = useHttpClient();
const { const { suggestedRelationships } = useAllSuggestedRelationships({
data: metadataTables, dataSourceName,
isFetching: isMetadataPending, isEnabled: true,
isLoading: isMetadataLoading, omitTracked: false,
error: metadataError, });
refetch: refetchMetadata,
} = useMetadata(MetadataSelectors.getTables(dataSourceName));
const fetchRelationships = async () => { const fetchRelationships = async () => {
const _tableRelationships = []; const { metadata } = await exportMetadata({ httpClient });
if (metadataTables && !isMetadataLoading) {
for (const metadataTable of metadataTables) {
const fkConstraints = await DataSource(
httpClient
).getTableFkRelationships({
dataSourceName,
table: metadataTable.table,
});
const currentMetadataSource = metadata.sources?.find(
source => source.name === dataSourceName
);
const metadataTables = currentMetadataSource?.tables || [];
const _tableRelationships = [];
if (metadataTables) {
for (const metadataTable of metadataTables) {
const tableRelationships = getTableRelationships( const tableRelationships = getTableRelationships(
metadataTable, metadataTable,
dataSourceName, dataSourceName,
fkConstraints suggestedRelationships
); );
_tableRelationships.push(...tableRelationships); _tableRelationships.push(...tableRelationships);
} }
@ -51,6 +47,7 @@ export const useTrackedRelationships = (dataSourceName: string) => {
isLoading: isLoadingRelationships, isLoading: isLoadingRelationships,
isFetching: isFetchingRelationships, isFetching: isFetchingRelationships,
refetch: refetchRelationships, refetch: refetchRelationships,
error,
} = useQuery({ } = useQuery({
queryFn: fetchRelationships, queryFn: fetchRelationships,
queryKey: getTrackedRelationshipsCacheKey(dataSourceName), queryKey: getTrackedRelationshipsCacheKey(dataSourceName),
@ -58,10 +55,9 @@ export const useTrackedRelationships = (dataSourceName: string) => {
return { return {
data: relationships || [], data: relationships || [],
isFetching: isMetadataPending || isFetchingRelationships, isFetching: isFetchingRelationships,
isLoading: isMetadataLoading || isLoadingRelationships, isLoading: isLoadingRelationships,
error: [metadataError], error: [error],
refetchRelationships, refetchRelationships,
refetchMetadata,
}; };
}; };

View File

@ -0,0 +1,155 @@
import { useEffect } from 'react';
import { useQuery, useQueryClient } from 'react-query';
import { LocalRelationship } from '../../../types';
import { getDriverPrefix, runMetadataQuery } from '../../../../DataSource';
import { MetadataSelectors } from '../../../../hasura-metadata-api';
import { useMetadata } from '../../../../hasura-metadata-api/useMetadata';
import { useHttpClient } from '../../../../Network';
import {
addConstraintName,
SuggestedRelationshipsResponse,
} from './useSuggestedRelationships';
import { useMetadataMigration } from '../../../../MetadataAPI';
import { NamingConvention, Table } from '../../../../hasura-metadata-types';
import { getTrackedRelationshipsCacheKey } from '../../../../Data/TrackResources/components/hooks/useTrackedRelationships';
export type AddSuggestedRelationship = {
name: string;
columnNames: string[];
relationshipType: 'object' | 'array';
toTable?: Table;
fromTable?: Table;
};
type UseSuggestedRelationshipsArgs = {
dataSourceName: string;
existingRelationships?: LocalRelationship[];
isEnabled: boolean;
omitTracked: boolean;
};
export const getAllSuggestedRelationshipsCacheQuery = (
dataSourceName: string,
omitTracked: boolean
) => ['all_suggested_relationships', dataSourceName, omitTracked];
export const useAllSuggestedRelationships = ({
dataSourceName,
isEnabled,
omitTracked,
}: UseSuggestedRelationshipsArgs) => {
const { data: metadataSource, isFetching } = useMetadata(
MetadataSelectors.findSource(dataSourceName)
);
const dataSourcePrefix = metadataSource?.kind
? getDriverPrefix(metadataSource?.kind)
: undefined;
const namingConvention: NamingConvention =
metadataSource?.customization?.naming_convention || 'hasura-default';
const httpClient = useHttpClient();
const {
data,
refetch: refetchAllSuggestedRelationships,
isLoading: isLoadingAllSuggestedRelationships,
isFetching: isFetchingAllSuggestedRelationships,
...rest
} = useQuery({
queryKey: getAllSuggestedRelationshipsCacheQuery(
dataSourceName,
omitTracked
),
queryFn: async () => {
const body = {
type: `${dataSourcePrefix}_suggest_relationships`,
args: {
omit_tracked: omitTracked,
source: dataSourceName,
},
};
const result = await runMetadataQuery<SuggestedRelationshipsResponse>({
httpClient,
body,
});
return result;
},
enabled: isEnabled && !isFetching,
refetchOnWindowFocus: false,
});
useEffect(() => {
if (dataSourcePrefix) {
refetchAllSuggestedRelationships();
}
}, [dataSourcePrefix]);
const rawSuggestedRelationships = data?.relationships || [];
const metadataMutation = useMetadataMigration({});
const queryClient = useQueryClient();
const onAddMultipleSuggestedRelationships = async (
relationships: AddSuggestedRelationship[]
) => {
const queries = relationships.map(relationship => {
return {
type: `${dataSourcePrefix}_create_${relationship.relationshipType}_relationship`,
args: {
table: relationship.fromTable,
name: relationship.name,
source: dataSourceName,
using: {
foreign_key_constraint_on:
relationship.relationshipType === 'object'
? relationship.columnNames
: {
table: relationship.toTable,
columns: relationship.columnNames,
},
},
},
};
});
await metadataMutation.mutateAsync(
{
query: {
type: 'bulk',
args: queries,
},
},
{
onSettled: () => {
queryClient.invalidateQueries({
queryKey: getAllSuggestedRelationshipsCacheQuery(
dataSourceName,
omitTracked
),
});
queryClient.invalidateQueries({
queryKey: getTrackedRelationshipsCacheKey(dataSourceName),
});
},
}
);
};
const relationshipsWithConstraintName = addConstraintName(
rawSuggestedRelationships,
namingConvention
);
return {
suggestedRelationships: relationshipsWithConstraintName,
isLoadingSuggestedRelationships: isLoadingAllSuggestedRelationships,
isFetchingSuggestedRelationships: isFetchingAllSuggestedRelationships,
refetchSuggestedRelationships: refetchAllSuggestedRelationships,
onAddMultipleSuggestedRelationships,
...rest,
};
};

View File

@ -145,6 +145,14 @@ export const removeExistingRelationships = ({
return false; return false;
}); });
type AddSuggestedRelationship = {
name: string;
columnNames: string[];
relationshipType: 'object' | 'array';
toTable?: Table;
fromTable?: Table;
};
export const getSuggestedRelationshipsCacheQuery = ( export const getSuggestedRelationshipsCacheQuery = (
dataSourceName: string, dataSourceName: string,
table: Table table: Table
@ -208,13 +216,7 @@ export const useSuggestedRelationships = ({
relationshipType, relationshipType,
toTable, toTable,
fromTable, fromTable,
}: { }: AddSuggestedRelationship) => {
name: string;
columnNames: string[];
relationshipType: 'object' | 'array';
toTable?: Table;
fromTable?: Table;
}) => {
setAddingSuggestedRelationship(true); setAddingSuggestedRelationship(true);
await metadataMutation.mutateAsync({ await metadataMutation.mutateAsync({

View File

@ -1,36 +1,7 @@
import { DataSource } from '../../DataSource';
import { Table } from '../../hasura-metadata-types'; import { Table } from '../../hasura-metadata-types';
import { useHttpClient } from '../../Network';
import { useQuery } from 'react-query';
import { useMetadata, MetadataSelectors } from '../../hasura-metadata-api'; import { useMetadata, MetadataSelectors } from '../../hasura-metadata-api';
import {
DEFAULT_STALE_TIME,
generateQueryKeys,
} from '../utils/queryClientUtils';
import { tableRelationships } from '../utils/tableRelationships'; import { tableRelationships } from '../utils/tableRelationships';
import { useAllSuggestedRelationships } from '../components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
const useFkConstraints = ({
dataSourceName,
table,
}: {
dataSourceName: string;
table: Table;
}) => {
const httpClient = useHttpClient();
return useQuery({
queryKey: generateQueryKeys.fkConstraints({ table, dataSourceName }),
queryFn: async () => {
const result = await DataSource(httpClient).getTableFkRelationships({
dataSourceName,
table,
});
return result;
},
refetchOnWindowFocus: false,
staleTime: DEFAULT_STALE_TIME,
});
};
export const useListAllDatabaseRelationships = ({ export const useListAllDatabaseRelationships = ({
dataSourceName, dataSourceName,
@ -47,16 +18,24 @@ export const useListAllDatabaseRelationships = ({
} = useMetadata(MetadataSelectors.findTable(dataSourceName, table)); } = useMetadata(MetadataSelectors.findTable(dataSourceName, table));
const { const {
data: fkConstraints, suggestedRelationships,
isFetching: isDALIntrospectionPending, isLoadingSuggestedRelationships,
isLoading: isDALIntrospectionLoading, isFetchingSuggestedRelationships,
error: dalError, error,
} = useFkConstraints({ dataSourceName, table }); } = useAllSuggestedRelationships({
dataSourceName,
isEnabled: true,
omitTracked: false,
});
return { return {
data: tableRelationships(metadataTable, dataSourceName, fkConstraints), data: tableRelationships(
isFetching: isMetadataPending || isDALIntrospectionPending, metadataTable,
isLoading: isMetadataLoading || isDALIntrospectionLoading, dataSourceName,
error: [metadataError, dalError], suggestedRelationships
),
isFetching: isMetadataPending || isFetchingSuggestedRelationships,
isLoading: isMetadataLoading || isLoadingSuggestedRelationships,
error: [metadataError, error],
}; };
}; };

View File

@ -1,7 +1,4 @@
import { import { isSameTableObjectRelationship } from '../../DataSource';
isSameTableObjectRelationship,
TableFkRelationships,
} from '../../DataSource';
import { areTablesEqual } from '../../hasura-metadata-api'; import { areTablesEqual } from '../../hasura-metadata-api';
import { import {
Legacy_SourceToRemoteSchemaRelationship, Legacy_SourceToRemoteSchemaRelationship,
@ -19,6 +16,7 @@ import {
LocalRelationship, LocalRelationship,
RemoteDatabaseRelationship, RemoteDatabaseRelationship,
RemoteSchemaRelationship, RemoteSchemaRelationship,
SuggestedRelationship,
} from '../types'; } from '../types';
const getKeyValuePair = (arr1: string[], arr2: string[]) => { const getKeyValuePair = (arr1: string[], arr2: string[]) => {
@ -35,7 +33,7 @@ const getFkDefinition = (
| SameTableObjectRelationship | SameTableObjectRelationship
| LocalTableObjectRelationship | LocalTableObjectRelationship
| LocalTableArrayRelationship, | LocalTableArrayRelationship,
fkConstraints: TableFkRelationships[] suggestedRelationships: SuggestedRelationship[]
): { toTable: Table; mapping: Record<string, string> } => { ): { toTable: Table; mapping: Record<string, string> } => {
if (isSameTableObjectRelationship(relationship)) { if (isSameTableObjectRelationship(relationship)) {
const fromTable = table; const fromTable = table;
@ -45,18 +43,18 @@ const getFkDefinition = (
? relationship.using.foreign_key_constraint_on ? relationship.using.foreign_key_constraint_on
: [relationship.using.foreign_key_constraint_on]; : [relationship.using.foreign_key_constraint_on];
const matchingFkConstraint = fkConstraints.find( const matchingFkConstraint = suggestedRelationships.find(
fkConstraint => suggestedRelationship =>
areTablesEqual(fromTable, fkConstraint.from.table) && areTablesEqual(fromTable, suggestedRelationship.from.table) &&
isEqual(fromColumns.sort(), fkConstraint.from.column) isEqual(fromColumns.sort(), suggestedRelationship.from.columns)
); );
return { return {
toTable: matchingFkConstraint?.to.table, toTable: matchingFkConstraint?.to.table,
mapping: matchingFkConstraint mapping: matchingFkConstraint
? getKeyValuePair( ? getKeyValuePair(
matchingFkConstraint.from.column, matchingFkConstraint.from.columns,
matchingFkConstraint.to.column matchingFkConstraint.to.columns
) )
: {}, : {},
}; };
@ -68,17 +66,18 @@ const getFkDefinition = (
? [relationship.using.foreign_key_constraint_on.column] ? [relationship.using.foreign_key_constraint_on.column]
: relationship.using.foreign_key_constraint_on.columns; : relationship.using.foreign_key_constraint_on.columns;
const matchingFkConstraint = fkConstraints.find( const matchingFkConstraint = suggestedRelationships.find(
fkConstraint => suggestedRelationship =>
areTablesEqual(toTable, fkConstraint.to.table) && areTablesEqual(toTable, suggestedRelationship.to.table) &&
isEqual(toColumn.sort(), fkConstraint.to.column) isEqual(toColumn.sort(), suggestedRelationship.to.columns)
); );
return { return {
toTable: matchingFkConstraint?.from.table, toTable: matchingFkConstraint?.from.table,
mapping: matchingFkConstraint mapping: matchingFkConstraint
? getKeyValuePair( ? getKeyValuePair(
matchingFkConstraint.to.column, matchingFkConstraint.to.columns,
matchingFkConstraint.from.column matchingFkConstraint.from.columns
) )
: {}, : {},
}; };
@ -110,12 +109,12 @@ export const adaptLocalObjectRelationshipWithFkConstraint = ({
table, table,
dataSourceName, dataSourceName,
relationship, relationship,
fkConstraints, suggestedRelationships,
}: { }: {
table: Table; table: Table;
dataSourceName: string; dataSourceName: string;
relationship: SameTableObjectRelationship | LocalTableObjectRelationship; relationship: SameTableObjectRelationship | LocalTableObjectRelationship;
fkConstraints: TableFkRelationships[]; suggestedRelationships: SuggestedRelationship[];
}): LocalRelationship => { }): LocalRelationship => {
return { return {
name: relationship.name, name: relationship.name,
@ -123,7 +122,7 @@ export const adaptLocalObjectRelationshipWithFkConstraint = ({
fromTable: table, fromTable: table,
relationshipType: 'Object', relationshipType: 'Object',
type: 'localRelationship', type: 'localRelationship',
definition: getFkDefinition(table, relationship, fkConstraints), definition: getFkDefinition(table, relationship, suggestedRelationships),
}; };
}; };
@ -153,12 +152,12 @@ export const adaptLocalArrayRelationshipWithFkConstraint = ({
table, table,
dataSourceName, dataSourceName,
relationship, relationship,
fkConstraints, suggestedRelationships,
}: { }: {
table: Table; table: Table;
dataSourceName: string; dataSourceName: string;
relationship: LocalTableArrayRelationship; relationship: LocalTableArrayRelationship;
fkConstraints: TableFkRelationships[]; suggestedRelationships: SuggestedRelationship[];
}): LocalRelationship => { }): LocalRelationship => {
return { return {
name: relationship.name, name: relationship.name,
@ -166,7 +165,7 @@ export const adaptLocalArrayRelationshipWithFkConstraint = ({
fromTable: table, fromTable: table,
relationshipType: 'Array', relationshipType: 'Array',
type: 'localRelationship', type: 'localRelationship',
definition: getFkDefinition(table, relationship, fkConstraints), definition: getFkDefinition(table, relationship, suggestedRelationships),
}; };
}; };

View File

@ -3,7 +3,6 @@ import {
isManualArrayRelationship, isManualArrayRelationship,
isManualObjectRelationship, isManualObjectRelationship,
isRemoteSchemaRelationship, isRemoteSchemaRelationship,
TableFkRelationships,
} from '../../DataSource'; } from '../../DataSource';
import { MetadataTable } from '../../hasura-metadata-types'; import { MetadataTable } from '../../hasura-metadata-types';
import { import {
@ -11,6 +10,7 @@ import {
Relationship, Relationship,
RemoteDatabaseRelationship, RemoteDatabaseRelationship,
RemoteSchemaRelationship, RemoteSchemaRelationship,
SuggestedRelationship,
} from '../types'; } from '../types';
import { import {
adaptLegacyRemoteSchemaRelationship, adaptLegacyRemoteSchemaRelationship,
@ -25,7 +25,7 @@ import {
export function tableRelationships( export function tableRelationships(
metadataTable: MetadataTable | undefined, metadataTable: MetadataTable | undefined,
dataSourceName: string, dataSourceName: string,
fkConstraints: TableFkRelationships[] | undefined suggestedRelationships: SuggestedRelationship[]
): Relationship[] { ): Relationship[] {
const table = metadataTable?.table; const table = metadataTable?.table;
// adapt local array relationships // adapt local array relationships
@ -43,7 +43,7 @@ export function tableRelationships(
table, table,
dataSourceName, dataSourceName,
relationship, relationship,
fkConstraints: fkConstraints ?? [], suggestedRelationships,
}); });
}); });
@ -61,7 +61,7 @@ export function tableRelationships(
table, table,
dataSourceName, dataSourceName,
relationship, relationship,
fkConstraints: fkConstraints ?? [], suggestedRelationships,
}); });
}); });

View File

@ -1,9 +1,8 @@
import { areTablesEqual } from '../../../../../hasura-metadata-api';
import { tableRelationships } from '../../../../../DatabaseRelationships/utils/tableRelationships'; import { tableRelationships } from '../../../../../DatabaseRelationships/utils/tableRelationships';
import { useTablesFkConstraints } from './useTablesFkConstraints';
import { useTablesWithColumns } from './useTablesWithColumns'; import { useTablesWithColumns } from './useTablesWithColumns';
import { useSources } from '../../../../../MetadataAPI'; import { useSources } from '../../../../../MetadataAPI';
import { Tables } from '../components'; import { Tables } from '../components';
import { useAllSuggestedRelationships } from '../../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
export const usePermissionTables = ({ export const usePermissionTables = ({
dataSourceName, dataSourceName,
@ -14,23 +13,26 @@ export const usePermissionTables = ({
const { data: tables, isLoading: isLoadingTables } = useTablesWithColumns({ const { data: tables, isLoading: isLoadingTables } = useTablesWithColumns({
dataSourceName, dataSourceName,
}); });
const { data: fkConstraints, isLoading: isDALIntrospectionLoading } =
useTablesFkConstraints({ dataSourceName, tables }); const { suggestedRelationships, isLoadingSuggestedRelationships } =
if (isLoadingTables || isDALIntrospectionLoading || isLoadingSources) useAllSuggestedRelationships({
dataSourceName,
isEnabled: true,
omitTracked: false,
});
if (isLoadingTables || isLoadingSuggestedRelationships || isLoadingSources)
return []; return [];
return ( return (
tables?.map(({ metadataTable, columns }) => { tables?.map(({ metadataTable, columns }) => {
const relationships = fkConstraints?.find(({ table }) =>
areTablesEqual(table, metadataTable.table)
)?.relationships;
return { return {
table: metadataTable.table, table: metadataTable.table,
dataSource: sources?.find(source => source.name === dataSourceName), dataSource: sources?.find(source => source.name === dataSourceName),
relationships: tableRelationships( relationships: tableRelationships(
metadataTable, metadataTable,
dataSourceName, dataSourceName,
relationships suggestedRelationships
), ),
columns, columns,
}; };