Skip to content

Commit

Permalink
feat(mutation): support append to arrays in update mutation (with fie…
Browse files Browse the repository at this point in the history
…ldName_add input field)
  • Loading branch information
Andras Toth committed Dec 14, 2015
1 parent b8662a3 commit 6aa5820
Show file tree
Hide file tree
Showing 6 changed files with 202 additions and 27 deletions.
102 changes: 102 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,9 +512,13 @@ mutation AddUser {
age: Float,
createdAt: Date,
friends: [ID],
friends_add: [ID],
nums: [Float],
nums_add: [Float],
bools: [Boolean],
bools_add: [Boolean],
strings: [String],
strings_add: [String],
removed: Boolean,
id: ID!,
clientMutationId: String!
Expand Down Expand Up @@ -545,6 +549,104 @@ mutation UpdateUser {
}
```

Append to array (`fieldName_add`):

```
mutation UpdateUser {
updateUser(input: {clientMutationId: "1", id: "VXNlcjo1NjI2NDE1MjU2NWQ2Y2E3NGUzNzc4NDM=", friends_add: ["VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYjQ=", "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYjk="]}) {
changedUser {
id
friends {
count
edges {
node {
name
id
}
}
}
}
}
}
```
```json
{
"data": {
"updateUser": {
"clientMutationId": "1",
"changedUser": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYTM=",
"friends": {
"count": 10,
"edges": [
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkOWI=",
"name": "User0"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkOWM=",
"name": "User1"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkOWQ=",
"name": "User2"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkOWU=",
"name": "User3"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkOWY=",
"name": "User4"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYTA=",
"name": "User5"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYTE=",
"name": "User6"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYTI=",
"name": "User7"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYjQ=",
"name": "User25"
}
},
{
"node": {
"id": "VXNlcjo1NjZlYmI1MWRmZGRmYjYyYmFjNTRkYjk=",
"name": "User30"
}
}
]
}
}
}
}
}
```

##### _Delete mutation_

* arguments: `input: deleteUserInput!{
Expand Down
3 changes: 2 additions & 1 deletion example/index.js
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
require('babel/register');
require('babel-register');
require('babel-polyfill');
require('./app');
8 changes: 4 additions & 4 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
"start": "node index.js"
},
"dependencies": {
"@risingstack/graffiti": "^2.0.0",
"@risingstack/graffiti": "^2.1.1",
"@risingstack/graffiti-mongoose": "../",
"babel-runtime": "^5.8.20",
"koa": "^1.1.0"
"babel-runtime": "^6.3.13",
"koa": "^1.1.2"
},
"devDependencies": {
"babel": "^5.8.21"
"babel": "^6.3.13"
}
}
65 changes: 60 additions & 5 deletions src/e2e.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ describe('e2e', () => {

describe('with fragments', () => {
it('should support fragments', async function Test() {
// FIXME it fails in node {}
const result = await graphql(schema, `
query GetUser {
user(id: "${user2._id}") {
Expand Down Expand Up @@ -184,15 +183,15 @@ describe('e2e', () => {

describe('plural query', () => {
it('should get data from database and filter by number', async function Test() {
const result = await graphql(schema, `{
let result = await graphql(schema, `{
users(age: 28) {
_id
name
age
}
}`);

expect(result.data.users).to.deep.include.members([
const expected = [
{
_id: user1._id.toString(),
name: 'Foo',
Expand All @@ -202,7 +201,29 @@ describe('e2e', () => {
name: 'Bar',
age: 28
}
]);
];

expect(result.data.users).to.deep.include.members(expected);

result = await graphql(schema, `{
users(id: ["${user1._id.toString()}", "${user2._id.toString()}"]) {
_id
name
age
}
}`);

expect(result.data.users).to.deep.include.members(expected);

result = await graphql(schema, `{
users(ids: ["${user1._id.toString()}", "${user2._id.toString()}"]) {
_id
name
age
}
}`);

expect(result.data.users).to.deep.include.members(expected);
});

it('should get data from database and filter by array of _id(s)', async function Test() {
Expand Down Expand Up @@ -365,17 +386,51 @@ describe('e2e', () => {
result = await graphql(schema, `
mutation updateUserMutation {
updateUser(input: {id: "${id}", name: "Updated Test User", clientMutationId: "2"}) {
clientMutationId
changedUser {
name
friends {
count
}
}
}
}
`);
expect(result).to.containSubset({
data: {
updateUser: {
clientMutationId: '2',
changedUser: {
name: 'Updated Test User',
friends: {
count: 0
}
}
}
}
});

result = await graphql(schema, `
mutation updateUserMutation {
updateUser(input: {id: "${id}", friends_add: ["${id}"], clientMutationId: "3"}) {
clientMutationId
changedUser {
name
friends {
count
}
}
}
}
`);
expect(result).to.containSubset({
data: {
updateUser: {
clientMutationId: '3',
changedUser: {
name: 'Updated Test User'
friends: {
count: 1
}
}
}
}
Expand Down
39 changes: 22 additions & 17 deletions src/query/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ import viewer from '../model/viewer';

function processId({id, _id = id}) {
// global or mongo id
if (isString(_id) && _id.length === 40 && _id.endsWith('=')) {
return fromGlobalId(_id).id;
if (isString(_id) && !/^[a-fA-F0-9]{24}$/.test(_id)) {
const {type, id} = fromGlobalId(_id);
if (type) {
return id;
}
}

return _id;
Expand All @@ -15,7 +18,6 @@ function processId({id, _id = id}) {
function getCount(Collection, selector) {
if (selector && (isArray(selector.id) || isArray(selector._id))) {
const {id, _id = id} = selector;
delete selector._id;
delete selector.id;
selector._id = {
$in: _id.map((id) => processId({id}))
Expand Down Expand Up @@ -62,17 +64,23 @@ function addOne(Collection, args) {
});
}

function updateOne(Collection, args, info) {
const _id = processId(args);
delete args.id;
delete args._id;
function updateOne(Collection, {id, _id, ...args}, info) {
_id = processId({id, _id});

forEach(args, (arg, key) => {
if (isArray(arg)) {
args[key] = arg.map((id) => processId({id}));
} else {
args[key] = processId({id: arg});
}

if (key.endsWith('_add')) {
const values = args[key];
args.$push = {
[key.slice(0, -4)]: {$each: values}
};
delete args[key];
}
});

return Collection.update({_id}, args).then((res) => {
Expand All @@ -86,8 +94,7 @@ function updateOne(Collection, args, info) {

function deleteOne(Collection, args) {
const _id = processId(args);
delete args.id;
delete args._id;

return Collection.remove({_id}).then(({result}) => {
return {
id: toGlobalId(Collection.modelName, _id),
Expand All @@ -99,7 +106,6 @@ function deleteOne(Collection, args) {
function getList(Collection, selector, options = {}, info = null) {
if (selector && (isArray(selector.id) || isArray(selector._id))) {
const {id, _id = id} = selector;
delete selector._id;
delete selector.id;
selector._id = {
$in: _id.map((id) => processId({id}))
Expand Down Expand Up @@ -129,7 +135,7 @@ function getOneResolver(graffitiModel) {
}

function getAddOneMutateHandler(graffitiModel) {
return (args) => {
return ({clientMutationId, ...args}) => { // eslint-disable-line
const Collection = graffitiModel.model;
if (Collection) {
return addOne(Collection, args);
Expand All @@ -140,7 +146,7 @@ function getAddOneMutateHandler(graffitiModel) {
}

function getUpdateOneMutateHandler(graffitiModel) {
return (args) => {
return ({clientMutationId, ...args}) => { // eslint-disable-line
const Collection = graffitiModel.model;
if (Collection) {
return updateOne(Collection, args);
Expand All @@ -151,7 +157,7 @@ function getUpdateOneMutateHandler(graffitiModel) {
}

function getDeleteOneMutateHandler(graffitiModel) {
return (args) => {
return ({clientMutationId, ...args}) => { // eslint-disable-line
const Collection = graffitiModel.model;
if (Collection) {
return deleteOne(Collection, args);
Expand All @@ -162,10 +168,9 @@ function getDeleteOneMutateHandler(graffitiModel) {
}

function getListResolver(graffitiModel) {
return (root, args, info) => {
if (args && args.ids) {
args.id = args.ids;
delete args.ids;
return (root, {ids, ...args} = {}, info) => {
if (ids) {
args.id = ids;
}

const Collection = graffitiModel.model;
Expand Down
12 changes: 12 additions & 0 deletions src/schema/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,17 @@ function getMutationField(graffitiModel, type, viewer, hooks = {}) {
return inputFields;
}, {});

const updateInputFields = reduce(fields, (inputFields, field) => {
if (field.type instanceof GraphQLObjectType && field.type.name.endsWith('Connection')) {
inputFields[`${field.name}_add`] = {
name: field.name,
type: new GraphQLList(GraphQLID)
};
}

return inputFields;
}, {});

const changedName = `changed${name}`;
const edgeName = `${changedName}Edge`;
const nodeName = `${changedName}Node`;
Expand Down Expand Up @@ -161,6 +172,7 @@ function getMutationField(graffitiModel, type, viewer, hooks = {}) {
name: updateName,
inputFields: {
...inputFields,
...updateInputFields,
id: idField
},
outputFields: {
Expand Down

0 comments on commit 6aa5820

Please sign in to comment.