Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
170 changes: 88 additions & 82 deletions LinkDirective.js
Original file line number Diff line number Diff line change
@@ -1,100 +1,106 @@
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { GraphQLList, GraphQLObjectType, GraphQLNonNull } from 'graphql/type';
import { mapSchema, getDirectives } from '@graphql-tools/utils';
import { GraphQLList, GraphQLObjectType, GraphQLNonNull } from 'graphql';
import { Mongo } from 'meteor/mongo';
import { setupMongoDirective } from './MongoDirective';

export default class LinkDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field, details) {
const { objectType } = details;
const { args } = this;
export default function linkDirectiveTransformer(schema) {
return mapSchema(schema, {
[mapSchema.MAP_FIELD_DEFINITION]: (field, fieldName, typeName) => {
const directives = getDirectives(schema, field);
const linkDirective = directives.link;

if (linkDirective) {
const objectType = schema.getType(typeName);

if (!objectType._mongoCollectionName) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set the link: ${fieldName} but your object type does not have @mongo directive set-up`
);
}

if (!objectType._mongoCollectionName) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set the link: ${
field.name
} but your object type does not have @mongo directive set-up`
);
}
const isArrayField = field.type instanceof GraphQLList;
let referencedType;

const isArrayField = field.type instanceof GraphQLList;
let referencedType;
if (isArrayField) {
referencedType = field.type.ofType;
} else {
referencedType = field.type;
}

if (isArrayField) {
referencedType = field.type.ofType;
} else {
referencedType = field.type;
}
if (referencedType instanceof GraphQLNonNull) {
referencedType = referencedType.ofType;
} else {
if (!(referencedType instanceof GraphQLObjectType)) {
throw new Meteor.Error(
'invalid-type',
`You are trying to attach a link on a invalid type. @link directive only works with GraphQLObjectType `
);
}
}

if (referencedType instanceof GraphQLNonNull) {
referencedType = referencedType.ofType;
} else {
if (!(referencedType instanceof GraphQLObjectType)) {
throw new Meteor.Error(
'invalid-type',
`You are trying to attach a link on a invalid type. @link directive only works with GraphQLObjectType `
);
}
}
let referencedCollectionName = referencedType._mongoCollectionName;
if (!referencedCollectionName) {
const objectNodeDirectives = referencedType.astNode.directives;
const mongoDirective = objectNodeDirectives.find(directive => {
return directive.name.value === 'mongo';
});

let referencedCollectionName = referencedType._mongoCollectionName;
if (!referencedCollectionName) {
const objectNodeDirectives = referencedType.astNode.directives;
const mongoDirective = objectNodeDirectives.find(directive => {
return directive.name.value === 'mongo';
});
if (mongoDirective) {
const nameArgument = mongoDirective.arguments.find(
argument => argument.name.value === 'name'
);

if (mongoDirective) {
const nameArgument = mongoDirective.arguments.find(
argument => argument.name.value === 'name'
);
setupMongoDirective(referencedType, {
name: nameArgument.value.value,
});

setupMongoDirective(referencedType, {
name: nameArgument.value.value,
});
referencedCollectionName = nameArgument.value.value;
} else {
throw new Meteor.Error(
'invalid-collection',
`The referenced type does not have a collection setup using @mongo directive`
);
}
}

referencedCollectionName = nameArgument.value.value;
} else {
throw new Meteor.Error(
'invalid-collection',
`The referenced type does not have a collection setup using @mongo directive`
);
}
}
const thisCollectionName = objectType._mongoCollectionName;

const thisCollectionName = objectType._mongoCollectionName;
const referencedCollection = Mongo.Collection.get(referencedCollectionName);
const thisCollection = Mongo.Collection.get(thisCollectionName);

const referencedCollection = Mongo.Collection.get(referencedCollectionName);
const thisCollection = Mongo.Collection.get(thisCollectionName);
let config = {};
if (linkDirective.to) {
config = Object.assign({}, linkDirective);
config.inversedBy = linkDirective.to;
delete config.to;
} else {
if (linkDirective.field) {
config = Object.assign(
{
type: isArrayField ? 'many' : 'one',
field: linkDirective.field,
index: true,
},
linkDirective
);
} else {
throw new Meteor.Error(
`invalid-args`,
`You have provided invalid arguments for this link in ${thisCollectionName}. The "field" property is missing.`
);
}
}

let config = {};
if (args.to) {
config = Object.assign({}, args);
config.inversedBy = args.to;
delete config.to;
} else {
if (args.field) {
config = Object.assign(
{
type: isArrayField ? 'many' : 'one',
field: args.field,
index: true,
thisCollection.addLinks({
[fieldName]: {
collection: referencedCollection,
...config,
},
args
);
} else {
throw new Meteor.Error(
`invalid-args`,
`You have provided invalid arguments for this link in ${thisCollectionName}. The "field" property is missing.`
);
});
}

return field;
}

thisCollection.addLinks({
[field.name]: {
collection: referencedCollection,
...config,
},
});
}
});
}
75 changes: 40 additions & 35 deletions MapToDirective.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,52 @@
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { GraphQLScalarType, GraphQLObjectType } from 'graphql/type';
import { mapSchema, getDirectives } from '@graphql-tools/utils';
import { GraphQLScalarType } from 'graphql';
import { Mongo } from 'meteor/mongo';

function resolve(path, obj) {
return path.split('.').reduce(function(prev, curr) {
return prev ? prev[curr] : undefined;
}, obj || self);
}
export default class MapToDirective extends SchemaDirectiveVisitor {
visitFieldDefinition(field, details) {
const { objectType } = details;
const { args } = this;

if (!objectType._mongoCollectionName) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set mapTo: ${
field.name
} but your object type does not have @mongo directive set-up`
);
}
export default function mapToDirectiveTransformer(schema) {
return mapSchema(schema, {
[mapSchema.MAP_FIELD_DEFINITION]: (field, fieldName, typeName) => {
const directives = getDirectives(schema, field);
const mapDirective = directives.map;

if (mapDirective && mapDirective.to) {
const objectType = schema.getType(typeName);

if (!objectType._mongoCollectionName) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set mapTo: ${fieldName} but your object type does not have @mongo directive set-up`
);
}

const isScalar = field.type instanceof GraphQLScalarType;
if (!isScalar) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set the mapTo directive on a non-scalar on field ${
field.name
}`
);
}
const isScalar = field.type instanceof GraphQLScalarType;
if (!isScalar) {
throw new Meteor.Error(
'collection-not-found',
`You are trying to set the mapTo directive on a non-scalar on field ${fieldName}`
);
}

const collection = Mongo.Collection.get(objectType._mongoCollectionName);
const collection = Mongo.Collection.get(objectType._mongoCollectionName);

collection.addReducers({
[field.name]: {
body: {
[args.to]: 1,
},
reduce(obj) {
return resolve(args.to, obj);
},
},
});
}
collection.addReducers({
[fieldName]: {
body: {
[mapDirective.to]: 1,
},
reduce(obj) {
return resolve(mapDirective.to, obj);
},
},
});
}

return field;
}
});
}
27 changes: 13 additions & 14 deletions MongoDirective.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { mapSchema, getDirectives } from '@graphql-tools/utils';
import { Mongo } from 'meteor/mongo';

export default class MongoDirective extends SchemaDirectiveVisitor {
/**
* @param {GraphQLObjectType} type
*/
visitObject(type) {
if (type._mongoCollectionName) {
// it has already been setup by a link directive somewhere
return;
export default function mongoDirectiveTransformer(schema) {
return mapSchema(schema, {
[mapSchema.MAP_OBJECT_TYPE]: (type) => {
const directives = getDirectives(schema, type);
const mongoDirective = directives.mongo;

if (mongoDirective && mongoDirective.name) {
setupMongoDirective(type, { name: mongoDirective.name });
}

return type;
}

setupMongoDirective(type, this.args);
}

visitFieldDefinition() {}
});
}

export function setupMongoDirective(type, args) {
Expand Down
22 changes: 11 additions & 11 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import directiveDefinitions from './directiveDefinitions';
import LinkDirective from './LinkDirective';
import MapToDirective from './MapToDirective';
import MongoDirective from './MongoDirective';
import linkDirectiveTransformer from './LinkDirective';
import mapToDirectiveTransformer from './MapToDirective';
import mongoDirectiveTransformer from './MongoDirective';

const directives = {
mongo: MongoDirective,
link: LinkDirective,
map: MapToDirective,
const transformers = {
mongo: mongoDirectiveTransformer,
link: linkDirectiveTransformer,
map: mapToDirectiveTransformer,
};

export {
directives,
transformers,
directiveDefinitions,
LinkDirective,
MapToDirective,
MongoDirective,
linkDirectiveTransformer,
mapToDirectiveTransformer,
mongoDirectiveTransformer,
};
6 changes: 5 additions & 1 deletion package.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ Package.describe({
documentation: 'README.md',
});

// @graphql-tools/utils is provided as a peer dependency from the main package.json
// This ensures all packages use the same GraphQL-related packages to avoid
// "Cannot use GraphQLSchema from another module or realm" errors

Package.onUse(function(api) {
api.versionsFrom('1.3');
api.versionsFrom(['1.3', '3.0']);
api.use('ecmascript');
api.mainModule('index.js', 'server');
});
Expand Down