diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6cfe4c30c..655c5cc93 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,13 +1,15 @@ -## 🍰 Pullrequest +## 🍰 Pull Request +XXX + ### Issues - + - fixes #XXX - relates #XXX ---> -- None ### Todo -- [X] None + +- [ ] XXX list here … diff --git a/backend/README.md b/backend/README.md index 6d837856c..083606b09 100644 --- a/backend/README.md +++ b/backend/README.md @@ -60,6 +60,7 @@ your `.env` configuration file. Your backend is up and running at [http://localhost:4000/](http://localhost:4000/) This will start the GraphQL service \(by default on localhost:4000\) where you can issue GraphQL requests or access GraphQL Playground in the browser. +More details about our GraphQL playground and how to use it with ocelot.social can be found [here](./src/graphql/GraphQL-Playground.md). ![GraphQL Playground](../.gitbook/assets/graphql-playground.png) diff --git a/backend/package.json b/backend/package.json index 62188a650..f6299ee48 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,7 +15,7 @@ "dev": "nodemon --exec babel-node src/ -e js,gql", "dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql", "lint": "eslint src --config .eslintrc.js", - "test": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --runInBand --coverage", + "test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --forceExit --detectOpenHandles --runInBand --coverage --logHeapUsage", "db:clean": "babel-node src/db/clean.js", "db:reset": "yarn run db:clean", "db:seed": "babel-node src/db/seed.js", @@ -103,14 +103,14 @@ "mustache": "^4.2.0", "neo4j-driver": "^4.0.2", "neo4j-graphql-js": "^2.11.5", - "neode": "^0.4.7", + "neode": "^0.4.8", "node-fetch": "~2.6.1", "nodemailer": "^6.4.4", "nodemailer-html-to-text": "^3.2.0", "npm-run-all": "~4.1.5", "request": "~2.88.2", "sanitize-html": "~1.22.0", - "slug": "~4.0.2", + "slug": "~6.0.0", "subscriptions-transport-ws": "^0.9.19", "trunc-html": "~1.1.2", "uuid": "~8.3.2", @@ -129,7 +129,7 @@ "eslint-plugin-import": "~2.20.2", "eslint-plugin-jest": "~23.8.2", "eslint-plugin-node": "~11.1.0", - "eslint-plugin-prettier": "~3.1.2", + "eslint-plugin-prettier": "~3.4.1", "eslint-plugin-promise": "~4.3.1", "eslint-plugin-standard": "~4.0.1", "jest": "~25.3.0", diff --git a/backend/src/constants/categories.js b/backend/src/constants/categories.js new file mode 100644 index 000000000..3676fed0b --- /dev/null +++ b/backend/src/constants/categories.js @@ -0,0 +1,98 @@ +export const categories = [ + { + icon: 'networking', + name: 'networking', + description: 'Kooperation, Aktionsbündnisse, Solidarität, Hilfe', + }, + { + icon: 'home', + name: 'home', + description: 'Bauen, Lebensgemeinschaften, Tiny Houses, Gemüsegarten', + }, + { + icon: 'energy', + name: 'energy', + description: 'Öl, Gas, Kohle, Wind, Wasserkraft, Biogas, Atomenergie, ...', + }, + { + icon: 'psyche', + name: 'psyche', + description: 'Seele, Gefühle, Glück', + }, + { + icon: 'movement', + name: 'body-and-excercise', + description: 'Sport, Yoga, Massage, Tanzen, Entspannung', + }, + { + icon: 'balance-scale', + name: 'law', + description: 'Menschenrechte, Gesetze, Verordnungen', + }, + { + icon: 'finance', + name: 'finance', + description: 'Geld, Finanzsystem, Alternativwährungen, ...', + }, + { + icon: 'child', + name: 'children', + description: 'Familie, Pädagogik, Schule, Prägung', + }, + { + icon: 'mobility', + name: 'mobility', + description: 'Reise, Verkehr, Elektromobilität', + }, + { + icon: 'shopping-cart', + name: 'economy', + description: 'Handel, Konsum, Marketing, Lebensmittel, Lieferketten, ...', + }, + { + icon: 'peace', + name: 'peace', + description: 'Krieg, Militär, soziale Verteidigung, Waffen, Cyberattacken', + }, + { + icon: 'politics', + name: 'politics', + description: 'Demokratie, Mitbestimmung, Wahlen, Korruption, Parteien', + }, + { + icon: 'nature', + name: 'nature', + description: 'Tiere, Pflanzen, Landwirtschaft, Ökologie, Artenvielfalt', + }, + { + icon: 'science', + name: 'science', + description: 'Bildung, Hochschule, Publikationen, ...', + }, + { + icon: 'health', + name: 'health', + description: 'Medizin, Ernährung, WHO, Impfungen, Schadstoffe, ...', + }, + { + icon: 'media', + name: 'it-and-media', + description: + 'Nachrichten, Manipulation, Datenschutz, Überwachung, Datenkraken, AI, Software, Apps', + }, + { + icon: 'spirituality', + name: 'spirituality', + description: 'Religion, Werte, Ethik', + }, + { + icon: 'culture', + name: 'culture', + description: 'Kunst, Theater, Musik, Fotografie, Film', + }, + { + icon: 'miscellaneous', + name: 'miscellaneous', + description: '', + }, +] diff --git a/backend/src/db/migrate/store.js b/backend/src/db/migrate/store.js index 377caf0b0..d6fd25bd8 100644 --- a/backend/src/db/migrate/store.js +++ b/backend/src/db/migrate/store.js @@ -1,6 +1,8 @@ import { getDriver, getNeode } from '../../db/neo4j' import { hashSync } from 'bcryptjs' import { v4 as uuid } from 'uuid' +import { categories } from '../../constants/categories' +import CONFIG from '../../config' const defaultAdmin = { email: 'admin@example.org', @@ -10,6 +12,29 @@ const defaultAdmin = { slug: 'admin', } +const createCategories = async (session) => { + const createCategoriesTxResultPromise = session.writeTransaction(async (txc) => { + categories.forEach(({ icon, name }, index) => { + const id = `cat${index + 1}` + txc.run( + `MERGE (c:Category { + icon: "${icon}", + slug: "${name}", + name: "${name}", + id: "${id}", + createdAt: toString(datetime()) + })`, + ) + }) + }) + try { + await createCategoriesTxResultPromise + console.log('Successfully created categories!') // eslint-disable-line no-console + } catch (error) { + console.log(`Error creating categories: ${error}`) // eslint-disable-line no-console + } +} + const createDefaultAdminUser = async (session) => { const readTxResultPromise = session.readTransaction(async (txc) => { const result = await txc.run('MATCH (user:User) RETURN count(user) AS userCount') @@ -45,7 +70,7 @@ const createDefaultAdminUser = async (session) => { }) try { await createAdminTxResultPromise - console.log('Successfully created default admin user') // eslint-disable-line no-console + console.log('Successfully created default admin user!') // eslint-disable-line no-console } catch (error) { console.log(error) // eslint-disable-line no-console } @@ -58,6 +83,7 @@ class Store { const { driver } = neode const session = driver.session() await createDefaultAdminUser(session) + if (CONFIG.CATEGORIES_ACTIVE) await createCategories(session) const writeTxResultPromise = session.writeTransaction(async (txc) => { await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices return Promise.all( diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js index 46c5870e0..482517e69 100644 --- a/backend/src/db/seed.js +++ b/backend/src/db/seed.js @@ -6,6 +6,7 @@ import faker from '@faker-js/faker' import Factory from '../db/factories' import { getNeode, getDriver } from '../db/neo4j' import { gql } from '../helpers/jest' +import { categories } from '../constants/categories' if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot seed the database in a non-staging and real production environment!`) @@ -267,104 +268,16 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] dagobert.relateTo(louie, 'blocked'), ]) - await Promise.all([ - Factory.build('category', { - id: 'cat1', - name: 'Just For Fun', - slug: 'just-for-fun', - icon: 'smile', + await Promise.all( + categories.map(({ icon, name }, index) => { + Factory.build('category', { + id: `cat${index + 1}`, + slug: name, + name, + icon, + }) }), - Factory.build('category', { - id: 'cat2', - name: 'Happiness & Values', - slug: 'happiness-values', - icon: 'heart-o', - }), - Factory.build('category', { - id: 'cat3', - name: 'Health & Wellbeing', - slug: 'health-wellbeing', - icon: 'medkit', - }), - Factory.build('category', { - id: 'cat4', - name: 'Environment & Nature', - slug: 'environment-nature', - icon: 'tree', - }), - Factory.build('category', { - id: 'cat5', - name: 'Animal Protection', - slug: 'animal-protection', - icon: 'paw', - }), - Factory.build('category', { - id: 'cat6', - name: 'Human Rights & Justice', - slug: 'human-rights-justice', - icon: 'balance-scale', - }), - Factory.build('category', { - id: 'cat7', - name: 'Education & Sciences', - slug: 'education-sciences', - icon: 'graduation-cap', - }), - Factory.build('category', { - id: 'cat8', - name: 'Cooperation & Development', - slug: 'cooperation-development', - icon: 'users', - }), - Factory.build('category', { - id: 'cat9', - name: 'Democracy & Politics', - slug: 'democracy-politics', - icon: 'university', - }), - Factory.build('category', { - id: 'cat10', - name: 'Economy & Finances', - slug: 'economy-finances', - icon: 'money', - }), - Factory.build('category', { - id: 'cat11', - name: 'Energy & Technology', - slug: 'energy-technology', - icon: 'flash', - }), - Factory.build('category', { - id: 'cat12', - name: 'IT, Internet & Data Privacy', - slug: 'it-internet-data-privacy', - icon: 'mouse-pointer', - }), - Factory.build('category', { - id: 'cat13', - name: 'Art, Culture & Sport', - slug: 'art-culture-sport', - icon: 'paint-brush', - }), - Factory.build('category', { - id: 'cat14', - name: 'Freedom of Speech', - slug: 'freedom-of-speech', - icon: 'bullhorn', - }), - Factory.build('category', { - id: 'cat15', - name: 'Consumption & Sustainability', - slug: 'consumption-sustainability', - icon: 'shopping-cart', - }), - Factory.build('category', { - id: 'cat16', - name: 'Global Peace & Nonviolence', - slug: 'global-peace-nonviolence', - icon: 'angellist', - }), - ]) + ) const [environment, nature, democracy, freedom] = await Promise.all([ Factory.build('tag', { diff --git a/backend/src/graphql/GraphQL-Playground.md b/backend/src/graphql/GraphQL-Playground.md new file mode 100644 index 000000000..af248f112 --- /dev/null +++ b/backend/src/graphql/GraphQL-Playground.md @@ -0,0 +1,108 @@ +# GraphQL Playground + +To use GraphQL Playground, we need to know some basics: + +## How To Login? + +First, we need to have a user from ocelot.social to log in as. +The user can be created by seeding the Neo4j database from the backend or by multiple GraphQL mutations. + +### Seed The Neo4j Database + +In your browser you can reach the GraphQL Playground under `http://localhost:4000/`, if the database and the backend are running, see [backend](../../README.md). +There you will also find instructions on how to seed the database. + +### Use GraphQL Mutations To Create A User + +TODO: Describe how to create a user using GraphQL mutations! + +### Login Via GraphQL + +You can register a user by sending the query: + +```gql +mutation { + login(email: "user@example.org", password: "1234") +} +``` + +Or use `"moderator@example.org"` or `"admin@example.org"` for the roll you need. + +If all goes well, you will receive a QGL response like: + +```json +{ + "data": { + "login": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTY2MjAyMzMwNSwiZXhwIjoxNzI1MTM4NTA1LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.atBS-SOeS784HPeFl_5s8sRWehEAU1BkgcOZFD8d4bU" + } +} +``` + +You can use this response to set an HTTP header when you click `HTTP HEADERS` in the footer. +Just set it with the login token you received in response: + +```json +{ + "Authorization": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6InUzIiwibmFtZSI6Ikplbm55IFJvc3RvY2siLCJzbHVnIjoiamVubnktcm9zdG9jayIsImlhdCI6MTY2MjAyMzMwNSwiZXhwIjoxNzI1MTM4NTA1LCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjQwMDAiLCJzdWIiOiJ1MyJ9.atBS-SOeS784HPeFl_5s8sRWehEAU1BkgcOZFD8d4bU" +} +``` + +This token is used for all other queries and mutations you send to the backend. + +## Query And Mutate + +When you are logged in and open a new playground tab by clicking "+", you can create a new group by sending the following mutation: + +```gql +mutation { + CreateGroup( + # id: "" + name: "My Group" + # slug: "" + about: "We will save the world" + description: "

English:

This group is hidden.

What is our group for?

This group was created to allow investigative journalists to share and collaborate.

How does it work?

Here you can internally share posts and comments about them.


Deutsch:

Diese Gruppe ist verborgen.

Wofür ist unsere Gruppe?

Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.

Wie funktioniert das?

Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.

