neo4j-graphql / neo4j-graphql-js

NOTE: This project is no longer actively maintained. Please consider using the official Neo4j GraphQL Library (linked in README).
Other
611 stars 147 forks source link

makeAugmentedSchema with isFederated:true breaks @search directive and searchSchema function #594

Open MeetSnowmaker opened 3 years ago

MeetSnowmaker commented 3 years ago

Hello there,

I tried to implement the new @search directive on a federated microservice.

[nodemon] starting `babel-node services/graph_canvas_service/src/index.js`
[nodemon] spawning
[nodemon] child pid: 21404
augmentedSchema
(node:18004) UnhandledPromiseRejectionWarning: TypeError: schema.getTypeMap is not a function
    at mapSearchDirectives (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\neo4j-graphql-js\dist\schemaSearch.js:72:33)
    at schemaSearch (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\neo4j-graphql-js\dist\schemaSearch.js:43:30)
    at _callee$ (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\neo4j-graphql-js\dist\index.js:541:62)    at tryCatch (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\regenerator-runtime\runtime.js:63:40)
    at Generator.invoke [as _invoke] (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\regenerator-runtime\runtime.js:293:22)
    at Generator.next (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\regenerator-runtime\runtime.js:118:21)
    at asyncGeneratorStep (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:5:24)
    at _next (C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:27:9)
    at C:\projects\someLearning\backend\services\graph_canvas_service\node_modules\@babel\runtime-corejs2\helpers\asyncToGenerator.js:34:7
    at new Promise (<anonymous>)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:18004) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 2)
(node:18004) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are
not handled will terminate the Node.js process with a non-zero exit code.
[nodemon] clean exit - waiting for changes before restart

After a few hours I started to reduce the configuration line by line. It appears when the functions restructures the schema to be @apollo/federation compatible some functions being 'lost in translation'.

Sample code with some debugging rampage: to reproduce the error uncomment the line // isFederated: true,

import '@babel/polyfill/noConflict';

import {
  // neo4jgraphql,
  makeAugmentedSchema,
  searchSchema,
  // augmentSchema,
  // cypher,
} from 'neo4j-graphql-js';
import neo4j from 'neo4j-driver';

import { ApolloServer, gql } from 'apollo-server';
import { buildFederatedSchema } from '@apollo/federation';
import fs from 'fs';

import Mutation from './resolvers/Mutation';
import { Query } from './resolvers/Query';

const generateApolloService = async () => {
  const driver = neo4j.driver(
    process.env.NEO4J_URI || 'bolt://localhost:7687',
    neo4j.auth.basic(
      process.env.NEO4J_USER || 'neo4j',
      process.env.NEO4J_PASSWORD || 'letmein',
    ),
  );

  const typeDefs = gql(
    fs.readFileSync(__dirname.concat('/schema.graphql'), 'utf8'),
  );

  const resolvers = {
    Mutation,
    // Query,
  };

  const schema = await makeAugmentedSchema({
    // typeDefs: typeDefs,
    typeDefs: `

    type Movie {
      title: String @search
      year: Int
      imdbRating: Float
      genres: [Genre] @relation(name: "IN_GENRE", direction: OUT)
  }
  type Genre {
      name: String
      movies: [Movie] @relation(name: "IN_GENRE", direction: IN)
  }
    `,
    // resolvers: resolvers,
    config: {
      experimental: true,
      // isFederated: true,
      query: true,
      mutation: true,
      debug: true,
    },
  });

  console.log('augmentedSchema');

  const wtf = await searchSchema({ schema, driver, debug: true });

  console.log('wtf', wtf);

  const apolloMicroService = new ApolloServer({
    schema: buildFederatedSchema([schema]),
    // dataSources: () => ({
    //   withdrawalAPI: new WithdrawalAPI(),
    // }),
    context: ({ req }) => {
      return {
        driver: driver,
        // claims: req.headers?.claims ? JSON.parse(req.headers.claims) || null,
      };
    },
    debug: true,
    tracing: true,
  });

  return apolloMicroService;
};

export { generateApolloService as default };

After removing isFederated from config it runs as expected

[searchSchema] Search indexes dropped using Cypher:
  CALL db.indexes() YIELD name, provider WHERE provider = "fulltext-1.0"
  CALL db.index.fulltext.drop(name)
  RETURN TRUE

[searchSchema] Search indexes created using Cypher:
  CALL db.index.fulltext.createNodeIndex("MovieSearch",["Movie"],["title"])
  RETURN TRUE

I guess I could manually call the drop and create Cypher queries on init for now. Only the searchSchema({ schema, driver, debug: true }); breaks the schema and resolvers are generated and working as expected.

Keep up the good work 🚀

michaeldgraham commented 3 years ago

https://github.com/neo4j-graphql/neo4j-graphql-js/issues/608