diff --git a/package.json b/package.json index 671e3406b..166ba7fce 100644 --- a/package.json +++ b/package.json @@ -55,8 +55,8 @@ "linkifyjs": "~2.1.8", "lodash": "~4.17.11", "ms": "~2.1.1", - "neo4j-driver": "~1.7.2", - "neo4j-graphql-js": "~2.3.1", + "neo4j-driver": "~1.7.3", + "neo4j-graphql-js": "~2.4.1", "node-fetch": "~2.3.0", "npm-run-all": "~4.1.5", "sanitize-html": "~1.20.0", diff --git a/src/resolvers/follow.spec.js b/src/resolvers/follow.spec.js new file mode 100644 index 000000000..3c16560e5 --- /dev/null +++ b/src/resolvers/follow.spec.js @@ -0,0 +1,115 @@ +import Factory from '../seed/factories' +import { GraphQLClient } from 'graphql-request' +import { host, login } from '../jest/helpers' + +const factory = Factory() +let clientUser1 + +const mutationFollowUser = (id) => ` + mutation { + follow(id: "${id}", type: User) + } +` +const mutationUnfollowUser = (id) => ` + mutation { + unfollow(id: "${id}", type: User) + } +` + +beforeEach(async () => { + await factory.create('User', { + id: 'u1', + email: 'test@example.org', + password: '1234' + }) + await factory.create('User', { + id: 'u2', + email: 'test2@example.org', + password: '1234' + }) +}) + +afterEach(async () => { + await factory.cleanDatabase() +}) + +describe('follow ', () => { + describe('(un)follow user', () => { + let headersUser1 + beforeEach(async () => { + headersUser1 = await login({ email: 'test@example.org', password: '1234' }) + clientUser1 = new GraphQLClient(host, { headers: headersUser1 }) + }) + + it('I can follow another user', async () => { + const res = await clientUser1.request( + mutationFollowUser('u2') + ) + const expected = { + follow: true + } + expect(res).toMatchObject(expected) + + const { User } = await clientUser1.request(`{ + User(id: "u2") { + followedBy { id } + followedByCurrentUser + } + }`) + const expected2 = { + followedBy: [ + { id: 'u1' } + ], + followedByCurrentUser: true + } + expect(User[0]).toMatchObject(expected2) + }) + + it('I can unfollow a user', async () => { + // follow + await clientUser1.request( + mutationFollowUser('u2') + ) + const expected = { + unfollow: true + } + // unfollow + const res = await clientUser1.request(mutationUnfollowUser('u2')) + expect(res).toMatchObject(expected) + + const { User } = await clientUser1.request(`{ + User(id: "u2") { + followedBy { id } + followedByCurrentUser + } + }`) + const expected2 = { + followedBy: [], + followedByCurrentUser: false + } + expect(User[0]).toMatchObject(expected2) + }) + + it('I can`t follow myself', async () => { + const res = await clientUser1.request( + mutationFollowUser('u1') + ) + const expected = { + follow: false + } + expect(res).toMatchObject(expected) + + const { User } = await clientUser1.request(`{ + User(id: "u1") { + followedBy { id } + followedByCurrentUser + } + }`) + const expected2 = { + followedBy: [], + followedByCurrentUser: false + } + expect(User[0]).toMatchObject(expected2) + }) + }) +}) diff --git a/src/resolvers/shout.spec.js b/src/resolvers/shout.spec.js new file mode 100644 index 000000000..490191c7a --- /dev/null +++ b/src/resolvers/shout.spec.js @@ -0,0 +1,126 @@ +import Factory from '../seed/factories' +import { GraphQLClient } from 'graphql-request' +import { host, login } from '../jest/helpers' + +const factory = Factory() +let clientUser1, clientUser2 + +const mutationShoutPost = (id) => ` + mutation { + shout(id: "${id}", type: Post) + } +` +const mutationUnshoutPost = (id) => ` + mutation { + unshout(id: "${id}", type: Post) + } +` + +beforeEach(async () => { + await factory.create('User', { + id: 'u1', + email: 'test@example.org', + password: '1234' + }) + await factory.create('User', { + id: 'u2', + email: 'test2@example.org', + password: '1234' + }) +}) + +afterEach(async () => { + await factory.cleanDatabase() +}) + +describe('shout ', () => { + describe('(un)shout foreign post', () => { + let headersUser1, headersUser2 + beforeEach(async () => { + headersUser1 = await login({ email: 'test@example.org', password: '1234' }) + headersUser2 = await login({ email: 'test2@example.org', password: '1234' }) + clientUser1 = new GraphQLClient(host, { headers: headersUser1 }) + clientUser2 = new GraphQLClient(host, { headers: headersUser2 }) + + await clientUser1.request(` + mutation { + CreatePost(id: "p1", title: "Post Title 1", content: "Some Post Content 1") { + id + title + } + } + `) + await clientUser2.request(` + mutation { + CreatePost(id: "p2", title: "Post Title 2", content: "Some Post Content 2") { + id + title + } + } + `) + }) + + it('I shout a post of another user', async () => { + const res = await clientUser1.request( + mutationShoutPost('p2') + ) + const expected = { + shout: true + } + expect(res).toMatchObject(expected) + + const { Post } = await clientUser1.request(`{ + Post(id: "p2") { + shoutedByCurrentUser + } + }`) + const expected2 = { + shoutedByCurrentUser: true + } + expect(Post[0]).toMatchObject(expected2) + }) + + it('I unshout a post of another user', async () => { + // shout + await clientUser1.request( + mutationShoutPost('p2') + ) + const expected = { + unshout: true + } + // unshout + const res = await clientUser1.request(mutationUnshoutPost('p2')) + expect(res).toMatchObject(expected) + + const { Post } = await clientUser1.request(`{ + Post(id: "p2") { + shoutedByCurrentUser + } + }`) + const expected2 = { + shoutedByCurrentUser: false + } + expect(Post[0]).toMatchObject(expected2) + }) + + it('I can`t shout my own post', async () => { + const res = await clientUser1.request( + mutationShoutPost('p1') + ) + const expected = { + shout: false + } + expect(res).toMatchObject(expected) + + const { Post } = await clientUser1.request(`{ + Post(id: "p1") { + shoutedByCurrentUser + } + }`) + const expected2 = { + shoutedByCurrentUser: false + } + expect(Post[0]).toMatchObject(expected2) + }) + }) +}) diff --git a/src/schema.graphql b/src/schema.graphql index a7b2927e4..3a3a266df 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -1,6 +1,8 @@ type Query { isLoggedIn: Boolean! + "Get the currently logged in User based on the given JWT Token" currentUser: User + "Get the latest Network Statistics" statistics: Statistics! findPosts(filter: String!, limit: Int = 10): [Post]! @cypher( statement: """ @@ -16,9 +18,39 @@ type Query { ) } type Mutation { + "Get a JWT Token for the given Email and password" login(email: String!, password: String!): String! signup(email: String!, password: String!): Boolean! report(resource: Resource!, description: String): Report + "Shout the given Type and ID" + shout(id: ID!, type: ShoutTypeEnum): Boolean! @cypher(statement: """ + MATCH (n {id: $id})<-[:WROTE]-(wu:User), (u:User {id: $cypherParams.currentUserId}) + WHERE $type IN labels(n) AND NOT wu.id = $cypherParams.currentUserId + MERGE (u)-[r:SHOUTED]->(n) + RETURN COUNT(r) > 0 + """) + "Unshout the given Type and ID" + unshout(id: ID!, type: ShoutTypeEnum): Boolean! @cypher(statement: """ + MATCH (:User {id: $cypherParams.currentUserId})-[r:SHOUTED]->(n {id: $id}) + WHERE $type IN labels(n) + DELETE r + RETURN COUNT(r) > 0 + """) + + "Follow the given Type and ID" + follow(id: ID!, type: FollowTypeEnum): Boolean! @cypher(statement: """ + MATCH (n {id: $id}), (u:User {id: $cypherParams.currentUserId}) + WHERE $type IN labels(n) AND NOT $id = $cypherParams.currentUserId + MERGE (u)-[r:FOLLOWS]->(n) + RETURN COUNT(r) > 0 + """) + "Unfollow the given Type and ID" + unfollow(id: ID!, type: FollowTypeEnum): Boolean! @cypher(statement: """ + MATCH (:User {id: $cypherParams.currentUserId})-[r:FOLLOWS]->(n {id: $id}) + WHERE $type IN labels(n) + DELETE r + RETURN COUNT(r) > 0 + """) disable(resource: Resource!): Boolean! enable(resource: Resource!): Boolean! } @@ -99,13 +131,19 @@ type User { updatedAt: String friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") - friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(r)") + friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)") following: [User]! @relation(name: "FOLLOWS", direction: "OUT") - followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(r)") + followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)") followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN") - followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(r)") + followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)") + + "Is the currently logged in user following that user?" + followedByCurrentUser: Boolean! @cypher(statement: """ + MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) + RETURN COUNT(u) >= 1 + """) #contributions: [WrittenPost]! #contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]! @@ -124,7 +162,7 @@ type User { commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true RETURN COUNT(r)") shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT") - shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)") + shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)") organizationsCreated: [Organization] @relation(name: "CREATED_ORGA", direction: "OUT") organizationsOwned: [Organization] @relation(name: "OWNING_ORGA", direction: "OUT") @@ -165,7 +203,13 @@ type Post { commentsCount: Int! @cypher(statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) RETURN COUNT(r)") shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN") - shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)") + shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)") + + "Has the currently logged in user shouted that post?" + shoutedByCurrentUser: Boolean! @cypher(statement: """ + MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId}) + RETURN COUNT(u) >= 1 + """) } type Comment { @@ -219,6 +263,16 @@ enum BadgeStatusEnum { permanent temporary } +enum ShoutTypeEnum { + Post + Organization + Project +} +enum FollowTypeEnum { + User + Organization + Project +} type Organization { id: ID! @@ -240,7 +294,7 @@ type Tag { name: String! taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN") taggedOrganizations: [Organization]! @relation(name: "TAGGED", direction: "IN") - taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(p)") + taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)") taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)") deleted: Boolean disabled: Boolean diff --git a/src/seed/factories/index.js b/src/seed/factories/index.js index 469ca2277..e19239ece 100644 --- a/src/seed/factories/index.js +++ b/src/seed/factories/index.js @@ -90,6 +90,32 @@ export default function Factory (options = {}) { this.lastResponse = await this.graphQLClient.request(mutation, variables) return this }, + async shout (properties) { + const { id, type } = properties + const mutation = ` + mutation { + shout( + id: "${id}", + type: ${type} + ) + } + ` + this.lastResponse = await this.graphQLClient.request(mutation) + return this + }, + async follow (properties) { + const { id, type } = properties + const mutation = ` + mutation { + follow( + id: "${id}", + type: ${type} + ) + } + ` + this.lastResponse = await this.graphQLClient.request(mutation) + return this + }, async cleanDatabase () { this.lastResponse = await cleanDatabase({ driver: this.neo4jDriver }) return this diff --git a/src/seed/seed-db.js b/src/seed/seed-db.js index 310089ef7..b16e9f323 100644 --- a/src/seed/seed-db.js +++ b/src/seed/seed-db.js @@ -23,6 +23,15 @@ import Factory from './factories' f.create('User', { id: 'u7', name: 'Dagobert', role: 'user', email: 'dagobert@example.org' }) ]) + const [ asAdmin, asModerator, asUser, asTick, asTrick, asTrack ] = await Promise.all([ + Factory().authenticateAs({ email: 'admin@example.org', password: '1234' }), + Factory().authenticateAs({ email: 'moderator@example.org', password: '1234' }), + Factory().authenticateAs({ email: 'user@example.org', password: '1234' }), + Factory().authenticateAs({ email: 'tick@example.org', password: '1234' }), + Factory().authenticateAs({ email: 'trick@example.org', password: '1234' }), + Factory().authenticateAs({ email: 'track@example.org', password: '1234' }) + ]) + await Promise.all([ f.relate('User', 'Badges', { from: 'b6', to: 'u1' }), f.relate('User', 'Badges', { from: 'b5', to: 'u2' }), @@ -30,12 +39,6 @@ import Factory from './factories' f.relate('User', 'Badges', { from: 'b3', to: 'u4' }), f.relate('User', 'Badges', { from: 'b2', to: 'u5' }), f.relate('User', 'Badges', { from: 'b1', to: 'u6' }), - f.relate('User', 'Following', { from: 'u1', to: 'u2' }), - f.relate('User', 'Following', { from: 'u2', to: 'u3' }), - f.relate('User', 'Following', { from: 'u3', to: 'u4' }), - f.relate('User', 'Following', { from: 'u4', to: 'u5' }), - f.relate('User', 'Following', { from: 'u5', to: 'u6' }), - f.relate('User', 'Following', { from: 'u6', to: 'u7' }), f.relate('User', 'Friends', { from: 'u1', to: 'u2' }), f.relate('User', 'Friends', { from: 'u1', to: 'u3' }), f.relate('User', 'Friends', { from: 'u2', to: 'u3' }), @@ -44,6 +47,21 @@ import Factory from './factories' f.relate('User', 'Blacklisted', { from: 'u7', to: 'u6' }) ]) + await Promise.all([ + asAdmin + .follow({ id: 'u3', type: 'User' }), + asModerator + .follow({ id: 'u4', type: 'User' }), + asUser + .follow({ id: 'u4', type: 'User' }), + asTick + .follow({ id: 'u6', type: 'User' }), + asTrick + .follow({ id: 'u4', type: 'User' }), + asTrack + .follow({ id: 'u3', type: 'User' }) + ]) + await Promise.all([ f.create('Category', { id: 'cat1', name: 'Just For Fun', slug: 'justforfun', icon: 'smile' }), f.create('Category', { id: 'cat2', name: 'Happyness & Values', slug: 'happyness-values', icon: 'heart-o' }), @@ -70,15 +88,6 @@ import Factory from './factories' f.create('Tag', { id: 't4', name: 'Freiheit' }) ]) - const [ asAdmin, asModerator, asUser, asTick, asTrick, asTrack ] = await Promise.all([ - Factory().authenticateAs({ email: 'admin@example.org', password: '1234' }), - Factory().authenticateAs({ email: 'moderator@example.org', password: '1234' }), - Factory().authenticateAs({ email: 'user@example.org', password: '1234' }), - Factory().authenticateAs({ email: 'tick@example.org', password: '1234' }), - Factory().authenticateAs({ email: 'trick@example.org', password: '1234' }), - Factory().authenticateAs({ email: 'track@example.org', password: '1234' }) - ]) - await Promise.all([ asAdmin.create('Post', { id: 'p0' }), asModerator.create('Post', { id: 'p1' }), @@ -143,13 +152,26 @@ import Factory from './factories' f.relate('Post', 'Tags', { from: 'p14', to: 't2' }), f.relate('Post', 'Tags', { from: 'p15', to: 't3' }) ]) + await Promise.all([ - f.relate('User', 'Shouted', { from: 'u1', to: 'p2' }), - f.relate('User', 'Shouted', { from: 'u1', to: 'p3' }), - f.relate('User', 'Shouted', { from: 'u2', to: 'p1' }), - f.relate('User', 'Shouted', { from: 'u3', to: 'p1' }), - f.relate('User', 'Shouted', { from: 'u3', to: 'p4' }), - f.relate('User', 'Shouted', { from: 'u4', to: 'p1' }) + asAdmin + .shout({ id: 'p2', type: 'Post' }), + asAdmin + .shout({ id: 'p6', type: 'Post' }), + asModerator + .shout({ id: 'p0', type: 'Post' }), + asModerator + .shout({ id: 'p6', type: 'Post' }), + asUser + .shout({ id: 'p6', type: 'Post' }), + asUser + .shout({ id: 'p7', type: 'Post' }), + asTick + .shout({ id: 'p8', type: 'Post' }), + asTick + .shout({ id: 'p9', type: 'Post' }), + asTrack + .shout({ id: 'p10', type: 'Post' }) ]) await Promise.all([ diff --git a/src/server.js b/src/server.js index 2fc3af871..8e1cd36da 100644 --- a/src/server.js +++ b/src/server.js @@ -45,7 +45,10 @@ const createServer = (options) => { return { driver, user, - req: request + req: request, + cypherParams: { + currentUserId: user ? user.id : null + } } }, schema: schema, diff --git a/yarn.lock b/yarn.lock index 3ee900990..38e52cad9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1285,6 +1285,14 @@ apollo-env@0.3.3: core-js "3.0.0-beta.13" node-fetch "^2.2.0" +apollo-errors@^1.9.0: + version "1.9.0" + resolved "https://registry.yarnpkg.com/apollo-errors/-/apollo-errors-1.9.0.tgz#f1ed0ca0a6be5cd2f24e2eaa7b0860a10146ff51" + integrity sha512-XVukHd0KLvgY6tNjsPS3/Re3U6RQlTKrTbIpqqeTMo2N34uQMr+H1UheV21o8hOZBAFosvBORVricJiP5vfmrw== + dependencies: + assert "^1.4.1" + extendable-error "^0.1.5" + apollo-graphql@^0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.1.1.tgz#dc5eac3062abf9f063ac9869f0ef5c54fdc136e5" @@ -1577,6 +1585,13 @@ assert-plus@1.0.0, assert-plus@^1.0.0: resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= +assert@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" + integrity sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE= + dependencies: + util "0.10.3" + assertion-error@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" @@ -3003,6 +3018,11 @@ extend@^3.0.0, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +extendable-error@^0.1.5: + version "0.1.5" + resolved "https://registry.yarnpkg.com/extendable-error/-/extendable-error-0.1.5.tgz#122308a7097bc89a263b2c4fbf089c78140e3b6d" + integrity sha1-EiMIpwl7yJomOyxPvwiceBQOO20= + external-editor@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.0.3.tgz#5866db29a97826dbe4bf3afd24070ead9ea43a27" @@ -3350,6 +3370,15 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.15.tgz#ffb703e1066e8a0eeaa4c8b80ba9253eeefbfb00" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== +graphql-auth-directives@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/graphql-auth-directives/-/graphql-auth-directives-2.1.0.tgz#85b83817844e2ec5fba8fe5de444287d6dd0f85a" + integrity sha512-mRVsjeMeMABPyjxyzl9mhkcW02YBwSj7dnu7C6wy2dIhiby6xTKy6Q54C8KeqXSYsy6ua4VmBH++d7GKqpvIoA== + dependencies: + apollo-errors "^1.9.0" + graphql-tools "^4.0.4" + jsonwebtoken "^8.3.0" + graphql-custom-directives@~0.2.14: version "0.2.14" resolved "https://registry.yarnpkg.com/graphql-custom-directives/-/graphql-custom-directives-0.2.14.tgz#88611b8cb074477020ad85af47bfe168c4c23992" @@ -3783,6 +3812,11 @@ inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= +inherits@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" + integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= + ini@^1.3.4, ini@~1.3.0: version "1.3.5" resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" @@ -4642,7 +4676,7 @@ jsonify@~0.0.0: resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" integrity sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM= -jsonwebtoken@~8.5.0: +jsonwebtoken@^8.3.0, jsonwebtoken@~8.5.0: version "8.5.0" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.0.tgz#ebd0ca2a69797816e1c5af65b6c759787252947e" integrity sha512-IqEycp0znWHNA11TpYi77bVgyBO/pGESDh7Ajhas+u0ttkGkKYIIAjniL4Bw5+oVejVF+SYkaI7XKfwCCyeTuA== @@ -5137,21 +5171,22 @@ negotiator@0.6.1: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" integrity sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk= -neo4j-driver@^1.7.2, neo4j-driver@~1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-1.7.2.tgz#c72a6dfa6bd2106b00a42794dc52a82b227b48e0" - integrity sha512-0IvCFYhcP9hb5JveZk33epbReDKpFTn2u5vAa8zzGG344i6yFqZrBo0mtC114ciP9zFjAtfNOP72mRm8+NV0Fg== +neo4j-driver@^1.7.2, neo4j-driver@~1.7.3: + version "1.7.3" + resolved "https://registry.yarnpkg.com/neo4j-driver/-/neo4j-driver-1.7.3.tgz#1c1108ab26b7243975f1b20045daf31d8f685207" + integrity sha512-UCNOFiQdouq14PvZGTr+psy657BJsBpO6O2cJpP+NprZnEF4APrDzAcydPZSFxE1nfooLNc50vfuZ0q54UyY2Q== dependencies: babel-runtime "^6.26.0" text-encoding "^0.6.4" uri-js "^4.2.1" -neo4j-graphql-js@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/neo4j-graphql-js/-/neo4j-graphql-js-2.3.1.tgz#9a5de7e312594d63481e947a0cbe4e08b05ffed3" - integrity sha512-9DExWXD2vFdDJOmqorT1ygFOUEos7KF8KyF8Wt3jYxejWUuq+a5fAFBu7+YDH8QbvA23paKPEX0Pn1nS+Q5C1A== +neo4j-graphql-js@~2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/neo4j-graphql-js/-/neo4j-graphql-js-2.4.1.tgz#a1de65340fb6f1ad0ae6aadab90a0bb78b490b32" + integrity sha512-Py6RJuMT7A03Hzoo6qfKyo6DUnHQgbZlBcgucnhgQjbeysAzvM3F02UAVn/HxEtOgowAeGWjyjJvwozoNtiiqQ== dependencies: graphql "^14.0.2" + graphql-auth-directives "^2.0.0" lodash "^4.17.11" neo4j-driver "^1.7.2" @@ -7186,6 +7221,13 @@ util.promisify@^1.0.0: define-properties "^1.1.2" object.getownpropertydescriptors "^2.0.3" +util@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" + integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= + dependencies: + inherits "2.0.1" + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"