From e6503cba695c6a7ff7f02d079fef02450d4b1c62 Mon Sep 17 00:00:00 2001 From: johnymontana Date: Wed, 18 Dec 2019 13:03:26 -0700 Subject: [PATCH 1/5] Initial updates for Neo4j v4.0 --- .circleci/config.yml | 58 ++++++++++++++++++++++ example/apollo-server/movies-middleware.js | 2 +- package-lock.json | 20 ++++---- package.json | 2 +- scripts/start-neo4j.sh | 1 + src/index.js | 1 + src/translate.js | 2 +- src/utils.js | 2 +- 8 files changed, 74 insertions(+), 14 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 654b4b28..f50d5698 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -123,6 +123,16 @@ references: NEO4J_VERSION: '3.5.4' APOC_VERSION: '3.5.0.2' DATASTORE_VERSION: '3_5' + env_neo4j40ce: &env_neo4j40ce + NEO4J_DIST: 'community' + NEO4J_VERSION: '4.0.0' + APOC_VERSION: '4.0.0-rc01' + DATASTORE_VERSION: '4_0' + env_neo4j40ee: &env_neo4j40ee + NEO4J_DIST: 'enterprise' + NEO4J_VERSION: '4.0.0' + APOC_VERSION: '4.0.0-rc01' + DATASTORE_VERSION: '4_0' install_neo4j_steps: &install_neo4j_steps - checkout @@ -165,6 +175,16 @@ jobs: environment: *env_neo4j35ee steps: *install_neo4j_steps + install_neo4j40ce: + docker: *node10 + environment: *env_neo4j40ce + steps: *install_neo4j_steps + + install_neo4j40ee: + docker: *node10 + environment: *env_neo4j40ee + steps: *install_neo4j_steps + # Node 8 install_for_node8: docker: *node8 @@ -190,6 +210,16 @@ jobs: environment: *env_neo4j35ee steps: *run_tests_steps + neo4j40ce_node8: + docker: *node8 + environment: *env_neo4j40ce + steps: *run_tests_steps + + neo4j40ee_node8: + docker: *node8 + environment: *env_neo4j40ee + steps: *run_tests_steps + # Node 10 install_for_node10: docker: *node10 @@ -215,6 +245,16 @@ jobs: environment: *env_neo4j35ee steps: *run_tests_steps + neo4j40ce_node10: + docker: *node10 + environment: *env_neo4j40ce + steps: *run_tests_steps + + neo4j40ee_node10: + docker: *node10 + environment: *env_neo4j40ee + steps: *run_tests_steps + workflows: version: 2 integration_test: @@ -225,6 +265,8 @@ workflows: - install_neo4j34ce - install_neo4j35ce - install_neo4j35ee + - install_neo4j40ce + - install_neo4j40ee - neo4j34ee_node8: requires: @@ -242,6 +284,14 @@ workflows: requires: - install_neo4j35ee - install_for_node8 + - neo4j40ce_node8: + requires: + - install_neo4j40ce + - install_for_node8 + - neo4j40ee_node8: + requires: + - install_neo4j40ee + - install_for_node8 - neo4j34ee_node10: requires: @@ -259,3 +309,11 @@ workflows: requires: - install_neo4j35ee - install_for_node10 + - neo4j40ce_node10: + requires: + - install_neo4j40ce + - install_for_node10 + - neo4j40ee_node10: + requires: + - install_neo4j40ee + - install_for_node10 diff --git a/example/apollo-server/movies-middleware.js b/example/apollo-server/movies-middleware.js index 6db58510..106a33c3 100644 --- a/example/apollo-server/movies-middleware.js +++ b/example/apollo-server/movies-middleware.js @@ -2,7 +2,7 @@ import { makeAugmentedSchema } from '../../src/index'; import { ApolloServer } from 'apollo-server-express'; import express from 'express'; import bodyParser from 'body-parser'; -import { v1 as neo4j } from 'neo4j-driver'; +import neo4j from 'neo4j-driver'; import { typeDefs, resolvers } from './movies-schema'; const schema = makeAugmentedSchema({ diff --git a/package-lock.json b/package-lock.json index c358e0cf..9e9315f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4796,9 +4796,9 @@ } }, "handlebars": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.5.tgz", - "integrity": "sha512-0Ce31oWVB7YidkaTq33ZxEbN+UDxMMgThvCe8ptgQViymL5DPis9uLdTA13MiRPhgvqyxIegugrP97iK3JeBHg==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz", + "integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==", "dev": true, "requires": { "neo-async": "^2.6.0", @@ -6519,11 +6519,12 @@ "dev": true }, "neo4j-driver": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-1.7.6.tgz", - "integrity": "sha512-6c3ALO3vYDfUqNoCy8OFzq+fQ7q/ab3LCuJrmm8P04M7RmyRCCnUtJ8IzSTGbiZvyhcehGK+azNDAEJhxPV/hA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-4.0.1.tgz", + "integrity": "sha512-SqBhXyyyayVs5gV/6BrgdKbcmU5AsYQXkFAiYO74XAE8XPLJ1HVR/Hu4wjonAX7+70DsalkWEiFN1c6UaCVzlQ==", "requires": { "@babel/runtime": "^7.5.5", + "rxjs": "^6.5.2", "text-encoding-utf-8": "^1.0.2", "uri-js": "^4.2.2" } @@ -8035,7 +8036,6 @@ "version": "6.5.3", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.3.tgz", "integrity": "sha512-wuYsAYYFdWTAnAaPoKGNhfpWwKZbJW+HgAJ+mImp+Epl7BG8oNWBCTyRM8gba9k4lk8BgWdoYm21Mo/RYhhbgA==", - "dev": true, "requires": { "tslib": "^1.9.0" } @@ -8899,9 +8899,9 @@ } }, "uglify-js": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.3.tgz", - "integrity": "sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g==", + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.7.2.tgz", + "integrity": "sha512-uhRwZcANNWVLrxLfNFEdltoPNhECUR3lc+UdJoG9CBpMcSnKyWA94tc3eAujB1GcMY5Uwq8ZMp4qWpxWYDQmaA==", "dev": true, "optional": true, "requires": { diff --git a/package.json b/package.json index e4732ea4..a7be4bcf 100755 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "graphql": "^14.2.1", "graphql-auth-directives": "^2.1.0", "lodash": "^4.17.15", - "neo4j-driver": "^1.7.3" + "neo4j-driver": "^4.0.1" }, "ava": { "require": [ diff --git a/scripts/start-neo4j.sh b/scripts/start-neo4j.sh index 52d77ce3..440e2b96 100755 --- a/scripts/start-neo4j.sh +++ b/scripts/start-neo4j.sh @@ -16,6 +16,7 @@ else nc -z 127.0.0.1 $BOLT_PORT is_up=$? if [ $is_up -eq 0 ]; then + ./neo4j/bin/neo4j-admin set-initial-password letmein echo echo "Successfully started, neo4j bolt available on $BOLT_PORT" break diff --git a/src/index.js b/src/index.js index 47798695..75d0685f 100644 --- a/src/index.js +++ b/src/index.js @@ -57,6 +57,7 @@ instead: \`DEBUG=neo4j-graphql-js\`. debug('%s', query); debug('%s', JSON.stringify(cypherParams, null, 2)); + // TODO: Is this a 4.0 driver instance? Check bolt path for default database name and use that when creating the session const session = context.driver.session(); let result; diff --git a/src/translate.js b/src/translate.js index d013b2c3..d0d0e606 100644 --- a/src/translate.js +++ b/src/translate.js @@ -56,7 +56,7 @@ import { } from 'graphql'; import { buildCypherSelection } from './selections'; import _ from 'lodash'; -import { v1 as neo4j } from 'neo4j-driver'; +import neo4j from 'neo4j-driver'; const derivedTypesParamName = interfaceName => `${interfaceName}_derivedTypes`; diff --git a/src/utils.js b/src/utils.js index 8e79d55a..e9be7b12 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,5 +1,5 @@ import { isObjectType, parse, GraphQLInt } from 'graphql'; -import { v1 as neo4j } from 'neo4j-driver'; +import neo4j from 'neo4j-driver'; import _ from 'lodash'; import filter from 'lodash/filter'; import { Neo4jTypeName } from './augment/types/types'; From f32492e46c9ece38270ed4a8ab70933295ff83ad Mon Sep 17 00:00:00 2001 From: johnymontana Date: Wed, 18 Dec 2019 13:26:46 -0700 Subject: [PATCH 2/5] Workaround for Neo4j 4.0 database load For historical reasons we copy the graph.db directory for the test database instead of using neo4j-admin load. This workaround ensures the test database will start when databases are loaded this way. This is a temporary workaround and the test database will be loaded via a Cypher script in the future. --- scripts/start-neo4j.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/start-neo4j.sh b/scripts/start-neo4j.sh index 440e2b96..06ffb68d 100755 --- a/scripts/start-neo4j.sh +++ b/scripts/start-neo4j.sh @@ -7,6 +7,7 @@ if [ ! -d "neo4j/data/databases/graph.db" ]; then exit 1 else echo "dbms.allow_upgrade=true" >> ./neo4j/conf/neo4j.conf + echo "dbms.recovery.fail_on_missing_files=false" >> ./neo4j/conf/neo4j.conf # Set initial and max heap to workaround JVM in docker issues dbms_memory_heap_initial_size="2048m" dbms_memory_heap_max_size="2048m" ./neo4j/bin/neo4j start echo "Waiting up to 2 minutes for neo4j bolt port ($BOLT_PORT)" @@ -16,7 +17,6 @@ else nc -z 127.0.0.1 $BOLT_PORT is_up=$? if [ $is_up -eq 0 ]; then - ./neo4j/bin/neo4j-admin set-initial-password letmein echo echo "Successfully started, neo4j bolt available on $BOLT_PORT" break From 02fa72d63467d299c8ab9dfee15bd6ca813499b7 Mon Sep 17 00:00:00 2001 From: johnymontana Date: Wed, 18 Dec 2019 13:53:09 -0700 Subject: [PATCH 3/5] Fix invalid coordinates --- test/integration/integration.test.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index a51d0822..3a4eb606 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -226,8 +226,8 @@ test.serial('Create node mutation (not-isolated)', async t => { imdbRating: 1, location: { __typename: '_Neo4jPoint', - longitude: 46.870035, - latitude: -113.990976, + longitude: -113.990976, + latitude: 46.870035, height: 12.3 } } @@ -246,8 +246,8 @@ test.serial('Create node mutation (not-isolated)', async t => { poster: "www.movieposter.com/img.png" imdbRating: 1.0 location: { - longitude: 46.870035 - latitude: -113.990976 + latitude: 46.870035 + longitude: -113.990976 height: 12.3 } ) { @@ -287,8 +287,8 @@ test.serial('Merge node mutation (not-isolated)', async t => { imdbRating: 1, location: { __typename: '_Neo4jPoint', - longitude: 46.870035, - latitude: -113.990976, + latitude: 46.870035, + longitude: -113.990976, height: 12.3 } } @@ -307,8 +307,8 @@ test.serial('Merge node mutation (not-isolated)', async t => { poster: "www.movieposter.com/img.png" imdbRating: 1.0 location: { - longitude: 46.870035 - latitude: -113.990976 + latitude: 46.870035 + longitude: -113.990976 height: 12.3 } ) { @@ -318,8 +318,8 @@ test.serial('Merge node mutation (not-isolated)', async t => { poster imdbRating location { - longitude latitude + longitude height } } From 006bf2547ba97da3a7d39145dd150d8e46525f9b Mon Sep 17 00:00:00 2001 From: johnymontana Date: Wed, 18 Dec 2019 14:12:15 -0700 Subject: [PATCH 4/5] Cast SKIP and LIMIT parameters to integers It seems Neo4j v4.0 requires an explicit cast to integer for parameters used with SKIP and LIMIT. --- src/utils.js | 4 +++- test/unit/cypherTest.test.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/utils.js b/src/utils.js index e9be7b12..f1c3e018 100644 --- a/src/utils.js +++ b/src/utils.js @@ -809,7 +809,9 @@ export const initializeMutationParams = ({ }; export const getOuterSkipLimit = (first, offset) => - `${offset > 0 ? ` SKIP $offset` : ''}${first > -1 ? ' LIMIT $first' : ''}`; + `${offset > 0 ? ` SKIP toInteger($offset)` : ''}${ + first > -1 ? ' LIMIT toInteger($first)' : '' + }`; export const getPayloadSelections = resolveInfo => { const filteredFieldNodes = filter( diff --git a/test/unit/cypherTest.test.js b/test/unit/cypherTest.test.js index 9552b212..f5cf7bec 100644 --- a/test/unit/cypherTest.test.js +++ b/test/unit/cypherTest.test.js @@ -38,7 +38,7 @@ test('Simple skip limit', t => { } } `, - expectedCypherQuery = `MATCH (\`movie\`:\`Movie\`${ADDITIONAL_MOVIE_LABELS} {title:$title}) RETURN \`movie\` { .title , .year } AS \`movie\` SKIP $offset LIMIT $first`; + expectedCypherQuery = `MATCH (\`movie\`:\`Movie\`${ADDITIONAL_MOVIE_LABELS} {title:$title}) RETURN \`movie\` { .title , .year } AS \`movie\` SKIP toInteger($offset) LIMIT toInteger($first)`; t.plan(3); return Promise.all([ @@ -1748,7 +1748,7 @@ test('orderBy test - descending, top level - augmented schema', t => { } } `, - expectedCypherQuery = `MATCH (\`movie\`:\`Movie\`${ADDITIONAL_MOVIE_LABELS} {year:$year}) WITH \`movie\` ORDER BY movie.title DESC RETURN \`movie\` { .title ,actors: [(\`movie\`)<-[:\`ACTED_IN\`]-(\`movie_actors\`:\`Actor\`) | movie_actors { .name }][..3] } AS \`movie\` LIMIT $first`; + expectedCypherQuery = `MATCH (\`movie\`:\`Movie\`${ADDITIONAL_MOVIE_LABELS} {year:$year}) WITH \`movie\` ORDER BY movie.title DESC RETURN \`movie\` { .title ,actors: [(\`movie\`)<-[:\`ACTED_IN\`]-(\`movie_actors\`:\`Actor\`) | movie_actors { .name }][..3] } AS \`movie\` LIMIT toInteger($first)`; t.plan(1); From c187701566424e05307416359d4ca154d340d6e2 Mon Sep 17 00:00:00 2001 From: johnymontana Date: Wed, 18 Dec 2019 14:24:14 -0700 Subject: [PATCH 5/5] Don't use deprecated syntax for Cypher parameters --- example/apollo-server/movies-schema.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/example/apollo-server/movies-schema.js b/example/apollo-server/movies-schema.js index 5b51affc..0254ca6b 100644 --- a/example/apollo-server/movies-schema.js +++ b/example/apollo-server/movies-schema.js @@ -14,16 +14,16 @@ type Movie { imdbRating: Float ratings: [Rated] genres: [Genre] @relation(name: "IN_GENRE", direction: "OUT") - similar(first: Int = 3, offset: Int = 0, limit: Int = 5): [Movie] @cypher(statement: "WITH {this} AS this MATCH (this)--(:Genre)--(o:Movie) RETURN o LIMIT {limit}") - mostSimilar: Movie @cypher(statement: "WITH {this} AS this RETURN this") - degree: Int @cypher(statement: "WITH {this} AS this RETURN SIZE((this)--())") + similar(first: Int = 3, offset: Int = 0, limit: Int = 5): [Movie] @cypher(statement: "MATCH (this)--(:Genre)--(o:Movie) RETURN o LIMIT $limit") + mostSimilar: Movie @cypher(statement: "RETURN this") + degree: Int @cypher(statement: "RETURN SIZE((this)--())") actors(first: Int = 3, offset: Int = 0): [Actor] @relation(name: "ACTED_IN", direction:"IN") avgStars: Float filmedIn: State @relation(name: "FILMED_IN", direction: "OUT") location: Point locations: [Point] - scaleRating(scale: Int = 3): Float @cypher(statement: "WITH $this AS this RETURN $scale * this.imdbRating") - scaleRatingFloat(scale: Float = 1.5): Float @cypher(statement: "WITH $this AS this RETURN $scale * this.imdbRating") + scaleRating(scale: Int = 3): Float @cypher(statement: "RETURN $scale * this.imdbRating") + scaleRatingFloat(scale: Float = 1.5): Float @cypher(statement: "RETURN $scale * this.imdbRating") _id: ID }