-
-
Notifications
You must be signed in to change notification settings - Fork 831
Description
I've faced problem, that my schema directives don't work at all. There are numerous issues about directives in tracker, maybe they all are related to this
I have multiple modular schemas that are merged.
For example a schema:
directive @auth(scopes: [String]) on FIELD | FIELD_DEFINITION
type TypeTranslated implements Node {
id: ID!
createdAt: DateTime!
updatedAt: DateTime!
color: String! @auth(scopes: ["foo"])
title: String!
documents: [Document!]!
}
And another:
directive @relation(service: String!, type: String!, batch: String!, key: String = "id") on FIELD_DEFINITION
type Document implements Node {
id: ID!
responsible: ID! @relation(service: "cc-structure", type: "Unit", batch: "units(where: {id_in: $ids})")
}
Directive resolvers are:
import { GraphQLEnumValue, GraphQLField } from 'graphql';
import { SchemaDirectiveVisitor } from 'graphql-tools';
export default class RelationDirective extends SchemaDirectiveVisitor {
constructor(config: any) {
console.log(config);
super(config);
}
visitFieldDefinition(field: GraphQLField<any, any>) {
const {service, type, batch, key} = this.args;
console.log(JSON.stringify({relation: {
service, type, batch, key: key || 'id'
}}));
field.description = JSON.stringify({relation: {
service, type, batch, key: key || 'id'
}});
}
}
import {SchemaDirectiveVisitor} from "graphql-tools";
import {GraphQLField, GraphQLObjectType} from "graphql";
import {guard} from "../../server";
export default class AuthDirective extends SchemaDirectiveVisitor {
visitObject(object: GraphQLObjectType) {
const fields = object.getFields();
for (let field in fields) {
const f: GraphQLField = fields[field];
f.resolve = guard(f.resolve, this.args.scopes);
}
return object;
}
// remove field if not authentificated
visitFieldDefinition(field: GraphQLField) {
console.log(field.resolve);
field.resolve = guard(field.resolve, this.args.scopes, ()=>null);
return field;
}
}
And nothing is fired.
After long digging I've found the following:
// schemas - array of parsed schemas
console.log(schemas.map(s => s._directives)); // here I see in console my auth and relation directives
this._schemaCache = mergeSchemas({schemas: [...schemas, ...this._overrides], schemaDirectives: {
auth: AuthDirective,
relation: RelationDirective,
}});
console.log(this._schemaCache._directives); // and here they are gone and only default ones left
Had no luck to make this work, a huge stopper :(
UPD
I've used this to test and run it at different points of execution:
SchemaDirectiveVisitor.visitSchemaDirectives(schema, {
relation: class extends SchemaDirectiveVisitor {
visitFieldDefinition(field: GraphQLField<any, any>) {
console.log(field);
}
}
});
Running it over this._schemaCache
from example above results in console.log calls.
BUT!
I further need to collect several merged and processed schemas and to merge them again:
let schemas = builders.map(b => b.buildSchema());
let overrides = [].concat(...builders.map(b => b.getOverrides())).filter(v => !!v);
const merged = mergeSchemas({schemas: [...schemas, ...overrides], schemaDirectives: {
auth: authDirective.default,
relation: relationDirective.default,
}});
And running above visitor over merged
results in nothing.
But there is an important thing: overrides contains string typedefs — I use them to resolve some merging conflicts with graphql-import
. If I remove them — visitor works. So the problem is in merging schema with string typedefs