" + groupType: hidden + actionRadius: interplanetary + categoryIds: ["cat12"] + ) { + id + name + slug + createdAt + updatedAt + disabled + deleted + about + description + groupType + actionRadius + myRole + } +} +``` + +You will receive the answer: + +```json +{ + "data": { + "CreateGroup": { + "id": "2e3bbadb-804b-4ebc-a673-2d7c7f05e827", + "name": "My Group", + "slug": "my-group", + "createdAt": "2022-09-01T09:44:47.969Z", + "updatedAt": "2022-09-01T09:44:47.969Z", + "disabled": false, + "deleted": false, + "about": "We will save the world", + "description": "

English:

This group is hidden.

What is our group for?

This group was created to allow investigative journalists to share and collaborate.

How does it work?

Here you can internally share posts and comments about them.


Deutsch:

Diese Gruppe ist verborgen.

Wofür ist unsere Gruppe?

Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.

Wie funktioniert das?

Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.

", + "groupType": "hidden", + "actionRadius": "interplanetary", + "myRole": "owner" + } + } +} +``` + +If you look into the Neo4j database with your browser and search the groups, you will now also find your new group. +For more details about our Neo4j database read [here](../../../neo4j/README.md). diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index b10389f50..71a44f225 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -178,6 +178,7 @@ export default shield( GenerateInviteCode: isAuthenticated, switchUserRole: isAdmin, markTeaserAsViewed: allow, + saveCategorySettings: isAuthenticated, }, User: { email: or(isMyOwn, isAdmin), @@ -188,5 +189,6 @@ export default shield( debug, allowExternalErrors, fallbackRule: allow, + fallbackError: Error('Not Authorized!'), }, ) diff --git a/backend/src/middleware/permissionsMiddleware.spec.js b/backend/src/middleware/permissionsMiddleware.spec.js index e29e0a4af..9d71314bb 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.js +++ b/backend/src/middleware/permissionsMiddleware.spec.js @@ -102,7 +102,7 @@ describe('authorization', () => { await expect( query({ query: userQuery, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { User: [null] }, }) }) @@ -132,7 +132,7 @@ describe('authorization', () => { await expect( query({ query: userQuery, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { User: [null] }, }) }) @@ -147,7 +147,7 @@ describe('authorization', () => { await expect( query({ query: userQuery, variables: { name: 'Owner' } }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { User: [null] }, }) }) @@ -198,7 +198,7 @@ describe('authorization', () => { it('denies permission', async () => { await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { Signup: null }, }) }) @@ -288,7 +288,7 @@ describe('authorization', () => { it('denies permission', async () => { await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { Signup: null }, }) }) diff --git a/backend/src/models/Category.js b/backend/src/models/Category.js index ea617adc8..9a3f47fd0 100644 --- a/backend/src/models/Category.js +++ b/backend/src/models/Category.js @@ -9,8 +9,7 @@ export default { updatedAt: { type: 'string', isoDate: true, - required: true, - default: () => new Date().toISOString(), + required: false, }, post: { type: 'relationship', diff --git a/backend/src/schema/resolvers/comments.spec.js b/backend/src/schema/resolvers/comments.spec.js index cbd2b98fc..33b33ed3e 100644 --- a/backend/src/schema/resolvers/comments.spec.js +++ b/backend/src/schema/resolvers/comments.spec.js @@ -88,10 +88,10 @@ describe('CreateComment', () => { variables = { ...variables, postId: 'p1', - content: "I'm not authorised to comment", + content: "I'm not authorized to comment", } const { errors } = await mutate({ mutation: createCommentMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -107,14 +107,14 @@ describe('CreateComment', () => { variables = { ...variables, postId: 'p1', - content: "I'm authorised to comment", + content: "I'm authorized to comment", } }) it('creates a comment', async () => { await expect(mutate({ mutation: createCommentMutation, variables })).resolves.toMatchObject( { - data: { CreateComment: { content: "I'm authorised to comment" } }, + data: { CreateComment: { content: "I'm authorized to comment" } }, errors: undefined, }, ) @@ -150,7 +150,7 @@ describe('UpdateComment', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: updateCommentMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -162,7 +162,7 @@ describe('UpdateComment', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: updateCommentMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -217,7 +217,7 @@ describe('UpdateComment', () => { it('returns null', async () => { const { data, errors } = await mutate({ mutation: updateCommentMutation, variables }) expect(data).toMatchObject({ UpdateComment: null }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) }) @@ -242,7 +242,7 @@ describe('DeleteComment', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { const result = await mutate({ mutation: deleteCommentMutation, variables }) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -254,7 +254,7 @@ describe('DeleteComment', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: deleteCommentMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) diff --git a/backend/src/schema/resolvers/donations.spec.js b/backend/src/schema/resolvers/donations.spec.js index a9210f6a5..11df0c67b 100644 --- a/backend/src/schema/resolvers/donations.spec.js +++ b/backend/src/schema/resolvers/donations.spec.js @@ -72,7 +72,7 @@ describe('donations', () => { it('throws authorization error', async () => { authenticatedUser = undefined await expect(query({ query: donationsQuery, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -106,7 +106,7 @@ describe('donations', () => { await expect( mutate({ mutation: updateDonationsMutation, variables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -126,7 +126,7 @@ describe('donations', () => { mutate({ mutation: updateDonationsMutation, variables }), ).resolves.toMatchObject({ data: { UpdateDonations: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -145,7 +145,7 @@ describe('donations', () => { mutate({ mutation: updateDonationsMutation, variables }), ).resolves.toMatchObject({ data: { UpdateDonations: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/emails.spec.js b/backend/src/schema/resolvers/emails.spec.js index 39b70ac0b..b527ca3b0 100644 --- a/backend/src/schema/resolvers/emails.spec.js +++ b/backend/src/schema/resolvers/emails.spec.js @@ -63,7 +63,7 @@ describe('AddEmailAddress', () => { it('throws AuthorizationError', async () => { await expect(mutate({ mutation, variables })).resolves.toMatchObject({ data: { AddEmailAddress: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -169,7 +169,7 @@ describe('VerifyEmailAddress', () => { it('throws AuthorizationError', async () => { await expect(mutate({ mutation, variables })).resolves.toMatchObject({ data: { VerifyEmailAddress: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/follow.spec.js b/backend/src/schema/resolvers/follow.spec.js index 9cc8403e5..e0b2a277a 100644 --- a/backend/src/schema/resolvers/follow.spec.js +++ b/backend/src/schema/resolvers/follow.spec.js @@ -117,7 +117,7 @@ describe('follow', () => { variables, }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { followUser: null }, }) }) @@ -191,7 +191,7 @@ describe('follow', () => { authenticatedUser = null await expect(mutate({ mutation: mutationUnfollowUser, variables })).resolves.toMatchObject({ data: { unfollowUser: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/moderation.spec.js b/backend/src/schema/resolvers/moderation.spec.js index 662be41bd..3c2926c14 100644 --- a/backend/src/schema/resolvers/moderation.spec.js +++ b/backend/src/schema/resolvers/moderation.spec.js @@ -120,7 +120,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: disableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -134,7 +134,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: disableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -218,7 +218,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: disableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -232,7 +232,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: disableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -488,7 +488,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -507,7 +507,7 @@ describe('moderate resources', () => { await expect( mutate({ mutation: reviewMutation, variables: enableVariables }), ).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/notifications.spec.js b/backend/src/schema/resolvers/notifications.spec.js index 2ea468914..924154488 100644 --- a/backend/src/schema/resolvers/notifications.spec.js +++ b/backend/src/schema/resolvers/notifications.spec.js @@ -165,7 +165,7 @@ describe('given some notifications', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { const { errors } = await query({ query: notificationQuery }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -313,7 +313,7 @@ describe('given some notifications', () => { mutation: markAsReadMutation, variables: { ...variables, id: 'p1' }, }) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index b09bb3edd..d9a04732c 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -358,7 +358,7 @@ export default { undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'], hasMany: { tags: '-[:TAGGED]->(related:Tag)', - // categories: '-[:CATEGORIZED]->(related:Category)', + categories: '-[:CATEGORIZED]->(related:Category)', comments: '<-[:COMMENTS]-(related:Comment)', shoutedBy: '<-[:SHOUTED]-(related:User)', emotions: '<-[related:EMOTED]', diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 2c8b7e90b..52bd8fcd0 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -281,7 +281,7 @@ describe('CreatePost', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: createPostMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -369,7 +369,7 @@ describe('UpdatePost', () => { it('throws authorization error', async () => { authenticatedUser = null expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { UpdatePost: null }, }) }) @@ -382,7 +382,7 @@ describe('UpdatePost', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: updatePostMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -547,7 +547,7 @@ describe('pin posts', () => { it('throws authorization error', async () => { authenticatedUser = null await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { pinPost: null }, }) }) @@ -556,7 +556,7 @@ describe('pin posts', () => { describe('ordinary users', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { pinPost: null }, }) }) @@ -571,7 +571,7 @@ describe('pin posts', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: pinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { pinPost: null }, }) }) @@ -854,7 +854,7 @@ describe('unpin posts', () => { it('throws authorization error', async () => { authenticatedUser = null await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { unpinPost: null }, }) }) @@ -863,7 +863,7 @@ describe('unpin posts', () => { describe('users cannot unpin posts', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { unpinPost: null }, }) }) @@ -878,7 +878,7 @@ describe('unpin posts', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: unpinPostMutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], data: { unpinPost: null }, }) }) @@ -975,7 +975,7 @@ describe('DeletePost', () => { describe('unauthenticated', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: deletePostMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -986,7 +986,7 @@ describe('DeletePost', () => { it('throws authorization error', async () => { const { errors } = await mutate({ mutation: deletePostMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1128,7 +1128,7 @@ describe('emotions', () => { variables, }) - expect(addPostEmotions.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(addPostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -1249,7 +1249,7 @@ describe('emotions', () => { mutation: removePostEmotionsMutation, variables: removePostEmotionsVariables, }) - expect(removePostEmotions.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(removePostEmotions.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) diff --git a/backend/src/schema/resolvers/registration.spec.js b/backend/src/schema/resolvers/registration.spec.js index 573af1d35..ebe36456b 100644 --- a/backend/src/schema/resolvers/registration.spec.js +++ b/backend/src/schema/resolvers/registration.spec.js @@ -61,7 +61,7 @@ describe('Signup', () => { CONFIG.INVITE_REGISTRATION = false CONFIG.PUBLIC_REGISTRATION = false await expect(mutate({ mutation, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) diff --git a/backend/src/schema/resolvers/reports.spec.js b/backend/src/schema/resolvers/reports.spec.js index 09d0869fc..7cf4d9a34 100644 --- a/backend/src/schema/resolvers/reports.spec.js +++ b/backend/src/schema/resolvers/reports.spec.js @@ -130,7 +130,7 @@ describe('file a report on a resource', () => { authenticatedUser = null await expect(mutate({ mutation: fileReportMutation, variables })).resolves.toMatchObject({ data: { fileReport: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -729,7 +729,7 @@ describe('file a report on a resource', () => { authenticatedUser = null expect(query({ query: reportsQuery })).resolves.toMatchObject({ data: { reports: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -739,7 +739,7 @@ describe('file a report on a resource', () => { authenticatedUser = await currentUser.toJson() expect(query({ query: reportsQuery })).resolves.toMatchObject({ data: { reports: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) diff --git a/backend/src/schema/resolvers/rewards.spec.js b/backend/src/schema/resolvers/rewards.spec.js index 718378699..f44cf51f6 100644 --- a/backend/src/schema/resolvers/rewards.spec.js +++ b/backend/src/schema/resolvers/rewards.spec.js @@ -97,7 +97,7 @@ describe('rewards', () => { authenticatedUser = null await expect(mutate({ mutation: rewardMutation, variables })).resolves.toMatchObject({ data: { reward: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -255,7 +255,7 @@ describe('rewards', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: rewardMutation, variables })).resolves.toMatchObject({ data: { reward: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -308,7 +308,7 @@ describe('rewards', () => { authenticatedUser = null await expect(mutate({ mutation: unrewardMutation, variables })).resolves.toMatchObject({ data: { unreward: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -341,7 +341,7 @@ describe('rewards', () => { it('throws authorization error', async () => { await expect(mutate({ mutation: unrewardMutation, variables })).resolves.toMatchObject({ data: { unreward: null }, - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/shout.spec.js b/backend/src/schema/resolvers/shout.spec.js index a4e9890d5..087c452da 100644 --- a/backend/src/schema/resolvers/shout.spec.js +++ b/backend/src/schema/resolvers/shout.spec.js @@ -90,7 +90,7 @@ describe('shout and unshout posts', () => { variables = { id: 'post-to-shout-id' } authenticatedUser = undefined await expect(mutate({ mutation: mutationShoutPost, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) @@ -165,7 +165,7 @@ describe('shout and unshout posts', () => { authenticatedUser = undefined variables = { id: 'post-to-shout-id' } await expect(mutate({ mutation: mutationUnshoutPost, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) }) diff --git a/backend/src/schema/resolvers/socialMedia.spec.js b/backend/src/schema/resolvers/socialMedia.spec.js index bb7886c3f..fc9ee8f70 100644 --- a/backend/src/schema/resolvers/socialMedia.spec.js +++ b/backend/src/schema/resolvers/socialMedia.spec.js @@ -94,7 +94,7 @@ describe('SocialMedia', () => { const user = null const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -186,7 +186,7 @@ describe('SocialMedia', () => { const user = null const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -195,7 +195,7 @@ describe('SocialMedia', () => { const user = someUser const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -222,7 +222,7 @@ describe('SocialMedia', () => { variables.id = 'some-id' const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) }) @@ -249,7 +249,7 @@ describe('SocialMedia', () => { const user = null const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -258,7 +258,7 @@ describe('SocialMedia', () => { const user = someUser const result = await socialMediaAction(user, mutation, variables) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) diff --git a/backend/src/schema/resolvers/user_management.js b/backend/src/schema/resolvers/user_management.js index beb2cddb3..d88eafdae 100644 --- a/backend/src/schema/resolvers/user_management.js +++ b/backend/src/schema/resolvers/user_management.js @@ -20,16 +20,22 @@ export default { const result = await transaction.run( ` MATCH (user:User {id: $id}) - WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] as media - RETURN user {.*, socialMedia: media } as user + OPTIONAL MATCH (category:Category) WHERE NOT ((user)-[:NOT_INTERESTED_IN]->(category)) + OPTIONAL MATCH (cats:Category) + WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] AS media, category, toString(COUNT(cats)) AS categoryCount + RETURN user {.*, socialMedia: media, activeCategories: collect(category.id) } AS user, categoryCount `, { id: user.id }, ) - log(result) - return result.records.map((record) => record.get('user')) + const [categoryCount] = result.records.map((record) => record.get('categoryCount')) + const [currentUser] = result.records.map((record) => record.get('user')) + // frontend expects empty array when all categories are selected + if (currentUser.activeCategories.length === parseInt(categoryCount)) + currentUser.activeCategories = [] + return currentUser }) try { - const [currentUser] = await currentUserTransactionPromise + const currentUser = await currentUserTransactionPromise return currentUser } finally { session.close() diff --git a/backend/src/schema/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js index 2dcb14855..e7f2f3ed1 100644 --- a/backend/src/schema/resolvers/user_management.spec.js +++ b/backend/src/schema/resolvers/user_management.spec.js @@ -6,6 +6,7 @@ import { createTestClient } from 'apollo-server-testing' import createServer, { context } from '../../server' import encode from '../../jwt/encode' import { getNeode } from '../../db/neo4j' +import { categories } from '../../constants/categories' const neode = getNeode() let query, mutate, variables, req, user @@ -118,6 +119,7 @@ describe('currentUser', () => { } email role + activeCategories } } ` @@ -172,6 +174,52 @@ describe('currentUser', () => { } await respondsWith(expected) }) + + describe('with categories in DB', () => { + beforeEach(async () => { + await Promise.all( + categories.map(async ({ icon, name }, index) => { + await Factory.build('category', { + id: `cat${index + 1}`, + slug: name, + name, + icon, + }) + }), + ) + }) + + it('returns empty array for all categories', async () => { + await respondsWith({ + data: { + currentUser: expect.objectContaining({ activeCategories: [] }), + }, + }) + }) + + describe('with categories saved for current user', () => { + const saveCategorySettings = gql` + mutation ($activeCategories: [String]) { + saveCategorySettings(activeCategories: $activeCategories) + } + ` + beforeEach(async () => { + await mutate({ + mutation: saveCategorySettings, + variables: { activeCategories: ['cat1', 'cat3', 'cat5', 'cat7'] }, + }) + }) + + it('returns only the saved active categories', async () => { + const result = await query({ query: currentUserQuery, variables }) + expect(result.data.currentUser.activeCategories).toHaveLength(4) + expect(result.data.currentUser.activeCategories).toContain('cat1') + expect(result.data.currentUser.activeCategories).toContain('cat3') + expect(result.data.currentUser.activeCategories).toContain('cat5') + expect(result.data.currentUser.activeCategories).toContain('cat7') + }) + }) + }) }) }) }) @@ -310,8 +358,8 @@ describe('change password', () => { }) describe('unauthenticated', () => { - it('throws "Not Authorised!"', async () => { - await respondsWith({ errors: [{ message: 'Not Authorised!' }] }) + it('throws "Not Authorized!"', async () => { + await respondsWith({ errors: [{ message: 'Not Authorized!' }] }) }) }) diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index 5dc78c5e1..12f00ffb6 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -269,6 +269,49 @@ export default { session.close() } }, + saveCategorySettings: async (object, args, context, resolveInfo) => { + const { activeCategories } = args + const { + user: { id }, + } = context + + const session = context.driver.session() + await session.writeTransaction((transaction) => { + return transaction.run( + ` + MATCH (user:User { id: $id })-[previousCategories:NOT_INTERESTED_IN]->(category:Category) + DELETE previousCategories + RETURN user, category + `, + { id }, + ) + }) + + // frontend gives [] when all categories are selected (default) + if (activeCategories.length === 0) return true + + const writeTxResultPromise = session.writeTransaction(async (transaction) => { + const saveCategorySettingsResponse = await transaction.run( + ` + MATCH (category:Category) WHERE NOT category.id IN $activeCategories + MATCH (user:User { id: $id }) + MERGE (user)-[r:NOT_INTERESTED_IN]->(category) + RETURN user, r, category + `, + { id, activeCategories }, + ) + const [user] = await saveCategorySettingsResponse.records.map((record) => + record.get('user'), + ) + return user + }) + try { + await writeTxResultPromise + return true + } finally { + session.close() + } + }, }, User: { email: async (parent, params, context, resolveInfo) => { diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index 9a88dc945..d8fce3b29 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -3,6 +3,7 @@ import { gql } from '../../helpers/jest' import { getNeode, getDriver } from '../../db/neo4j' import createServer from '../../server' import { createTestClient } from 'apollo-server-testing' +import { categories } from '../../constants/categories' const categoryIds = ['cat9'] let user @@ -56,6 +57,12 @@ const switchUserRoleMutation = gql` } ` +const saveCategorySettings = gql` + mutation ($activeCategories: [String]) { + saveCategorySettings(activeCategories: $activeCategories) + } +` + beforeAll(async () => { await cleanDatabase() @@ -101,7 +108,7 @@ describe('User', () => { it('is forbidden', async () => { await expect(query({ query: userQuery, variables })).resolves.toMatchObject({ - errors: [{ message: 'Not Authorised!' }], + errors: [{ message: 'Not Authorized!' }], }) }) @@ -207,7 +214,7 @@ describe('UpdateUser', () => { it('is not allowed to change other user accounts', async () => { const { errors } = await mutate({ mutation: updateUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) @@ -500,7 +507,7 @@ describe('switch user role', () => { expect.objectContaining({ errors: [ expect.objectContaining({ - message: 'Not Authorised!', + message: 'Not Authorized!', }), ], }), @@ -544,3 +551,140 @@ describe('switch user role', () => { }) }) }) + +describe('save category settings', () => { + beforeEach(async () => { + await Promise.all( + categories.map(({ icon, name }, index) => { + Factory.build('category', { + id: `cat${index + 1}`, + slug: name, + name, + icon, + }) + }), + ) + }) + + beforeEach(async () => { + user = await Factory.build('user', { + id: 'user', + role: 'user', + }) + variables = { + activeCategories: ['cat1', 'cat3', 'cat5'], + } + }) + + describe('not authenticated', () => { + beforeEach(async () => { + authenticatedUser = undefined + }) + + it('throws an error', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [ + expect.objectContaining({ + message: 'Not Authorized!', + }), + ], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + const userQuery = gql` + query ($id: ID) { + User(id: $id) { + activeCategories + } + } + ` + + describe('no categories saved', () => { + it('returns true for active categories mutation', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + data: { saveCategorySettings: true }, + }), + ) + }) + + describe('query for user', () => { + beforeEach(async () => { + await mutate({ mutation: saveCategorySettings, variables }) + }) + + it('returns the active categories when user is queried', async () => { + await expect( + query({ query: userQuery, variables: { id: authenticatedUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']), + }, + ], + }, + }), + ) + }) + }) + }) + + describe('categories already saved', () => { + beforeEach(async () => { + variables = { + activeCategories: ['cat1', 'cat3', 'cat5'], + } + await mutate({ mutation: saveCategorySettings, variables }) + variables = { + activeCategories: ['cat10', 'cat11', 'cat12', 'cat8', 'cat9'], + } + }) + + it('returns true', async () => { + await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual( + expect.objectContaining({ + data: { saveCategorySettings: true }, + }), + ) + }) + + describe('query for user', () => { + beforeEach(async () => { + await mutate({ mutation: saveCategorySettings, variables }) + }) + + it('returns the new active categories when user is queried', async () => { + await expect( + query({ query: userQuery, variables: { id: authenticatedUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + activeCategories: expect.arrayContaining([ + 'cat10', + 'cat11', + 'cat12', + 'cat8', + 'cat9', + ]), + }, + ], + }, + }), + ) + }) + }) + }) + }) +}) diff --git a/backend/src/schema/resolvers/users/mutedUsers.spec.js b/backend/src/schema/resolvers/users/mutedUsers.spec.js index 36b526268..d8efacfae 100644 --- a/backend/src/schema/resolvers/users/mutedUsers.spec.js +++ b/backend/src/schema/resolvers/users/mutedUsers.spec.js @@ -58,7 +58,7 @@ describe('mutedUsers', () => { it('throws permission error', async () => { const { query } = createTestClient(server) const result = await query({ query: mutedUserQuery }) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) describe('authenticated and given a muted user', () => { @@ -116,7 +116,7 @@ describe('muteUser', () => { it('throws permission error', async () => { const result = await muteAction({ id: 'u2' }) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) describe('authenticated', () => { @@ -333,7 +333,7 @@ describe('unmuteUser', () => { it('throws permission error', async () => { const result = await unmuteAction({ id: 'u2' }) - expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!') + expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!') }) describe('authenticated', () => { diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql index 871e73ad8..c3fcf932b 100644 --- a/backend/src/schema/types/type/User.gql +++ b/backend/src/schema/types/type/User.gql @@ -114,6 +114,14 @@ type User { badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)") emotions: [EMOTED] + + activeCategories: [String] @cypher( + statement: """ + MATCH (category:Category) + WHERE NOT ((this)-[:NOT_INTERESTED_IN]->(category)) + RETURN collect(category.id) + """ + ) } @@ -220,4 +228,6 @@ type Mutation { unblockUser(id: ID!): User switchUserRole(role: UserRole!, id: ID!): User + + saveCategorySettings(activeCategories: [String]): Boolean } diff --git a/backend/yarn.lock b/backend/yarn.lock index 24bd00b3a..48c05947e 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4253,10 +4253,10 @@ eslint-plugin-node@~11.1.0: resolve "^1.10.1" semver "^6.1.0" -eslint-plugin-prettier@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" - integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== +eslint-plugin-prettier@~3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" + integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== dependencies: prettier-linter-helpers "^1.0.0" @@ -7574,10 +7574,10 @@ neo4j-graphql-js@^2.11.5: lodash "^4.17.15" neo4j-driver "^4.0.1" -neode@^0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.7.tgz#033007b57a2ee167e9ee5537493086db08d005eb" - integrity sha512-YXlc187JRpeKCBcUIkY6nimXXG+Tvlopfe71/FPno2THrwmYt5mm0RPHZ+mXF2O1Xg6zvjKvOpCpDz2vHBfroQ== +neode@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.8.tgz#0889b4fc7f1bf0b470b01fa5b8870373b5d47ad6" + integrity sha512-pb91NfCOg4Fj5o+98H+S2XYC+ByQfbdhwcc1UVuzuUQ0Ezzj+jWz8NmKWU8ZfCH6l4plk71yDAPd2eTwpt+Xvg== dependencies: "@hapi/joi" "^15.1.1" dotenv "^4.0.0" @@ -9262,10 +9262,10 @@ slug@^0.9.2: dependencies: unicode ">= 0.3.1" -slug@~4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/slug/-/slug-4.0.2.tgz#35a62b4e71582778ac08bb30a1bf439fd0a43ea7" - integrity sha512-c5XbWkwxHU13gAdSvBHQgnGy2sxv/REMz0ugcM0SOSBCO/N4wfU0TDBC3pgdOwVGjZwGnLBTRljXzdVYE+KYNw== +slug@~6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/slug/-/slug-6.0.0.tgz#39637b32e5a873bc692812a630842880499ed6c9" + integrity sha512-0MpNLyCSUSf0G1nAZmp9gY1cvesPP35a1Live25vZ23gWQ5SAopF0N+0hk9KI4ytNuTebJrHGNrgTnxboofcSg== smart-buffer@^4.1.0: version "4.1.0" diff --git a/package.json b/package.json index 7756ac8e6..d7463adac 100644 --- a/package.json +++ b/package.json @@ -42,10 +42,10 @@ "jsonwebtoken": "^8.5.1", "mock-socket": "^9.0.3", "neo4j-driver": "^4.3.4", - "neode": "^0.4.7", + "neode": "^0.4.8", "npm-run-all": "^4.1.5", "rosie": "^2.1.0", - "slug": "^5.1.0" + "slug": "^6.0.0" }, "resolutions": { "set-value": "^2.0.1" diff --git a/webapp/assets/_new/icons/svgs/child.svg b/webapp/assets/_new/icons/svgs/child.svg new file mode 100644 index 000000000..fcb5651f0 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/child.svg @@ -0,0 +1,5 @@ + + +child + + diff --git a/webapp/assets/_new/icons/svgs/culture.svg b/webapp/assets/_new/icons/svgs/culture.svg new file mode 100644 index 000000000..d63e38cb4 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/culture.svg @@ -0,0 +1,20 @@ + + + + + + + + diff --git a/webapp/assets/_new/icons/svgs/desktop.svg b/webapp/assets/_new/icons/svgs/desktop.svg new file mode 100644 index 000000000..ba1ef8431 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/desktop.svg @@ -0,0 +1,5 @@ + + +desktop + + diff --git a/webapp/assets/_new/icons/svgs/ellipsis-h.svg b/webapp/assets/_new/icons/svgs/ellipsis-h.svg new file mode 100644 index 000000000..eb7deeab0 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/ellipsis-h.svg @@ -0,0 +1,5 @@ + + +ellipsis-h + + diff --git a/webapp/assets/_new/icons/svgs/energy.svg b/webapp/assets/_new/icons/svgs/energy.svg new file mode 100644 index 000000000..5035a5586 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/energy.svg @@ -0,0 +1,14 @@ + + + diff --git a/webapp/assets/_new/icons/svgs/finance.svg b/webapp/assets/_new/icons/svgs/finance.svg new file mode 100644 index 000000000..74081bc6a --- /dev/null +++ b/webapp/assets/_new/icons/svgs/finance.svg @@ -0,0 +1,13 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/health.svg b/webapp/assets/_new/icons/svgs/health.svg new file mode 100644 index 000000000..acf50d7c1 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/health.svg @@ -0,0 +1,7 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/home.svg b/webapp/assets/_new/icons/svgs/home.svg new file mode 100644 index 000000000..b1a13b06f --- /dev/null +++ b/webapp/assets/_new/icons/svgs/home.svg @@ -0,0 +1,5 @@ + + +home + + diff --git a/webapp/assets/_new/icons/svgs/lightbulb.svg b/webapp/assets/_new/icons/svgs/lightbulb.svg new file mode 100644 index 000000000..1c19c81b1 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/lightbulb.svg @@ -0,0 +1,5 @@ + + +lightbulb-o + + diff --git a/webapp/assets/_new/icons/svgs/media.svg b/webapp/assets/_new/icons/svgs/media.svg new file mode 100644 index 000000000..d63c98610 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/media.svg @@ -0,0 +1,7 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/miscellaneous.svg b/webapp/assets/_new/icons/svgs/miscellaneous.svg new file mode 100644 index 000000000..07f8dbe3f --- /dev/null +++ b/webapp/assets/_new/icons/svgs/miscellaneous.svg @@ -0,0 +1,13 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/mobility.svg b/webapp/assets/_new/icons/svgs/mobility.svg new file mode 100644 index 000000000..9e36ec21e --- /dev/null +++ b/webapp/assets/_new/icons/svgs/mobility.svg @@ -0,0 +1,11 @@ + + + diff --git a/webapp/assets/_new/icons/svgs/movement.svg b/webapp/assets/_new/icons/svgs/movement.svg new file mode 100644 index 000000000..81052875d --- /dev/null +++ b/webapp/assets/_new/icons/svgs/movement.svg @@ -0,0 +1,19 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/music.svg b/webapp/assets/_new/icons/svgs/music.svg new file mode 100644 index 000000000..b84b87800 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/music.svg @@ -0,0 +1,5 @@ + + +music + + diff --git a/webapp/assets/_new/icons/svgs/nature.svg b/webapp/assets/_new/icons/svgs/nature.svg new file mode 100644 index 000000000..d40250af4 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/nature.svg @@ -0,0 +1,18 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/networking.svg b/webapp/assets/_new/icons/svgs/networking.svg new file mode 100644 index 000000000..b8d35da69 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/networking.svg @@ -0,0 +1,22 @@ + + + + + diff --git a/webapp/assets/_new/icons/svgs/peace.svg b/webapp/assets/_new/icons/svgs/peace.svg new file mode 100644 index 000000000..408601cae --- /dev/null +++ b/webapp/assets/_new/icons/svgs/peace.svg @@ -0,0 +1,8 @@ + + + diff --git a/webapp/assets/_new/icons/svgs/politics.svg b/webapp/assets/_new/icons/svgs/politics.svg new file mode 100644 index 000000000..35322097d --- /dev/null +++ b/webapp/assets/_new/icons/svgs/politics.svg @@ -0,0 +1,12 @@ + + + + + diff --git a/webapp/assets/_new/icons/svgs/psyche.svg b/webapp/assets/_new/icons/svgs/psyche.svg new file mode 100644 index 000000000..8c285d5ca --- /dev/null +++ b/webapp/assets/_new/icons/svgs/psyche.svg @@ -0,0 +1,8 @@ + + + diff --git a/webapp/assets/_new/icons/svgs/save.svg b/webapp/assets/_new/icons/svgs/save.svg new file mode 100644 index 000000000..31c1d8459 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/save.svg @@ -0,0 +1,5 @@ + + +save + + diff --git a/webapp/assets/_new/icons/svgs/science.svg b/webapp/assets/_new/icons/svgs/science.svg new file mode 100644 index 000000000..9d3211223 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/science.svg @@ -0,0 +1,25 @@ + + + + diff --git a/webapp/assets/_new/icons/svgs/spirituality.svg b/webapp/assets/_new/icons/svgs/spirituality.svg new file mode 100644 index 000000000..0c2757071 --- /dev/null +++ b/webapp/assets/_new/icons/svgs/spirituality.svg @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/webapp/assets/_new/icons/svgs/suitcase.svg b/webapp/assets/_new/icons/svgs/suitcase.svg new file mode 100644 index 000000000..ceca5cbad --- /dev/null +++ b/webapp/assets/_new/icons/svgs/suitcase.svg @@ -0,0 +1,5 @@ + + +suitcase + + diff --git a/webapp/components/CategoriesSelect/CategoriesSelect.vue b/webapp/components/CategoriesSelect/CategoriesSelect.vue index b7d71de2d..92779444f 100644 --- a/webapp/components/CategoriesSelect/CategoriesSelect.vue +++ b/webapp/components/CategoriesSelect/CategoriesSelect.vue @@ -9,6 +9,11 @@ :disabled="isDisabled(category.id)" :icon="category.icon" size="small" + v-tooltip="{ + content: $t(`contribution.category.description.${category.slug}`), + placement: 'bottom-start', + delay: { show: 1500 }, + }" > {{ $t(`contribution.category.name.${category.slug}`) }} diff --git a/webapp/components/CommentCard/CommentCard.story.js b/webapp/components/CommentCard/CommentCard.story.js index 467a125d5..75078657e 100644 --- a/webapp/components/CommentCard/CommentCard.story.js +++ b/webapp/components/CommentCard/CommentCard.story.js @@ -18,8 +18,7 @@ const comment = { author: { id: '1', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, slug: 'jenny-rostock', name: 'Rainer Unsinn', diff --git a/webapp/components/ContributionForm/ContributionForm.spec.js b/webapp/components/ContributionForm/ContributionForm.spec.js index ce432fc42..f5083a8a0 100644 --- a/webapp/components/ContributionForm/ContributionForm.spec.js +++ b/webapp/components/ContributionForm/ContributionForm.spec.js @@ -202,7 +202,7 @@ describe('ContributionForm.vue', () => { beforeEach(async () => { jest.useFakeTimers() mocks.$apollo.mutate = jest.fn().mockRejectedValueOnce({ - message: 'Not Authorised!', + message: 'Not Authorized!', }) wrapper = Wrapper() postTitleInput = wrapper.find('.ds-input') @@ -213,7 +213,7 @@ describe('ContributionForm.vue', () => { it('shows an error toaster when apollo mutation rejects', async () => { await wrapper.find('form').trigger('submit') await mocks.$apollo.mutate - await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') + await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!') }) }) }) diff --git a/webapp/components/DeleteData/DeleteData.spec.js b/webapp/components/DeleteData/DeleteData.spec.js index 70f98424a..c540c9832 100644 --- a/webapp/components/DeleteData/DeleteData.spec.js +++ b/webapp/components/DeleteData/DeleteData.spec.js @@ -29,7 +29,7 @@ describe('DeleteData.vue', () => { }, }, }) - .mockRejectedValue({ message: 'Not authorised!' }), + .mockRejectedValue({ message: 'Not Authorized!' }), }, $toast: { error: jest.fn(), @@ -180,7 +180,7 @@ describe('DeleteData.vue', () => { // second submission causes mutation to reject await deleteAccountBtn.trigger('click') await mocks.$apollo.mutate - expect(mocks.$toast.error).toHaveBeenCalledWith('Not authorised!') + expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!') }) }) }) diff --git a/webapp/components/DeleteData/DeleteData.vue b/webapp/components/DeleteData/DeleteData.vue index 21b842fe6..a0cdcf29b 100644 --- a/webapp/components/DeleteData/DeleteData.vue +++ b/webapp/components/DeleteData/DeleteData.vue @@ -103,7 +103,7 @@ export default { this.$apollo .mutate({ mutation: gql` - mutation($id: ID!, $resource: [Deletable]) { + mutation ($id: ID!, $resource: [Deletable]) { DeleteUser(id: $id, resource: $resource) { id } diff --git a/webapp/components/Editor/Editor.story.js b/webapp/components/Editor/Editor.story.js index dfcc3cdf4..6e38eab7f 100644 --- a/webapp/components/Editor/Editor.story.js +++ b/webapp/components/Editor/Editor.story.js @@ -9,8 +9,7 @@ const embed = { title: 'Video Titel', // html: null, description: 'Video Description', - html: - '', + html: '', } const plugins = [ diff --git a/webapp/components/Editor/nodes/Embed.spec.js b/webapp/components/Editor/nodes/Embed.spec.js index e87ee1570..372b189f9 100644 --- a/webapp/components/Editor/nodes/Embed.spec.js +++ b/webapp/components/Editor/nodes/Embed.spec.js @@ -33,8 +33,7 @@ describe('Embed.vue', () => { video: null, lang: 'de', sources: ['resource', 'oembed'], - html: - '', + html: '', }), } }) diff --git a/webapp/components/Embed/EmbedComponent.spec.js b/webapp/components/Embed/EmbedComponent.spec.js index fd7452c01..cd3526368 100644 --- a/webapp/components/Embed/EmbedComponent.spec.js +++ b/webapp/components/Embed/EmbedComponent.spec.js @@ -114,8 +114,7 @@ describe('EmbedComponent.vue', () => { video: null, lang: 'de', sources: ['resource', 'oembed'], - html: - '', + html: '', } wrapper = Wrapper() }) diff --git a/webapp/components/Emotions/Emotions.vue b/webapp/components/Emotions/Emotions.vue index e75c8848d..b4dc14a61 100644 --- a/webapp/components/Emotions/Emotions.vue +++ b/webapp/components/Emotions/Emotions.vue @@ -83,7 +83,7 @@ export default { this.$apollo .query({ query: gql` - query($postId: ID!, $data: _EMOTEDInput!) { + query ($postId: ID!, $data: _EMOTEDInput!) { PostsEmotionsCountByEmotion(postId: $postId, data: $data) } `, diff --git a/webapp/components/FilterMenu/CategoriesFilter.spec.js b/webapp/components/FilterMenu/CategoriesFilter.spec.js index ef0f2ad90..813f7b33c 100644 --- a/webapp/components/FilterMenu/CategoriesFilter.spec.js +++ b/webapp/components/FilterMenu/CategoriesFilter.spec.js @@ -15,8 +15,19 @@ describe('CategoriesFilter.vue', () => { 'posts/filteredCategoryIds': jest.fn(() => []), } + const apolloMutationMock = jest.fn().mockResolvedValue({ + data: { saveCategorySettings: true }, + }) + const mocks = { $t: jest.fn((string) => string), + $apollo: { + mutate: apolloMutationMock, + }, + $toast: { + success: jest.fn(), + error: jest.fn(), + }, } const Wrapper = () => { @@ -76,5 +87,14 @@ describe('CategoriesFilter.vue', () => { expect(mutations['posts/RESET_CATEGORIES']).toHaveBeenCalledTimes(1) }) }) + + describe('save categories', () => { + it('calls the API', async () => { + wrapper = await Wrapper() + const saveButton = wrapper.findAll('.categories-filter .sidebar .base-button').at(1) + saveButton.trigger('click') + expect(apolloMutationMock).toBeCalled() + }) + }) }) }) diff --git a/webapp/components/FilterMenu/CategoriesFilter.vue b/webapp/components/FilterMenu/CategoriesFilter.vue index 47e4bcc10..552aa26a0 100644 --- a/webapp/components/FilterMenu/CategoriesFilter.vue +++ b/webapp/components/FilterMenu/CategoriesFilter.vue @@ -7,6 +7,8 @@ icon="check" @click="resetCategories" /> +
+ @@ -24,6 +31,7 @@ + + diff --git a/webapp/components/FilterMenu/FilterMenu.spec.js b/webapp/components/FilterMenu/FilterMenu.spec.js index 9e7ebfec0..6e9741e79 100644 --- a/webapp/components/FilterMenu/FilterMenu.spec.js +++ b/webapp/components/FilterMenu/FilterMenu.spec.js @@ -8,6 +8,9 @@ let wrapper describe('FilterMenu.vue', () => { const mocks = { $t: jest.fn((string) => string), + $env: { + CATEGORIES_ACTIVE: true, + }, } const getters = { diff --git a/webapp/components/LocaleSwitch/LocaleSwitch.vue b/webapp/components/LocaleSwitch/LocaleSwitch.vue index b302afd76..622bf1ce9 100644 --- a/webapp/components/LocaleSwitch/LocaleSwitch.vue +++ b/webapp/components/LocaleSwitch/LocaleSwitch.vue @@ -82,7 +82,7 @@ export default { try { await this.$apollo.mutate({ mutation: gql` - mutation($id: ID!, $locale: String) { + mutation ($id: ID!, $locale: String) { UpdateUser(id: $id, locale: $locale) { id locale diff --git a/webapp/components/LoginForm/LoginForm.spec.js b/webapp/components/LoginForm/LoginForm.spec.js index 430fdbe21..10fc2c622 100644 --- a/webapp/components/LoginForm/LoginForm.spec.js +++ b/webapp/components/LoginForm/LoginForm.spec.js @@ -12,6 +12,8 @@ config.stubs['nuxt-link'] = '' config.stubs['locale-switch'] = '' config.stubs['client-only'] = '' +const authUserMock = jest.fn().mockReturnValue({ activeCategories: [] }) + describe('LoginForm', () => { let mocks let propsData @@ -26,10 +28,15 @@ describe('LoginForm', () => { storeMocks = { getters: { 'auth/pending': () => false, + 'auth/user': authUserMock, }, actions: { 'auth/login': jest.fn(), }, + mutations: { + 'posts/TOGGLE_CATEGORY': jest.fn(), + 'posts/RESET_CATEGORIES': jest.fn(), + }, } const store = new Vuex.Store(storeMocks) mocks = { @@ -43,20 +50,46 @@ describe('LoginForm', () => { } describe('fill in email and password and submit', () => { - const fillIn = (wrapper, opts = {}) => { + const fillIn = async (wrapper, opts = {}) => { const { email = 'email@example.org', password = '1234' } = opts wrapper.find('input[name="email"]').setValue(email) wrapper.find('input[name="password"]').setValue(password) - wrapper.find('form').trigger('submit') + await wrapper.find('form').trigger('submit') } - it('dispatches login with form data', () => { - fillIn(Wrapper()) + it('dispatches login with form data', async () => { + await fillIn(Wrapper()) expect(storeMocks.actions['auth/login']).toHaveBeenCalledWith(expect.any(Object), { email: 'email@example.org', password: '1234', }) }) + + describe('setting saved categories', () => { + beforeEach(() => { + jest.clearAllMocks() + }) + + describe('no categories saved', () => { + it('resets the categories', async () => { + await fillIn(Wrapper()) + expect(storeMocks.mutations['posts/RESET_CATEGORIES']).toBeCalled() + expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).not.toBeCalled() + }) + }) + + describe('categories saved', () => { + it('sets the categories', async () => { + authUserMock.mockReturnValue({ activeCategories: ['cat1', 'cat9', 'cat12'] }) + await fillIn(Wrapper()) + expect(storeMocks.mutations['posts/RESET_CATEGORIES']).toBeCalled() + expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledTimes(3) + expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat1') + expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat9') + expect(storeMocks.mutations['posts/TOGGLE_CATEGORY']).toBeCalledWith({}, 'cat12') + }) + }) + }) }) describe('Visibility of password', () => { diff --git a/webapp/components/LoginForm/LoginForm.vue b/webapp/components/LoginForm/LoginForm.vue index 0d85ca33a..59a6d7a24 100644 --- a/webapp/components/LoginForm/LoginForm.vue +++ b/webapp/components/LoginForm/LoginForm.vue @@ -58,6 +58,7 @@ import PageParamsLink from '~/components/_new/features/PageParamsLink/PageParams import LocaleSwitch from '~/components/LocaleSwitch/LocaleSwitch' import Logo from '~/components/Logo/Logo' import ShowPassword from '../ShowPassword/ShowPassword.vue' +import { mapGetters, mapMutations } from 'vuex' export default { components: { @@ -84,12 +85,27 @@ export default { iconName() { return this.showPassword ? 'eye-slash' : 'eye' }, + ...mapGetters({ + currentUser: 'auth/user', + }), }, methods: { + ...mapMutations({ + toggleCategory: 'posts/TOGGLE_CATEGORY', + resetCategories: 'posts/RESET_CATEGORIES', + }), async onSubmit() { const { email, password } = this.form try { await this.$store.dispatch('auth/login', { email, password }) + if (this.currentUser && this.currentUser.activeCategories) { + this.resetCategories() + if (this.currentUser.activeCategories.length > 0) { + this.currentUser.activeCategories.forEach((categoryId) => { + this.toggleCategory(categoryId) + }) + } + } this.$toast.success(this.$t('login.success')) this.$emit('success') } catch (err) { diff --git a/webapp/components/Modal/DeleteUserModal.vue b/webapp/components/Modal/DeleteUserModal.vue index 87aa557d8..8618582ee 100644 --- a/webapp/components/Modal/DeleteUserModal.vue +++ b/webapp/components/Modal/DeleteUserModal.vue @@ -127,7 +127,7 @@ export default { this.$apollo .mutate({ mutation: gql` - mutation($id: ID!, $resource: [Deletable]) { + mutation ($id: ID!, $resource: [Deletable]) { DeleteUser(id: $id, resource: $resource) { id } diff --git a/webapp/components/Modal/DisableModal.spec.js b/webapp/components/Modal/DisableModal.spec.js index ed306a6cc..b7e52b5a0 100644 --- a/webapp/components/Modal/DisableModal.spec.js +++ b/webapp/components/Modal/DisableModal.spec.js @@ -25,7 +25,7 @@ describe('DisableModal.vue', () => { $t: jest.fn(), $apollo: { mutate: jest.fn().mockResolvedValueOnce().mockRejectedValue({ - message: 'Not Authorised!', + message: 'Not Authorized!', }), }, location: { @@ -184,7 +184,7 @@ describe('DisableModal.vue', () => { }) it('shows an error toaster when mutation rejects', async () => { - await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') + await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!') }) }) }) diff --git a/webapp/components/Modal/DisableModal.vue b/webapp/components/Modal/DisableModal.vue index 19ef64332..d28033ff8 100644 --- a/webapp/components/Modal/DisableModal.vue +++ b/webapp/components/Modal/DisableModal.vue @@ -53,7 +53,7 @@ export default { // await this.modalData.buttons.confirm.callback() await this.$apollo.mutate({ mutation: gql` - mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) { + mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) { review(resourceId: $resourceId, disable: $disable, closed: $closed) { disable } diff --git a/webapp/components/Password/Change.vue b/webapp/components/Password/Change.vue index cd955973f..94b69a8f6 100644 --- a/webapp/components/Password/Change.vue +++ b/webapp/components/Password/Change.vue @@ -65,7 +65,7 @@ export default { async handleSubmit(data) { this.loading = true const mutation = gql` - mutation($oldPassword: String!, $password: String!) { + mutation ($oldPassword: String!, $password: String!) { changePassword(oldPassword: $oldPassword, newPassword: $password) } ` diff --git a/webapp/components/PasswordReset/ChangePassword.vue b/webapp/components/PasswordReset/ChangePassword.vue index e65a7a04f..c53afcc01 100644 --- a/webapp/components/PasswordReset/ChangePassword.vue +++ b/webapp/components/PasswordReset/ChangePassword.vue @@ -93,7 +93,7 @@ export default { methods: { async handleSubmitPassword() { const mutation = gql` - mutation($nonce: String!, $email: String!, $password: String!) { + mutation ($nonce: String!, $email: String!, $password: String!) { resetPassword(nonce: $nonce, email: $email, newPassword: $password) } ` diff --git a/webapp/components/PasswordReset/Request.vue b/webapp/components/PasswordReset/Request.vue index 5f4baf357..5398c13ed 100644 --- a/webapp/components/PasswordReset/Request.vue +++ b/webapp/components/PasswordReset/Request.vue @@ -85,7 +85,7 @@ export default { }, async handleSubmit() { const mutation = gql` - mutation($email: String!) { + mutation ($email: String!) { requestPasswordReset(email: $email) } ` diff --git a/webapp/components/PostTeaser/PostTeaser.story.js b/webapp/components/PostTeaser/PostTeaser.story.js index c82396101..e77e85585 100644 --- a/webapp/components/PostTeaser/PostTeaser.story.js +++ b/webapp/components/PostTeaser/PostTeaser.story.js @@ -17,8 +17,7 @@ export const post = { author: { id: 'u3', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, slug: 'jenny-rostock', name: 'Rainer Unsinn', diff --git a/webapp/components/PostTeaser/PostTeaser.vue b/webapp/components/PostTeaser/PostTeaser.vue index a973ca31f..75eefbfb2 100644 --- a/webapp/components/PostTeaser/PostTeaser.vue +++ b/webapp/components/PostTeaser/PostTeaser.vue @@ -33,7 +33,7 @@ v-tooltip="{ content: $t(`contribution.category.name.${category.slug}`), placement: 'bottom-start', - delay: { show: 500 }, + delay: { show: 1500 }, }" :icon="category.icon" /> diff --git a/webapp/components/Registration/RegistrationSlideEmail.vue b/webapp/components/Registration/RegistrationSlideEmail.vue index 045269f00..6ba443996 100644 --- a/webapp/components/Registration/RegistrationSlideEmail.vue +++ b/webapp/components/Registration/RegistrationSlideEmail.vue @@ -35,7 +35,7 @@ import normalizeEmail from '~/components/utils/NormalizeEmail' import translateErrorMessage from '~/components/utils/TranslateErrorMessage' export const SignupMutation = gql` - mutation($email: String!, $inviteCode: String) { + mutation ($email: String!, $inviteCode: String) { Signup(email: $email, inviteCode: $inviteCode) { email } @@ -165,9 +165,8 @@ export default { }) this.setButtonValues() - const { email: responseEmail } = this.sliderData.sliders[ - this.sliderIndex - ].data.response.Signup + const { email: responseEmail } = + this.sliderData.sliders[this.sliderIndex].data.response.Signup this.$toast.success( this.$t('components.registration.email.form.success', { email: responseEmail }), ) diff --git a/webapp/components/Registration/RegistrationSlideInvite.vue b/webapp/components/Registration/RegistrationSlideInvite.vue index f9ce15ee9..48c62d412 100644 --- a/webapp/components/Registration/RegistrationSlideInvite.vue +++ b/webapp/components/Registration/RegistrationSlideInvite.vue @@ -25,7 +25,7 @@ import gql from 'graphql-tag' import CONSTANTS_REGISTRATION from './../../constants/registration' export const isValidInviteCodeQuery = gql` - query($code: ID!) { + query ($code: ID!) { isValidInviteCode(code: $code) } ` diff --git a/webapp/components/Registration/RegistrationSlideNonce.vue b/webapp/components/Registration/RegistrationSlideNonce.vue index 147e84336..b9de7afd1 100644 --- a/webapp/components/Registration/RegistrationSlideNonce.vue +++ b/webapp/components/Registration/RegistrationSlideNonce.vue @@ -30,7 +30,7 @@ import CONSTANTS_REGISTRATION from './../../constants/registration' import EmailDisplayAndVerify from './EmailDisplayAndVerify' export const verifyNonceQuery = gql` - query($email: String!, $nonce: String!) { + query ($email: String!, $nonce: String!) { VerifyNonce(email: $email, nonce: $nonce) } ` diff --git a/webapp/components/Registration/Signup.vue b/webapp/components/Registration/Signup.vue index 86142c338..91b9ecd61 100644 --- a/webapp/components/Registration/Signup.vue +++ b/webapp/components/Registration/Signup.vue @@ -70,7 +70,7 @@ import { SweetalertIcon } from 'vue-sweetalert-icons' import translateErrorMessage from '~/components/utils/TranslateErrorMessage' export const SignupMutation = gql` - mutation($email: String!, $inviteCode: String) { + mutation ($email: String!, $inviteCode: String) { Signup(email: $email, inviteCode: $inviteCode) { email } diff --git a/webapp/components/ReleaseModal/ReleaseModal.spec.js b/webapp/components/ReleaseModal/ReleaseModal.spec.js index d1dafa5c9..9cded646f 100644 --- a/webapp/components/ReleaseModal/ReleaseModal.spec.js +++ b/webapp/components/ReleaseModal/ReleaseModal.spec.js @@ -25,7 +25,7 @@ describe('ReleaseModal.vue', () => { }, $t: jest.fn(), $apollo: { - mutate: jest.fn().mockResolvedValueOnce().mockRejectedValue({ message: 'Not Authorised!' }), + mutate: jest.fn().mockResolvedValueOnce().mockRejectedValue({ message: 'Not Authorized!' }), }, location: { reload: jest.fn(), @@ -181,7 +181,7 @@ describe('ReleaseModal.vue', () => { }) it('shows an error toaster when mutation rejects', async () => { - await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorised!') + await expect(mocks.$toast.error).toHaveBeenCalledWith('Not Authorized!') }) }) }) diff --git a/webapp/components/ReleaseModal/ReleaseModal.vue b/webapp/components/ReleaseModal/ReleaseModal.vue index fef0d79d0..fceb4b7a1 100644 --- a/webapp/components/ReleaseModal/ReleaseModal.vue +++ b/webapp/components/ReleaseModal/ReleaseModal.vue @@ -52,7 +52,7 @@ export default { // await this.modalData.buttons.confirm.callback() await this.$apollo.mutate({ mutation: gql` - mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) { + mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) { review(resourceId: $resourceId, disable: $disable, closed: $closed) { disable } diff --git a/webapp/components/generic/SearchableInput/SearchableInput.story.js b/webapp/components/generic/SearchableInput/SearchableInput.story.js index 62e422a67..f483129eb 100644 --- a/webapp/components/generic/SearchableInput/SearchableInput.story.js +++ b/webapp/components/generic/SearchableInput/SearchableInput.story.js @@ -78,8 +78,7 @@ export const searchResults = [ id: 'u1', __typename: 'User', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, name: 'Peter Lustig', slug: 'peter-lustig', @@ -88,8 +87,7 @@ export const searchResults = [ id: 'cdbca762-0632-4564-b646-415a0c42d8b8', __typename: 'User', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, name: 'Herbert Schultz', slug: 'herbert-schultz', @@ -98,8 +96,7 @@ export const searchResults = [ id: 'u2', __typename: 'User', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, name: 'Bob der Baumeister', slug: 'bob-der-baumeister', @@ -108,8 +105,7 @@ export const searchResults = [ id: '7b654f72-f4da-4315-8bed-39de0859754b', __typename: 'User', avatar: { - url: - 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', + url: 'https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/db/dbc9e03ebcc384b920c31542af2d27dd8eea9dc2_full.jpg', }, name: 'Tonya Mohr', slug: 'tonya-mohr', diff --git a/webapp/graphql/CommentMutations.js b/webapp/graphql/CommentMutations.js index a73ebb00c..191edf217 100644 --- a/webapp/graphql/CommentMutations.js +++ b/webapp/graphql/CommentMutations.js @@ -4,7 +4,7 @@ export default (i18n) => { const lang = i18n.locale().toUpperCase() return { CreateComment: gql` - mutation($postId: ID!, $content: String!) { + mutation ($postId: ID!, $content: String!) { CreateComment(postId: $postId, content: $content) { id contentExcerpt @@ -36,7 +36,7 @@ export default (i18n) => { } `, UpdateComment: gql` - mutation($content: String!, $id: ID!) { + mutation ($content: String!, $id: ID!) { UpdateComment(content: $content, id: $id) { id contentExcerpt diff --git a/webapp/graphql/Donations.js b/webapp/graphql/Donations.js index e412ee4fa..573158cbf 100644 --- a/webapp/graphql/Donations.js +++ b/webapp/graphql/Donations.js @@ -13,7 +13,7 @@ export const DonationsQuery = () => gql` export const UpdateDonations = () => { return gql` - mutation($showDonations: Boolean, $goal: Int, $progress: Int) { + mutation ($showDonations: Boolean, $goal: Int, $progress: Int) { UpdateDonations(showDonations: $showDonations, goal: $goal, progress: $progress) { id showDonations diff --git a/webapp/graphql/EmailAddress.js b/webapp/graphql/EmailAddress.js index 675ec6bed..b3b4df695 100644 --- a/webapp/graphql/EmailAddress.js +++ b/webapp/graphql/EmailAddress.js @@ -1,7 +1,7 @@ import gql from 'graphql-tag' export const AddEmailAddressMutation = gql` - mutation($email: String!) { + mutation ($email: String!) { AddEmailAddress(email: $email) { email createdAt @@ -10,7 +10,7 @@ export const AddEmailAddressMutation = gql` ` export const VerifyEmailAddressMutation = gql` - mutation($email: String!, $nonce: String!) { + mutation ($email: String!, $nonce: String!) { VerifyEmailAddress(email: $email, nonce: $nonce) { email verifiedAt diff --git a/webapp/graphql/EmbedQuery.js b/webapp/graphql/EmbedQuery.js index 685095d34..70450722d 100644 --- a/webapp/graphql/EmbedQuery.js +++ b/webapp/graphql/EmbedQuery.js @@ -2,7 +2,7 @@ import gql from 'graphql-tag' export default function () { return gql` - query($url: String!) { + query ($url: String!) { embed(url: $url) { type title diff --git a/webapp/graphql/Moderation.js b/webapp/graphql/Moderation.js index f4796d080..8dc20f987 100644 --- a/webapp/graphql/Moderation.js +++ b/webapp/graphql/Moderation.js @@ -3,7 +3,7 @@ import gql from 'graphql-tag' export const reportsListQuery = () => { // no limit for the moment like before: "reports(first: 20, orderBy: createdAt_desc)" return gql` - query( + query ( $orderBy: ReportOrdering $first: Int $offset: Int @@ -94,7 +94,7 @@ export const reportsListQuery = () => { export const reportMutation = () => { return gql` - mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { + mutation ($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { fileReport( resourceId: $resourceId reasonCategory: $reasonCategory @@ -108,7 +108,7 @@ export const reportMutation = () => { export const reviewMutation = () => { return gql` - mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) { + mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) { review(resourceId: $resourceId, disable: $disable, closed: $closed) { disable } diff --git a/webapp/graphql/PostMutations.js b/webapp/graphql/PostMutations.js index af91d245e..ee61efc3b 100644 --- a/webapp/graphql/PostMutations.js +++ b/webapp/graphql/PostMutations.js @@ -3,7 +3,7 @@ import gql from 'graphql-tag' export default () => { return { CreatePost: gql` - mutation($title: String!, $content: String!, $categoryIds: [ID], $image: ImageInput) { + mutation ($title: String!, $content: String!, $categoryIds: [ID], $image: ImageInput) { CreatePost(title: $title, content: $content, categoryIds: $categoryIds, image: $image) { title slug @@ -18,7 +18,7 @@ export default () => { } `, UpdatePost: gql` - mutation( + mutation ( $id: ID! $title: String! $content: String! @@ -52,14 +52,14 @@ export default () => { } `, DeletePost: gql` - mutation($id: ID!) { + mutation ($id: ID!) { DeletePost(id: $id) { id } } `, AddPostEmotionsMutation: gql` - mutation($to: _PostInput!, $data: _EMOTEDInput!) { + mutation ($to: _PostInput!, $data: _EMOTEDInput!) { AddPostEmotions(to: $to, data: $data) { emotion from { @@ -72,7 +72,7 @@ export default () => { } `, RemovePostEmotionsMutation: gql` - mutation($to: _PostInput!, $data: _EMOTEDInput!) { + mutation ($to: _PostInput!, $data: _EMOTEDInput!) { RemovePostEmotions(to: $to, data: $data) { emotion from { @@ -85,7 +85,7 @@ export default () => { } `, pinPost: gql` - mutation($id: ID!) { + mutation ($id: ID!) { pinPost(id: $id) { id title @@ -102,7 +102,7 @@ export default () => { } `, unpinPost: gql` - mutation($id: ID!) { + mutation ($id: ID!) { unpinPost(id: $id) { id title @@ -119,7 +119,7 @@ export default () => { } `, markTeaserAsViewed: gql` - mutation($id: ID!) { + mutation ($id: ID!) { markTeaserAsViewed(id: $id) { id } diff --git a/webapp/graphql/Registration.js b/webapp/graphql/Registration.js index fcf9c52a7..b6cc610d9 100644 --- a/webapp/graphql/Registration.js +++ b/webapp/graphql/Registration.js @@ -1,6 +1,6 @@ import gql from 'graphql-tag' export const SignupVerificationMutation = gql` - mutation( + mutation ( $nonce: String! $name: String! $email: String! diff --git a/webapp/graphql/SaveCategories.js b/webapp/graphql/SaveCategories.js new file mode 100644 index 000000000..d6db34b8a --- /dev/null +++ b/webapp/graphql/SaveCategories.js @@ -0,0 +1,9 @@ +import gql from 'graphql-tag' + +export default () => { + return gql` + mutation ($activeCategories: [String]) { + saveCategorySettings(activeCategories: $activeCategories) + } + ` +} diff --git a/webapp/graphql/Search.js b/webapp/graphql/Search.js index 96e507b2d..56f5d7c4c 100644 --- a/webapp/graphql/Search.js +++ b/webapp/graphql/Search.js @@ -5,7 +5,7 @@ export const searchQuery = gql` ${userFragment} ${postFragment} - query($query: String!) { + query ($query: String!) { searchResults(query: $query, limit: 5) { __typename ... on Post { @@ -33,7 +33,7 @@ export const searchPosts = gql` ${postFragment} ${tagsCategoriesAndPinnedFragment} - query($query: String!, $firstPosts: Int, $postsOffset: Int) { + query ($query: String!, $firstPosts: Int, $postsOffset: Int) { searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) { postCount posts { @@ -55,7 +55,7 @@ export const searchPosts = gql` export const searchUsers = gql` ${userFragment} - query($query: String!, $firstUsers: Int, $usersOffset: Int) { + query ($query: String!, $firstUsers: Int, $usersOffset: Int) { searchUsers(query: $query, firstUsers: $firstUsers, usersOffset: $usersOffset) { userCount users { @@ -67,7 +67,7 @@ export const searchUsers = gql` ` export const searchHashtags = gql` - query($query: String!, $firstHashtags: Int, $hashtagsOffset: Int) { + query ($query: String!, $firstHashtags: Int, $hashtagsOffset: Int) { searchHashtags(query: $query, firstHashtags: $firstHashtags, hashtagsOffset: $hashtagsOffset) { hashtagCount hashtags { diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index b3f131238..053cb022f 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -68,7 +68,7 @@ export const notificationQuery = (i18n) => { ${commentFragment} ${postFragment} - query($read: Boolean, $orderBy: NotificationOrdering, $first: Int, $offset: Int) { + query ($read: Boolean, $orderBy: NotificationOrdering, $first: Int, $offset: Int) { notifications(read: $read, orderBy: $orderBy, first: $first, offset: $offset) { id read @@ -107,7 +107,7 @@ export const markAsReadMutation = (i18n) => { ${commentFragment} ${postFragment} - mutation($id: ID!) { + mutation ($id: ID!) { markAsRead(id: $id) { id read @@ -180,7 +180,7 @@ export const followUserMutation = (i18n) => { ${userFragment} ${userCountsFragment} - mutation($id: ID!) { + mutation ($id: ID!) { followUser(id: $id) { ...user ...userCounts @@ -200,7 +200,7 @@ export const unfollowUserMutation = (i18n) => { ${userFragment} ${userCountsFragment} - mutation($id: ID!) { + mutation ($id: ID!) { unfollowUser(id: $id) { ...user ...userCounts @@ -217,7 +217,7 @@ export const unfollowUserMutation = (i18n) => { export const updateUserMutation = () => { return gql` - mutation( + mutation ( $id: ID! $slug: String $name: String @@ -260,7 +260,7 @@ export const updateUserMutation = () => { } export const checkSlugAvailableQuery = gql` - query($slug: String!) { + query ($slug: String!) { User(slug: $slug) { slug } @@ -285,6 +285,7 @@ export const currentUserQuery = gql` id url } + activeCategories } } ` @@ -303,7 +304,7 @@ export const userDataQuery = (i18n) => { ${userFragment} ${postFragment} ${commentFragment} - query($id: ID!) { + query ($id: ID!) { userData(id: $id) { user { ...user diff --git a/webapp/graphql/admin/Roles.js b/webapp/graphql/admin/Roles.js index 4365cb533..4015ad06c 100644 --- a/webapp/graphql/admin/Roles.js +++ b/webapp/graphql/admin/Roles.js @@ -10,7 +10,7 @@ export const FetchAllRoles = () => { export const updateUserRole = (role, id) => { return gql` - mutation($role: UserRole!, $id: ID!) { + mutation ($role: UserRole!, $id: ID!) { switchUserRole(role: $role, id: $id) { name role diff --git a/webapp/graphql/location.js b/webapp/graphql/location.js index 0fa2c2a2d..1fff64782 100644 --- a/webapp/graphql/location.js +++ b/webapp/graphql/location.js @@ -1,7 +1,7 @@ import gql from 'graphql-tag' export const queryLocations = () => gql` - query($place: String!, $lang: String!) { + query ($place: String!, $lang: String!) { queryLocations(place: $place, lang: $lang) { place_name id diff --git a/webapp/graphql/settings/BlockedUsers.js b/webapp/graphql/settings/BlockedUsers.js index be1765138..0c642b352 100644 --- a/webapp/graphql/settings/BlockedUsers.js +++ b/webapp/graphql/settings/BlockedUsers.js @@ -20,7 +20,7 @@ export const blockedUsers = () => { export const blockUser = () => { return gql` - mutation($id: ID!) { + mutation ($id: ID!) { blockUser(id: $id) { id name @@ -33,7 +33,7 @@ export const blockUser = () => { export const unblockUser = () => { return gql` - mutation($id: ID!) { + mutation ($id: ID!) { unblockUser(id: $id) { id name diff --git a/webapp/graphql/settings/MutedUsers.js b/webapp/graphql/settings/MutedUsers.js index f498534e3..10db085af 100644 --- a/webapp/graphql/settings/MutedUsers.js +++ b/webapp/graphql/settings/MutedUsers.js @@ -20,7 +20,7 @@ export const mutedUsers = () => { export const muteUser = () => { return gql` - mutation($id: ID!) { + mutation ($id: ID!) { muteUser(id: $id) { id name @@ -33,7 +33,7 @@ export const muteUser = () => { export const unmuteUser = () => { return gql` - mutation($id: ID!) { + mutation ($id: ID!) { unmuteUser(id: $id) { id name diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue index b8a377aec..be8a0834b 100644 --- a/webapp/layouts/default.vue +++ b/webapp/layouts/default.vue @@ -17,7 +17,16 @@ + + + + + { +describe('change-password', () => { let wrapper let mocks diff --git a/webapp/pages/post/_id.vue b/webapp/pages/post/_id.vue index 8d425eab7..14cc65a4e 100644 --- a/webapp/pages/post/_id.vue +++ b/webapp/pages/post/_id.vue @@ -19,7 +19,7 @@ import PersistentLinks from '~/mixins/persistentLinks.js' const options = { queryId: gql` - query($idOrSlug: ID) { + query ($idOrSlug: ID) { Post(id: $idOrSlug) { id slug @@ -27,7 +27,7 @@ const options = { } `, querySlug: gql` - query($idOrSlug: String) { + query ($idOrSlug: String) { Post(slug: $idOrSlug) { id slug diff --git a/webapp/pages/post/_id/_slug/index.vue b/webapp/pages/post/_id/_slug/index.vue index d02a448da..ff5c9a498 100644 --- a/webapp/pages/post/_id/_slug/index.vue +++ b/webapp/pages/post/_id/_slug/index.vue @@ -54,6 +54,11 @@ :key="category.id" :icon="category.icon" :name="$t(`contribution.category.name.${category.slug}`)" + v-tooltip="{ + content: $t(`contribution.category.description.${category.slug}`), + placement: 'bottom-start', + delay: { show: 1500 }, + }" /> diff --git a/webapp/pages/profile/_id.vue b/webapp/pages/profile/_id.vue index b9bbef83e..7a771425d 100644 --- a/webapp/pages/profile/_id.vue +++ b/webapp/pages/profile/_id.vue @@ -8,7 +8,7 @@ import PersistentLinks from '~/mixins/persistentLinks.js' const options = { queryId: gql` - query($idOrSlug: ID) { + query ($idOrSlug: ID) { User(id: $idOrSlug) { id slug @@ -16,7 +16,7 @@ const options = { } `, querySlug: gql` - query($idOrSlug: String) { + query ($idOrSlug: String) { User(slug: $idOrSlug) { id slug diff --git a/webapp/pages/settings/my-social-media.vue b/webapp/pages/settings/my-social-media.vue index a985371d6..d392032bb 100644 --- a/webapp/pages/settings/my-social-media.vue +++ b/webapp/pages/settings/my-social-media.vue @@ -112,7 +112,7 @@ export default { let mutation, variables, successMessage if (isCreation) { mutation = gql` - mutation($url: String!) { + mutation ($url: String!) { CreateSocialMedia(url: $url) { id url @@ -123,7 +123,7 @@ export default { successMessage = thisList.$t('settings.social-media.successAdd') } else { mutation = gql` - mutation($id: ID!, $url: String!) { + mutation ($id: ID!, $url: String!) { UpdateSocialMedia(id: $id, url: $url) { id url @@ -160,7 +160,7 @@ export default { try { await thisList.$apollo.mutate({ mutation: gql` - mutation($id: ID!) { + mutation ($id: ID!) { DeleteSocialMedia(id: $id) { id url diff --git a/webapp/yarn.lock b/webapp/yarn.lock index ef71a2a53..b5ba692fa 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5538,11 +5538,6 @@ alphanum-sort@^1.0.0: resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= -amdefine@>=0.0.4: - version "1.0.1" - resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" - integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU= - ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" @@ -6223,11 +6218,6 @@ array-filter@~0.0.0: resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" integrity sha1-fajPLiZijtcygDWB/SH2fKzS7uw= -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - integrity sha1-3wEKoSh+Fku9pvlyOwqWoexBh6E= - array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -6392,11 +6382,6 @@ async-each@^1.0.1: resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== -async-foreach@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542" - integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI= - async-limiter@~1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" @@ -7016,13 +7001,6 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - integrity sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo= - dependencies: - inherits "~2.0.0" - bluebird@^3.1.1, bluebird@^3.5.1: version "3.5.4" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.4.tgz#d6cc661595de30d5b3af5fcedd3c0b3ef6ec5714" @@ -7450,19 +7428,6 @@ camelcase-css@2.0.1: resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - integrity sha1-MIvur/3ygRkFHvodkyITyRuPkuc= - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= - camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" @@ -7567,7 +7532,7 @@ chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3. escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3: +chalk@^1.0.0, chalk@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= @@ -8289,18 +8254,18 @@ cookie-signature@1.0.6: resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= -cookie-universal-nuxt@~2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.1.5.tgz#669f2ff95b1bc1962c86edd69c954f60729e71e5" - integrity sha512-P0WCTKIyemWNtHi9lxrS5cxZmieOIEjt28B7Alu6cdSB9RqtUtpkqYyV9PRK6oJrT5eIPDYjHsJQlh6SUrFJOg== +cookie-universal-nuxt@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/cookie-universal-nuxt/-/cookie-universal-nuxt-2.2.2.tgz#107815f03f5b769de7018670d6370368205387bb" + integrity sha512-Pr6P5UCzl1EAvPh7z7jFkknBw0KTdykm6gFmPHrH4LV9s3flVmAH0ZP/ZqUXcp6b0SKZfizkn+XR1cO+QinGhQ== dependencies: "@types/cookie" "^0.3.3" - cookie-universal "^2.1.5" + cookie-universal "^2.2.2" -cookie-universal@^2.1.5: - version "2.1.5" - resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.1.5.tgz#9a6cefbfb61c750a1b8ee2610bf71566bd719544" - integrity sha512-nqOOmEkovCQxNYGIyzhcwsmh4c7xnxe7RWdiYFOoml9MP4L32IlU3LdPC7r7nQEnnM+9Uxlk/UhtvBl5is6M/w== +cookie-universal@^2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/cookie-universal/-/cookie-universal-2.2.2.tgz#415a4d67b6f7f0819c4914cd69b8c2f496111d30" + integrity sha512-nUXF6HH2YKbn8vGcdSzWJhjRkDHbnbekuVu2nsRu00zYsX7o/H3xGJRlPVoM4wX/8cpJYpyi9nDt+boER0Wjug== dependencies: "@types/cookie" "^0.3.3" cookie "^0.4.0" @@ -8544,10 +8509,10 @@ cropperjs@^1.5.5: resolved "https://registry.yarnpkg.com/cropperjs/-/cropperjs-1.5.6.tgz#82faf432bec709d828f2f7a96d1179198edaf0e2" integrity sha512-eAgWf4j7sNJIG329qUHIFi17PSV0VtuWyAu9glZSgu/KlQSrfTQOC2zAz+jHGa5fAB+bJldEnQwvJEaJ8zRf5A== -cross-env@~7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.2.tgz#bd5ed31339a93a3418ac4f3ca9ca3403082ae5f9" - integrity sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw== +cross-env@~7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf" + integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw== dependencies: cross-spawn "^7.0.1" @@ -8559,7 +8524,7 @@ cross-fetch@^3.0.4: node-fetch "2.6.0" whatwg-fetch "3.0.0" -cross-spawn@7.0.3: +cross-spawn@7.0.3, cross-spawn@^7.0.0, cross-spawn@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== @@ -8568,14 +8533,6 @@ cross-spawn@7.0.3: shebang-command "^2.0.0" which "^2.0.1" -cross-spawn@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982" - integrity sha1-ElYDfsufDF9549bvE14wdwGEuYI= - dependencies: - lru-cache "^4.0.1" - which "^1.2.9" - cross-spawn@^5.0.1: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" @@ -8596,15 +8553,6 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.0, cross-spawn@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.1.tgz#0ab56286e0f7c24e153d04cc2aa027e43a9a5d14" - integrity sha512-u7v4o84SwFpD32Z8IIcPZ6z1/ie24O6RU3RbtL5Y316l3KuHVPx9ItBgWQ6VlfAFnRnTtMUrsQ9MUUTuEZjogg== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -8915,13 +8863,6 @@ cuint@^0.2.2: resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" integrity sha1-QICG1AlVDCYxFVYZ6fp7ytw7mRs= -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - integrity sha1-mI3zP+qxke95mmE2nddsF635V+o= - dependencies: - array-find-index "^1.0.1" - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -9000,7 +8941,7 @@ debug@^3.0.0, debug@^3.0.1, debug@^3.1.0, debug@^3.2.6: dependencies: ms "^2.1.1" -decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -10805,16 +10746,6 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -fstream@^1.0.0, fstream@^1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.12.tgz#4e8ba8ee2d48be4f7d0de505455548eae5932045" - integrity sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg== - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - function-bind@^1.0.2, function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -10859,7 +10790,7 @@ gauge@~2.7.3: strip-ansi "^3.0.1" wide-align "^1.1.0" -gaze@1.1.3, gaze@^1.0.0: +gaze@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/gaze/-/gaze-1.1.3.tgz#c441733e13b927ac8c0ff0b4c3b033f28812924a" integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== @@ -10911,11 +10842,6 @@ get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - integrity sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - get-stdin@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b" @@ -11046,7 +10972,7 @@ glob@7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== @@ -11995,18 +11921,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= -in-publish@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51" - integrity sha1-4g/146KvwmkDILbcVSaCqcf631E= - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - integrity sha1-ji1INIdCEhtKghi3oTfppSBJ3IA= - dependencies: - repeating "^2.0.0" - indent-string@^3.0.0, indent-string@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289" @@ -12035,7 +11949,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -12359,13 +12273,6 @@ is-extglob@^2.1.0, is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - integrity sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko= - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" @@ -13215,11 +13122,6 @@ jimp-compact@^0.8.0: resolved "https://registry.yarnpkg.com/jimp-compact/-/jimp-compact-0.8.4.tgz#0878a0c30f22d2d4f8b33e96722eb09d20770627" integrity sha512-9mvZ7/TJ28bWtdx0RxmfiOTzSom4zuRniFTLtJHfNL6HxQdnRtjmX8XIRjmofgVXj2TW/GgSuZKB3dSZ5hNhKg== -js-base64@^2.1.8: - version "2.5.1" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.5.1.tgz#1efa39ef2c5f7980bb1784ade4a8af2de3291121" - integrity sha512-M7kLczedRMYX4L8Mdh4MzyAMM9O5osx+4FcOQuTvr3A9F2D9S5JXheN0ewNbrvK2UatkTRhL5ejGmGSjNMiZuw== - js-beautify@^1.6.12: version "1.10.2" resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.10.2.tgz#88c9099cd6559402b124cfab18754936f8a7b178" @@ -13754,7 +13656,7 @@ loader-utils@^0.2.16: json5 "^0.5.0" object-assign "^4.0.1" -loader-utils@^1.0.1, loader-utils@^1.0.2, loader-utils@^1.0.4, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: +loader-utils@^1.0.2, loader-utils@^1.0.4, loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== @@ -13913,7 +13815,7 @@ lodash.xorby@^4.7.0: resolved "https://registry.yarnpkg.com/lodash.xorby/-/lodash.xorby-4.7.0.tgz#9c19a6f9f063a6eb53dd03c1b6871799801463d7" integrity sha1-nBmm+fBjputT3QPBtocXmYAUY9c= -lodash@4.x, lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: +lodash@4.x, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.17.5, lodash@~4.17.10: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -13958,14 +13860,6 @@ loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - integrity sha1-W0b4AUft7leIcPCG0Eghz5mOVR8= - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - lower-case-first@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/lower-case-first/-/lower-case-first-1.0.2.tgz#e5da7c26f29a7073be02d52bac9980e5922adfa1" @@ -14102,11 +13996,6 @@ map-cache@^0.2.2: resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - integrity sha1-2TPOuSBdgr3PSIb2dCvcK03qFG0= - map-or-similar@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/map-or-similar/-/map-or-similar-1.5.0.tgz#6de2653174adfb5d9edc33c69d3e92a1b76faf08" @@ -14229,22 +14118,6 @@ mensch@0.3.4: resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g== -meow@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - integrity sha1-cstmi0JSKCkKu/qFaJJYcwioAfs= - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -14420,7 +14293,7 @@ minimist@^0.1.0: resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.1.0.tgz#99df657a52574c21c9057497df742790b2b4c0de" integrity sha1-md9lelJXTCHJBXSX33QnkLK0wN4= -minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: +minimist@^1.1.1, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= @@ -14512,7 +14385,7 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: +mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1: version "0.5.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.4.tgz#fd01504a6797ec5c9be81ff43d204961ed64a512" integrity sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw== @@ -14566,7 +14439,7 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.12.1, nan@^2.13.2: +nan@^2.12.1: version "2.13.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7" integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw== @@ -14685,24 +14558,6 @@ node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.6.1: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== -node-gyp@^3.8.0: - version "3.8.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c" - integrity sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA== - dependencies: - fstream "^1.0.0" - glob "^7.0.3" - graceful-fs "^4.1.2" - mkdirp "^0.5.0" - nopt "2 || 3" - npmlog "0 || 1 || 2 || 3 || 4" - osenv "0" - request "^2.87.0" - rimraf "2" - semver "~5.3.0" - tar "^2.0.0" - which "1" - node-int64@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -14816,29 +14671,6 @@ node-res@^5.0.1: on-finished "^2.3.0" vary "^1.1.2" -node-sass@^4.12.0: - version "4.13.1" - resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.13.1.tgz#9db5689696bb2eec2c32b98bfea4c7a2e992d0a3" - integrity sha512-TTWFx+ZhyDx1Biiez2nB0L3YrCZ/8oHagaDalbuBSlqXgUPsdkUSzJsVxeDO9LtPB49+Fh3WQl3slABo6AotNw== - dependencies: - async-foreach "^0.1.3" - chalk "^1.1.1" - cross-spawn "^3.0.0" - gaze "^1.0.0" - get-stdin "^4.0.1" - glob "^7.0.3" - in-publish "^2.0.0" - lodash "^4.17.15" - meow "^3.7.0" - mkdirp "^0.5.1" - nan "^2.13.2" - node-gyp "^3.8.0" - npmlog "^4.0.0" - request "^2.88.0" - sass-graph "^2.2.4" - stdout-stream "^1.4.0" - "true-case-path" "^1.0.2" - nodemon@^1.19.4: version "1.19.4" resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-1.19.4.tgz#56db5c607408e0fdf8920d2b444819af1aae0971" @@ -14855,13 +14687,6 @@ nodemon@^1.19.4: undefsafe "^2.0.2" update-notifier "^2.5.0" -"nopt@2 || 3": - version "3.0.6" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" - integrity sha1-xkZdvwirzU2zWTF/eaxopkayj/k= - dependencies: - abbrev "1" - nopt@^4.0.1, nopt@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -14877,7 +14702,7 @@ nopt@~1.0.10: dependencies: abbrev "1" -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: +normalize-package-data@^2.3.2, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== @@ -14953,7 +14778,7 @@ npm-run-path@^4.0.0: dependencies: path-key "^3.0.0" -"npmlog@0 || 1 || 2 || 3 || 4", npmlog@^4.0.0, npmlog@^4.0.2, npmlog@^4.1.2: +npmlog@^4.0.2, npmlog@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" integrity sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg== @@ -15273,7 +15098,7 @@ os-tmpdir@^1.0.0, os-tmpdir@~1.0.2: resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= -osenv@0, osenv@^0.1.4: +osenv@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== @@ -16597,6 +16422,11 @@ prettier@~2.2.1: resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5" integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q== +prettier@~2.7.1: + version "2.7.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" + integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== + pretty-bytes@^5.3.0: version "5.3.0" resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2" @@ -17627,14 +17457,6 @@ recursive-readdir@2.2.2: dependencies: minimatch "3.0.4" -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - integrity sha1-z5Fqsf1fHxbfsggi3W7H9zDCr94= - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - redeyed@~2.1.0: version "2.1.1" resolved "https://registry.yarnpkg.com/redeyed/-/redeyed-2.1.1.tgz#8984b5815d99cb220469c99eeeffe38913e6cc0b" @@ -17887,13 +17709,6 @@ repeat-string@^1.5.4, repeat-string@^1.6.1: resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - integrity sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo= - dependencies: - is-finite "^1.0.0" - request-promise-core@1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9" @@ -17910,7 +17725,7 @@ request-promise-native@^1.0.5, request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -"request@>=2.76.0 <3.0.0", request@^2.87.0, request@^2.88.0, request@^2.88.2: +"request@>=2.76.0 <3.0.0", request@^2.87.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -18062,7 +17877,7 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= -rimraf@2, rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: +rimraf@2.6.3, rimraf@^2.2.8, rimraf@^2.5.4, rimraf@^2.6.1, rimraf@^2.6.2, rimraf@^2.6.3, rimraf@~2.6.2: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== @@ -18183,27 +17998,6 @@ sane@^4.0.3: minimist "^1.1.1" walker "~1.0.5" -sass-graph@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" - integrity sha1-E/vWPNHK8JCLn9k0dq1DpR0eC0k= - dependencies: - glob "^7.0.0" - lodash "^4.0.0" - scss-tokenizer "^0.2.3" - yargs "^7.0.0" - -sass-loader@^7.1.0: - version "7.3.1" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-7.3.1.tgz#a5bf68a04bcea1c13ff842d747150f7ab7d0d23f" - integrity sha512-tuU7+zm0pTCynKYHpdqaPpe+MMTQ76I9TPZ7i4/5dZsigE350shQWe5EZNl5dBidM49TPET75tNqRbcsUZWeNA== - dependencies: - clone-deep "^4.0.1" - loader-utils "^1.0.1" - neo-async "^2.5.0" - pify "^4.0.1" - semver "^6.3.0" - sass-loader@~10.1.1: version "10.1.1" resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.1.1.tgz#4ddd5a3d7638e7949065dd6e9c7c04037f7e663d" @@ -18296,14 +18090,6 @@ schema-utils@^3.0.0: ajv "^6.12.5" ajv-keywords "^3.5.2" -scss-tokenizer@^0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/scss-tokenizer/-/scss-tokenizer-0.2.3.tgz#8eb06db9a9723333824d3f5530641149847ce5d1" - integrity sha1-jrBtualyMzOCTT9VMGQRSYR85dE= - dependencies: - js-base64 "^2.1.8" - source-map "^0.4.2" - select@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" @@ -18345,11 +18131,6 @@ semver@^7.3.4: dependencies: lru-cache "^6.0.0" -semver@~5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" - integrity sha1-myzl094C0XxgEq0yaqa00M9U+U8= - send@0.17.1: version "0.17.1" resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" @@ -18739,13 +18520,6 @@ source-map@0.5.6: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" integrity sha1-dc449SvwczxafwwRjYEzSiu19BI= -source-map@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - integrity sha1-66T12pwNyZneaAMti092FzZSA2s= - dependencies: - amdefine ">=0.0.4" - source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -18905,13 +18679,6 @@ std-env@^2.2.1: dependencies: ci-info "^1.6.0" -stdout-stream@^1.4.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/stdout-stream/-/stdout-stream-1.4.1.tgz#5ac174cdd5cd726104aa0c0b2bd83815d8d535de" - integrity sha512-j4emi03KXqJWcIeF8eIXkjMFN1Cmb8gUlDYGeBALLPo5qdyTfA9bOtl8m33lRoC+vFMkP3gl0WsDr6+gzxbbTA== - dependencies: - readable-stream "^2.0.1" - stealthy-require@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" @@ -19212,13 +18979,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - integrity sha1-DHlipq3vp7vUrDZkYKY4VSrhoKI= - dependencies: - get-stdin "^4.0.1" - strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" @@ -19420,15 +19180,6 @@ tapable@^1.0.0, tapable@^1.0.0-beta.5, tapable@^1.1.3: resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== -tar@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.2.tgz#0ca8848562c7299b8b446ff6a4d60cdbb23edc40" - integrity sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA== - dependencies: - block-stream "*" - fstream "^1.0.12" - inherits "2" - tar@^4: version "4.4.8" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.8.tgz#b19eec3fde2a96e64666df9fdb40c5ca1bc3747d" @@ -19888,11 +19639,6 @@ treeify@^1.1.0: resolved "https://registry.yarnpkg.com/treeify/-/treeify-1.1.0.tgz#4e31c6a463accd0943879f30667c4fdaff411bb8" integrity sha512-1m4RA7xVAJrSGrrXGs0L3YTwyvBs2S8PbRHaLZAkFw7JR8oIFwYtysxlBZhYIa7xSyiYJKZ3iGrrk55cGA3i9A== -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - integrity sha1-WIeWa7WCpFA6QetST301ARgVphM= - trim-trailing-lines@^1.0.0: version "1.1.4" resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.4.tgz#bd4abbec7cc880462f10b2c8b5ce1d8d1ec7c2c0" @@ -19908,13 +19654,6 @@ trough@^1.0.0: resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.5.tgz#b8b639cefad7d0bb2abd37d433ff8293efa5f406" integrity sha512-rvuRbTarPXmMb79SmzEp8aqXNKcK+y0XaB298IXueQ8I2PsrATcPBCSPyK/dDNa2iWOhKlfNnOjdAOTBU/nkFA== -"true-case-path@^1.0.2": - version "1.0.3" - resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-1.0.3.tgz#f813b5a8c86b40da59606722b144e3225799f47d" - integrity sha512-m6s2OdQe5wgpFMC+pAJ+q9djG82O2jcHPOI6RNg1yy9rCYR+WD6Nbpl32fDpfC56nirdRy+opFa/Vk7HYhqaew== - dependencies: - glob "^7.1.2" - trunc-html@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/trunc-html/-/trunc-html-1.1.2.tgz#1e97d51f67d470b67662b1a670e6d0ea7a8edafe" @@ -20878,14 +20617,12 @@ vue-svg-loader@~0.16.0: loader-utils "^1.2.3" svg-to-vue "^0.7.0" -vue-sweetalert-icons@~4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/vue-sweetalert-icons/-/vue-sweetalert-icons-4.3.0.tgz#0808632cb6bfa67bf07afab1ae683352c038af7d" - integrity sha512-8SgzgyqppIj/gQt6Y5JLXPnqt1pEq50w6TeQ1B3aVd1mWm3gHTeWHWujiycjouo8too1fgtWkn3mi16vumKSJw== +vue-sweetalert-icons@~4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/vue-sweetalert-icons/-/vue-sweetalert-icons-4.3.1.tgz#dad763abb5084b015ba3e209dd28dcf1371b030c" + integrity sha512-FqKcMB8Ebgb32UyzvhIBzj23U0NRP91cTXovDYfYwNHpJ1TAFBEHoemgyu01h2Wp+UJhytvQ+13GL+GAs8QkWw== dependencies: color "^3.1.2" - node-sass "^4.12.0" - sass-loader "^7.1.0" validate-color "^2.1.0" vue-template-compiler@^2.6.11: @@ -21228,7 +20965,7 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= -which@1, which@^1.2.9, which@^1.3.1: +which@^1.2.9, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -21515,13 +21252,6 @@ yargs-parser@^4.2.0: dependencies: camelcase "^3.0.0" -yargs-parser@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" - integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= - dependencies: - camelcase "^3.0.0" - yargs@6.6.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" @@ -21558,25 +21288,6 @@ yargs@^15.4.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^7.0.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" - integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^5.0.0" - yargs@~1.2.6: version "1.2.6" resolved "https://registry.yarnpkg.com/yargs/-/yargs-1.2.6.tgz#9c7b4a82fd5d595b2bf17ab6dcc43135432fe34b" diff --git a/yarn.lock b/yarn.lock index 4e2303801..6103f10a2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4481,10 +4481,10 @@ neo4j-driver@^4.2.2, neo4j-driver@^4.3.4: neo4j-driver-core "^4.3.4" rxjs "^6.6.3" -neode@^0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.7.tgz#033007b57a2ee167e9ee5537493086db08d005eb" - integrity sha512-YXlc187JRpeKCBcUIkY6nimXXG+Tvlopfe71/FPno2THrwmYt5mm0RPHZ+mXF2O1Xg6zvjKvOpCpDz2vHBfroQ== +neode@^0.4.8: + version "0.4.8" + resolved "https://registry.yarnpkg.com/neode/-/neode-0.4.8.tgz#0889b4fc7f1bf0b470b01fa5b8870373b5d47ad6" + integrity sha512-pb91NfCOg4Fj5o+98H+S2XYC+ByQfbdhwcc1UVuzuUQ0Ezzj+jWz8NmKWU8ZfCH6l4plk71yDAPd2eTwpt+Xvg== dependencies: "@hapi/joi" "^15.1.1" dotenv "^4.0.0" @@ -5426,10 +5426,10 @@ slice-ansi@0.0.4: resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU= -slug@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/slug/-/slug-5.1.0.tgz#8a7e30ca1c3a6dc40cf74e269750913a865edb0b" - integrity sha512-IS39jKR6m+puU8zWgH6ruwx1sfzFNJ6Ai5PKIlUqd0X8C3ca7PB49Cvm0uayqgEt1jgaojO2wWEsQJngnh7fDA== +slug@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/slug/-/slug-6.0.0.tgz#39637b32e5a873bc692812a630842880499ed6c9" + integrity sha512-0MpNLyCSUSf0G1nAZmp9gY1cvesPP35a1Live25vZ23gWQ5SAopF0N+0hk9KI4ytNuTebJrHGNrgTnxboofcSg== snapdragon-node@^2.0.1: version "2.1.1"