Skip to content

mergeSchemas with string typedefs drops schema directives #862

@terion-name

Description

@terion-name

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockingPrevents production or dev due to perf, bug, build error, etc..

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions