Skip to content

Commit

Permalink
Fixes Weakky#2 for SCALAR Type
Browse files Browse the repository at this point in the history
  • Loading branch information
ivopatty committed May 1, 2019
1 parent 60bc364 commit 0ddf390
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 9 deletions.
44 changes: 41 additions & 3 deletions src/buildVariables.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,8 @@ describe('buildVariables', () => {
author: { connect: { id: 'author1' } },
tags: {
connect: [{ id: 'tags2' }],
disconnect: []
disconnect: [],
update: []
},
title: 'Foo'
}
Expand Down Expand Up @@ -502,7 +503,8 @@ describe('buildVariables', () => {
author: { connect: { id: 'author1' } },
tags: {
connect: [{ id: 'tags2' }],
disconnect: []
disconnect: [],
update: []
},
title: 'Foo'
}
Expand Down Expand Up @@ -536,13 +538,49 @@ describe('buildVariables', () => {
author: { connect: { id: 'author1' } },
tags: {
connect: [],
disconnect: [{id: 'tags3'}]
disconnect: [{id: 'tags3'}],
update: []
},
title: 'Foo'
}
});
});

it('can update nested scalars', () => {
const params = {
data: {
id: 'postId',
tags: [{ id: 'tags1', name: 'test' }, { id: 'tags2' }],
tagsIds: ['tags1', 'tags2'],
author: { id: 'author1' },
title: 'Foo'
},
previousData: {
tags: [{ id: 'tags1', name: 'works' }, { id: 'tags2' }],
tagsIds: ['tags1', 'tags2']
}
};

expect(
buildVariables(introspectionResult as unknown as IntrospectionResult)(
{ type: { name: 'Post' } } as Resource,
UPDATE,
params
)
).toEqual({
where: { id: 'postId' },
data: {
author: { connect: { id: 'author1' } },
tags: {
connect: [],
disconnect: [],
update: [{where: {id: 'tags1'}, data: {name: 'test'}}]
},
title: 'Foo'
}
});

})
});

describe('GET_MANY', () => {
Expand Down
15 changes: 10 additions & 5 deletions src/buildVariables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import isObject from 'lodash/isObject';
import { CREATE, DELETE, GET_LIST, GET_MANY, GET_MANY_REFERENCE, GET_ONE, UPDATE } from 'react-admin';
import { IntrospectionResult, Resource } from './constants/interfaces';

import { PRISMA_CONNECT, PRISMA_CREATE, PRISMA_DISCONNECT } from './constants/mutations';
import { computeFieldsToAddRemoveUpdate } from './utils/computeAddRemoveUpdate';
import {PRISMA_CONNECT, PRISMA_CREATE, PRISMA_DISCONNECT, PRISMA_UPDATE} from './constants/mutations';
import {computedNestedFieldsToUpdate, computeFieldsToAddRemoveUpdate} from './utils/computeAddRemoveUpdate';

import getFinalType from './utils/getFinalType';

Expand Down Expand Up @@ -272,23 +272,28 @@ const buildUpdateVariables = (introspectionResults: IntrospectionResult) => (

if (Array.isArray(params.data[key])) {
//TODO: Make connect, disconnect and update overridable
//TODO: Make updates working
const {
fieldsToAdd,
fieldsToRemove, /* fieldsToUpdate */
fieldsToRemove, fieldsToUpdate
} = computeFieldsToAddRemoveUpdate(
params.previousData[`${key}Ids`],
params.data[`${key}Ids`],
);

const updatedFields: any[] = computedNestedFieldsToUpdate(
fieldsToUpdate,
params.previousData[key],
params.data[key]
);

return {
...acc,
data: {
...acc.data,
[key]: {
[PRISMA_CONNECT]: fieldsToAdd,
[PRISMA_DISCONNECT]: fieldsToRemove,
//[PRISMA_UPDATE]: fieldsToUpdate
[PRISMA_UPDATE]: updatedFields
},
},
};
Expand Down
72 changes: 71 additions & 1 deletion src/utils/computeAddRemoveUpdate.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import difference from 'lodash/difference';

import _ from 'lodash'
type ID = string;
interface IDObject {
id: string
}

const formatId = (id: ID) => ({ id });

Expand All @@ -21,3 +24,70 @@ export const computeFieldsToAddRemoveUpdate = (oldIds: ID[], newIds: ID[]) => ({
fieldsToRemove: computeFieldsToRemove(oldIds, newIds),
fieldsToUpdate: computeFieldsToUpdate(oldIds, newIds)
});

/**
* When updating a relation also update the nested fields on the relation.
*
* Calculates the difference between the previousData passed from ReactAdmin
* and the fields changed by the user and only sends the changed values
* back to the GraphQL API.
*
* @param idList A List of IDs of values that may have been changed
* @param previousData The previous data retrieved before editing
* @param newData The data containing changes from the user
*/
export const computedNestedFieldsToUpdate =
(idList: IDObject[], previousData: any, newData: any): any => {
const ids = idList.map(obj => obj.id);
return ids.map((id) => ({
data: objectDifferentiation(
objectFinder(previousData, id),
objectFinder(newData, id)),
where: {id}}))
.filter(hasData)
};

/**
* Calculate the difference between two objects.
*
* Based on the left object determine what fields have changed on the
* right side object. We do not check for the ID value as these are
* always the same so they're handled by the logic here.
*
* @param left Object for comparison to
* @param right The object to get the difference from
*/
const objectDifferentiation = (left: any, right: any): object => {
// return if the objects are identical
if(left === right) return {};
const output = _.mapValues(left, (data, name) => {
if(right[name] === data) return undefined;
// TODO: Handle possibility for deep nesting
if(data.constructor === Object) return undefined;
if(data.constructor === Array) return undefined;
else return right[name]
});
Object.keys(output).forEach(key => output[key] === undefined && delete output[key])
return output
};

/**
* Find an object in an array of object by its ID value.
*
* Does assume the values inserted here posses an ID object in their body.
*
* @param array Array of objects to find the specified value in
* @param idValue The ID to search for
*/
function objectFinder(array: object[], idValue: string) {
return array.find((obj: any) => obj['id'] && obj['id'] === idValue)
}

/**
* Checks for an empty object.
*
* @param object The object to check against.
*/
function hasData(object: {data: object, where: {id: string}}){
return Object.keys(object.data).length !== 0
}

0 comments on commit 0ddf390

Please sign in to comment.