Skip to content

Commit

Permalink
Mutations for poly associations #1
Browse files Browse the repository at this point in the history
  • Loading branch information
bradleyboy committed Jan 10, 2018
1 parent fd9b416 commit af5f7a6
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 6 deletions.
60 changes: 59 additions & 1 deletion src/__tests__/arguments.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
makeCreateArgs,
makeUpdateArgs,
makeDeleteArgs,
makePolyArgs,
} from '../builders/arguments';
import { GraphQLString, GraphQLNonNull, GraphQLInt } from 'graphql';

Expand All @@ -17,7 +18,26 @@ const db = new Sequelize({
});

const model = db.define(
'model',
'posts',
{
id: {
type: INTEGER,
primaryKey: true,
},
title: {
type: TEXT,
allowNull: true,
},
userId: {
type: INTEGER,
allowNull: false,
},
},
{ timestamps: false }
);

const model2 = db.define(
'categories',
{
id: {
type: INTEGER,
Expand Down Expand Up @@ -64,4 +84,42 @@ describe('getPkField', () => {
id: { type: new GraphQLNonNull(GraphQLInt) },
});
});

it('makePolyArgs', () => {
const args = makePolyArgs(model, model2);
expect(args).toEqual({
id: { type: new GraphQLNonNull(GraphQLInt) },
categoryId: { type: new GraphQLNonNull(GraphQLInt) },
});
});

it('makePolyArgs with non-equal keys', () => {
const posts = db.define(
'posts',
{
postId: {
type: INTEGER,
primaryKey: true,
},
},
{ timestamps: false }
);

const categories = db.define(
'categories',
{
categoryId: {
type: INTEGER,
primaryKey: true,
},
},
{ timestamps: false }
);

const args = makePolyArgs(posts, categories);
expect(args).toEqual({
postId: { type: new GraphQLNonNull(GraphQLInt) },
categoryId: { type: new GraphQLNonNull(GraphQLInt) },
});
});
});
4 changes: 4 additions & 0 deletions src/__tests__/associations.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ describe('join associations', () => {
options: {
through: 'post_user',
foreignKey: 'post_id',
timestamps: false,
},
},
{
Expand All @@ -100,6 +101,7 @@ describe('join associations', () => {
options: {
through: 'post_user',
foreignKey: 'user_id',
timestamps: false,
},
},
]);
Expand Down Expand Up @@ -129,6 +131,7 @@ describe('join associations', () => {
options: {
through: 'post_user',
foreignKey: 'post_id',
timestamps: false,
},
},
{
Expand All @@ -138,6 +141,7 @@ describe('join associations', () => {
options: {
through: 'post_user',
foreignKey: 'user_id',
timestamps: false,
},
},
]);
Expand Down
29 changes: 28 additions & 1 deletion src/builders/arguments.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { attributeFields } from 'graphql-sequelize';

import { singular } from 'pluralize';
import { GraphQLBoolean, GraphQLNonNull } from 'graphql';
import camelcase from 'camelcase';

export const getPkFieldKey = model => {
return Object.keys(model.attributes).find(key => {
Expand Down Expand Up @@ -39,3 +40,29 @@ export const makeDeleteArgs = model => {

return { [pk]: fields[pk] };
};

export const getPolyKeys = (model, otherModel) => {
const key = getPkFieldKey(model);
const otherKey = getPkFieldKey(otherModel);

if (otherKey === key) {
return [
key,
otherKey,
camelcase(`${singular(otherModel.name)}_${otherKey}`),
];
}

return [key, otherKey, otherKey];
};

export const makePolyArgs = (model, otherModel) => {
const [key, otherKey, otherKeyFormatted] = getPolyKeys(model, otherModel);
const fields = attributeFields(model);
const otherFields = attributeFields(otherModel);

return {
[key]: fields[key],
[otherKeyFormatted]: otherFields[otherKey],
};
};
2 changes: 2 additions & 0 deletions src/builders/associations.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ const formJoinTableAssociations = (a, b, aKey, bKey, table) => {
options: {
through: table,
foreignKey: aKey,
timestamps: false,
},
},
{
Expand All @@ -21,6 +22,7 @@ const formJoinTableAssociations = (a, b, aKey, bKey, table) => {
options: {
through: table,
foreignKey: bKey,
timestamps: false,
},
},
];
Expand Down
40 changes: 37 additions & 3 deletions src/builders/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ import {
findModelKey,
formatFieldName,
formatTypeName,
pascalCase,
} from '../utils';
import { joinTableAssociations, tableAssociations } from './associations';
import {
makeCreateArgs,
makeUpdateArgs,
makeDeleteArgs,
getPkFieldKey,
makePolyArgs,
getPolyKeys,
} from './arguments';

const deletedType = new GraphQLObjectType({
name: 'DeletedStatus',
const GenericResponseType = new GraphQLObjectType({
name: 'GenericResponse',
fields: {
success: { type: GraphQLBoolean },
},
Expand Down Expand Up @@ -202,7 +205,7 @@ const build = db => {
};

mutations[`delete${type}`] = {
type: deletedType,
type: GenericResponseType,
args: makeDeleteArgs(model),
resolve: async (obj, values, info) => {
const thing = await model.findOne({
Expand All @@ -216,6 +219,37 @@ const build = db => {
};
},
};

fieldAssociations.belongsToMany.forEach(sides => {
const [other] = sides.filter(side => side !== model.name);
const nameBits = [formatTypeName(model.name), formatTypeName(other)];

['add', 'remove'].forEach(prefix => {
const connector = prefix === 'add' ? 'To' : 'From';
const name = `${prefix}${nameBits.join(connector)}`;
mutations[name] = {
type: GenericResponseType,
args: makePolyArgs(model, models[other]),
resolve: async (obj, values, info) => {
const key = getPkFieldKey(model);
const [, , otherArgumentKey] = getPolyKeys(model, models[other]);

const thingOne = await model.findById(values[key]);
const thingTwo = await models[other].findById(
values[otherArgumentKey]
);

const method = `${prefix}${pascalCase(singular(other))}`;

await thingOne[method](thingTwo);

return {
success: true,
};
},
};
});
});
});

const query = new GraphQLObjectType({
Expand Down
2 changes: 1 addition & 1 deletion src/utils/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const formatTypeName = name => {
return pascalCase(singular(name));
};

const pascalCase = string => {
export const pascalCase = string => {
const cameled = camelcase(string);
return cameled.substr(0, 1).toUpperCase() + cameled.substr(1);
};
Expand Down

0 comments on commit af5f7a6

Please sign in to comment.