Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Alpha.3 - schemaDirectives not in use #349

Closed
lirbank opened this issue Jul 21, 2021 · 9 comments · Fixed by #356
Closed

Alpha.3 - schemaDirectives not in use #349

lirbank opened this issue Jul 21, 2021 · 9 comments · Fixed by #356
Labels
bug Something isn't working

Comments

@lirbank
Copy link

lirbank commented Jul 21, 2021

Per https://neo4j.com/docs/graphql-manual/2.0/api-reference/ and https://neo4j.com/docs/graphql-manual/current/api-reference/ one may provide custom directives via the schemaDirectives option to the constructor.

This used to work up until 2.0.0-alpha.2, but upgrading to 2.0.0-alpha.3 it seems the custom directives provided are ignored.

System:

"@neo4j/graphql": "2.0.0-alpha.3",

@lirbank lirbank added bug Something isn't working inbox labels Jul 21, 2021
@darrellwarde
Copy link
Contributor

Hey @lirbank, we actually have an integration for this (https://github.com/neo4j/graphql/blob/master/packages/graphql/tests/integration/custom-directives.int.test.ts#L39) which passed when we did the release, and is still passing now.

Could you give any further details about your specific use case? Ideally something easily reproducible in an integration test!

@lirbank
Copy link
Author

lirbank commented Jul 22, 2021

Hi @darrellwarde, thanks for the reference. That test passes in our setup as well. However directives doesn't seem to work in mutations - at least not at the top level.

This test passes in alpha 2, but not in alpha 3 or alpha 4:

import neo4j from 'neo4j-driver';
import { Neo4jGraphQL } from '@neo4j/graphql';
import { SchemaDirectiveVisitor } from '@graphql-tools/utils';
import { graphql } from 'graphql';

type Field = Parameters<SchemaDirectiveVisitor['visitFieldDefinition']>[0];
type Details = Parameters<SchemaDirectiveVisitor['visitFieldDefinition']>[1];

class DisallowDirective extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: Field, details: Details) {
    field.resolve = function (...args) {
      // Disallow any and all access, all the time
      throw new Error('go away');
    };
  }
}

// NOTE: Neo4jGraphQL schemaDirectives type is not compatible with ApolloServer
// schemaDirectives type or @graphql-tools/utils schemaDirectives type, so we're
// casting the type here to force compatibility.
//
// https://www.apollographql.com/docs/apollo-server/v2
// https://www.graphql-tools.com/docs/legacy-schema-directives/
type SchemaDirectives = ConstructorParameters<
  typeof Neo4jGraphQL
>[number]['schemaDirectives'];

const schemaDirectives = {
  disallow: DisallowDirective,
} as unknown as SchemaDirectives;

const neo4jGraphQL = new Neo4jGraphQL({
  typeDefs: /* GraphQL */ `
    directive @disallow on FIELD_DEFINITION

    type Mutation {
      doStuff: String! @disallow
    }

    type Query {
      noop: Boolean
    }
  `,

  driver: neo4j.driver('bolt://localhost:7687'),
  resolvers: { Mutation: { doStuff: () => 'OK' } },
  schemaDirectives,
});

test('DisallowDirective', async () => {
  const gqlResult = await graphql({
    schema: neo4jGraphQL.schema,
    source: /* GraphQL */ `
      mutation {
        doStuff
      }
    `,
    contextValue: { driver: neo4j.driver('bolt://localhost:7687') },
  });

  expect(gqlResult.data).toBeNull();
  expect(gqlResult.errors).toBeTruthy();
});

@lirbank
Copy link
Author

lirbank commented Jul 23, 2021

Here are some more expanded tests. All fail on Alpha 4 but pass on Alpha 2.

import neo4j from 'neo4j-driver';
import { Neo4jGraphQL } from '@neo4j/graphql';
import { SchemaDirectiveVisitor } from '@graphql-tools/utils';
import { graphql } from 'graphql';

type Field = Parameters<SchemaDirectiveVisitor['visitFieldDefinition']>[0];
type Details = Parameters<SchemaDirectiveVisitor['visitFieldDefinition']>[1];

class DisallowDirective extends SchemaDirectiveVisitor {
  public visitFieldDefinition(field: Field, details: Details) {
    field.resolve = function (...args) {
      throw new Error('go away');
    };
  }
}

// NOTE: Neo4jGraphQL schemaDirectives type is not compatible with ApolloServer
// schemaDirectives type or @graphql-tools/utils schemaDirectives type, so we're
// casting the type here to force compatibility.
//
// https://www.apollographql.com/docs/apollo-server/v2
// https://www.graphql-tools.com/docs/legacy-schema-directives/
type SchemaDirectives = ConstructorParameters<
  typeof Neo4jGraphQL
