diff --git a/backend/src/graphql-schema.js b/backend/src/graphql-schema.js index 1e13c95f4..e88151898 100644 --- a/backend/src/graphql-schema.js +++ b/backend/src/graphql-schema.js @@ -6,6 +6,8 @@ import statistics from './resolvers/statistics.js' import reports from './resolvers/reports.js' import posts from './resolvers/posts.js' import moderation from './resolvers/moderation.js' +import follow from './resolvers/follow.js' +import shout from './resolvers/shout.js' import rewards from './resolvers/rewards.js' import socialMedia from './resolvers/socialMedia.js' import notifications from './resolvers/notifications' @@ -27,6 +29,8 @@ export const resolvers = { ...reports.Mutation, ...posts.Mutation, ...moderation.Mutation, + ...follow.Mutation, + ...shout.Mutation, ...rewards.Mutation, ...socialMedia.Mutation, ...notifications.Mutation diff --git a/backend/src/resolvers/follow.js b/backend/src/resolvers/follow.js new file mode 100644 index 000000000..df7b58891 --- /dev/null +++ b/backend/src/resolvers/follow.js @@ -0,0 +1,51 @@ +export default { + Mutation: { + follow: async (_object, params, context, _resolveInfo) => { + const { id, type } = params + + const session = context.driver.session() + let transactionRes = await session.run( + `MATCH (node {id: $id}), (user:User {id: $userId}) + WHERE $type IN labels(node) AND NOT $id = $userId + MERGE (user)-[relation:FOLLOWS]->(node) + RETURN COUNT(relation) > 0 as isFollowed`, + { + id, + type, + userId: context.user.id + } + ) + + const [isFollowed] = transactionRes.records.map(record => { + return record.get('isFollowed') + }) + + session.close() + + return isFollowed + }, + + unfollow: async (_object, params, context, _resolveInfo) => { + const { id, type } = params + const session = context.driver.session() + + let transactionRes = await session.run( + `MATCH (user:User {id: $userId})-[relation:FOLLOWS]->(node {id: $id}) + WHERE $type IN labels(node) + DELETE relation + RETURN COUNT(relation) > 0 as isFollowed`, + { + id, + type, + userId: context.user.id + } + ) + const [isFollowed] = transactionRes.records.map(record => { + return record.get('isFollowed') + }) + session.close() + + return isFollowed + } + } +} diff --git a/backend/src/resolvers/rewards.js b/backend/src/resolvers/rewards.js index 6bf6ddeea..a7a8c1ab7 100644 --- a/backend/src/resolvers/rewards.js +++ b/backend/src/resolvers/rewards.js @@ -4,7 +4,7 @@ export default { const { fromBadgeId, toUserId } = params const session = context.driver.session() - let sessionRes = await session.run( + let transactionRes = await session.run( `MATCH (badge:Badge {id: $badgeId}), (rewardedUser:User {id: $rewardedUserId}) MERGE (badge)-[:REWARDED]->(rewardedUser) RETURN rewardedUser {.id}`, @@ -14,7 +14,7 @@ export default { } ) - const [rewardedUser] = sessionRes.records.map(record => { + const [rewardedUser] = transactionRes.records.map(record => { return record.get('rewardedUser') }) @@ -27,7 +27,7 @@ export default { const { fromBadgeId, toUserId } = params const session = context.driver.session() - let sessionRes = await session.run( + let transactionRes = await session.run( `MATCH (badge:Badge {id: $badgeId})-[reward:REWARDED]->(rewardedUser:User {id: $rewardedUserId}) DELETE reward RETURN rewardedUser {.id}`, @@ -36,7 +36,7 @@ export default { rewardedUserId: toUserId } ) - const [rewardedUser] = sessionRes.records.map(record => { + const [rewardedUser] = transactionRes.records.map(record => { return record.get('rewardedUser') }) session.close() diff --git a/backend/src/resolvers/shout.js b/backend/src/resolvers/shout.js new file mode 100644 index 000000000..69c39a3a9 --- /dev/null +++ b/backend/src/resolvers/shout.js @@ -0,0 +1,51 @@ +export default { + Mutation: { + shout: async (_object, params, context, _resolveInfo) => { + const { id, type } = params + + const session = context.driver.session() + let transactionRes = await session.run( + `MATCH (node {id: $id})<-[:WROTE]-(userWritten:User), (user:User {id: $userId}) + WHERE $type IN labels(node) AND NOT userWritten.id = $userId + MERGE (user)-[relation:SHOUTED]->(node) + RETURN COUNT(relation) > 0 as isShouted`, + { + id, + type, + userId: context.user.id + } + ) + + const [isShouted] = transactionRes.records.map(record => { + return record.get('isShouted') + }) + + session.close() + + return isShouted + }, + + unshout: async (_object, params, context, _resolveInfo) => { + const { id, type } = params + const session = context.driver.session() + + let transactionRes = await session.run( + `MATCH (user:User {id: $userId})-[relation:SHOUTED]->(node {id: $id}) + WHERE $type IN labels(node) + DELETE relation + RETURN COUNT(relation) > 0 as isShouted`, + { + id, + type, + userId: context.user.id + } + ) + const [isShouted] = transactionRes.records.map(record => { + return record.get('isShouted') + }) + session.close() + + return isShouted + } + } +} diff --git a/backend/src/schema.graphql b/backend/src/schema.graphql index 94e28d0d7..ff8b04dfc 100644 --- a/backend/src/schema.graphql +++ b/backend/src/schema.graphql @@ -1,8 +1,8 @@ type Query { isLoggedIn: Boolean! - "Get the currently logged in User based on the given JWT Token" + # Get the currently logged in User based on the given JWT Token currentUser: User - "Get the latest Network Statistics" + # Get the latest Network Statistics statistics: Statistics! findPosts(filter: String!, limit: Int = 10): [Post]! @cypher( statement: """ @@ -18,7 +18,7 @@ type Query { ) } type Mutation { - "Get a JWT Token for the given Email and password" + # Get a JWT Token for the given Email and password login(email: String!, password: String!): String! signup(email: String!, password: String!): Boolean! changePassword(oldPassword:String!, newPassword: String!): String! @@ -27,34 +27,14 @@ type Mutation { enable(id: ID!): ID reward(fromBadgeId: ID!, toUserId: ID!): ID unreward(fromBadgeId: ID!, toUserId: ID!): ID - "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 - """) + # Shout the given Type and ID + shout(id: ID!, type: ShoutTypeEnum): Boolean! + # Unshout the given Type and ID + unshout(id: ID!, type: ShoutTypeEnum): Boolean! + # Follow the given Type and ID + follow(id: ID!, type: FollowTypeEnum): Boolean! + # Unfollow the given Type and ID + unfollow(id: ID!, type: FollowTypeEnum): Boolean! } type Statistics { @@ -144,11 +124,13 @@ type User { followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN") 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 - """) + # 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]! @@ -156,11 +138,13 @@ type User { # statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp" # ) contributions: [Post]! @relation(name: "WROTE", direction: "OUT") - contributionsCount: Int! @cypher(statement: """ - MATCH (this)-[:WROTE]->(r:Post) - WHERE (NOT exists(r.deleted) OR r.deleted = false) - AND (NOT exists(r.disabled) OR r.disabled = false) - RETURN COUNT(r)""" + contributionsCount: Int! @cypher( + statement: """ + MATCH (this)-[:WROTE]->(r:Post) + WHERE (NOT exists(r.deleted) OR r.deleted = false) + AND (NOT exists(r.disabled) OR r.disabled = false) + RETURN COUNT(r) + """ ) comments: [Comment]! @relation(name: "WROTE", direction: "OUT") @@ -197,11 +181,13 @@ type Post { createdAt: String updatedAt: String - relatedContributions: [Post]! @cypher(statement: """ - MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post) - RETURN DISTINCT post - LIMIT 10 - """) + relatedContributions: [Post]! @cypher( + statement: """ + MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post) + RETURN DISTINCT post + LIMIT 10 + """ + ) tags: [Tag]! @relation(name: "TAGGED", direction: "OUT") categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT") @@ -212,11 +198,13 @@ type Post { 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(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 - """) + # 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 { @@ -315,6 +303,7 @@ type Tag { deleted: Boolean disabled: Boolean } + type SharedInboxEndpoint { id: ID! uri: String