diff --git a/backend/.env.template b/backend/.env.template index abc62b2dc..e905d1eb6 100644 --- a/backend/.env.template +++ b/backend/.env.template @@ -4,7 +4,7 @@ NEO4J_PASSWORD=letmein GRAPHQL_PORT=4000 GRAPHQL_URI=http://localhost:4000 CLIENT_URI=http://localhost:3000 -MOCK=false +MOCKS=false JWT_SECRET="b/&&7b78BF&fv/Vd" MAPBOX_TOKEN="pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ" diff --git a/backend/package.json b/backend/package.json index d8d0408f4..d861f9b6c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -11,7 +11,7 @@ "lint": "eslint src --config .eslintrc.js", "test": "run-s test:jest test:cucumber", "test:before:server": "cross-env GRAPHQL_URI=http://localhost:4123 GRAPHQL_PORT=4123 yarn run dev 2> /dev/null", - "test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null", + "test:before:seeder": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DEBUG=true DISABLED_MIDDLEWARES=permissions,activityPub yarn run dev 2> /dev/null", "test:jest:cmd": "wait-on tcp:4001 tcp:4123 && jest --forceExit --detectOpenHandles --runInBand", "test:cucumber:cmd": "wait-on tcp:4001 tcp:4123 && cucumber-js --require-module @babel/register --exit test/", "test:jest:cmd:debug": "wait-on tcp:4001 tcp:4123 && node --inspect-brk ./node_modules/.bin/jest -i --forceExit --detectOpenHandles --runInBand", @@ -19,8 +19,8 @@ "test:cucumber": " cross-env CLIENT_URI=http://localhost:4123 run-p --race test:before:* 'test:cucumber:cmd {@}' --", "test:jest:debug": "run-p --race test:before:* 'test:jest:cmd:debug {@}' --", "db:script:seed": "wait-on tcp:4001 && babel-node src/seed/seed-db.js", - "db:reset": "babel-node src/seed/reset-db.js", - "db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DISABLED_MIDDLEWARES=permissions run-p --race dev db:script:seed" + "db:reset": "cross-env DEBUG=true babel-node src/seed/reset-db.js", + "db:seed": "cross-env GRAPHQL_URI=http://localhost:4001 GRAPHQL_PORT=4001 DEBUG=true DISABLED_MIDDLEWARES=permissions run-p --race dev db:script:seed" }, "author": "Human Connection gGmbH", "license": "MIT", @@ -47,7 +47,7 @@ "apollo-client": "~2.5.1", "apollo-link-context": "~1.0.14", "apollo-link-http": "~1.5.14", - "apollo-server": "~2.5.1", + "apollo-server": "~2.6.1", "bcryptjs": "~2.4.3", "cheerio": "~1.0.0-rc.3", "cors": "~2.8.5", @@ -109,4 +109,4 @@ "prettier": "~1.17.1", "supertest": "~4.0.2" } -} +} \ No newline at end of file diff --git a/backend/src/activitypub/ActivityPub.js b/backend/src/activitypub/ActivityPub.js index da1056362..12671f330 100644 --- a/backend/src/activitypub/ActivityPub.js +++ b/backend/src/activitypub/ActivityPub.js @@ -4,9 +4,9 @@ import request from 'request' import as from 'activitystrea.ms' import NitroDataSource from './NitroDataSource' import router from './routes' -import dotenv from 'dotenv' import Collections from './Collections' import uuid from 'uuid/v4' +import CONFIG from '../config' const debug = require('debug')('ea') let activityPub = null @@ -22,11 +22,7 @@ export default class ActivityPub { static init(server) { if (!activityPub) { - dotenv.config() - activityPub = new ActivityPub( - process.env.CLIENT_URI || 'http://localhost:3000', - process.env.GRAPHQL_URI || 'http://localhost:4000', - ) + activityPub = new ActivityPub(CONFIG.CLIENT_URI, CONFIG.GRAPHQL_URI) // integrate into running graphql express server server.express.set('ap', activityPub) diff --git a/backend/src/activitypub/security/index.js b/backend/src/activitypub/security/index.js index 7f619acbe..9b48b7ed9 100644 --- a/backend/src/activitypub/security/index.js +++ b/backend/src/activitypub/security/index.js @@ -1,13 +1,15 @@ -import dotenv from 'dotenv' -import { resolve } from 'path' +// import dotenv from 'dotenv' +// import { resolve } from 'path' import crypto from 'crypto' import request from 'request' +import CONFIG from './../../config' const debug = require('debug')('ea:security') -dotenv.config({ path: resolve('src', 'activitypub', '.env') }) +// TODO Does this reference a local config? Why? +// dotenv.config({ path: resolve('src', 'activitypub', '.env') }) export function generateRsaKeyPair(options = {}) { - const { passphrase = process.env.PRIVATE_KEY_PASSPHRASE } = options + const { passphrase = CONFIG.PRIVATE_KEY_PASSPHRASE } = options return crypto.generateKeyPairSync('rsa', { modulusLength: 4096, publicKeyEncoding: { @@ -31,7 +33,7 @@ export function createSignature(options) { url, headers = {}, algorithm = 'rsa-sha256', - passphrase = process.env.PRIVATE_KEY_PASSPHRASE, + passphrase = CONFIG.PRIVATE_KEY_PASSPHRASE, } = options if (!SUPPORTED_HASH_ALGORITHMS.includes(algorithm)) { throw Error(`SIGNING: Unsupported hashing algorithm = ${algorithm}`) diff --git a/backend/src/activitypub/utils/index.js b/backend/src/activitypub/utils/index.js index ee7ae2606..3927f4056 100644 --- a/backend/src/activitypub/utils/index.js +++ b/backend/src/activitypub/utils/index.js @@ -2,6 +2,7 @@ import { activityPub } from '../ActivityPub' import gql from 'graphql-tag' import { createSignature } from '../security' import request from 'request' +import CONFIG from './../../config' const debug = require('debug')('ea:utils') export function extractNameFromId(uri) { @@ -38,7 +39,7 @@ export function throwErrorIfApolloErrorOccurred(result) { export function signAndSend(activity, fromName, targetDomain, url) { // fix for development: replace with http url = url.indexOf('localhost') > -1 ? url.replace('https', 'http') : url - debug(`passhprase = ${process.env.PRIVATE_KEY_PASSPHRASE}`) + debug(`passhprase = ${CONFIG.PRIVATE_KEY_PASSPHRASE}`) return new Promise(async (resolve, reject) => { debug('inside signAndSend') // get the private key diff --git a/backend/src/bootstrap/neo4j.js b/backend/src/bootstrap/neo4j.js index 292983359..bfa68acf3 100644 --- a/backend/src/bootstrap/neo4j.js +++ b/backend/src/bootstrap/neo4j.js @@ -1,15 +1,13 @@ import { v1 as neo4j } from 'neo4j-driver' -import dotenv from 'dotenv' - -dotenv.config() +import CONFIG from './../config' let driver export function getDriver(options = {}) { const { - uri = process.env.NEO4J_URI || 'bolt://localhost:7687', - username = process.env.NEO4J_USERNAME || 'neo4j', - password = process.env.NEO4J_PASSWORD || 'neo4j', + uri = CONFIG.NEO4J_URI, + username = CONFIG.NEO4J_USERNAME, + password = CONFIG.NEO4J_PASSWORD, } = options if (!driver) { driver = neo4j.driver(uri, neo4j.auth.basic(username, password)) diff --git a/backend/src/config/index.js b/backend/src/config/index.js new file mode 100644 index 000000000..5cc455495 --- /dev/null +++ b/backend/src/config/index.js @@ -0,0 +1,34 @@ +import dotenv from 'dotenv' + +dotenv.config() + +export const requiredConfigs = { + MAPBOX_TOKEN: process.env.MAPBOX_TOKEN, + JWT_SECRET: process.env.JWT_SECRET, + PRIVATE_KEY_PASSPHRASE: process.env.PRIVATE_KEY_PASSPHRASE, +} + +export const neo4jConfigs = { + NEO4J_URI: process.env.NEO4J_URI || 'bolt://localhost:7687', + NEO4J_USERNAME: process.env.NEO4J_USERNAME || 'neo4j', + NEO4J_PASSWORD: process.env.NEO4J_PASSWORD || 'neo4j', +} + +export const serverConfigs = { + GRAPHQL_PORT: process.env.GRAPHQL_PORT || 4000, + CLIENT_URI: process.env.CLIENT_URI || 'http://localhost:3000', + GRAPHQL_URI: process.env.GRAPHQL_URI || 'http://localhost:4000', +} + +export const developmentConfigs = { + DEBUG: process.env.NODE_ENV !== 'production' && process.env.DEBUG === 'true', + MOCKS: process.env.MOCKS === 'true', + DISABLED_MIDDLEWARES: process.env.DISABLED_MIDDLEWARES || '', +} + +export default { + ...requiredConfigs, + ...neo4jConfigs, + ...serverConfigs, + ...developmentConfigs, +} diff --git a/backend/src/graphql-schema.js b/backend/src/graphql-schema.js deleted file mode 100644 index 01b40dfc5..000000000 --- a/backend/src/graphql-schema.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as typeDefs } from './types' -export { default as resolvers } from './resolvers' diff --git a/backend/src/index.js b/backend/src/index.js index 2095d171f..f28e58947 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -1,17 +1,18 @@ import createServer from './server' import ActivityPub from './activitypub/ActivityPub' +import CONFIG from './config' const serverConfig = { - port: process.env.GRAPHQL_PORT || 4000, + port: CONFIG.GRAPHQL_PORT, // cors: { // credentials: true, - // origin: [process.env.CLIENT_URI] // your frontend url. + // origin: [CONFIG.CLIENT_URI] // your frontend url. // } } const server = createServer() server.start(serverConfig, options => { /* eslint-disable-next-line no-console */ - console.log(`GraphQLServer ready at ${process.env.GRAPHQL_URI} 🚀`) + console.log(`GraphQLServer ready at ${CONFIG.GRAPHQL_URI} 🚀`) ActivityPub.init(server) }) diff --git a/backend/src/jwt/decode.js b/backend/src/jwt/decode.js index d4485952d..b98357103 100644 --- a/backend/src/jwt/decode.js +++ b/backend/src/jwt/decode.js @@ -1,11 +1,12 @@ import jwt from 'jsonwebtoken' +import CONFIG from './../config' export default async (driver, authorizationHeader) => { if (!authorizationHeader) return null const token = authorizationHeader.replace('Bearer ', '') let id = null try { - const decoded = await jwt.verify(token, process.env.JWT_SECRET) + const decoded = await jwt.verify(token, CONFIG.JWT_SECRET) id = decoded.sub } catch (err) { return null diff --git a/backend/src/jwt/encode.js b/backend/src/jwt/encode.js index 49aa17bd0..97c6dcd66 100644 --- a/backend/src/jwt/encode.js +++ b/backend/src/jwt/encode.js @@ -1,15 +1,16 @@ import jwt from 'jsonwebtoken' import ms from 'ms' +import CONFIG from './../config' // Generate an Access Token for the given User ID export default function encode(user) { - const token = jwt.sign(user, process.env.JWT_SECRET, { + const token = jwt.sign(user, CONFIG.JWT_SECRET, { expiresIn: ms('1d'), - issuer: process.env.GRAPHQL_URI, - audience: process.env.CLIENT_URI, + issuer: CONFIG.GRAPHQL_URI, + audience: CONFIG.CLIENT_URI, subject: user.id.toString(), }) - // jwt.verifySignature(token, process.env.JWT_SECRET, (err, data) => { + // jwt.verifySignature(token, CONFIG.JWT_SECRET, (err, data) => { // console.log('token verification:', err, data) // }) return token diff --git a/backend/src/middleware/activityPubMiddleware.js b/backend/src/middleware/activityPubMiddleware.js index 43da21e26..f3ced42f9 100644 --- a/backend/src/middleware/activityPubMiddleware.js +++ b/backend/src/middleware/activityPubMiddleware.js @@ -1,10 +1,8 @@ import { generateRsaKeyPair } from '../activitypub/security' import { activityPub } from '../activitypub/ActivityPub' import as from 'activitystrea.ms' -import dotenv from 'dotenv' const debug = require('debug')('backend:schema') -dotenv.config() export default { Mutation: { diff --git a/backend/src/middleware/index.js b/backend/src/middleware/index.js index bef6ceac9..754c59bbb 100644 --- a/backend/src/middleware/index.js +++ b/backend/src/middleware/index.js @@ -1,42 +1,63 @@ -import activityPubMiddleware from './activityPubMiddleware' -import passwordMiddleware from './passwordMiddleware' -import softDeleteMiddleware from './softDeleteMiddleware' -import sluggifyMiddleware from './sluggifyMiddleware' -import fixImageUrlsMiddleware from './fixImageUrlsMiddleware' -import excerptMiddleware from './excerptMiddleware' -import dateTimeMiddleware from './dateTimeMiddleware' -import xssMiddleware from './xssMiddleware' -import permissionsMiddleware from './permissionsMiddleware' -import userMiddleware from './userMiddleware' -import includedFieldsMiddleware from './includedFieldsMiddleware' -import orderByMiddleware from './orderByMiddleware' -import validationMiddleware from './validation' -import notificationsMiddleware from './notifications' +import CONFIG from './../config' +import activityPub from './activityPubMiddleware' +import password from './passwordMiddleware' +import softDelete from './softDeleteMiddleware' +import sluggify from './sluggifyMiddleware' +import fixImageUrls from './fixImageUrlsMiddleware' +import excerpt from './excerptMiddleware' +import dateTime from './dateTimeMiddleware' +import xss from './xssMiddleware' +import permissions from './permissionsMiddleware' +import user from './userMiddleware' +import includedFields from './includedFieldsMiddleware' +import orderBy from './orderByMiddleware' +import validation from './validation' +import notifications from './notifications' export default schema => { - let middleware = [ - passwordMiddleware, - dateTimeMiddleware, - validationMiddleware, - sluggifyMiddleware, - excerptMiddleware, - notificationsMiddleware, - xssMiddleware, - fixImageUrlsMiddleware, - softDeleteMiddleware, - userMiddleware, - includedFieldsMiddleware, - orderByMiddleware, + const middlewares = { + permissions: permissions, + activityPub: activityPub, + password: password, + dateTime: dateTime, + validation: validation, + sluggify: sluggify, + excerpt: excerpt, + notifications: notifications, + xss: xss, + fixImageUrls: fixImageUrls, + softDelete: softDelete, + user: user, + includedFields: includedFields, + orderBy: orderBy, + } + + let order = [ + 'permissions', + 'activityPub', + 'password', + 'dateTime', + 'validation', + 'sluggify', + 'excerpt', + 'notifications', + 'xss', + 'fixImageUrls', + 'softDelete', + 'user', + 'includedFields', + 'orderBy', ] // add permisions middleware at the first position (unless we're seeding) - // NOTE: DO NOT SET THE PERMISSION FLAT YOUR SELF - if (process.env.NODE_ENV !== 'production') { - const DISABLED_MIDDLEWARES = process.env.DISABLED_MIDDLEWARES || '' - const disabled = DISABLED_MIDDLEWARES.split(',') - if (!disabled.includes('activityPub')) middleware.unshift(activityPubMiddleware) - if (!disabled.includes('permissions')) - middleware.unshift(permissionsMiddleware.generate(schema)) + if (CONFIG.DEBUG) { + const disabledMiddlewares = CONFIG.DISABLED_MIDDLEWARES.split(',') + order = order.filter(key => { + return !disabledMiddlewares.includes(key) + }) + /* eslint-disable-next-line no-console */ + console.log(`Warning: "${disabledMiddlewares}" middlewares have been disabled.`) } - return middleware + + return order.map(key => middlewares[key]) } diff --git a/backend/src/middleware/nodes/locations.js b/backend/src/middleware/nodes/locations.js index a0adeb57f..62d1e3a65 100644 --- a/backend/src/middleware/nodes/locations.js +++ b/backend/src/middleware/nodes/locations.js @@ -2,6 +2,7 @@ import request from 'request' import { UserInputError } from 'apollo-server' import isEmpty from 'lodash/isEmpty' import asyncForEach from '../../helpers/asyncForEach' +import CONFIG from './../../config' const fetch = url => { return new Promise((resolve, reject) => { @@ -58,11 +59,12 @@ const createOrUpdateLocations = async (userId, locationName, driver) => { if (isEmpty(locationName)) { return } - const mapboxToken = process.env.MAPBOX_TOKEN const res = await fetch( `https://api.mapbox.com/geocoding/v5/mapbox.places/${encodeURIComponent( locationName, - )}.json?access_token=${mapboxToken}&types=region,place,country&language=${locales.join(',')}`, + )}.json?access_token=${CONFIG.MAPBOX_TOKEN}&types=region,place,country&language=${locales.join( + ',', + )}`, ) if (!res || !res.features || !res.features[0]) { diff --git a/backend/src/middleware/orderByMiddleware.spec.js b/backend/src/middleware/orderByMiddleware.spec.js index 658447160..450220cd6 100644 --- a/backend/src/middleware/orderByMiddleware.spec.js +++ b/backend/src/middleware/orderByMiddleware.spec.js @@ -1,6 +1,6 @@ +import { GraphQLClient } from 'graphql-request' import Factory from '../seed/factories' import { host } from '../jest/helpers' -import { GraphQLClient } from 'graphql-request' let client let headers diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index b1a08a14d..bc9b4c525 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -16,11 +16,15 @@ const isAdmin = rule()(async (parent, args, { user }, info) => { return user && user.role === 'admin' }) -const isMyOwn = rule({ cache: 'no_cache' })(async (parent, args, context, info) => { +const isMyOwn = rule({ + cache: 'no_cache', +})(async (parent, args, context, info) => { return context.user.id === parent.id }) -const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { +const belongsToMe = rule({ + cache: 'no_cache', +})(async (_, args, context) => { const { driver, user: { id: userId }, @@ -32,7 +36,10 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { MATCH (u:User {id: $userId})<-[:NOTIFIED]-(n:Notification {id: $notificationId}) RETURN n `, - { userId, notificationId }, + { + userId, + notificationId, + }, ) const [notification] = result.records.map(record => { return record.get('n') @@ -41,21 +48,27 @@ const belongsToMe = rule({ cache: 'no_cache' })(async (_, args, context) => { return Boolean(notification) }) -const onlyEnabledContent = rule({ cache: 'strict' })(async (parent, args, ctx, info) => { +const onlyEnabledContent = rule({ + cache: 'strict', +})(async (parent, args, ctx, info) => { const { disabled, deleted } = args return !(disabled || deleted) }) -const isAuthor = rule({ cache: 'no_cache' })(async (parent, args, { user, driver }) => { +const isAuthor = rule({ + cache: 'no_cache', +})(async (parent, args, { user, driver }) => { if (!user) return false const session = driver.session() - const { id: postId } = args + const { id: resourceId } = args const result = await session.run( ` - MATCH (post:Post {id: $postId})<-[:WROTE]-(author) + MATCH (resource {id: $resourceId})<-[:WROTE]-(author) RETURN author `, - { postId }, + { + resourceId, + }, ) const [author] = result.records.map(record => { return record.get('author') @@ -100,6 +113,7 @@ const permissions = shield({ enable: isModerator, disable: isModerator, CreateComment: isAuthenticated, + DeleteComment: isAuthor, // CreateUser: allow, }, User: { diff --git a/backend/src/middleware/permissionsMiddleware.spec.js b/backend/src/middleware/permissionsMiddleware.spec.js index fc1815631..6cf9dc302 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.js +++ b/backend/src/middleware/permissionsMiddleware.spec.js @@ -1,6 +1,6 @@ +import { GraphQLClient } from 'graphql-request' import Factory from '../seed/factories' import { host, login } from '../jest/helpers' -import { GraphQLClient } from 'graphql-request' const factory = Factory() diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js index 7ca4ec193..79bba0a5d 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.js +++ b/backend/src/middleware/slugifyMiddleware.spec.js @@ -1,6 +1,6 @@ +import { GraphQLClient } from 'graphql-request' import Factory from '../seed/factories' import { host, login } from '../jest/helpers' -import { GraphQLClient } from 'graphql-request' let authenticatedClient let headers diff --git a/backend/src/middleware/softDeleteMiddleware.spec.js b/backend/src/middleware/softDeleteMiddleware.spec.js index 4265599dd..388f44a3c 100644 --- a/backend/src/middleware/softDeleteMiddleware.spec.js +++ b/backend/src/middleware/softDeleteMiddleware.spec.js @@ -1,6 +1,6 @@ +import { GraphQLClient } from 'graphql-request' import Factory from '../seed/factories' import { host, login } from '../jest/helpers' -import { GraphQLClient } from 'graphql-request' const factory = Factory() let client diff --git a/backend/src/middleware/userMiddleware.js b/backend/src/middleware/userMiddleware.js index 079ba310a..29e512ebd 100644 --- a/backend/src/middleware/userMiddleware.js +++ b/backend/src/middleware/userMiddleware.js @@ -1,9 +1,5 @@ -import dotenv from 'dotenv' - import createOrUpdateLocations from './nodes/locations' -dotenv.config() - export default { Mutation: { CreateUser: async (resolve, root, args, context, info) => { diff --git a/backend/src/schema/index.js b/backend/src/schema/index.js new file mode 100644 index 000000000..d294d8aba --- /dev/null +++ b/backend/src/schema/index.js @@ -0,0 +1,24 @@ +import { makeAugmentedSchema } from 'neo4j-graphql-js' +import CONFIG from './../config' +import applyScalars from './../bootstrap/scalars' +import applyDirectives from './../bootstrap/directives' +import typeDefs from './types' +import resolvers from './resolvers' + +export default applyScalars( + applyDirectives( + makeAugmentedSchema({ + typeDefs, + resolvers, + config: { + query: { + exclude: ['Notfication', 'Statistics', 'LoggedInUser'], + }, + mutation: { + exclude: ['Notfication', 'Statistics', 'LoggedInUser'], + }, + debug: CONFIG.DEBUG, + }, + }), + ), +) diff --git a/backend/src/resolvers/badges.spec.js b/backend/src/schema/resolvers/badges.spec.js similarity index 98% rename from backend/src/resolvers/badges.spec.js rename to backend/src/schema/resolvers/badges.spec.js index 8383d9838..a0dbafe00 100644 --- a/backend/src/resolvers/badges.spec.js +++ b/backend/src/schema/resolvers/badges.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let client diff --git a/backend/src/resolvers/comments.js b/backend/src/schema/resolvers/comments.js similarity index 90% rename from backend/src/resolvers/comments.js rename to backend/src/schema/resolvers/comments.js index 949b77041..7f3b11da4 100644 --- a/backend/src/resolvers/comments.js +++ b/backend/src/schema/resolvers/comments.js @@ -53,6 +53,11 @@ export default { ) session.close() + return comment + }, + DeleteComment: async (object, params, context, resolveInfo) => { + const comment = await neo4jgraphql(object, params, context, resolveInfo, false) + return comment }, }, diff --git a/backend/src/resolvers/comments.spec.js b/backend/src/schema/resolvers/comments.spec.js similarity index 62% rename from backend/src/resolvers/comments.spec.js rename to backend/src/schema/resolvers/comments.spec.js index 451c559f1..55b946bb9 100644 --- a/backend/src/resolvers/comments.spec.js +++ b/backend/src/schema/resolvers/comments.spec.js @@ -1,6 +1,7 @@ -import Factory from '../seed/factories' +import gql from 'graphql-tag' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let client @@ -21,22 +22,22 @@ afterEach(async () => { }) describe('CreateComment', () => { - const createCommentMutation = ` - mutation($postId: ID, $content: String!) { - CreateComment(postId: $postId, content: $content) { - id - content + const createCommentMutation = gql` + mutation($postId: ID, $content: String!) { + CreateComment(postId: $postId, content: $content) { + id + content + } } - } ` - const createPostMutation = ` - mutation($id: ID!, $title: String!, $content: String!) { - CreatePost(id: $id, title: $title, content: $content) { - id + const createPostMutation = gql` + mutation($id: ID!, $title: String!, $content: String!) { + CreatePost(id: $id, title: $title, content: $content) { + id + } } - } ` - const commentQueryForPostId = ` + const commentQueryForPostId = gql` query($content: String) { Comment(content: $content) { postId @@ -59,8 +60,13 @@ describe('CreateComment', () => { describe('authenticated', () => { let headers beforeEach(async () => { - headers = await login({ email: 'test@example.org', password: '1234' }) - client = new GraphQLClient(host, { headers }) + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) createCommentVariables = { postId: 'p1', content: "I'm authorised to comment", @@ -88,15 +94,25 @@ describe('CreateComment', () => { it('assigns the authenticated user as author', async () => { await client.request(createCommentMutation, createCommentVariables) - const { User } = await client.request(`{ + const { User } = await client.request(gql` + { User(email: "test@example.org") { comments { content } } - }`) + } + `) - expect(User).toEqual([{ comments: [{ content: "I'm authorised to comment" }] }]) + expect(User).toEqual([ + { + comments: [ + { + content: "I'm authorised to comment", + }, + ], + }, + ]) }) it('throw an error if an empty string is sent from the editor as content', async () => { @@ -186,7 +202,98 @@ describe('CreateComment', () => { commentQueryForPostId, commentQueryVariablesByContent, ) - expect(Comment).toEqual([{ postId: null }]) + expect(Comment).toEqual([ + { + postId: null, + }, + ]) + }) + }) +}) + +describe('DeleteComment', () => { + const deleteCommentMutation = gql` + mutation($id: ID!) { + DeleteComment(id: $id) { + id + } + } + ` + + let deleteCommentVariables = { + id: 'c1', + } + + beforeEach(async () => { + const asAuthor = Factory() + await asAuthor.create('User', { + email: 'author@example.org', + password: '1234', + }) + await asAuthor.authenticateAs({ + email: 'author@example.org', + password: '1234', + }) + await asAuthor.create('Post', { + id: 'p1', + content: 'Post to be commented', + }) + await asAuthor.create('Comment', { + id: 'c1', + postId: 'p1', + content: 'Comment to be deleted', + }) + }) + + describe('unauthenticated', () => { + it('throws authorization error', async () => { + client = new GraphQLClient(host) + await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated but not the author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('throws authorization error', async () => { + await expect(client.request(deleteCommentMutation, deleteCommentVariables)).rejects.toThrow( + 'Not Authorised', + ) + }) + }) + + describe('authenticated as author', () => { + beforeEach(async () => { + let headers + headers = await login({ + email: 'author@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) + }) + + it('deletes the comment', async () => { + const expected = { + DeleteComment: { + id: 'c1', + }, + } + await expect(client.request(deleteCommentMutation, deleteCommentVariables)).resolves.toEqual( + expected, + ) }) }) }) diff --git a/backend/src/resolvers/fileUpload/index.js b/backend/src/schema/resolvers/fileUpload/index.js similarity index 100% rename from backend/src/resolvers/fileUpload/index.js rename to backend/src/schema/resolvers/fileUpload/index.js diff --git a/backend/src/resolvers/fileUpload/spec.js b/backend/src/schema/resolvers/fileUpload/spec.js similarity index 100% rename from backend/src/resolvers/fileUpload/spec.js rename to backend/src/schema/resolvers/fileUpload/spec.js diff --git a/backend/src/resolvers/follow.js b/backend/src/schema/resolvers/follow.js similarity index 100% rename from backend/src/resolvers/follow.js rename to backend/src/schema/resolvers/follow.js diff --git a/backend/src/resolvers/follow.spec.js b/backend/src/schema/resolvers/follow.spec.js similarity index 97% rename from backend/src/resolvers/follow.spec.js rename to backend/src/schema/resolvers/follow.spec.js index 4a361b03d..d29e17938 100644 --- a/backend/src/resolvers/follow.spec.js +++ b/backend/src/schema/resolvers/follow.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let clientUser1 diff --git a/backend/src/resolvers/index.js b/backend/src/schema/resolvers/index.js similarity index 100% rename from backend/src/resolvers/index.js rename to backend/src/schema/resolvers/index.js diff --git a/backend/src/resolvers/moderation.js b/backend/src/schema/resolvers/moderation.js similarity index 100% rename from backend/src/resolvers/moderation.js rename to backend/src/schema/resolvers/moderation.js diff --git a/backend/src/resolvers/moderation.spec.js b/backend/src/schema/resolvers/moderation.spec.js similarity index 99% rename from backend/src/resolvers/moderation.spec.js rename to backend/src/schema/resolvers/moderation.spec.js index 835e9e535..b1dec603b 100644 --- a/backend/src/resolvers/moderation.spec.js +++ b/backend/src/schema/resolvers/moderation.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let client diff --git a/backend/src/resolvers/notifications.js b/backend/src/schema/resolvers/notifications.js similarity index 100% rename from backend/src/resolvers/notifications.js rename to backend/src/schema/resolvers/notifications.js diff --git a/backend/src/resolvers/notifications.spec.js b/backend/src/schema/resolvers/notifications.spec.js similarity index 98% rename from backend/src/resolvers/notifications.spec.js rename to backend/src/schema/resolvers/notifications.spec.js index 37d8c83ff..3876a4be3 100644 --- a/backend/src/resolvers/notifications.spec.js +++ b/backend/src/schema/resolvers/notifications.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let client diff --git a/backend/src/resolvers/posts.js b/backend/src/schema/resolvers/posts.js similarity index 100% rename from backend/src/resolvers/posts.js rename to backend/src/schema/resolvers/posts.js diff --git a/backend/src/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js similarity index 98% rename from backend/src/resolvers/posts.spec.js rename to backend/src/schema/resolvers/posts.spec.js index e4175ff09..9e2ec70a2 100644 --- a/backend/src/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let client diff --git a/backend/src/resolvers/reports.js b/backend/src/schema/resolvers/reports.js similarity index 100% rename from backend/src/resolvers/reports.js rename to backend/src/schema/resolvers/reports.js diff --git a/backend/src/resolvers/reports.spec.js b/backend/src/schema/resolvers/reports.spec.js similarity index 98% rename from backend/src/resolvers/reports.spec.js rename to backend/src/schema/resolvers/reports.spec.js index 19b5e6f9e..6b996b016 100644 --- a/backend/src/resolvers/reports.spec.js +++ b/backend/src/schema/resolvers/reports.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() diff --git a/backend/src/resolvers/rewards.js b/backend/src/schema/resolvers/rewards.js similarity index 100% rename from backend/src/resolvers/rewards.js rename to backend/src/schema/resolvers/rewards.js diff --git a/backend/src/resolvers/rewards.spec.js b/backend/src/schema/resolvers/rewards.spec.js similarity index 98% rename from backend/src/resolvers/rewards.spec.js rename to backend/src/schema/resolvers/rewards.spec.js index e2b67b25d..2bdd9a39b 100644 --- a/backend/src/resolvers/rewards.spec.js +++ b/backend/src/schema/resolvers/rewards.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() diff --git a/backend/src/resolvers/shout.js b/backend/src/schema/resolvers/shout.js similarity index 100% rename from backend/src/resolvers/shout.js rename to backend/src/schema/resolvers/shout.js diff --git a/backend/src/resolvers/shout.spec.js b/backend/src/schema/resolvers/shout.spec.js similarity index 97% rename from backend/src/resolvers/shout.spec.js rename to backend/src/schema/resolvers/shout.spec.js index 46570efa0..a94f7ca0b 100644 --- a/backend/src/resolvers/shout.spec.js +++ b/backend/src/schema/resolvers/shout.spec.js @@ -1,6 +1,6 @@ -import Factory from '../seed/factories' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() let clientUser1, clientUser2 diff --git a/backend/src/resolvers/socialMedia.js b/backend/src/schema/resolvers/socialMedia.js similarity index 100% rename from backend/src/resolvers/socialMedia.js rename to backend/src/schema/resolvers/socialMedia.js diff --git a/backend/src/resolvers/socialMedia.spec.js b/backend/src/schema/resolvers/socialMedia.spec.js similarity index 73% rename from backend/src/resolvers/socialMedia.spec.js rename to backend/src/schema/resolvers/socialMedia.spec.js index 5143587b1..38850761c 100644 --- a/backend/src/resolvers/socialMedia.spec.js +++ b/backend/src/schema/resolvers/socialMedia.spec.js @@ -1,13 +1,14 @@ -import Factory from '../seed/factories' +import gql from 'graphql-tag' import { GraphQLClient } from 'graphql-request' -import { host, login } from '../jest/helpers' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() -describe('CreateSocialMedia', () => { +describe('SocialMedia', () => { let client let headers - const mutationC = ` + const mutationC = gql` mutation($url: String!) { CreateSocialMedia(url: $url) { id @@ -15,7 +16,7 @@ describe('CreateSocialMedia', () => { } } ` - const mutationD = ` + const mutationD = gql` mutation($id: ID!) { DeleteSocialMedia(id: $id) { id @@ -42,19 +43,28 @@ describe('CreateSocialMedia', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { client = new GraphQLClient(host) - const variables = { url: 'http://nsosp.org' } + const variables = { + url: 'http://nsosp.org', + } await expect(client.request(mutationC, variables)).rejects.toThrow('Not Authorised') }) }) describe('authenticated', () => { beforeEach(async () => { - headers = await login({ email: 'test@example.org', password: '1234' }) - client = new GraphQLClient(host, { headers }) + headers = await login({ + email: 'test@example.org', + password: '1234', + }) + client = new GraphQLClient(host, { + headers, + }) }) it('creates social media with correct URL', async () => { - const variables = { url: 'http://nsosp.org' } + const variables = { + url: 'http://nsosp.org', + } await expect(client.request(mutationC, variables)).resolves.toEqual( expect.objectContaining({ CreateSocialMedia: { @@ -66,11 +76,15 @@ describe('CreateSocialMedia', () => { }) it('deletes social media', async () => { - const creationVariables = { url: 'http://nsosp.org' } + const creationVariables = { + url: 'http://nsosp.org', + } const { CreateSocialMedia } = await client.request(mutationC, creationVariables) const { id } = CreateSocialMedia - const deletionVariables = { id } + const deletionVariables = { + id, + } const expected = { DeleteSocialMedia: { id: id, @@ -81,12 +95,16 @@ describe('CreateSocialMedia', () => { }) it('rejects empty string', async () => { - const variables = { url: '' } + const variables = { + url: '', + } await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL') }) it('validates URLs', async () => { - const variables = { url: 'not-a-url' } + const variables = { + url: 'not-a-url', + } await expect(client.request(mutationC, variables)).rejects.toThrow('Input is not a URL') }) }) diff --git a/backend/src/resolvers/statistics.js b/backend/src/schema/resolvers/statistics.js similarity index 100% rename from backend/src/resolvers/statistics.js rename to backend/src/schema/resolvers/statistics.js diff --git a/backend/src/resolvers/user_management.js b/backend/src/schema/resolvers/user_management.js similarity index 98% rename from backend/src/resolvers/user_management.js rename to backend/src/schema/resolvers/user_management.js index e2fd5acf1..eb07a07b3 100644 --- a/backend/src/resolvers/user_management.js +++ b/backend/src/schema/resolvers/user_management.js @@ -1,4 +1,4 @@ -import encode from '../jwt/encode' +import encode from '../../jwt/encode' import bcrypt from 'bcryptjs' import { AuthenticationError } from 'apollo-server' import { neo4jgraphql } from 'neo4j-graphql-js' diff --git a/backend/src/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js similarity index 98% rename from backend/src/resolvers/user_management.spec.js rename to backend/src/schema/resolvers/user_management.spec.js index 9dff9e388..cf648a6bd 100644 --- a/backend/src/resolvers/user_management.spec.js +++ b/backend/src/schema/resolvers/user_management.spec.js @@ -1,8 +1,9 @@ import gql from 'graphql-tag' -import Factory from '../seed/factories' import { GraphQLClient, request } from 'graphql-request' import jwt from 'jsonwebtoken' -import { host, login } from '../jest/helpers' +import CONFIG from './../../config' +import Factory from '../../seed/factories' +import { host, login } from '../../jest/helpers' const factory = Factory() @@ -185,7 +186,7 @@ describe('login', () => { }), ) const token = data.login - jwt.verify(token, process.env.JWT_SECRET, (err, data) => { + jwt.verify(token, CONFIG.JWT_SECRET, (err, data) => { expect(data.email).toEqual('test@example.org') expect(err).toBeNull() }) diff --git a/backend/src/resolvers/users.js b/backend/src/schema/resolvers/users.js similarity index 100% rename from backend/src/resolvers/users.js rename to backend/src/schema/resolvers/users.js diff --git a/backend/src/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js similarity index 95% rename from backend/src/resolvers/users.spec.js rename to backend/src/schema/resolvers/users.spec.js index 22096b6c8..a5c50f4f9 100644 --- a/backend/src/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -1,6 +1,6 @@ import { GraphQLClient } from 'graphql-request' -import { host } from '../jest/helpers' -import Factory from '../seed/factories' +import { host } from '../../jest/helpers' +import Factory from '../../seed/factories' const factory = Factory() let client diff --git a/backend/src/types/enum/BadgeStatus.gql b/backend/src/schema/types/enum/BadgeStatus.gql similarity index 100% rename from backend/src/types/enum/BadgeStatus.gql rename to backend/src/schema/types/enum/BadgeStatus.gql diff --git a/backend/src/types/enum/BadgeType.gql b/backend/src/schema/types/enum/BadgeType.gql similarity index 100% rename from backend/src/types/enum/BadgeType.gql rename to backend/src/schema/types/enum/BadgeType.gql diff --git a/backend/src/types/enum/UserGroup.gql b/backend/src/schema/types/enum/UserGroup.gql similarity index 100% rename from backend/src/types/enum/UserGroup.gql rename to backend/src/schema/types/enum/UserGroup.gql diff --git a/backend/src/types/enum/Visibility.gql b/backend/src/schema/types/enum/Visibility.gql similarity index 100% rename from backend/src/types/enum/Visibility.gql rename to backend/src/schema/types/enum/Visibility.gql diff --git a/backend/src/types/index.js b/backend/src/schema/types/index.js similarity index 100% rename from backend/src/types/index.js rename to backend/src/schema/types/index.js diff --git a/backend/src/types/scalar/Date.gql_ b/backend/src/schema/types/scalar/Date.gql_ similarity index 100% rename from backend/src/types/scalar/Date.gql_ rename to backend/src/schema/types/scalar/Date.gql_ diff --git a/backend/src/types/scalar/DateTime.gql_ b/backend/src/schema/types/scalar/DateTime.gql_ similarity index 100% rename from backend/src/types/scalar/DateTime.gql_ rename to backend/src/schema/types/scalar/DateTime.gql_ diff --git a/backend/src/types/scalar/Time.gql_ b/backend/src/schema/types/scalar/Time.gql_ similarity index 100% rename from backend/src/types/scalar/Time.gql_ rename to backend/src/schema/types/scalar/Time.gql_ diff --git a/backend/src/types/scalar/Upload.gql b/backend/src/schema/types/scalar/Upload.gql similarity index 100% rename from backend/src/types/scalar/Upload.gql rename to backend/src/schema/types/scalar/Upload.gql diff --git a/backend/src/types/schema.gql b/backend/src/schema/types/schema.gql similarity index 100% rename from backend/src/types/schema.gql rename to backend/src/schema/types/schema.gql diff --git a/backend/src/types/schema_full.gql_ b/backend/src/schema/types/schema_full.gql_ similarity index 100% rename from backend/src/types/schema_full.gql_ rename to backend/src/schema/types/schema_full.gql_ diff --git a/backend/src/types/type/Badge.gql b/backend/src/schema/types/type/Badge.gql similarity index 100% rename from backend/src/types/type/Badge.gql rename to backend/src/schema/types/type/Badge.gql diff --git a/backend/src/types/type/Category.gql b/backend/src/schema/types/type/Category.gql similarity index 100% rename from backend/src/types/type/Category.gql rename to backend/src/schema/types/type/Category.gql diff --git a/backend/src/types/type/Comment.gql b/backend/src/schema/types/type/Comment.gql similarity index 100% rename from backend/src/types/type/Comment.gql rename to backend/src/schema/types/type/Comment.gql diff --git a/backend/src/types/type/Post.gql b/backend/src/schema/types/type/Post.gql similarity index 100% rename from backend/src/types/type/Post.gql rename to backend/src/schema/types/type/Post.gql diff --git a/backend/src/types/type/Tag.gql b/backend/src/schema/types/type/Tag.gql similarity index 100% rename from backend/src/types/type/Tag.gql rename to backend/src/schema/types/type/Tag.gql diff --git a/backend/src/types/type/User.gql b/backend/src/schema/types/type/User.gql similarity index 100% rename from backend/src/types/type/User.gql rename to backend/src/schema/types/type/User.gql diff --git a/backend/src/seed/reset-db.js b/backend/src/seed/reset-db.js index 3197a6e18..5f4319f73 100644 --- a/backend/src/seed/reset-db.js +++ b/backend/src/seed/reset-db.js @@ -1,10 +1,8 @@ import { cleanDatabase } from './factories' -import dotenv from 'dotenv' +import CONFIG from './../config' -dotenv.config() - -if (process.env.NODE_ENV === 'production') { - throw new Error(`YOU CAN'T CLEAN THE DATABASE WITH NODE_ENV=${process.env.NODE_ENV}`) +if (!CONFIG.DEBUG) { + throw new Error(`YOU CAN'T CLEAN THE DATABASE WITH DEBUG=${CONFIG.DEBUG}`) } ;(async function() { diff --git a/backend/src/server.js b/backend/src/server.js index 59261302f..7692f0d2c 100644 --- a/backend/src/server.js +++ b/backend/src/server.js @@ -1,48 +1,27 @@ -import { GraphQLServer } from 'graphql-yoga' -import { makeAugmentedSchema } from 'neo4j-graphql-js' -import { typeDefs, resolvers } from './graphql-schema' import express from 'express' -import dotenv from 'dotenv' +import helmet from 'helmet' +import { GraphQLServer } from 'graphql-yoga' +import CONFIG, { requiredConfigs } from './config' import mocks from './mocks' import middleware from './middleware' -import applyDirectives from './bootstrap/directives' -import applyScalars from './bootstrap/scalars' import { getDriver } from './bootstrap/neo4j' -import helmet from 'helmet' import decode from './jwt/decode' +import schema from './schema' -dotenv.config() -// check env and warn -const requiredEnvVars = ['MAPBOX_TOKEN', 'JWT_SECRET', 'PRIVATE_KEY_PASSPHRASE'] -requiredEnvVars.forEach(env => { - if (!process.env[env]) { - throw new Error(`ERROR: "${env}" env variable is missing.`) +// check required configs and throw error +// TODO check this directly in config file - currently not possible due to testsetup +Object.entries(requiredConfigs).map(entry => { + if (!entry[1]) { + throw new Error(`ERROR: "${entry[0]}" env variable is missing.`) } }) const driver = getDriver() -const debug = process.env.NODE_ENV !== 'production' && process.env.DEBUG === 'true' - -let schema = makeAugmentedSchema({ - typeDefs, - resolvers, - config: { - query: { - exclude: ['Notfication', 'Statistics', 'LoggedInUser'], - }, - mutation: { - exclude: ['Notfication', 'Statistics', 'LoggedInUser'], - }, - debug: debug, - }, -}) -schema = applyScalars(applyDirectives(schema)) const createServer = options => { const defaults = { context: async ({ request }) => { - const authorizationHeader = request.headers.authorization || '' - const user = await decode(driver, authorizationHeader) + const user = await decode(driver, request.headers.authorization) return { driver, user, @@ -52,11 +31,11 @@ const createServer = options => { }, } }, - schema: schema, - debug: debug, - tracing: debug, + schema, + debug: CONFIG.DEBUG, + tracing: CONFIG.DEBUG, middlewares: middleware(schema), - mocks: process.env.MOCK === 'true' ? mocks : false, + mocks: CONFIG.MOCKS ? mocks : false, } const server = new GraphQLServer(Object.assign({}, defaults, options)) diff --git a/backend/yarn.lock b/backend/yarn.lock index f4a968e16..d56871d83 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -9,13 +9,6 @@ dependencies: apollo-env "0.5.1" -"@apollographql/apollo-tools@^0.3.6-alpha.1": - version "0.3.6-alpha.1" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.6-alpha.1.tgz#5199b36c65c2fddc4f8bc8bb97642f74e9fb00c5" - integrity sha512-fq74In3Vw9OmtKHze0L5/Ns/pdTZOqUeFVC6Um9NRgziVehXz/qswsh2r3+dsn82uqoa/AlvckHtd6aPPPYj9g== - dependencies: - apollo-env "0.4.1-alpha.1" - "@apollographql/graphql-playground-html@1.6.20": version "1.6.20" resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.20.tgz#bf9f2acdf319c0959fad8ec1239741dd2ead4e8d" @@ -1288,14 +1281,6 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -apollo-cache-control@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.6.1.tgz#c73ff521fe606faf18edcbd3463c421a966f3e5d" - integrity sha512-M3cDeQDXtRxYPQ/sL4pu3IVE5Q/9jpBlENB2IjwxTDir+WFZbJV1CAnvVwyJdL1DvS6ESR35DFOurJH4Ws/OPA== - dependencies: - apollo-server-env "2.3.0" - graphql-extensions "0.6.1" - apollo-cache-control@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.7.1.tgz#3d4fba232f561f096f61051e103bf58ee4bf8b54" @@ -1353,14 +1338,6 @@ apollo-client@~2.5.1: tslib "^1.9.3" zen-observable "^0.8.0" -apollo-datasource@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.4.0.tgz#f042641fd2593fa5f4f002fc30d1fb1a20284df8" - integrity sha512-6QkgnLYwQrW0qv+yXIf617DojJbGmza2XJXUlgnzrGGhxzfAynzEjaLyYkc8rYS1m82vjrl9EOmLHTcnVkvZAQ== - dependencies: - apollo-server-caching "0.4.0" - apollo-server-env "2.3.0" - apollo-datasource@0.5.0: version "0.5.0" resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.5.0.tgz#7a8c97e23da7b9c15cb65103d63178ab19eca5e9" @@ -1376,18 +1353,6 @@ apollo-engine-reporting-protobuf@0.3.0: dependencies: protobufjs "^6.8.6" -apollo-engine-reporting@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.1.1.tgz#f5a3240bc5c5afb210ff8c45d72995de7b0d2a13" - integrity sha512-K7BDsj99jr8ftd9NIuHL4oF/S7CBFcgMGjL0ChhfxpkgUv80FPxJ+9Fs+9ZkKIVylV3PCi2WnihpDeEO10eZAw== - dependencies: - apollo-engine-reporting-protobuf "0.3.0" - apollo-graphql "^0.2.1-alpha.1" - apollo-server-core "2.5.1" - apollo-server-env "2.3.0" - async-retry "^1.2.1" - graphql-extensions "0.6.1" - apollo-engine-reporting@1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.2.1.tgz#0b77fad2e9221d62f4a29b8b4fab8f7f47dcc1d6" @@ -1400,15 +1365,6 @@ apollo-engine-reporting@1.2.1: async-retry "^1.2.1" graphql-extensions "0.7.1" -apollo-env@0.4.1-alpha.1: - version "0.4.1-alpha.1" - resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.4.1-alpha.1.tgz#10d3ea508b8f3ba03939ef4e6ec4b2b5db77e8f1" - integrity sha512-4qWiaUKWh92jvKxxRsiZSjmW9YH9GWSG1W6X+S1BcC1uqtPiHsem7ExG9MMTt+UrzHsbpQLctj12xk8lI4lgCg== - dependencies: - core-js "3.0.0-beta.13" - node-fetch "^2.2.0" - sha.js "^2.4.11" - apollo-env@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.5.1.tgz#b9b0195c16feadf0fe9fd5563edb0b9b7d9e97d3" @@ -1426,14 +1382,6 @@ apollo-errors@^1.9.0: assert "^1.4.1" extendable-error "^0.1.5" -apollo-graphql@^0.2.1-alpha.1: - version "0.2.1-alpha.1" - resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.2.1-alpha.1.tgz#a0cc0bd65e03c7e887c96c9f53421f3c6dd7b599" - integrity sha512-kObCSpYRHEf4IozJV+TZAXEL2Yni2DpzQckohJNYXg5/KRAF20jJ7lHxuJz+kMQrc7QO4wYGSa29HuFZH2AtQA== - dependencies: - apollo-env "0.4.1-alpha.1" - lodash.sortby "^4.7.0" - apollo-graphql@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.3.1.tgz#d13b80cc0cae3fe7066b81b80914c6f983fac8d7" @@ -1492,32 +1440,6 @@ apollo-server-caching@0.4.0: dependencies: lru-cache "^5.0.0" -apollo-server-core@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.5.1.tgz#0fdb6cfca56a0f5b5b3aecffb48db17b3c8e1d71" - integrity sha512-4QNrW1AUM3M/p0+hbBX/MsjSjZTy+2rt7JpiKKkG9RmeEIzd/VG7hwwwloAZSLjYx3twz0+BnASJ9y+rGEPC8A== - dependencies: - "@apollographql/apollo-tools" "^0.3.6" - "@apollographql/graphql-playground-html" "1.6.20" - "@types/ws" "^6.0.0" - apollo-cache-control "0.6.1" - apollo-datasource "0.4.0" - apollo-engine-reporting "1.1.1" - apollo-server-caching "0.4.0" - apollo-server-env "2.3.0" - apollo-server-errors "2.3.0" - apollo-server-plugin-base "0.4.1" - apollo-tracing "0.6.1" - fast-json-stable-stringify "^2.0.0" - graphql-extensions "0.6.1" - graphql-subscriptions "^1.0.0" - graphql-tag "^2.9.2" - graphql-tools "^4.0.0" - graphql-upload "^8.0.2" - sha.js "^2.4.11" - subscriptions-transport-ws "^0.9.11" - ws "^6.0.0" - apollo-server-core@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.6.1.tgz#d0d878b0a4959b6c661fc43300ce45b29996176a" @@ -1553,14 +1475,6 @@ apollo-server-core@^1.3.6, apollo-server-core@^1.4.0: apollo-tracing "^0.1.0" graphql-extensions "^0.0.x" -apollo-server-env@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.3.0.tgz#f0bf4484a6cc331a8c13763ded56e91beb16ba17" - integrity sha512-WIwlkCM/gir0CkoYWPMTCH8uGCCKB/aM074U1bKayvkFOBVO2VgG5x2kgsfkyF05IMQq2/GOTsKhNY7RnUEhTA== - dependencies: - node-fetch "^2.1.2" - util.promisify "^1.0.0" - apollo-server-env@2.4.0: version "2.4.0" resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.4.0.tgz#6611556c6b627a1636eed31317d4f7ea30705872" @@ -1574,10 +1488,10 @@ apollo-server-errors@2.3.0: resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.3.0.tgz#700622b66a16dffcad3b017e4796749814edc061" integrity sha512-rUvzwMo2ZQgzzPh2kcJyfbRSfVKRMhfIlhY7BzUfM4x6ZT0aijlgsf714Ll3Mbf5Fxii32kD0A/DmKsTecpccw== -apollo-server-express@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.5.1.tgz#b112d9795f2fb39076d9cbc109f5eeb7835bed6b" - integrity sha512-528wDQnOMIenDaICkYPFWQykdXQZwpygxd+Ar0PmZiaST042NSVExV4iRWI09p1THqfsuyHygqpkK+K94bUtBA== +apollo-server-express@2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/apollo-server-express/-/apollo-server-express-2.6.1.tgz#1e2649d3fd38c0c0a2c830090fd41e086b259c9f" + integrity sha512-TVu68LVp+COMGOXuxc0OFeCUQiPApxy7Isv2Vk85nikZV4t4FXlODB6PrRKf5rfvP31dvGsfE6GHPJTLLbKfyg== dependencies: "@apollographql/graphql-playground-html" "1.6.20" "@types/accepts" "^1.3.5" @@ -1585,7 +1499,7 @@ apollo-server-express@2.5.1: "@types/cors" "^2.8.4" "@types/express" "4.16.1" accepts "^1.3.5" - apollo-server-core "2.5.1" + apollo-server-core "2.6.1" body-parser "^1.18.3" cors "^2.8.4" graphql-subscriptions "^1.0.0" @@ -1613,11 +1527,6 @@ apollo-server-module-graphiql@^1.3.4, apollo-server-module-graphiql@^1.4.0: resolved "https://registry.yarnpkg.com/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz#c559efa285578820709f1769bb85d3b3eed3d8ec" integrity sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA== -apollo-server-plugin-base@0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.4.1.tgz#be380b28d71ad3b6b146d0d6a8f7ebf5675b07ff" - integrity sha512-D2G6Ca/KBdQgEbmSfYqZqYbdVJnk/rrSv7Vj2NntwjfL7WJf0TjufxYJlrTH5jF6xCbsszDNGqfmt2Nm8x/o4g== - apollo-server-plugin-base@0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.5.1.tgz#b81056666763879bdc98d8d58f3c4c43cbb30da6" @@ -1630,25 +1539,17 @@ apollo-server-testing@~2.6.1: dependencies: apollo-server-core "2.6.1" -apollo-server@~2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.5.1.tgz#bfcfbebc123f692c0e6d85b0c56739646bd1bb7e" - integrity sha512-eH3ubq300xhpFAxek28kb+5WZINXpWcwzyNqBQDbuasTlW8qSsqY7xrV6IIz6WUYKdX+ET0mx+Ta1DdaYQPrqw== +apollo-server@~2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/apollo-server/-/apollo-server-2.6.1.tgz#1b1fc6020b75c0913550da5fa0f2005c62f1bc53" + integrity sha512-Ed0zZjluRYPMC3Yr6oXQjcR11izu86nkjiS2MhjJA1mF8IXJfxbPp2hnX4Jf4vXPSkOP2e5ZHw0cdaIcu9GnRw== dependencies: - apollo-server-core "2.5.1" - apollo-server-express "2.5.1" + apollo-server-core "2.6.1" + apollo-server-express "2.6.1" express "^4.0.0" graphql-subscriptions "^1.0.0" graphql-tools "^4.0.0" -apollo-tracing@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.6.1.tgz#48a6d6040f9b2f2b4365a890c2e97cb763eb2392" - integrity sha512-rrDBgTHa9GDA3wY8O7rDsFwC6ePIVzRGxpUsThgmLvIVkkCr0KS4wJJ4C01c+v4xsOXNuQwx0IyYhxZt4twwcA== - dependencies: - apollo-server-env "2.3.0" - graphql-extensions "0.6.1" - apollo-tracing@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.7.1.tgz#6a7356b619f3aa0ca22c623b5d8bb1af5ca1c74c" @@ -2524,11 +2425,6 @@ core-js-pure@3.1.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.1.2.tgz#62fc435f35b7374b9b782013cdcb2f97e9f6dffa" integrity sha512-5ckIdBF26B3ldK9PM177y2ZcATP2oweam9RskHSoqfZCrJ2As6wVg8zJ1zTriFsZf6clj/N1ThDFRGaomMsh9w== -core-js@3.0.0-beta.13: - version "3.0.0-beta.13" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.13.tgz#7732c69be5e4758887917235fe7c0352c4cb42a1" - integrity sha512-16Q43c/3LT9NyePUJKL8nRIQgYWjcBhjJSMWg96PVSxoS0PeE0NHitPI3opBrs9MGGHjte1KoEVr9W63YKlTXQ== - core-js@^2.4.0, core-js@^2.5.3, core-js@^2.5.7: version "2.6.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.2.tgz#267988d7268323b349e20b4588211655f0e83944" @@ -3832,13 +3728,6 @@ graphql-deduplicator@^2.0.1: resolved "https://registry.yarnpkg.com/graphql-deduplicator/-/graphql-deduplicator-2.0.2.tgz#d8608161cf6be97725e178df0c41f6a1f9f778f3" integrity sha512-0CGmTmQh4UvJfsaTPppJAcHwHln8Ayat7yXXxdnuWT+Mb1dBzkbErabCWzjXyKh/RefqlGTTA7EQOZHofMaKJA== -graphql-extensions@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.6.1.tgz#e61c4cb901e336dc5993a61093a8678a021dda59" - integrity sha512-vB2WNQJn99pncHfvxgcdyVoazmG3cD8XzkgcaDrHTvV+xJGJEBP6056EWi0mNt1d6ukYyRS2zexdekmMCjcq0w== - dependencies: - "@apollographql/apollo-tools" "^0.3.6-alpha.1" - graphql-extensions@0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.7.1.tgz#f55b01ac8ddf09a215e21f34caeee3ae66a88f21" diff --git a/deployment/human-connection/templates/configmap.template.yaml b/deployment/human-connection/templates/configmap.template.yaml index baf41661a..87b51a7d3 100644 --- a/deployment/human-connection/templates/configmap.template.yaml +++ b/deployment/human-connection/templates/configmap.template.yaml @@ -4,7 +4,7 @@ data: GRAPHQL_PORT: "4000" GRAPHQL_URI: "http://nitro-backend.human-connection:4000" - MOCK: "false" + MOCKS: "false" NEO4J_URI: "bolt://nitro-neo4j.human-connection:7687" NEO4J_USER: "neo4j" NEO4J_AUTH: "none" diff --git a/deployment/legacy-migration/maintenance-worker/migration/mongo/.env b/deployment/legacy-migration/maintenance-worker/migration/mongo/.env index 602f51fc4..4c5f9e18c 100644 --- a/deployment/legacy-migration/maintenance-worker/migration/mongo/.env +++ b/deployment/legacy-migration/maintenance-worker/migration/mongo/.env @@ -12,5 +12,6 @@ # On Windows this resolves to C:\Users\dornhoeschen\AppData\Local\Temp\mongo-export (MinGW) EXPORT_PATH='/tmp/mongo-export/' EXPORT_MONGOEXPORT_BIN='mongoexport' +MONGO_EXPORT_SPLIT_SIZE=100 # On Windows use something like this -# EXPORT_MONGOEXPORT_BIN='C:\Program Files\MongoDB\Server\3.6\bin\mongoexport.exe' \ No newline at end of file +# EXPORT_MONGOEXPORT_BIN='C:\Program Files\MongoDB\Server\3.6\bin\mongoexport.exe' diff --git a/deployment/legacy-migration/maintenance-worker/migration/mongo/export.sh b/deployment/legacy-migration/maintenance-worker/migration/mongo/export.sh index 257bc4c61..abed9b0f5 100755 --- a/deployment/legacy-migration/maintenance-worker/migration/mongo/export.sh +++ b/deployment/legacy-migration/maintenance-worker/migration/mongo/export.sh @@ -10,7 +10,7 @@ set +o allexport function export_collection () { "${EXPORT_MONGOEXPORT_BIN}" --db ${MONGODB_DATABASE} --host localhost -d ${MONGODB_DATABASE} --port 27018 --username ${MONGODB_USERNAME} --password ${MONGODB_PASSWORD} --authenticationDatabase ${MONGODB_AUTH_DB} --collection $1 --collection $1 --out "${EXPORT_PATH}$1.json" mkdir -p ${EXPORT_PATH}splits/$1/ - split -l 1000 -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/ + split -l ${MONGO_EXPORT_SPLIT_SIZE} -a 3 ${EXPORT_PATH}$1.json ${EXPORT_PATH}splits/$1/ } # Delete old export & ensure directory diff --git a/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh b/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh index 08c05c8cc..ac256e3f0 100755 --- a/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh +++ b/deployment/legacy-migration/maintenance-worker/migration/neo4j/import.sh @@ -83,17 +83,17 @@ import_collection "contributions" import_collection "shouts" import_collection "comments" -import_collection "emotions" -import_collection "invites" -import_collection "notifications" -import_collection "organizations" -import_collection "pages" -import_collection "projects" -import_collection "settings" -import_collection "status" -import_collection "systemnotifications" -import_collection "userscandos" -import_collection "usersettings" +# import_collection "emotions" +# import_collection "invites" +# import_collection "notifications" +# import_collection "organizations" +# import_collection "pages" +# import_collection "projects" +# import_collection "settings" +# import_collection "status" +# import_collection "systemnotifications" +# import_collection "userscandos" +# import_collection "usersettings" echo "DONE" diff --git a/docker-compose.maintenance.yml b/docker-compose.maintenance.yml index 113b4492c..e536b1157 100644 --- a/docker-compose.maintenance.yml +++ b/docker-compose.maintenance.yml @@ -19,7 +19,7 @@ services: - GRAPHQL_URI=http://localhost:4000 - CLIENT_URI=http://localhost:3000 - JWT_SECRET=b/&&7b78BF&fv/Vd - - MOCK=false + - MOCKS=false - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - NEO4J_apoc_import_file_enabled=true @@ -30,6 +30,7 @@ services: - "MONGODB_AUTH_DB=${MONGODB_AUTH_DB}" - "MONGODB_DATABASE=${MONGODB_DATABASE}" - "UPLOADS_DIRECTORY=${UPLOADS_DIRECTORY}" + - "MONGO_EXPORT_SPLIT_SIZE=${MONGO_EXPORT_SPLIT_SIZE}" ports: - 7687:7687 - 7474:7474 diff --git a/docker-compose.yml b/docker-compose.yml index 896d1bef9..ca66217c2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -32,7 +32,7 @@ services: - GRAPHQL_URI=http://localhost:4000 - CLIENT_URI=http://localhost:3000 - JWT_SECRET=b/&&7b78BF&fv/Vd - - MOCK=false + - MOCKS=false - MAPBOX_TOKEN=pk.eyJ1IjoiaHVtYW4tY29ubmVjdGlvbiIsImEiOiJjajl0cnBubGoweTVlM3VwZ2lzNTNud3ZtIn0.KZ8KK9l70omjXbEkkbHGsQ - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 neo4j: diff --git a/scripts/docker_push.sh b/scripts/docker_push.sh index 1f627cf1a..9aa9a8e2a 100755 --- a/scripts/docker_push.sh +++ b/scripts/docker_push.sh @@ -2,5 +2,7 @@ echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-backend:latest $TRAVIS_BUILD_DIR/backend docker build --build-arg BUILD_COMMIT=$TRAVIS_COMMIT --target production -t humanconnection/nitro-web:latest $TRAVIS_BUILD_DIR/webapp +docker build -t humanconnection/nitro-maintenance-worker:latest $TRAVIS_BUILD_DIR/deployment/legacy-migration/maintenance-worker docker push humanconnection/nitro-backend:latest docker push humanconnection/nitro-web:latest +docker push humanconnection/nitro-maintenance-worker:latest diff --git a/webapp/components/Comment.spec.js b/webapp/components/Comment.spec.js index ebb9b8bf8..e899a05e1 100644 --- a/webapp/components/Comment.spec.js +++ b/webapp/components/Comment.spec.js @@ -14,11 +14,20 @@ describe('Comment.vue', () => { let propsData let mocks let getters + let wrapper + let Wrapper beforeEach(() => { propsData = {} mocks = { $t: jest.fn(), + $toast: { + success: jest.fn(), + error: jest.fn(), + }, + $apollo: { + mutate: jest.fn().mockResolvedValue(), + }, } getters = { 'auth/user': () => { @@ -29,11 +38,16 @@ describe('Comment.vue', () => { }) describe('shallowMount', () => { - const Wrapper = () => { + Wrapper = () => { const store = new Vuex.Store({ getters, }) - return shallowMount(Comment, { store, propsData, mocks, localVue }) + return shallowMount(Comment, { + store, + propsData, + mocks, + localVue, + }) } describe('given a comment', () => { @@ -45,7 +59,7 @@ describe('Comment.vue', () => { }) it('renders content', () => { - const wrapper = Wrapper() + wrapper = Wrapper() expect(wrapper.text()).toMatch('Hello I am a comment content') }) @@ -55,17 +69,17 @@ describe('Comment.vue', () => { }) it('renders no comment data', () => { - const wrapper = Wrapper() + wrapper = Wrapper() expect(wrapper.text()).not.toMatch('comment content') }) it('has no "disabled-content" css class', () => { - const wrapper = Wrapper() + wrapper = Wrapper() expect(wrapper.classes()).not.toContain('disabled-content') }) it('translates a placeholder', () => { - /* const wrapper = */ Wrapper() + wrapper = Wrapper() const calls = mocks.$t.mock.calls const expected = [['comment.content.unavailable-placeholder']] expect(calls).toEqual(expect.arrayContaining(expected)) @@ -77,16 +91,46 @@ describe('Comment.vue', () => { }) it('renders comment data', () => { - const wrapper = Wrapper() + wrapper = Wrapper() expect(wrapper.text()).toMatch('comment content') }) it('has a "disabled-content" css class', () => { - const wrapper = Wrapper() + wrapper = Wrapper() expect(wrapper.classes()).toContain('disabled-content') }) }) }) + + beforeEach(jest.useFakeTimers) + + describe('test callbacks', () => { + beforeEach(() => { + wrapper = Wrapper() + }) + + describe('deletion of Comment from List by invoking "deleteCommentCallback()"', () => { + beforeEach(() => { + wrapper.vm.deleteCommentCallback() + }) + + describe('after timeout', () => { + beforeEach(jest.runAllTimers) + + it('emits "deleteComment"', () => { + expect(wrapper.emitted().deleteComment.length).toBe(1) + }) + + it('does call mutation', () => { + expect(mocks.$apollo.mutate).toHaveBeenCalledTimes(1) + }) + + it('mutation is successful', () => { + expect(mocks.$toast.success).toHaveBeenCalledTimes(1) + }) + }) + }) + }) }) }) }) diff --git a/webapp/components/Comment.vue b/webapp/components/Comment.vue index 5bce3d480..4796dd783 100644 --- a/webapp/components/Comment.vue +++ b/webapp/components/Comment.vue @@ -14,6 +14,7 @@ placement="bottom-end" resource-type="comment" :resource="comment" + :callbacks="{ confirm: deleteCommentCallback, cancel: null }" style="float-right" :is-owner="isAuthor(author.id)" /> @@ -27,6 +28,7 @@ diff --git a/webapp/components/ContentMenu.vue b/webapp/components/ContentMenu.vue index 6e5bca4c0..69289cb6d 100644 --- a/webapp/components/ContentMenu.vue +++ b/webapp/components/ContentMenu.vue @@ -28,6 +28,7 @@ import Dropdown from '~/components/Dropdown' export default { + name: 'ContentMenu', components: { Dropdown, }, @@ -42,6 +43,7 @@ export default { return value.match(/(contribution|comment|organization|user)/) }, }, + callbacks: { type: Object, required: true }, }, computed: { routes() { @@ -49,7 +51,7 @@ export default { if (this.isOwner && this.resourceType === 'contribution') { routes.push({ - name: this.$t(`contribution.edit`), + name: this.$t(`post.menu.edit`), path: this.$router.resolve({ name: 'post-edit-id', params: { @@ -59,21 +61,29 @@ export default { icon: 'edit', }) routes.push({ - name: this.$t(`post.delete.title`), + name: this.$t(`post.menu.delete`), callback: () => { this.openModal('delete') }, icon: 'trash', }) } + if (this.isOwner && this.resourceType === 'comment') { + // routes.push({ + // name: this.$t(`comment.menu.edit`), + // callback: () => { + // /* eslint-disable-next-line no-console */ + // console.log('EDIT COMMENT') + // }, + // icon: 'edit' + // }) routes.push({ - name: this.$t(`comment.edit`), + name: this.$t(`comment.menu.delete`), callback: () => { - /* eslint-disable-next-line no-console */ - console.log('EDIT COMMENT') + this.openModal('delete') }, - icon: 'edit', + icon: 'trash', }) } @@ -125,6 +135,7 @@ export default { data: { type: this.resourceType, resource: this.resource, + callbacks: this.callbacks, }, }) }, diff --git a/webapp/components/Modal.spec.js b/webapp/components/Modal.spec.js index 52d13c4a0..0e2158e96 100644 --- a/webapp/components/Modal.spec.js +++ b/webapp/components/Modal.spec.js @@ -1,5 +1,6 @@ import { shallowMount, createLocalVue } from '@vue/test-utils' import Modal from './Modal.vue' +import DeleteModal from './Modal/DeleteModal.vue' import DisableModal from './Modal/DisableModal.vue' import ReportModal from './Modal/ReportModal.vue' import Vuex from 'vuex' @@ -29,7 +30,11 @@ describe('Modal.vue', () => { 'modal/SET_OPEN': mutations.SET_OPEN, }, }) - return mountMethod(Modal, { store, mocks, localVue }) + return mountMethod(Modal, { + store, + mocks, + localVue, + }) } } @@ -55,6 +60,7 @@ describe('Modal.vue', () => { it('initially empty', () => { wrapper = Wrapper() + expect(wrapper.contains(DeleteModal)).toBe(false) expect(wrapper.contains(DisableModal)).toBe(false) expect(wrapper.contains(ReportModal)).toBe(false) }) @@ -69,6 +75,10 @@ describe('Modal.vue', () => { id: 'c456', title: 'some title', }, + callbacks: { + confirm: null, + cancel: null, + }, }, } wrapper = Wrapper() @@ -83,6 +93,10 @@ describe('Modal.vue', () => { type: 'contribution', name: 'some title', id: 'c456', + callbacks: { + confirm: null, + cancel: null, + }, }) }) @@ -97,23 +111,49 @@ describe('Modal.vue', () => { it('passes author name to disable modal', () => { state.data = { type: 'comment', - resource: { id: 'c456', author: { name: 'Author name' } }, + resource: { + id: 'c456', + author: { + name: 'Author name', + }, + }, + callbacks: { + confirm: null, + cancel: null, + }, } wrapper = Wrapper() expect(wrapper.find(DisableModal).props()).toEqual({ type: 'comment', name: 'Author name', id: 'c456', + callbacks: { + confirm: null, + cancel: null, + }, }) }) it('does not crash if author is undefined', () => { - state.data = { type: 'comment', resource: { id: 'c456' } } + state.data = { + type: 'comment', + resource: { + id: 'c456', + }, + callbacks: { + confirm: null, + cancel: null, + }, + } wrapper = Wrapper() expect(wrapper.find(DisableModal).props()).toEqual({ type: 'comment', name: '', id: 'c456', + callbacks: { + confirm: null, + cancel: null, + }, }) }) }) diff --git a/webapp/components/Modal.vue b/webapp/components/Modal.vue index 88b89d407..efe1b8ab6 100644 --- a/webapp/components/Modal.vue +++ b/webapp/components/Modal.vue @@ -1,10 +1,12 @@