Skip to content

Commit 43d038b

Browse files
authored
Use react-query instead of apollo to remove a relationship (#5780)
1 parent 35396e2 commit 43d038b

File tree

6 files changed

+197
-14
lines changed

6 files changed

+197
-14
lines changed

frontend/app/src/entities/groups/ui/object-groups-list.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
import { QSP } from "@/config/qsp";
22
import { GroupDataFromAPI } from "@/entities/groups/api/types";
3-
import { REMOVE_RELATIONSHIP } from "@/entities/nodes/relationships/api/removeRelationship";
3+
import { useRemoveRelationships } from "@/entities/nodes/relationships/domain/remove-relationships/remove-relationships.mutation";
44
import { getObjectDetailsUrl2 } from "@/entities/nodes/utils";
55
import { schemaState } from "@/entities/schema/stores/schema.atom";
66
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
7-
import { useMutation } from "@/shared/api/graphql/useQuery";
87
import { Button } from "@/shared/components/buttons/button-primitive";
98
import ItemGroup from "@/shared/components/layouts/item-group";
109
import ModalDelete from "@/shared/components/modals/modal-delete";
@@ -18,7 +17,7 @@ import { Link } from "react-router";
1817

1918
type ObjectGroupsListProps = {
2019
className?: string;
21-
objectId?: string;
20+
objectId: string;
2221
groups: Array<GroupDataFromAPI>;
2322
};
2423

@@ -37,7 +36,7 @@ export default function ObjectGroupsList({ className, objectId, groups }: Object
3736
}
3837

3938
type ObjectGroupProps = {
40-
objectId?: string;
39+
objectId: string;
4140
group: GroupDataFromAPI;
4241
};
4342

@@ -81,11 +80,24 @@ const ObjectGroupItem = ({ objectId, group }: ObjectGroupProps) => {
8180
};
8281

8382
const RemoveGroupButton = ({ objectId, group }: ObjectGroupProps) => {
84-
const [removeGroup, { loading }] = useMutation(REMOVE_RELATIONSHIP, {
85-
variables: { relationshipName: "member_of_groups" },
86-
onCompleted: () => graphqlClient.refetchQueries({ include: ["GET_GROUPS"] }),
87-
});
8883
const [showDeleteModal, setShowDeleteModal] = useState(false);
84+
const { mutate: removeRelationships, isPending } = useRemoveRelationships();
85+
86+
const handleRemoveGroup = () => {
87+
removeRelationships(
88+
{
89+
objectId,
90+
relationshipName: "member_of_groups",
91+
relationshipIds: [group.id],
92+
},
93+
{
94+
onSuccess: () => {
95+
graphqlClient.refetchQueries({ include: ["GET_GROUPS"] });
96+
setShowDeleteModal(false);
97+
},
98+
}
99+
);
100+
};
89101

90102
return (
91103
<>
@@ -105,12 +117,10 @@ const RemoveGroupButton = ({ objectId, group }: ObjectGroupProps) => {
105117
title="Leave Group"
106118
description={`Are you sure you want to leave group ${group.display_label}?`}
107119
onCancel={() => setShowDeleteModal(false)}
108-
onDelete={() =>
109-
removeGroup({ variables: { objectId, relationshipIds: [{ id: group.id }] } })
110-
}
120+
onDelete={handleRemoveGroup}
111121
open={showDeleteModal}
112122
setOpen={() => setShowDeleteModal(false)}
113-
isLoading={loading}
123+
isLoading={isPending}
114124
/>
115125
</>
116126
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
2+
import { BranchContextParams } from "@/shared/api/types";
3+
import { gql } from "@apollo/client";
4+
5+
export const REMOVE_RELATIONSHIP = gql`
6+
mutation RelationshipRemove(
7+
$objectId: String!
8+
$relationshipName: String!
9+
$relationshipIds: [RelatedNodeInput]
10+
) {
11+
RelationshipRemove(data: { id: $objectId, name: $relationshipName, nodes: $relationshipIds }) {
12+
ok
13+
}
14+
}
15+
`;
16+
17+
export type RemoveRelationshipFromApiParams = BranchContextParams & {
18+
objectId: string;
19+
relationshipName: string;
20+
relationshipIds: Array<{ id: string }>;
21+
};
22+
23+
export const removeRelationshipsFromApi = ({
24+
objectId,
25+
relationshipName,
26+
relationshipIds,
27+
branchName,
28+
}: RemoveRelationshipFromApiParams) => {
29+
return graphqlClient.mutate({
30+
mutation: REMOVE_RELATIONSHIP,
31+
variables: {
32+
objectId,
33+
relationshipName,
34+
relationshipIds,
35+
},
36+
context: { branch: branchName },
37+
});
38+
};
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { useCurrentBranch } from "@/entities/branches/ui/branches-provider";
2+
import {
3+
RemoveRelationshipsParams,
4+
removeRelationships,
5+
} from "@/entities/nodes/relationships/domain/remove-relationships/remove-relationships";
6+
import { queryClient } from "@/shared/api/rest/client";
7+
import { useMutation } from "@tanstack/react-query";
8+
9+
export function useRemoveRelationships() {
10+
const { currentBranch } = useCurrentBranch();
11+
12+
return useMutation({
13+
mutationFn: async ({
14+
objectId,
15+
relationshipName,
16+
relationshipIds,
17+
}: Omit<RemoveRelationshipsParams, "branchName">) => {
18+
await removeRelationships({
19+
objectId,
20+
relationshipName,
21+
relationshipIds,
22+
branchName: currentBranch.name,
23+
});
24+
},
25+
onSuccess: () => {
26+
queryClient.invalidateQueries({
27+
predicate: (query) => query.queryKey.includes("objects"),
28+
});
29+
},
30+
});
31+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { removeRelationshipsFromApi } from "@/entities/nodes/relationships/api/remove-relationships-from-api";
2+
import { beforeEach, describe, expect, it, vi } from "vitest";
3+
import { removeRelationships } from "./remove-relationships";
4+
5+
vi.mock("@/entities/nodes/relationships/api/remove-relationships-from-api");
6+
7+
describe("removeRelationships", () => {
8+
beforeEach(() => {
9+
vi.clearAllMocks();
10+
});
11+
12+
it("should call removeRelationshipsFromApi with correctly transformed relationshipIds", async () => {
13+
// GIVEN
14+
const params = {
15+
branchName: "main",
16+
objectId: "object-1",
17+
relationshipName: "testRelationship",
18+
relationshipIds: ["rel-1", "rel-2"],
19+
};
20+
vi.mocked(removeRelationshipsFromApi).mockResolvedValueOnce(undefined!);
21+
22+
// WHEN
23+
await removeRelationships(params);
24+
25+
// THEN
26+
expect(removeRelationshipsFromApi).toHaveBeenCalledExactlyOnceWith({
27+
branchName: "main",
28+
objectId: "object-1",
29+
relationshipName: "testRelationship",
30+
relationshipIds: [{ id: "rel-1" }, { id: "rel-2" }],
31+
});
32+
});
33+
34+
it("should handle an empty relationshipIds array", async () => {
35+
// GIVEN
36+
const params = {
37+
branchName: "main",
38+
objectId: "object-2",
39+
relationshipName: "emptyRelationship",
40+
relationshipIds: [],
41+
};
42+
vi.mocked(removeRelationshipsFromApi).mockResolvedValueOnce(undefined!);
43+
44+
// WHEN
45+
await removeRelationships(params);
46+
47+
// THEN
48+
expect(removeRelationshipsFromApi).toHaveBeenCalledExactlyOnceWith({
49+
branchName: "main",
50+
objectId: "object-2",
51+
relationshipName: "emptyRelationship",
52+
relationshipIds: [],
53+
});
54+
});
55+
56+
it("should propagate errors thrown by removeRelationshipsFromApi", async () => {
57+
// GIVEN
58+
const params = {
59+
branchName: "main",
60+
objectId: "object-3",
61+
relationshipName: "errorRelationship",
62+
relationshipIds: ["rel-3"],
63+
};
64+
const error = new Error("API error");
65+
vi.mocked(removeRelationshipsFromApi).mockRejectedValueOnce(error);
66+
67+
// WHEN/THEN
68+
await expect(removeRelationships(params)).rejects.toThrow("API error");
69+
70+
expect(removeRelationshipsFromApi).toHaveBeenCalledExactlyOnceWith({
71+
branchName: "main",
72+
objectId: "object-3",
73+
relationshipName: "errorRelationship",
74+
relationshipIds: [{ id: "rel-3" }],
75+
});
76+
});
77+
});
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { removeRelationshipsFromApi } from "@/entities/nodes/relationships/api/remove-relationships-from-api";
2+
import { BranchContextParams } from "@/shared/api/types";
3+
4+
export type RemoveRelationshipsParams = BranchContextParams & {
5+
objectId: string;
6+
relationshipName: string;
7+
relationshipIds: Array<string>;
8+
};
9+
10+
export type RemoveRelationships = (params: RemoveRelationshipsParams) => Promise<void>;
11+
12+
export const removeRelationships: RemoveRelationships = async ({
13+
objectId,
14+
relationshipName,
15+
relationshipIds,
16+
branchName,
17+
}) => {
18+
await removeRelationshipsFromApi({
19+
objectId,
20+
relationshipName,
21+
relationshipIds: relationshipIds.map((id) => ({ id })),
22+
branchName,
23+
});
24+
};

frontend/app/src/shared/api/types.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
export type ContextParams = {
1+
export type BranchContextParams = {
22
branchName: string;
3-
atDate?: Date | null;
43
};
54

5+
export interface ContextParams extends BranchContextParams {
6+
atDate?: Date | null;
7+
}
8+
69
export type PaginationParams = {
710
limit?: number;
811
offset?: number;

0 commit comments

Comments
 (0)