Skip to content

Commit

Permalink
fix: cannot update array of objects when there is an _ids array present
Browse files Browse the repository at this point in the history
  • Loading branch information
macrozone committed Jul 19, 2022
1 parent 1969e6a commit bc5d645
Show file tree
Hide file tree
Showing 7 changed files with 131 additions and 38 deletions.
18 changes: 12 additions & 6 deletions packages/dataprovider/src/buildVariables/buildData.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { debug } from "console";
import {
IntrospectionInputObjectType,
IntrospectionInputType,
IntrospectionInputTypeRef,
IntrospectionInputValue,
IntrospectionListTypeRef,
IntrospectionNamedTypeRef,
IntrospectionNonNullTypeRef,
IntrospectionTypeRef,
} from "graphql";
import { isString } from "lodash";
import isEqual from "lodash/isEqual";
import isNil from "lodash/isNil";
import isObject from "lodash/isObject";
import { IntrospectionResult } from "../constants/interfaces";
import exhaust from "../utils/exhaust";
import getFinalType from "../utils/getFinalType";
import { sanitizeData } from "../utils/sanitizeData";
import { getSanitizedFieldData } from "./sanitizeData";

enum ModifiersParams {
connect = "connect",
Expand Down Expand Up @@ -381,18 +383,22 @@ export const buildData = (
params: UpdateParams | CreateParams,
introspectionResults: IntrospectionResult,
) => {
console.log("buildData", inputType, params);
if (!inputType) {
return {};
}
const data = sanitizeData(params.data);
const previousData =
"previousData" in params ? sanitizeData(params.previousData) : null;
const data = params.data;
const previousData = "previousData" in params ? params.previousData : null;
return inputType.inputFields.reduce((acc, field) => {
const key = field.name;
const fieldType =
field.type.kind === "NON_NULL" ? field.type.ofType : field.type;
const fieldData = data[key];
const previousFieldData = previousData?.[key] ?? null;
// we have to handle the convenience convention that adds _id(s) to the data
// the sanitize function merges that with other data
const fieldData = getSanitizedFieldData(data, field);
const previousFieldData = previousData
? getSanitizedFieldData(previousData, field)
: null;
// TODO in case the content of the array has changed but not the array itself?
if (
isEqual(fieldData, previousFieldData) ||
Expand Down
67 changes: 61 additions & 6 deletions packages/dataprovider/src/buildVariables/buildVariables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ describe("buildVariables", () => {
testIntrospection = await getTestIntrospectionNexus();
testUserResource = testIntrospection.resources.find(
(r) => r.type.kind === "OBJECT" && r.type.name === "User",
);
)!;
testBlogPostCommentResource = testIntrospection.resources.find(
(r) => r.type.kind === "OBJECT" && r.type.name === "BlogPostComment",
);
)!;
});

describe("GET_LIST", () => {
Expand Down Expand Up @@ -654,7 +654,7 @@ describe("buildVariables", () => {

// one

it("update an entity and change the relation", () => {
it("update an entity and change the relation when objects with new id is passed", () => {
const params = {
data: {
id: "einstein",
Expand All @@ -681,7 +681,34 @@ describe("buildVariables", () => {
});
});

it("update an entity and update also it's related entity", () => {
it("update an entity and change the relation when _id suffixed field is passed", () => {
const params = {
data: {
id: "einstein",
userSocialMedia_id: "newId",
},
previousData: {
userSocialMedia: "oldId",
},
};

expect(
buildVariables(testIntrospection, options)(
testUserResource,
UPDATE,
params,
),
).toEqual<NexusGenArgTypes["Mutation"]["updateOneUser"]>({
where: { id: "einstein" },
data: {
userSocialMedia: {
connect: { id: "newId" },
},
},
});
});

it("update an entity and update also it's related entity when id is the same", () => {
const params = {
data: {
id: "einstein",
Expand Down Expand Up @@ -965,6 +992,34 @@ describe("buildVariables", () => {
});
});

it("update an entity and connects and disconnects entities when _ids array is passed", () => {
const params = {
data: {
id: "einstein",
roles_ids: ["professor", "husband", "human"],
},
previousData: {
roles_ids: ["human", "student"],
},
};

expect(
buildVariables(testIntrospection, options)(
testUserResource,
UPDATE,
params,
),
).toEqual<NexusGenArgTypes["Mutation"]["updateOneUser"]>({
where: { id: "einstein" },
data: {
roles: {
connect: [{ id: "professor" }, { id: "husband" }],
disconnect: [{ id: "student" }],
},
},
});
});

it("update an entity and it's related entities accordingly even with mixed primitive and object ids", () => {
const params = {
data: {
Expand Down Expand Up @@ -1194,7 +1249,7 @@ describe("buildVariables", () => {
(r) =>
r.type.kind === "OBJECT" &&
r.type.name === "SomePublicRecordWithIntId",
);
)!;
expect(
buildVariables(testIntrospection, options)(resource, GET_ONE, params),
).toEqual<NexusGenArgTypes["Query"]["somePublicRecordWithIntId"]>({
Expand All @@ -1209,7 +1264,7 @@ describe("buildVariables", () => {
(r) =>
r.type.kind === "OBJECT" &&
r.type.name === "SomePublicRecordWithIntId",
);
)!;
const result = buildVariables(testIntrospection, options)(
resource,
GET_ONE,
Expand Down
2 changes: 2 additions & 0 deletions packages/dataprovider/src/buildVariables/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export interface UpdateParams {
const buildUpdateVariables =
(introspectionResults: IntrospectionResult, options: OurOptions) =>
(resource: Resource, params: UpdateParams, parentResource?: Resource) => {
console.log("buildUpdateVariables", params, resource);
const inputType = introspectionResults.types.find(
(t) => t.name === `${resource.type.name}UpdateInput`,
) as IntrospectionInputObjectType;
Expand Down Expand Up @@ -133,6 +134,7 @@ const buildCreateVariables =
export const buildVariables =
(introspectionResults: IntrospectionResult, options: OurOptions) =>
(resource: Resource, aorFetchType: FetchType, params: any) => {
console.log("buildVariables", aorFetchType, params, resource);
switch (aorFetchType) {
case GET_LIST: {
return buildGetListVariables(introspectionResults, options)(
Expand Down
46 changes: 46 additions & 0 deletions packages/dataprovider/src/buildVariables/sanitizeData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { IntrospectionInputValue } from "graphql";
import { isObject } from "lodash";

/**
* Due to some implementation details in react-admin, we have to add copies with suffixed keys of certain field data.
*
* the data contains then both the normal version and the _id version (which is just a string or an array of strings).
*
* We cannot override the record, as users might use either the object version or the string version
*/

export const getSanitizedFieldData = (
data: Record<string, any>,
field: IntrospectionInputValue,
) => {
const key = field.name;
const keyWithArraySuffix = `${key}_ids`;

if (data[keyWithArraySuffix]) {
// merge
if (data[key] && Array.isArray(data[key])) {
return data[key].map((entry, index) => {
if (isObject(entry)) {
return {
id: data[keyWithArraySuffix][index],
...entry,
};
}
return data[keyWithArraySuffix][index];
});
}
return data[keyWithArraySuffix];
}

const keyWithIdsSuffix = `${key}_id`;
if (data[keyWithIdsSuffix]) {
if (data[key] && isObject(data[key])) {
return {
id: data[keyWithIdsSuffix],
...data[key],
};
}
return data[keyWithIdsSuffix];
}
return data[key];
};
2 changes: 1 addition & 1 deletion packages/dataprovider/src/buildWhere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
Resource,
} from "./constants/interfaces";
import { OurOptions } from "./types";
import { sanitizeKey } from "./utils/sanitizeData";
import { sanitizeKey } from "./utils/sanitizeKey";

const getStringFilter = (
key: string,
Expand Down
25 changes: 0 additions & 25 deletions packages/dataprovider/src/utils/sanitizeData.ts

This file was deleted.

9 changes: 9 additions & 0 deletions packages/dataprovider/src/utils/sanitizeKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const sanitizeKey = (key: string) => {
if (key.endsWith("_ids")) {
return key.substring(0, key.lastIndexOf("_ids"));
}
if (key.endsWith("_id")) {
return key.substring(0, key.lastIndexOf("_id"));
}
return key;
};

0 comments on commit bc5d645

Please sign in to comment.