diff --git a/src/graphql-schema.js b/src/graphql-schema.js index 9f7699c77..b71c64ab7 100644 --- a/src/graphql-schema.js +++ b/src/graphql-schema.js @@ -4,17 +4,81 @@ import path from 'path' import bcrypt from 'bcryptjs' import zipObject from 'lodash/zipObject' import generateJwt from './jwt/generateToken' +import values from 'lodash/values' import { fixUrl } from './middleware/fixImageUrlsMiddleware' export const typeDefs = fs.readFileSync(process.env.GRAPHQL_SCHEMA || path.join(__dirname, "schema.graphql")) .toString('utf-8') +const query = (cypher, session) => { + return new Promise((resolve, reject) => { + let data = [] + session + .run(cypher) + .subscribe({ + onNext: function (record) { + let item = {} + record.keys.forEach(key => { + item[key] = record.get(key) + }) + data.push(item) + }, + onCompleted: function () { + session.close() + resolve(data) + }, + onError: function (error) { + reject(error) + } + }) + }) +} +const queryOne = (cypher, session) => { + return new Promise((resolve, reject) => { + query(cypher, session) + .then(res => { + resolve(res.length ? res.pop() : {}) + }) + .catch(err => { + reject(err) + }) + }) +} + export const resolvers = { Query: { - isLoggedIn: (parent, args, { user }) => { - console.log(user) + isLoggedIn: (parent, args, { driver, user }) => { return Boolean(user && user.id) + }, + statistics: async (parent, args, { driver, user }) => { + return new Promise(async (resolve) => { + const session = driver.session() + const queries = { + countUsers: `MATCH (r:User) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countUsers`, + countPosts: `MATCH (r:Post) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countPosts`, + countComments: `MATCH (r:Comment) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countComments`, + countNotifications: `MATCH (r:Notification) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countNotifications`, + countOrganizations: `MATCH (r:Organization) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countOrganizations`, + countProjects: `MATCH (r:Project) WHERE r.deleted <> true OR NOT exists(r.deleted) RETURN COUNT(r) AS countProjects`, + countInvites: `MATCH (r:Invite) WHERE r.wasUsed <> true OR NOT exists(r.wasUsed) RETURN COUNT(r) AS countInvites`, + countFollows: `MATCH (:User)-[r:FOLLOWS]->(:User) RETURN COUNT(r) AS countFollows`, + countShouts: `MATCH (:User)-[r:SHOUTED]->(:Post) RETURN COUNT(r) AS countShouts` + } + let data = { + countUsers: (await queryOne(queries.countUsers, session)).countUsers, + countPosts: (await queryOne(queries.countPosts, session)).countPosts, + countComments: (await queryOne(queries.countComments, session)).countComments, + countNotifications: (await queryOne(queries.countNotifications, session)).countNotifications, + countOrganizations: (await queryOne(queries.countOrganizations, session)).countOrganizations, + countProjects: (await queryOne(queries.countProjects, session)).countProjects, + countInvites: (await queryOne(queries.countInvites, session)).countInvites, + countFollows: (await queryOne(queries.countFollows, session)).countFollows, + countShouts: (await queryOne(queries.countShouts, session)).countShouts + } + resolve(data) + }) + } // usersBySubstring: neo4jgraphql }, @@ -53,9 +117,11 @@ export const resolvers = { token: generateJwt(u) }) } + session.close() throw new Error('Incorrect password.') } + session.close() throw new Error('No Such User exists.') } } diff --git a/src/schema.graphql b/src/schema.graphql index 0334b4124..8ec8310ca 100644 --- a/src/schema.graphql +++ b/src/schema.graphql @@ -1,5 +1,6 @@ type Query { isLoggedIn: Boolean! + statistics: Statistics! } type Mutation { login(email: String!, password: String!): LoggedInUser @@ -11,10 +12,22 @@ type LoggedInUser { name: String! avatar:String! email: String! - role: String!, + role: String! token: String! } +type Statistics { + countUsers: Int! + countPosts: Int! + countComments: Int! + countNotifications: Int! + countOrganizations: Int! + countProjects: Int! + countInvites: Int! + countFollows: Int! + countShouts: Int! +} + enum VisibilityEnum { Public Friends @@ -44,13 +57,13 @@ type WrittenComment @relation(name: "WROTE") { type User { id: ID! - name: String @default(to: "Anonymus") + name: String email: String slug: String password: String! avatar: String - deleted: Boolean @default(to: false) - disabled: Boolean @default(to: false) + deleted: Boolean + disabled: Boolean role: UserGroupEnum friends: [User]! @relation(name: "FRIENDS", direction: "BOTH") @@ -75,10 +88,10 @@ type User { ) comments: [WrittenComment]! - commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) RETURN COUNT(r)") + commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)") shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT") - shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) RETURN COUNT(r)") + shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)") organizationsCreated: [Organization] @relation(name: "CREATED_ORGA", direction: "OUT") organizationsOwned: [Organization] @relation(name: "OWNING_ORGA", direction: "OUT") @@ -98,8 +111,8 @@ type Post { contentExcerpt: String image: String visibility: VisibilityEnum - deleted: Boolean @default(to: false) - disabled: Boolean @default(to: false) + deleted: Boolean + disabled: Boolean relatedContributions: [Post]! @cypher(statement: """ MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post) @@ -114,7 +127,7 @@ 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) RETURN COUNT(r)") + shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)") } type Comment { @@ -123,8 +136,8 @@ type Comment { content: String! contentExcerpt: String post: Post @relation(name: "COMMENT", direction: "OUT") - deleted: Boolean @default(to: false) - disabled: Boolean @default(to: false) + deleted: Boolean + disabled: Boolean } type Category { @@ -163,8 +176,8 @@ type Organization { slug: String description: String! descriptionExcerpt: String - deleted: Boolean @default(to: false) - disabled: Boolean @default(to: false) + deleted: Boolean + disabled: Boolean tags: [Tag]! @relation(name: "TAGGED", direction: "OUT") categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT") @@ -176,5 +189,5 @@ type Tag { taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN") taggedOrganizations: [Organization]! @relation(name: "TAGGED", direction: "IN") taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(r) RETURN COUNT(r)") - deleted: Boolean @default(to: false) + deleted: Boolean }