>[number]['schemaDirectives'];

const schemaDirectives = {
  disallow: DisallowDirective,
} as unknown as SchemaDirectives;

const neo4jGraphQL = new Neo4jGraphQL({
  typeDefs: /* GraphQL */ `
    directive @disallow on FIELD_DEFINITION

    type NestedResult {
      stuff: String! @disallow
    }

    type Mutation {
      doStuff: String! @disallow
      doNestedStuff: NestedResult!
    }

    type Query {
      getStuff: String! @disallow
      getNestedStuff: NestedResult!
    }
  `,

  driver: neo4j.driver('bolt://localhost:7687'),
  resolvers: {
    NestedResult: {
      stuff: (parent: string) => parent,
    },

    Mutation: {
      doStuff: () => 'OK',
      doNestedStuff: () => 'OK',
    },

    Query: {
      getStuff: () => 'OK',
      getNestedStuff: () => 'OK',
    },
  },
  schemaDirectives,
});

test('mutation top - DisallowDirective', async () => {
  const gqlResult = await graphql({
    schema: neo4jGraphQL.schema,
    source: /* GraphQL */ `
      mutation {
        doStuff
      }
    `,
    contextValue: { driver: neo4j.driver('bolt://localhost:7687') },
  });

  expect(gqlResult.data).toBeNull();
  expect(gqlResult.errors && gqlResult.errors[0].message).toBe('go away');
});

test('query top - DisallowDirective', async () => {
  const gqlResult = await graphql({
    schema: neo4jGraphQL.schema,
    source: /* GraphQL */ `
      query {
        getStuff
      }
    `,
    contextValue: { driver: neo4j.driver('bolt://localhost:7687') },
  });

  expect(gqlResult.data).toBeNull();
  expect(gqlResult.errors && gqlResult.errors[0].message).toBe('go away');
});

test('mutation nested - DisallowDirective', async () => {
  const gqlResult = await graphql({
    schema: neo4jGraphQL.schema,
    source: /* GraphQL */ `
      mutation {
        doNestedStuff {
          stuff
        }
      }
    `,
    contextValue: { driver: neo4j.driver('bolt://localhost:7687') },
  });

  expect(gqlResult.data).toBeNull();
  expect(gqlResult.errors && gqlResult.errors[0].message).toBe('go away');
});

test('query nested - DisallowDirective', async () => {
  const gqlResult = await graphql({
    schema: neo4jGraphQL.schema,
    source: /* GraphQL */ `
      query {
        getNestedStuff {
          stuff
        }
      }
    `,
    contextValue: { driver: neo4j.driver('bolt://localhost:7687') },
  });

  expect(gqlResult.data).toBeNull();
  expect(gqlResult.errors && gqlResult.errors[0].message).toBe('go away');
});

It seems the issue has to do with throwing errors in the visitFieldDefinition method.

@lirbank
Copy link
Author

lirbank commented Jul 23, 2021

Versions:

"@neo4j/graphql": "2.0.0-alpha.2",
"@neo4j/graphql": "2.0.0-alpha.4",
"neo4j-driver": "4.3.1",
"@graphql-tools/utils": "7.10.0",
"graphql": "15.5.0",

@darrellwarde
Copy link
Contributor

Amazing @lirbank, thank you so much for the detailed examples, going to try to figure this one out today! 🙂

@darrellwarde
Copy link
Contributor

Fixed in #356, and hopefully you won't have to re-cast your schema directives once this is merged and released.

I would like to take this opportunity to ask, why are you using the legacy SchemaDirectiveVisitor class as opposed to the new functional approach? https://www.graphql-tools.com/docs/schema-directives/#implementing-schema-directives

@darrellwarde darrellwarde linked a pull request Jul 23, 2021 that will close this issue
6 tasks
@lirbank
Copy link
Author

lirbank commented Jul 23, 2021

Awesome! We're porting an existing GraphQL solution to Neo4jGraphQL, so we already have some directives that were written a long time ago, but works well and are quite hardened by now. That's the only reason we're using legacy directives. If we were to write new ones it'd be in the functional approach.

@darrellwarde
Copy link
Contributor

Hey @lirbank, I think I remember you telling us that before, thank you for the background.

I do, however, want to point out that @graphql-tools/[email protected] has just been released, and this completely removes support for legacy schema directives (https://github.com/ardatan/graphql-tools/releases/tag/%40graphql-tools%2Fschema%408.0.0).

We're not upgrading right now because the changes really break things for us. However, we will upgrade in the near future, so I would encourage you to perhaps think about rewriting those legacy schema directives so that you don't lose the ability to use our library when we do.

@lirbank
Copy link
Author

lirbank commented Jul 30, 2021

Thanks @darrellwarde, that's really good to know! I'll plan for rewriting the directives.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants