Skip to content

Commit 6a7e079

Browse files
authored
Merge pull request #271 from gadget-inc/fix-bulk-flat-params
Fix react bulk actions with flat params
2 parents 371d684 + 5c7342f commit 6a7e079

File tree

3 files changed

+161
-122
lines changed

3 files changed

+161
-122
lines changed

packages/react/spec/useAction.spec.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -457,12 +457,10 @@ describe("useAction", () => {
457457
expect(promiseResult.error).toBeFalsy();
458458
});
459459

460-
expect(variables).toMatchInlineSnapshot(`
461-
{
462-
"email": "[email protected]",
463-
"password": "password123!",
464-
}
465-
`);
460+
expect(variables).toEqual({
461+
462+
password: "password123!",
463+
});
466464
});
467465

468466
test("should throw if called without a model api identifier and there is an ambiguous field", async () => {

packages/react/spec/useBulkAction.spec.ts

Lines changed: 152 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -102,31 +102,7 @@ describe("useBulkAction", () => {
102102
expect(result.current[0].error).toBeFalsy();
103103
});
104104

105-
test("can execute a bulk create with params", async () => {
106-
const mockBulkCreate = {
107-
type: "action",
108-
operationName: "bulkCreateWidgets",
109-
namespace: null,
110-
modelApiIdentifier: "widget",
111-
modelSelectionField: "widgets",
112-
isBulk: true,
113-
defaultSelection: {
114-
id: true,
115-
name: true,
116-
},
117-
selectionType: {},
118-
optionsType: {},
119-
schemaType: null,
120-
variablesType: void 0,
121-
variables: {
122-
inputs: {
123-
required: true,
124-
type: "[BulkCreateWidgetsInput!]",
125-
},
126-
},
127-
hasReturnType: false,
128-
} as any;
129-
105+
test("can execute a bulk create with flattened params", async () => {
130106
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
131107
// @ts-ignore waiting for bulk params to be released gadget side
132108
const { result } = renderHook(() => useBulkAction<any, any, any, any>(mockBulkCreate), { wrapper: MockClientWrapper(bulkExampleApi) });
@@ -141,18 +117,20 @@ describe("useBulkAction", () => {
141117
expect(result.current[0].error).toBeFalsy();
142118

143119
expect(mockUrqlClient.executeMutation).toBeCalledTimes(1);
144-
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toMatchInlineSnapshot(`
145-
{
146-
"inputs": [
147-
{
148-
"name": "foo",
120+
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toEqual({
121+
inputs: [
122+
{
123+
widget: {
124+
name: "foo",
149125
},
150-
{
151-
"name": "bar",
126+
},
127+
{
128+
widget: {
129+
name: "bar",
152130
},
153-
],
154-
}
155-
`);
131+
},
132+
],
133+
});
156134

157135
mockUrqlClient.executeMutation.pushResponse("bulkCreateWidgets", {
158136
data: {
@@ -183,30 +161,6 @@ describe("useBulkAction", () => {
183161
});
184162

185163
test("can execute a bulk create with fully qualified params", async () => {
186-
const mockBulkCreate = {
187-
type: "action",
188-
operationName: "bulkCreateWidgets",
189-
namespace: null,
190-
modelApiIdentifier: "widget",
191-
modelSelectionField: "widgets",
192-
isBulk: true,
193-
defaultSelection: {
194-
id: true,
195-
name: true,
196-
},
197-
selectionType: {},
198-
optionsType: {},
199-
schemaType: null,
200-
variablesType: void 0,
201-
variables: {
202-
inputs: {
203-
required: true,
204-
type: "[BulkCreateWidgetsInput!]",
205-
},
206-
},
207-
hasReturnType: false,
208-
} as any;
209-
210164
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
211165
// @ts-ignore waiting for bulk params to be released gadget side
212166
const { result } = renderHook(() => useBulkAction<any, any, any, any>(mockBulkCreate), { wrapper: MockClientWrapper(bulkExampleApi) });
@@ -221,22 +175,20 @@ describe("useBulkAction", () => {
221175
expect(result.current[0].error).toBeFalsy();
222176

223177
expect(mockUrqlClient.executeMutation).toBeCalledTimes(1);
224-
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toMatchInlineSnapshot(`
225-
{
226-
"inputs": [
227-
{
228-
"widget": {
229-
"name": "foo",
230-
},
178+
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toEqual({
179+
inputs: [
180+
{
181+
widget: {
182+
name: "foo",
231183
},
232-
{
233-
"widget": {
234-
"name": "bar",
235-
},
184+
},
185+
{
186+
widget: {
187+
name: "bar",
236188
},
237-
],
238-
}
239-
`);
189+
},
190+
],
191+
});
240192

241193
mockUrqlClient.executeMutation.pushResponse("bulkCreateWidgets", {
242194
data: {
@@ -266,40 +218,79 @@ describe("useBulkAction", () => {
266218
expect(result.current[0].error).toBeFalsy();
267219
});
268220

269-
test("can execute a bulk update with params", async () => {
270-
const mockBulkUpdate = {
271-
type: "action",
272-
operationName: "bulkUpdateWidgets",
273-
namespace: null,
274-
modelApiIdentifier: "widget",
275-
modelSelectionField: "widgets",
276-
isBulk: true,
277-
defaultSelection: {
278-
id: true,
279-
name: true,
280-
},
281-
selectionType: {},
282-
optionsType: {},
283-
schemaType: null,
284-
variablesType: void 0,
285-
variables: {
286-
inputs: {
287-
required: true,
288-
type: "[BulkUpdateWidgetsInput!]",
221+
test("can execute a bulk update with flattened params", async () => {
222+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
223+
// @ts-ignore waiting for bulk params to be released gadget side
224+
const { result } = renderHook(() => useBulkAction<any, any, any, any>(mockBulkUpdate), { wrapper: MockClientWrapper(bulkExampleApi) });
225+
226+
let mutationPromise: any;
227+
act(() => {
228+
mutationPromise = result.current[1]([
229+
{ id: "123", name: "foo" },
230+
{ id: "124", name: "bar" },
231+
]);
232+
});
233+
234+
expect(result.current[0].data).toBeFalsy();
235+
expect(result.current[0].fetching).toBe(true);
236+
expect(result.current[0].error).toBeFalsy();
237+
238+
expect(mockUrqlClient.executeMutation).toBeCalledTimes(1);
239+
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toEqual({
240+
inputs: [
241+
{
242+
id: "123",
243+
widget: {
244+
name: "foo",
245+
},
246+
},
247+
{
248+
id: "124",
249+
widget: {
250+
name: "bar",
251+
},
252+
},
253+
],
254+
});
255+
256+
mockUrqlClient.executeMutation.pushResponse("bulkUpdateWidgets", {
257+
data: {
258+
bulkUpdateWidgets: {
259+
success: true,
260+
widgets: [
261+
{ id: "123", name: "foo" },
262+
{ id: "124", name: "bar" },
263+
],
289264
},
290265
},
291-
hasReturnType: false,
292-
} as any;
266+
stale: false,
267+
hasNext: false,
268+
});
293269

270+
await act(async () => {
271+
const promiseResult = await mutationPromise;
272+
expect(promiseResult.data!.length).toEqual(2);
273+
expect(promiseResult.data![0].id).toEqual("123");
274+
expect(promiseResult.data![1].id).toEqual("124");
275+
});
276+
277+
expect(result.current[0].data!.length).toEqual(2);
278+
expect(result.current[0].data![0].id).toEqual("123");
279+
expect(result.current[0].data![1].id).toEqual("124");
280+
expect(result.current[0].fetching).toBe(false);
281+
expect(result.current[0].error).toBeFalsy();
282+
});
283+
284+
test("can execute a bulk update with fully qualified params", async () => {
294285
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
295286
// @ts-ignore waiting for bulk params to be released gadget side
296287
const { result } = renderHook(() => useBulkAction<any, any, any, any>(mockBulkUpdate), { wrapper: MockClientWrapper(bulkExampleApi) });
297288

298289
let mutationPromise: any;
299290
act(() => {
300291
mutationPromise = result.current[1]([
301-
{ id: "123", name: "foo" },
302-
{ id: "124", name: "bar" },
292+
{ id: "123", widget: { name: "foo" } },
293+
{ id: "124", widget: { name: "bar" } },
303294
]);
304295
});
305296

@@ -308,20 +299,22 @@ describe("useBulkAction", () => {
308299
expect(result.current[0].error).toBeFalsy();
309300

310301
expect(mockUrqlClient.executeMutation).toBeCalledTimes(1);
311-
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toMatchInlineSnapshot(`
312-
{
313-
"inputs": [
314-
{
315-
"id": "123",
316-
"name": "foo",
302+
expect(mockUrqlClient.executeMutation.mock.calls[0][0].variables).toEqual({
303+
inputs: [
304+
{
305+
id: "123",
306+
widget: {
307+
name: "foo",
317308
},
318-
{
319-
"id": "124",
320-
"name": "bar",
309+
},
310+
{
311+
id: "124",
312+
widget: {
313+
name: "bar",
321314
},
322-
],
323-
}
324-
`);
315+
},
316+
],
317+
});
325318

326319
mockUrqlClient.executeMutation.pushResponse("bulkUpdateWidgets", {
327320
data: {
@@ -440,3 +433,53 @@ describe("useBulkAction", () => {
440433
expect(result.current[0]).toBe(beforeObject);
441434
});
442435
});
436+
437+
const mockBulkCreate = {
438+
type: "action",
439+
operationName: "bulkCreateWidgets",
440+
namespace: null,
441+
modelApiIdentifier: "widget",
442+
modelSelectionField: "widgets",
443+
isBulk: true,
444+
defaultSelection: {
445+
id: true,
446+
name: true,
447+
},
448+
selectionType: {},
449+
optionsType: {},
450+
schemaType: null,
451+
variablesType: void 0,
452+
variables: {
453+
inputs: {
454+
required: true,
455+
type: "[BulkCreateWidgetsInput!]",
456+
},
457+
},
458+
acceptsModelInput: true,
459+
hasReturnType: false,
460+
} as any;
461+
462+
const mockBulkUpdate = {
463+
type: "action",
464+
operationName: "bulkUpdateWidgets",
465+
namespace: null,
466+
modelApiIdentifier: "widget",
467+
modelSelectionField: "widgets",
468+
isBulk: true,
469+
defaultSelection: {
470+
id: true,
471+
name: true,
472+
},
473+
selectionType: {},
474+
optionsType: {},
475+
schemaType: null,
476+
variablesType: void 0,
477+
variables: {
478+
inputs: {
479+
required: true,
480+
type: "[BulkUpdateWidgetsInput!]",
481+
},
482+
},
483+
acceptsModelInput: true,
484+
hasReturnType: false,
485+
} as any;

packages/react/src/utils.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -323,14 +323,12 @@ export const disambiguateActionVariables = (
323323
}
324324

325325
let newVariables: Record<string, any>;
326-
const idVariable = Object.entries(action.variables).find(([key, value]) => key === "id" && value.type === "GadgetID");
327326

328-
if (action.acceptsModelInput || action.hasCreateOrUpdateEffect) {
327+
if (action.acceptsModelInput ?? action.hasCreateOrUpdateEffect) {
329328
if (
330-
(action.modelApiIdentifier in variables &&
331-
typeof variables[action.modelApiIdentifier] === "object" &&
332-
variables[action.modelApiIdentifier] !== null) ||
333-
!action.variables[action.modelApiIdentifier]
329+
action.modelApiIdentifier in variables &&
330+
typeof variables[action.modelApiIdentifier] === "object" &&
331+
variables[action.modelApiIdentifier] != null
334332
) {
335333
newVariables = variables;
336334
} else {
@@ -341,7 +339,7 @@ export const disambiguateActionVariables = (
341339
if (action.paramOnlyVariables?.includes(key)) {
342340
newVariables[key] = value;
343341
} else {
344-
if (idVariable && key === idVariable[0]) {
342+
if (key == "id") {
345343
newVariables.id = value;
346344
} else {
347345
newVariables[action.modelApiIdentifier][key] = value;

0 commit comments

Comments
 (0)