mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-15 01:12:56 +03:00
Fix suggested object
relationships are being tracked as array
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/9145 Co-authored-by: Vijay Prasanna <11921040+vijayprasanna13@users.noreply.github.com> GitOrigin-RevId: f4f404ce7fab461ad62138ac7115e95a62bdbb13
This commit is contained in:
parent
ca0f007bc3
commit
d0acdcf414
@ -13,10 +13,7 @@ import { SuggestedRelationshipWithName } from '../../../DatabaseRelationships/co
|
||||
import { RelationshipRow } from './RelationshipRow';
|
||||
import { SuggestedRelationshipTrackModal } from '../../../DatabaseRelationships/components/SuggestedRelationshipTrackModal/SuggestedRelationshipTrackModal';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import {
|
||||
AddSuggestedRelationship,
|
||||
useAllSuggestedRelationships,
|
||||
} from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
|
||||
import { useAllSuggestedRelationships } from '../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
|
||||
import { useCheckRows } from '../../../DatabaseRelationships/hooks/useCheckRows';
|
||||
import { useTrackedRelationships } from './hooks/useTrackedRelationships';
|
||||
|
||||
@ -24,20 +21,6 @@ interface UntrackedRelationshipsProps {
|
||||
dataSourceName: string;
|
||||
}
|
||||
|
||||
const adaptTrackRelationship = (
|
||||
relationship: SuggestedRelationshipWithName
|
||||
): AddSuggestedRelationship => {
|
||||
const isObjectRelationship = !!relationship.from?.constraint_name;
|
||||
return {
|
||||
name: relationship.constraintName,
|
||||
fromColumnNames: relationship.from.columns,
|
||||
toColumnNames: relationship.to.columns,
|
||||
relationshipType: isObjectRelationship ? 'object' : 'array',
|
||||
toTable: isObjectRelationship ? undefined : relationship.to.table,
|
||||
fromTable: relationship.from.table,
|
||||
};
|
||||
};
|
||||
|
||||
export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
|
||||
dataSourceName,
|
||||
}) => {
|
||||
@ -98,9 +81,7 @@ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
|
||||
const onTrackRelationship = async (
|
||||
relationship: SuggestedRelationshipWithName
|
||||
) => {
|
||||
await onAddMultipleSuggestedRelationships([
|
||||
adaptTrackRelationship(relationship),
|
||||
]);
|
||||
await onAddMultipleSuggestedRelationships([relationship]);
|
||||
};
|
||||
|
||||
const [isTrackingSelectedRelationships, setTrackingSelectedRelationships] =
|
||||
@ -112,10 +93,7 @@ export const UntrackedRelationships: React.VFC<UntrackedRelationshipsProps> = ({
|
||||
checkedIds.includes(rel.constraintName)
|
||||
);
|
||||
|
||||
const trackRelationships: AddSuggestedRelationship[] =
|
||||
selectedRelationships.map(adaptTrackRelationship);
|
||||
|
||||
await onAddMultipleSuggestedRelationships(trackRelationships);
|
||||
await onAddMultipleSuggestedRelationships(selectedRelationships);
|
||||
} catch (err) {
|
||||
setTrackingSelectedRelationships(false);
|
||||
}
|
||||
|
@ -0,0 +1,104 @@
|
||||
import { SuggestedRelationshipWithName } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships';
|
||||
import adaptTrackRelationship from './adaptTrackRelationship';
|
||||
|
||||
describe('adaptTrackRelationship', () => {
|
||||
it('returns the object relationship with constrain on the from table', () => {
|
||||
const objectRelationship: SuggestedRelationshipWithName = {
|
||||
type: 'object',
|
||||
from: {
|
||||
table: ['Album'],
|
||||
columns: ['artistId'],
|
||||
constraint_name: 'Album_artistId',
|
||||
},
|
||||
to: {
|
||||
table: ['Artist'],
|
||||
columns: ['id'],
|
||||
},
|
||||
constraintName: 'Album_artistId',
|
||||
};
|
||||
expect(adaptTrackRelationship(objectRelationship)).toEqual({
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'object',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'fromTable',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the object relationship with constrain on the to table', () => {
|
||||
const objectRelationship: SuggestedRelationshipWithName = {
|
||||
type: 'object',
|
||||
from: {
|
||||
table: ['Album'],
|
||||
columns: ['artistId'],
|
||||
},
|
||||
to: {
|
||||
table: ['Artist'],
|
||||
columns: ['id'],
|
||||
constraint_name: 'Album_artistId',
|
||||
},
|
||||
constraintName: 'Album_artistId',
|
||||
};
|
||||
expect(adaptTrackRelationship(objectRelationship)).toEqual({
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'object',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'toTable',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the array relationship with constrain on the from table', () => {
|
||||
const objectRelationship: SuggestedRelationshipWithName = {
|
||||
type: 'array',
|
||||
from: {
|
||||
table: ['Album'],
|
||||
columns: ['artistId'],
|
||||
constraint_name: 'Album_artistId',
|
||||
},
|
||||
to: {
|
||||
table: ['Artist'],
|
||||
columns: ['id'],
|
||||
},
|
||||
constraintName: 'Album_artistId',
|
||||
};
|
||||
expect(adaptTrackRelationship(objectRelationship)).toEqual({
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'array',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'fromTable',
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the array relationship with constrain on the to table', () => {
|
||||
const objectRelationship: SuggestedRelationshipWithName = {
|
||||
type: 'array',
|
||||
from: {
|
||||
table: ['Album'],
|
||||
columns: ['artistId'],
|
||||
},
|
||||
to: {
|
||||
table: ['Artist'],
|
||||
columns: ['id'],
|
||||
constraint_name: 'Album_artistId',
|
||||
},
|
||||
constraintName: 'Album_artistId',
|
||||
};
|
||||
expect(adaptTrackRelationship(objectRelationship)).toEqual({
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'array',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'toTable',
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,20 @@
|
||||
import { AddSuggestedRelationship } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useAllSuggestedRelationships';
|
||||
import { SuggestedRelationshipWithName } from '../../../../DatabaseRelationships/components/SuggestedRelationships/hooks/useSuggestedRelationships';
|
||||
|
||||
const adaptTrackRelationship = (
|
||||
relationship: SuggestedRelationshipWithName
|
||||
): AddSuggestedRelationship => {
|
||||
const isConstraintOnFromTable = !!relationship.from?.constraint_name;
|
||||
const isObjectRelationship = relationship.type === 'object';
|
||||
return {
|
||||
name: relationship.constraintName,
|
||||
fromColumnNames: relationship.from.columns,
|
||||
toColumnNames: relationship.to.columns,
|
||||
relationshipType: isObjectRelationship ? 'object' : 'array',
|
||||
toTable: relationship.to.table,
|
||||
fromTable: relationship.from.table,
|
||||
constraintOn: isConstraintOnFromTable ? 'fromTable' : 'toTable',
|
||||
};
|
||||
};
|
||||
|
||||
export default adaptTrackRelationship;
|
@ -33,14 +33,9 @@ export const SuggestedRelationshipTrackModal: React.VFC<
|
||||
|
||||
const onTrackRelationship = async (relationshipName: string) => {
|
||||
try {
|
||||
const isObjectRelationship = !!relationship.from?.constraint_name;
|
||||
|
||||
await onAddSuggestedRelationship({
|
||||
name: relationshipName,
|
||||
toColumnNames: relationship.to.columns,
|
||||
fromColumnNames: relationship.from.columns,
|
||||
relationshipType: isObjectRelationship ? 'object' : 'array',
|
||||
toTable: isObjectRelationship ? undefined : relationship.to.table,
|
||||
...relationship,
|
||||
constraintName: relationshipName,
|
||||
});
|
||||
|
||||
refetchSuggestedRelationships();
|
||||
|
@ -0,0 +1,59 @@
|
||||
import { getLocalRelationshipPayload } from './getLocalRelationshipPayload';
|
||||
|
||||
describe('getLocalRelationshipPayload', () => {
|
||||
it('returns an object relationship request payload with constrain on the fromTable', () => {
|
||||
expect(
|
||||
getLocalRelationshipPayload({
|
||||
dataSourcePrefix: 'pg',
|
||||
dataSourceName: 'aPostgres',
|
||||
relationship: {
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'object',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'fromTable',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
type: 'pg_create_object_relationship',
|
||||
args: {
|
||||
name: 'Album_artistId',
|
||||
source: 'aPostgres',
|
||||
table: ['Album'],
|
||||
using: {
|
||||
foreign_key_constraint_on: ['artistId'],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an array relationship request payload with constrain on the toTable', () => {
|
||||
expect(
|
||||
getLocalRelationshipPayload({
|
||||
dataSourcePrefix: 'pg',
|
||||
dataSourceName: 'aPostgres',
|
||||
relationship: {
|
||||
fromColumnNames: ['artistId'],
|
||||
toColumnNames: ['id'],
|
||||
fromTable: ['Album'],
|
||||
name: 'Album_artistId',
|
||||
relationshipType: 'array',
|
||||
toTable: ['Artist'],
|
||||
constraintOn: 'toTable',
|
||||
},
|
||||
})
|
||||
).toEqual({
|
||||
type: 'pg_create_array_relationship',
|
||||
args: {
|
||||
name: 'Album_artistId',
|
||||
source: 'aPostgres',
|
||||
table: ['Album'],
|
||||
using: {
|
||||
foreign_key_constraint_on: { columns: ['id'], table: ['Artist'] },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import { TMigration } from '../../../../MetadataAPI';
|
||||
import { AddSuggestedRelationship } from '../hooks/useAllSuggestedRelationships';
|
||||
|
||||
type GetRelationshipPayloadArgs = {
|
||||
dataSourcePrefix: string;
|
||||
dataSourceName: string;
|
||||
relationship: AddSuggestedRelationship;
|
||||
};
|
||||
|
||||
export type LocalRelationshipQuery = {
|
||||
table: unknown;
|
||||
name: string;
|
||||
source: string;
|
||||
using: {
|
||||
foreign_key_constraint_on: string[] | { table: unknown; columns: string[] };
|
||||
};
|
||||
};
|
||||
|
||||
export const getLocalRelationshipPayload = ({
|
||||
dataSourcePrefix,
|
||||
dataSourceName,
|
||||
relationship,
|
||||
}: GetRelationshipPayloadArgs): TMigration<LocalRelationshipQuery>['query'] => {
|
||||
return {
|
||||
type: `${dataSourcePrefix}_create_${relationship.relationshipType}_relationship`,
|
||||
args: {
|
||||
table: relationship.fromTable,
|
||||
name: relationship.name,
|
||||
source: dataSourceName,
|
||||
using: {
|
||||
foreign_key_constraint_on:
|
||||
relationship.constraintOn === 'fromTable'
|
||||
? relationship.fromColumnNames
|
||||
: {
|
||||
table: relationship.toTable,
|
||||
columns: relationship.toColumnNames,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
@ -8,8 +8,12 @@ import { useHttpClient } from '../../../../Network';
|
||||
import {
|
||||
addConstraintName,
|
||||
SuggestedRelationshipsResponse,
|
||||
SuggestedRelationshipWithName,
|
||||
} from './useSuggestedRelationships';
|
||||
import { useMetadataMigration } from '../../../../MetadataAPI';
|
||||
import {
|
||||
allowedMetadataTypes,
|
||||
useMetadataMigration,
|
||||
} from '../../../../MetadataAPI';
|
||||
import {
|
||||
BulkKeepGoingResponse,
|
||||
NamingConvention,
|
||||
@ -18,23 +22,19 @@ import {
|
||||
import { getTrackedRelationshipsCacheKey } from '../../../../Data/TrackResources/components/hooks/useTrackedRelationships';
|
||||
import { hasuraToast } from '../../../../../new-components/Toasts';
|
||||
import { useDriverRelationshipSupport } from '../../../../Data/hooks/useDriverRelationshipSupport';
|
||||
import adaptTrackRelationship from '../../../../Data/TrackResources/components/utils/adaptTrackRelationship';
|
||||
import {
|
||||
getLocalRelationshipPayload,
|
||||
LocalRelationshipQuery,
|
||||
} from '../adapters/getLocalRelationshipPayload';
|
||||
|
||||
type QueriesType =
|
||||
| {
|
||||
type: string;
|
||||
args: {
|
||||
table: unknown;
|
||||
name: string;
|
||||
source: string;
|
||||
using: {
|
||||
foreign_key_constraint_on:
|
||||
| string[]
|
||||
| { table: unknown; columns: string[] };
|
||||
};
|
||||
};
|
||||
type: allowedMetadataTypes;
|
||||
args: LocalRelationshipQuery;
|
||||
}[]
|
||||
| {
|
||||
type: string;
|
||||
type: allowedMetadataTypes;
|
||||
args: {
|
||||
table: unknown;
|
||||
name: string;
|
||||
@ -57,6 +57,7 @@ export type AddSuggestedRelationship = {
|
||||
relationshipType: 'object' | 'array';
|
||||
toTable?: Table;
|
||||
fromTable?: Table;
|
||||
constraintOn: 'fromTable' | 'toTable';
|
||||
};
|
||||
|
||||
type UseSuggestedRelationshipsArgs = {
|
||||
@ -135,8 +136,9 @@ export const useAllSuggestedRelationships = ({
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const onAddMultipleSuggestedRelationships = async (
|
||||
relationships: AddSuggestedRelationship[]
|
||||
suggestedRelationships: SuggestedRelationshipWithName[]
|
||||
) => {
|
||||
const relationships = suggestedRelationships.map(adaptTrackRelationship);
|
||||
let queries: QueriesType = [];
|
||||
|
||||
if (!driverSupportsLocalRelationship && !driverSupportsRemoteRelationship) {
|
||||
@ -148,25 +150,13 @@ export const useAllSuggestedRelationships = ({
|
||||
return;
|
||||
}
|
||||
if (driverSupportsLocalRelationship) {
|
||||
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.fromColumnNames
|
||||
: {
|
||||
table: relationship.toTable,
|
||||
columns: relationship.toColumnNames,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
queries = relationships.map(relationship =>
|
||||
getLocalRelationshipPayload({
|
||||
dataSourcePrefix: dataSourcePrefix || '',
|
||||
dataSourceName,
|
||||
relationship,
|
||||
})
|
||||
);
|
||||
} else if (driverSupportsRemoteRelationship) {
|
||||
queries = relationships.map(relationship => {
|
||||
return {
|
||||
|
@ -17,6 +17,8 @@ import { generateQueryKeys } from '../../../utils/queryClientUtils';
|
||||
import { useMetadataMigration } from '../../../../MetadataAPI';
|
||||
import { useDriverRelationshipSupport } from '../../../../Data/hooks/useDriverRelationshipSupport';
|
||||
import { hasuraToast } from '../../../../../new-components/Toasts/hasuraToast';
|
||||
import adaptTrackRelationship from '../../../../Data/TrackResources/components/utils/adaptTrackRelationship';
|
||||
import { getLocalRelationshipPayload } from '../adapters/getLocalRelationshipPayload';
|
||||
|
||||
type UseSuggestedRelationshipsArgs = {
|
||||
dataSourceName: string;
|
||||
@ -147,15 +149,6 @@ export const removeExistingRelationships = ({
|
||||
return false;
|
||||
});
|
||||
|
||||
type AddSuggestedRelationship = {
|
||||
name: string;
|
||||
fromColumnNames: string[];
|
||||
toColumnNames: string[];
|
||||
relationshipType: 'object' | 'array';
|
||||
toTable?: Table;
|
||||
fromTable?: Table;
|
||||
};
|
||||
|
||||
export const getSuggestedRelationshipsCacheQuery = (
|
||||
dataSourceName: string,
|
||||
table: Table
|
||||
@ -218,14 +211,11 @@ export const useSuggestedRelationships = ({
|
||||
const [isAddingSuggestedRelationship, setAddingSuggestedRelationship] =
|
||||
useState(false);
|
||||
|
||||
const onAddSuggestedRelationship = async ({
|
||||
name,
|
||||
fromColumnNames,
|
||||
toColumnNames,
|
||||
relationshipType,
|
||||
toTable,
|
||||
fromTable,
|
||||
}: AddSuggestedRelationship) => {
|
||||
const onAddSuggestedRelationship = async (
|
||||
relationship: SuggestedRelationshipWithName
|
||||
) => {
|
||||
const addRelationship = adaptTrackRelationship(relationship);
|
||||
|
||||
setAddingSuggestedRelationship(true);
|
||||
|
||||
if (!driverSupportsLocalRelationship && !driverSupportsRemoteRelationship) {
|
||||
@ -239,25 +229,21 @@ export const useSuggestedRelationships = ({
|
||||
|
||||
if (driverSupportsLocalRelationship) {
|
||||
await metadataMutation.mutateAsync({
|
||||
query: {
|
||||
type: `${dataSourcePrefix}_create_${relationshipType}_relationship`,
|
||||
args: {
|
||||
table: fromTable || table,
|
||||
name,
|
||||
source: dataSourceName,
|
||||
using: {
|
||||
foreign_key_constraint_on:
|
||||
relationshipType === 'object'
|
||||
? fromColumnNames
|
||||
: {
|
||||
table: toTable,
|
||||
columns: toColumnNames,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
query: getLocalRelationshipPayload({
|
||||
dataSourcePrefix: dataSourcePrefix || '',
|
||||
dataSourceName,
|
||||
relationship: addRelationship,
|
||||
}),
|
||||
});
|
||||
} else if (driverSupportsRemoteRelationship) {
|
||||
const {
|
||||
fromTable,
|
||||
name,
|
||||
relationshipType,
|
||||
fromColumnNames,
|
||||
toColumnNames,
|
||||
} = addRelationship;
|
||||
|
||||
await metadataMutation.mutateAsync({
|
||||
query: {
|
||||
type: `${dataSourcePrefix}_create_remote_relationship`,
|
||||
|
Loading…
Reference in New Issue
Block a user