mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge branch 'master' into 1188-Delete_teaser_image
This commit is contained in:
commit
798e4d4f5a
20
.github/ISSUE_TEMPLATE/refactor_tickets.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/refactor_tickets.md
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
name: 🔧 Refactor ticket
|
||||
about: Help us improve our code by refactoring it.
|
||||
labels: refactor
|
||||
title: 🔧 [Refactor]
|
||||
---
|
||||
|
||||
## :zap: Refactor ticket
|
||||
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
||||
|
||||
### Motive
|
||||
<!-- What is the purpose of this refactoring? If it's removing depcrecated code, please link to the deprecation notice. -->
|
||||
### Related issues
|
||||
<!-- Are there any related issues to link to? Please paste them below for reference. -->
|
||||
|
||||
### Implementation
|
||||
<!-- Please, document any ideas of how the code should be refactored. -->
|
||||
|
||||
### Additional context
|
||||
<!-- Add other context or background about the feature request here.-->
|
||||
@ -6,7 +6,6 @@ addons:
|
||||
- libgconf-2-4
|
||||
snaps:
|
||||
- docker
|
||||
- chromium
|
||||
|
||||
install:
|
||||
- yarn global add wait-on
|
||||
@ -19,7 +18,7 @@ before_script:
|
||||
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml build # just tagging, just be quite fast
|
||||
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml up -d
|
||||
- wait-on http://localhost:7474
|
||||
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml exec neo4j db_setup
|
||||
- docker-compose -f docker-compose.yml -f docker-compose.build-and-test.yml exec backend yarn run db:migrate init
|
||||
|
||||
script:
|
||||
- export CYPRESS_RETRIES=1
|
||||
@ -70,7 +69,3 @@ deploy:
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
branch: master
|
||||
- provider: script
|
||||
script: bash scripts/github_release.sh
|
||||
on:
|
||||
branch: master
|
||||
|
||||
7
.versionrc.json
Normal file
7
.versionrc.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"bumpFiles": [
|
||||
"package.json",
|
||||
"backend/package.json",
|
||||
"webapp/package.json"
|
||||
]
|
||||
}
|
||||
203
CHANGELOG.md
203
CHANGELOG.md
@ -4,6 +4,187 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.3.0](https://github.com/Human-Connection/Human-Connection/compare/v0.2.1...v0.3.0)
|
||||
|
||||
> 31 January 2020
|
||||
|
||||
- feat: 🍰 Direct Reply On Comment [`#2608`](https://github.com/Human-Connection/Human-Connection/pull/2608)
|
||||
- build(deps-dev): bump @babel/core from 7.8.3 to 7.8.4 in /backend [`#2938`](https://github.com/Human-Connection/Human-Connection/pull/2938)
|
||||
- fix: deploy script with new naming convention [`#2930`](https://github.com/Human-Connection/Human-Connection/pull/2930)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.8.3 to 7.8.4 in /backend [`#2940`](https://github.com/Human-Connection/Human-Connection/pull/2940)
|
||||
- build(deps-dev): bump @babel/cli from 7.8.3 to 7.8.4 in /backend [`#2934`](https://github.com/Human-Connection/Human-Connection/pull/2934)
|
||||
- build(deps-dev): bump @babel/core from 7.8.3 to 7.8.4 [`#2935`](https://github.com/Human-Connection/Human-Connection/pull/2935)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.8.3 to 7.8.4 in /webapp [`#2932`](https://github.com/Human-Connection/Human-Connection/pull/2932)
|
||||
- build(deps): bump @nuxtjs/axios from 5.9.3 to 5.9.4 in /webapp [`#2937`](https://github.com/Human-Connection/Human-Connection/pull/2937)
|
||||
- build(deps-dev): bump @babel/node from 7.8.3 to 7.8.4 in /backend [`#2936`](https://github.com/Human-Connection/Human-Connection/pull/2936)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.8.3 to 7.8.4 [`#2933`](https://github.com/Human-Connection/Human-Connection/pull/2933)
|
||||
- feat: Blocked users cannot comment on posts [`#2714`](https://github.com/Human-Connection/Human-Connection/pull/2714)
|
||||
- build(deps): bump @sentry/node from 5.11.1 to 5.11.2 in /backend [`#2927`](https://github.com/Human-Connection/Human-Connection/pull/2927)
|
||||
- build(deps): bump cookie-universal-nuxt from 2.1.0 to 2.1.1 in /webapp [`#2925`](https://github.com/Human-Connection/Human-Connection/pull/2925)
|
||||
- build(deps): bump @nuxtjs/sentry from 3.0.1 to 3.1.0 in /webapp [`#2928`](https://github.com/Human-Connection/Human-Connection/pull/2928)
|
||||
- docs(CONTRIBUTING): Add open-source bounty program [`#2899`](https://github.com/Human-Connection/Human-Connection/pull/2899)
|
||||
- build(deps-dev): bump eslint-config-prettier from 6.9.0 to 6.10.0 in /webapp [`#2926`](https://github.com/Human-Connection/Human-Connection/pull/2926)
|
||||
- build(deps): bump graphql-shield from 7.0.8 to 7.0.9 in /backend [`#2924`](https://github.com/Human-Connection/Human-Connection/pull/2924)
|
||||
- build(deps-dev): bump eslint-config-prettier from 6.9.0 to 6.10.0 in /backend [`#2923`](https://github.com/Human-Connection/Human-Connection/pull/2923)
|
||||
- feat(editor): Underline markup for posts+comments [`#2898`](https://github.com/Human-Connection/Human-Connection/pull/2898)
|
||||
- fix(backend): Add migration for muted relationship [`#2919`](https://github.com/Human-Connection/Human-Connection/pull/2919)
|
||||
- build(deps-dev): bump @storybook/addon-a11y from 5.3.8 to 5.3.9 in /webapp [`#2914`](https://github.com/Human-Connection/Human-Connection/pull/2914)
|
||||
- fix(webapp): 🐛 Adjust Avatar Sizes and 'z-index' [`#2871`](https://github.com/Human-Connection/Human-Connection/pull/2871)
|
||||
- build(deps-dev): bump @storybook/vue from 5.3.8 to 5.3.9 in /webapp [`#2913`](https://github.com/Human-Connection/Human-Connection/pull/2913)
|
||||
- build(deps): bump validator from 12.1.0 to 12.2.0 in /backend [`#2907`](https://github.com/Human-Connection/Human-Connection/pull/2907)
|
||||
- build(deps): bump graphql from 14.5.8 to 14.6.0 in /webapp [`#2905`](https://github.com/Human-Connection/Human-Connection/pull/2905)
|
||||
- build(deps): bump cross-env from 6.0.3 to 7.0.0 in /backend [`#2911`](https://github.com/Human-Connection/Human-Connection/pull/2911)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.3.8 to 5.3.9 in /webapp [`#2910`](https://github.com/Human-Connection/Human-Connection/pull/2910)
|
||||
- build(deps): bump slug from 2.1.0 to 2.1.1 in /backend [`#2906`](https://github.com/Human-Connection/Human-Connection/pull/2906)
|
||||
- build(deps): bump cross-env from 6.0.3 to 7.0.0 in /webapp [`#2916`](https://github.com/Human-Connection/Human-Connection/pull/2916)
|
||||
- build(deps): bump graphql from 14.5.8 to 14.6.0 in /backend [`#2909`](https://github.com/Human-Connection/Human-Connection/pull/2909)
|
||||
- build(deps-dev): bump storybook-design-token from 0.5.0 to 0.5.1 in /webapp [`#2915`](https://github.com/Human-Connection/Human-Connection/pull/2915)
|
||||
- build(deps): bump validator from 12.1.0 to 12.2.0 in /webapp [`#2912`](https://github.com/Human-Connection/Human-Connection/pull/2912)
|
||||
- build(deps-dev): bump @storybook/addon-actions from 5.3.8 to 5.3.9 in /webapp [`#2908`](https://github.com/Human-Connection/Human-Connection/pull/2908)
|
||||
- build(deps-dev): bump slug from 2.1.0 to 2.1.1 [`#2904`](https://github.com/Human-Connection/Human-Connection/pull/2904)
|
||||
- build(deps): bump graphql-shield from 7.0.7 to 7.0.8 in /backend [`#2903`](https://github.com/Human-Connection/Human-Connection/pull/2903)
|
||||
- build(deps-dev): bump cypress from 3.8.2 to 3.8.3 [`#2902`](https://github.com/Human-Connection/Human-Connection/pull/2902)
|
||||
- build(deps): bump metascraper-logo from 5.10.5 to 5.10.6 in /backend [`#2893`](https://github.com/Human-Connection/Human-Connection/pull/2893)
|
||||
- build(deps): bump metascraper-video from 5.10.5 to 5.10.6 in /backend [`#2892`](https://github.com/Human-Connection/Human-Connection/pull/2892)
|
||||
- build(deps): bump metascraper-image from 5.10.5 to 5.10.6 in /backend [`#2891`](https://github.com/Human-Connection/Human-Connection/pull/2891)
|
||||
- build(deps): bump metascraper-publisher from 5.10.5 to 5.10.6 in /backend [`#2890`](https://github.com/Human-Connection/Human-Connection/pull/2890)
|
||||
- build(deps-dev): bump codecov from 3.6.1 to 3.6.2 [`#2889`](https://github.com/Human-Connection/Human-Connection/pull/2889)
|
||||
- feat(db): Setup neo4j data migrations [`#2828`](https://github.com/Human-Connection/Human-Connection/pull/2828)
|
||||
- build(deps): bump metascraper from 5.10.5 to 5.10.6 in /backend [`#2877`](https://github.com/Human-Connection/Human-Connection/pull/2877)
|
||||
- build(deps): bump metascraper-url from 5.10.5 to 5.10.6 in /backend [`#2879`](https://github.com/Human-Connection/Human-Connection/pull/2879)
|
||||
- build(deps): bump metascraper-lang from 5.10.5 to 5.10.6 in /backend [`#2882`](https://github.com/Human-Connection/Human-Connection/pull/2882)
|
||||
- build(deps): bump metascraper-audio from 5.10.5 to 5.10.6 in /backend [`#2883`](https://github.com/Human-Connection/Human-Connection/pull/2883)
|
||||
- build(deps): bump metascraper-date from 5.10.5 to 5.10.6 in /backend [`#2878`](https://github.com/Human-Connection/Human-Connection/pull/2878)
|
||||
- build(deps): bump metascraper-title from 5.10.5 to 5.10.6 in /backend [`#2880`](https://github.com/Human-Connection/Human-Connection/pull/2880)
|
||||
- build(deps): bump metascraper-youtube from 5.10.5 to 5.10.6 in /backend [`#2881`](https://github.com/Human-Connection/Human-Connection/pull/2881)
|
||||
- build(deps): bump metascraper-author from 5.10.5 to 5.10.6 in /backend [`#2876`](https://github.com/Human-Connection/Human-Connection/pull/2876)
|
||||
- build(deps): bump metascraper-description from 5.10.5 to 5.10.6 in /backend [`#2875`](https://github.com/Human-Connection/Human-Connection/pull/2875)
|
||||
- build(deps): bump metascraper-soundcloud from 5.10.5 to 5.10.6 in /backend [`#2874`](https://github.com/Human-Connection/Human-Connection/pull/2874)
|
||||
- build(deps-dev): bump jest from 24.9.0 to 25.1.0 in /webapp [`#2868`](https://github.com/Human-Connection/Human-Connection/pull/2868)
|
||||
- build(deps-dev): bump @storybook/vue from 5.3.7 to 5.3.8 in /webapp [`#2867`](https://github.com/Human-Connection/Human-Connection/pull/2867)
|
||||
- build(deps-dev): bump babel-jest from 24.9.0 to 25.1.0 in /webapp [`#2869`](https://github.com/Human-Connection/Human-Connection/pull/2869)
|
||||
- build(deps-dev): bump @storybook/addon-actions from 5.3.7 to 5.3.8 in /webapp [`#2865`](https://github.com/Human-Connection/Human-Connection/pull/2865)
|
||||
- build(deps-dev): bump babel-jest from 24.9.0 to 25.1.0 in /backend [`#2863`](https://github.com/Human-Connection/Human-Connection/pull/2863)
|
||||
- build(deps-dev): bump expect from 24.9.0 to 25.1.0 [`#2861`](https://github.com/Human-Connection/Human-Connection/pull/2861)
|
||||
- build(deps-dev): bump @storybook/addon-a11y from 5.3.7 to 5.3.8 in /webapp [`#2866`](https://github.com/Human-Connection/Human-Connection/pull/2866)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.3.7 to 5.3.8 in /webapp [`#2864`](https://github.com/Human-Connection/Human-Connection/pull/2864)
|
||||
- build(deps-dev): bump jest from 24.9.0 to 25.1.0 in /backend [`#2862`](https://github.com/Human-Connection/Human-Connection/pull/2862)
|
||||
- docs(deployment): Explain how to setup metrics [`#2825`](https://github.com/Human-Connection/Human-Connection/pull/2825)
|
||||
- refactor(styleguide): Migrate Avatar component to monorepo [`#2700`](https://github.com/Human-Connection/Human-Connection/pull/2700)
|
||||
- feat: Convert block/unblock to mute/unmute [`#2686`](https://github.com/Human-Connection/Human-Connection/pull/2686)
|
||||
- removed obsolete German keys [`#2845`](https://github.com/Human-Connection/Human-Connection/pull/2845)
|
||||
- build(deps-dev): bump @vue/server-test-utils in /webapp [`#2852`](https://github.com/Human-Connection/Human-Connection/pull/2852)
|
||||
- build(deps-dev): bump @storybook/vue from 5.3.6 to 5.3.7 in /webapp [`#2857`](https://github.com/Human-Connection/Human-Connection/pull/2857)
|
||||
- build(deps-dev): bump @storybook/addon-a11y in /webapp [`#2858`](https://github.com/Human-Connection/Human-Connection/pull/2858)
|
||||
- build(deps): bump metascraper-image from 5.10.3 to 5.10.5 in /backend [`#2849`](https://github.com/Human-Connection/Human-Connection/pull/2849)
|
||||
- build(deps): bump wait-on from 3.3.0 to 4.0.0 in /backend [`#2848`](https://github.com/Human-Connection/Human-Connection/pull/2848)
|
||||
- build(deps): bump v-tooltip from 2.0.2 to 2.0.3 in /webapp [`#2856`](https://github.com/Human-Connection/Human-Connection/pull/2856)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.3.6 to 5.3.7 in /webapp [`#2855`](https://github.com/Human-Connection/Human-Connection/pull/2855)
|
||||
- build(deps): bump sanitize-html from 1.20.1 to 1.21.1 in /backend [`#2854`](https://github.com/Human-Connection/Human-Connection/pull/2854)
|
||||
- build(deps): bump metascraper-video from 5.10.3 to 5.10.5 in /backend [`#2853`](https://github.com/Human-Connection/Human-Connection/pull/2853)
|
||||
- build(deps): bump metascraper-date from 5.10.3 to 5.10.5 in /backend [`#2851`](https://github.com/Human-Connection/Human-Connection/pull/2851)
|
||||
- build(deps-dev): bump @vue/test-utils from 1.0.0-beta.30 to 1.0.0-beta.31 in /webapp [`#2850`](https://github.com/Human-Connection/Human-Connection/pull/2850)
|
||||
- build(deps): bump metascraper-logo from 5.10.3 to 5.10.5 in /backend [`#2847`](https://github.com/Human-Connection/Human-Connection/pull/2847)
|
||||
- build(deps): bump @hapi/joi from 17.0.2 to 17.1.0 in /backend [`#2846`](https://github.com/Human-Connection/Human-Connection/pull/2846)
|
||||
- Release 0.2.2 [`#2844`](https://github.com/Human-Connection/Human-Connection/pull/2844)
|
||||
- build(deps-dev): bump @storybook/addon-actions in /webapp [`#2842`](https://github.com/Human-Connection/Human-Connection/pull/2842)
|
||||
- build(deps): bump metascraper-title from 5.10.3 to 5.10.5 in /backend [`#2835`](https://github.com/Human-Connection/Human-Connection/pull/2835)
|
||||
- build(deps): bump metascraper-publisher in /backend [`#2836`](https://github.com/Human-Connection/Human-Connection/pull/2836)
|
||||
- build(deps): bump metascraper-audio from 5.10.3 to 5.10.5 in /backend [`#2840`](https://github.com/Human-Connection/Human-Connection/pull/2840)
|
||||
- build(deps): bump metascraper-author from 5.10.3 to 5.10.5 in /backend [`#2838`](https://github.com/Human-Connection/Human-Connection/pull/2838)
|
||||
- build(deps): bump metascraper-url from 5.10.3 to 5.10.5 in /backend [`#2832`](https://github.com/Human-Connection/Human-Connection/pull/2832)
|
||||
- build(deps): bump metascraper-lang from 5.10.3 to 5.10.5 in /backend [`#2831`](https://github.com/Human-Connection/Human-Connection/pull/2831)
|
||||
- refactor(modules): Various import fixes [`#2802`](https://github.com/Human-Connection/Human-Connection/pull/2802)
|
||||
- build(deps): bump metascraper-description from 5.10.3 to 5.10.5 in /backend [`#2839`](https://github.com/Human-Connection/Human-Connection/pull/2839)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.3.5 to 5.3.6 in /webapp [`#2834`](https://github.com/Human-Connection/Human-Connection/pull/2834)
|
||||
- build(deps): bump metascraper-youtube from 5.10.3 to 5.10.5 in /backend [`#2833`](https://github.com/Human-Connection/Human-Connection/pull/2833)
|
||||
- build(deps): bump metascraper from 5.10.3 to 5.10.5 in /backend [`#2830`](https://github.com/Human-Connection/Human-Connection/pull/2830)
|
||||
- build(deps): bump metascraper-soundcloud from 5.10.3 to 5.10.5 in /backend [`#2829`](https://github.com/Human-Connection/Human-Connection/pull/2829)
|
||||
- fix(translations): Remove duplicate and mistranslated item from code of conduct [`#2725`](https://github.com/Human-Connection/Human-Connection/pull/2725)
|
||||
- build(deps-dev): bump @storybook/addon-a11y from 5.3.3 to 5.3.6 in /webapp [`#2820`](https://github.com/Human-Connection/Human-Connection/pull/2820)
|
||||
- build(deps): bump metascraper from 5.10.2 to 5.10.3 in /backend [`#2808`](https://github.com/Human-Connection/Human-Connection/pull/2808)
|
||||
- build(deps-dev): bump @storybook/vue from 5.3.3 to 5.3.6 in /webapp [`#2819`](https://github.com/Human-Connection/Human-Connection/pull/2819)
|
||||
- build(deps): bump faker from `9fd8d7d` to `3b2fa4a` in /backend [`#2803`](https://github.com/Human-Connection/Human-Connection/pull/2803)
|
||||
- build(deps-dev): bump faker from `9fd8d7d` to `3b2fa4a` [`#2804`](https://github.com/Human-Connection/Human-Connection/pull/2804)
|
||||
- build(deps-dev): bump @storybook/addon-a11y in /webapp [`#2809`](https://github.com/Human-Connection/Human-Connection/pull/2809)
|
||||
- build(deps): bump uuid from 3.3.3 to 3.4.0 in /backend [`#2810`](https://github.com/Human-Connection/Human-Connection/pull/2810)
|
||||
- build(deps): bump metascraper-image from 5.9.5 to 5.10.3 in /backend [`#2811`](https://github.com/Human-Connection/Human-Connection/pull/2811)
|
||||
- build(deps-dev): bump node-sass from 4.13.0 to 4.13.1 in /webapp [`#2812`](https://github.com/Human-Connection/Human-Connection/pull/2812)
|
||||
- build(deps): bump metascraper-audio from 5.9.5 to 5.10.3 in /backend [`#2813`](https://github.com/Human-Connection/Human-Connection/pull/2813)
|
||||
- build(deps): bump metascraper-soundcloud in /backend [`#2815`](https://github.com/Human-Connection/Human-Connection/pull/2815)
|
||||
- build(deps-dev): bump @storybook/addon-notes in /webapp [`#2816`](https://github.com/Human-Connection/Human-Connection/pull/2816)
|
||||
- build(deps-dev): bump @storybook/addon-actions from 5.3.3 to 5.3.5 in /webapp [`#2807`](https://github.com/Human-Connection/Human-Connection/pull/2807)
|
||||
- build(deps): bump metascraper-description from 5.9.5 to 5.10.3 in /backend [`#2806`](https://github.com/Human-Connection/Human-Connection/pull/2806)
|
||||
- build(deps): bump mustache from 3.2.1 to 4.0.0 in /backend [`#2805`](https://github.com/Human-Connection/Human-Connection/pull/2805)
|
||||
- 🍰 feat(webapp): Display deployed version in footer [`#2728`](https://github.com/Human-Connection/Human-Connection/pull/2728)
|
||||
- fix: cypress breaks locally in login step [`#2776`](https://github.com/Human-Connection/Human-Connection/pull/2776)
|
||||
- build(deps-dev): bump @vue/test-utils from 1.0.0-beta.29 to 1.0.0-beta.30 in /webapp [`#2378`](https://github.com/Human-Connection/Human-Connection/pull/2378)
|
||||
- build(deps): bump metascraper-youtube from 5.9.5 to 5.10.3 in /backend [`#2794`](https://github.com/Human-Connection/Human-Connection/pull/2794)
|
||||
- build(deps): bump metascraper-video from 5.9.5 to 5.10.3 in /backend [`#2795`](https://github.com/Human-Connection/Human-Connection/pull/2795)
|
||||
- build(deps): bump metascraper-logo from 5.9.5 to 5.10.3 in /backend [`#2796`](https://github.com/Human-Connection/Human-Connection/pull/2796)
|
||||
- refactor(styleguide): improve emotion buttons and header responsiveness [`#2582`](https://github.com/Human-Connection/Human-Connection/pull/2582)
|
||||
- build(deps): bump metascraper-url from 5.9.5 to 5.10.3 in /backend [`#2793`](https://github.com/Human-Connection/Human-Connection/pull/2793)
|
||||
- build(deps): bump metascraper-author from 5.9.5 to 5.10.3 in /backend [`#2789`](https://github.com/Human-Connection/Human-Connection/pull/2789)
|
||||
- build(deps): bump metascraper-lang from 5.9.5 to 5.10.3 in /backend [`#2790`](https://github.com/Human-Connection/Human-Connection/pull/2790)
|
||||
- build(deps): bump metascraper-publisher from 5.9.5 to 5.10.3 in /backend [`#2792`](https://github.com/Human-Connection/Human-Connection/pull/2792)
|
||||
- build(deps): bump metascraper-title from 5.9.5 to 5.10.3 in /backend [`#2791`](https://github.com/Human-Connection/Human-Connection/pull/2791)
|
||||
- build(deps): bump @sentry/node from 5.11.0 to 5.11.1 in /backend [`#2788`](https://github.com/Human-Connection/Human-Connection/pull/2788)
|
||||
- build(deps): bump metascraper-date from 5.9.5 to 5.10.3 in /backend [`#2787`](https://github.com/Human-Connection/Human-Connection/pull/2787)
|
||||
- build(deps-dev): bump @babel/node from 7.8.0 to 7.8.3 in /backend [`#2754`](https://github.com/Human-Connection/Human-Connection/pull/2754)
|
||||
- refactor(styleguide): migrate and redesign buttons [`#2562`](https://github.com/Human-Connection/Human-Connection/pull/2562)
|
||||
- build(deps-dev): bump @babel/core from 7.8.0 to 7.8.3 in /backend [`#2760`](https://github.com/Human-Connection/Human-Connection/pull/2760)
|
||||
- build(deps-dev): bump @storybook/addon-actions from 5.3.2 to 5.3.3 in /webapp [`#2782`](https://github.com/Human-Connection/Human-Connection/pull/2782)
|
||||
- build(deps-dev): bump sass-loader from 8.0.0 to 8.0.2 in /webapp [`#2781`](https://github.com/Human-Connection/Human-Connection/pull/2781)
|
||||
- build(deps-dev): bump @babel/plugin-syntax-dynamic-import from 7.8.0 to 7.8.3 in /webapp [`#2780`](https://github.com/Human-Connection/Human-Connection/pull/2780)
|
||||
- build(deps-dev): bump @storybook/addon-a11y from 5.3.2 to 5.3.3 in /webapp [`#2779`](https://github.com/Human-Connection/Human-Connection/pull/2779)
|
||||
- build(deps): bump metascraper from 5.9.5 to 5.10.2 in /backend [`#2778`](https://github.com/Human-Connection/Human-Connection/pull/2778)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.7.7 to 7.8.3 in /webapp [`#2767`](https://github.com/Human-Connection/Human-Connection/pull/2767)
|
||||
- build(deps-dev): bump @babel/plugin-proposal-throw-expressions from 7.8.0 to 7.8.3 in /backend [`#2757`](https://github.com/Human-Connection/Human-Connection/pull/2757)
|
||||
- build(deps-dev): bump @storybook/vue from 5.3.1 to 5.3.3 in /webapp [`#2772`](https://github.com/Human-Connection/Human-Connection/pull/2772)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.8.2 to 7.8.3 [`#2758`](https://github.com/Human-Connection/Human-Connection/pull/2758)
|
||||
- build(deps-dev): bump eslint-plugin-import from 2.19.1 to 2.20.0 in /webapp [`#2748`](https://github.com/Human-Connection/Human-Connection/pull/2748)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.3.1 to 5.3.3 in /webapp [`#2771`](https://github.com/Human-Connection/Human-Connection/pull/2771)
|
||||
- build(deps-dev): bump @babel/core from 7.7.7 to 7.8.3 in /webapp [`#2769`](https://github.com/Human-Connection/Human-Connection/pull/2769)
|
||||
- build(deps-dev): bump @babel/register from 7.8.0 to 7.8.3 [`#2764`](https://github.com/Human-Connection/Human-Connection/pull/2764)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.8.2 to 7.8.3 in /backend [`#2755`](https://github.com/Human-Connection/Human-Connection/pull/2755)
|
||||
- build(deps-dev): bump eslint-plugin-jest from 23.3.0 to 23.6.0 in /webapp [`#2768`](https://github.com/Human-Connection/Human-Connection/pull/2768)
|
||||
- build(deps-dev): bump @babel/cli from 7.8.0 to 7.8.3 in /backend [`#2763`](https://github.com/Human-Connection/Human-Connection/pull/2763)
|
||||
- build(deps-dev): bump cypress-cucumber-preprocessor from 1.19.0 to 2.0.1 [`#2761`](https://github.com/Human-Connection/Human-Connection/pull/2761)
|
||||
- build(deps-dev): bump @storybook/addon-a11y from 5.2.8 to 5.3.2 in /webapp [`#2759`](https://github.com/Human-Connection/Human-Connection/pull/2759)
|
||||
- build(deps-dev): bump @babel/core from 7.8.0 to 7.8.3 [`#2756`](https://github.com/Human-Connection/Human-Connection/pull/2756)
|
||||
- build(deps-dev): bump @babel/register from 7.8.0 to 7.8.3 in /backend [`#2753`](https://github.com/Human-Connection/Human-Connection/pull/2753)
|
||||
- build(deps): [security] bump serialize-javascript from 2.1.0 to 2.1.2 in /webapp [`#2752`](https://github.com/Human-Connection/Human-Connection/pull/2752)
|
||||
- build(deps-dev): bump @babel/core from 7.7.7 to 7.8.0 in /backend [`#2743`](https://github.com/Human-Connection/Human-Connection/pull/2743)
|
||||
- build(deps-dev): bump @storybook/addon-actions from 5.2.8 to 5.3.2 in /webapp [`#2751`](https://github.com/Human-Connection/Human-Connection/pull/2751)
|
||||
- build(deps-dev): bump @babel/register from 7.7.7 to 7.8.0 in /backend [`#2735`](https://github.com/Human-Connection/Human-Connection/pull/2735)
|
||||
- build(deps-dev): bump @babel/plugin-syntax-dynamic-import from 7.7.4 to 7.8.0 in /webapp [`#2746`](https://github.com/Human-Connection/Human-Connection/pull/2746)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.7.7 to 7.8.2 in /backend [`#2739`](https://github.com/Human-Connection/Human-Connection/pull/2739)
|
||||
- build(deps-dev): bump @babel/cli from 7.7.7 to 7.8.0 in /backend [`#2744`](https://github.com/Human-Connection/Human-Connection/pull/2744)
|
||||
- Issues marked as bounty never become stale [`#2726`](https://github.com/Human-Connection/Human-Connection/pull/2726)
|
||||
- build(deps-dev): bump css-loader from 3.4.1 to 3.4.2 in /webapp [`#2747`](https://github.com/Human-Connection/Human-Connection/pull/2747)
|
||||
- build(deps-dev): bump @storybook/addon-notes from 5.2.8 to 5.3.1 in /webapp [`#2742`](https://github.com/Human-Connection/Human-Connection/pull/2742)
|
||||
- build(deps-dev): bump @babel/plugin-proposal-throw-expressions from 7.7.4 to 7.8.0 in /backend [`#2741`](https://github.com/Human-Connection/Human-Connection/pull/2741)
|
||||
- build(deps-dev): bump eslint-plugin-import from 2.19.1 to 2.20.0 in /backend [`#2737`](https://github.com/Human-Connection/Human-Connection/pull/2737)
|
||||
- build(deps-dev): bump @babel/preset-env from 7.7.7 to 7.8.2 [`#2732`](https://github.com/Human-Connection/Human-Connection/pull/2732)
|
||||
- build(deps): bump @nuxtjs/axios from 5.9.2 to 5.9.3 in /webapp [`#2740`](https://github.com/Human-Connection/Human-Connection/pull/2740)
|
||||
- build(deps-dev): bump @storybook/vue from 5.2.8 to 5.3.1 in /webapp [`#2738`](https://github.com/Human-Connection/Human-Connection/pull/2738)
|
||||
- build(deps-dev): bump cypress from 3.8.1 to 3.8.2 [`#2734`](https://github.com/Human-Connection/Human-Connection/pull/2734)
|
||||
- build(deps-dev): bump @babel/node from 7.7.7 to 7.8.0 in /backend [`#2733`](https://github.com/Human-Connection/Human-Connection/pull/2733)
|
||||
- build(deps-dev): bump eslint-plugin-jest from 23.3.0 to 23.6.0 in /backend [`#2731`](https://github.com/Human-Connection/Human-Connection/pull/2731)
|
||||
- build(deps-dev): bump @babel/core from 7.7.7 to 7.8.0 [`#2730`](https://github.com/Human-Connection/Human-Connection/pull/2730)
|
||||
- build(deps-dev): bump @babel/register from 7.7.7 to 7.8.0 [`#2729`](https://github.com/Human-Connection/Human-Connection/pull/2729)
|
||||
- build(deps): bump nuxt from 2.10.2 to 2.11.0 in /webapp [`#2552`](https://github.com/Human-Connection/Human-Connection/pull/2552)
|
||||
- Update yarn.lock after dependabot update [`#2724`](https://github.com/Human-Connection/Human-Connection/pull/2724)
|
||||
- build(deps): bump @nuxtjs/axios from 5.8.0 to 5.9.2 in /webapp [`#2657`](https://github.com/Human-Connection/Human-Connection/pull/2657)
|
||||
- Update to version 0.2.1 [`#2722`](https://github.com/Human-Connection/Human-Connection/pull/2722)
|
||||
- docs(deployment): Explain how to setup metrics (#2825) [`#2411`](https://github.com/Human-Connection/Human-Connection/issues/2411) [`#2777`](https://github.com/Human-Connection/Human-Connection/issues/2777)
|
||||
- refactor(modules): Various import fixes [`#2773`](https://github.com/Human-Connection/Human-Connection/issues/2773) [`#2774`](https://github.com/Human-Connection/Human-Connection/issues/2774)
|
||||
- feat(webapp): Display deployed version in footer [`#1831`](https://github.com/Human-Connection/Human-Connection/issues/1831)
|
||||
- fix #2229 [`#2229`](https://github.com/Human-Connection/Human-Connection/issues/2229)
|
||||
- refactor: Make `db:setup` init stage of `migrate` [`b063847`](https://github.com/Human-Connection/Human-Connection/commit/b063847849a84db885337dc8e84e75ddaf87011f)
|
||||
- Improve styling per @alina-beck review [`bcc1ab1`](https://github.com/Human-Connection/Human-Connection/commit/bcc1ab167e8b1dfdac1ec0a05a0c14e8234bcabc)
|
||||
- test(cypress): Cover "Pinned post" feature [`d49afc2`](https://github.com/Human-Connection/Human-Connection/commit/d49afc25cfa1c1f98ed04f78dd3ff826cd85ae25)
|
||||
|
||||
#### [v0.2.1](https://github.com/Human-Connection/Human-Connection/compare/v0.2.0...v0.2.1)
|
||||
|
||||
> 10 January 2020
|
||||
@ -109,9 +290,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Fix imageAspectRatio set to null UpdatePost [`#2588`](https://github.com/Human-Connection/Human-Connection/pull/2588)
|
||||
- Update to version 0.2.0 [`#2584`](https://github.com/Human-Connection/Human-Connection/pull/2584)
|
||||
- fixes #2659 [`#2659`](https://github.com/Human-Connection/Human-Connection/issues/2659)
|
||||
- Convert block/unblock to blacklist/whitelist [`c297b83`](https://github.com/Human-Connection/Human-Connection/commit/c297b83f873edc61ddec370633b9b65896c56591)
|
||||
- Rename blacklist/whitelist to mute/unmute [`ba3e9e1`](https://github.com/Human-Connection/Human-Connection/commit/ba3e9e1025bf432151c9bf1002045179b338ff7f)
|
||||
- build(deps-dev): bump storybook-design-token in /webapp [`88d39c4`](https://github.com/Human-Connection/Human-Connection/commit/88d39c4a427cb86527b06201f3f5e96d53ac09a0)
|
||||
- Specs for Searches [`bc3aa51`](https://github.com/Human-Connection/Human-Connection/commit/bc3aa519d0e7a6e0242ecd37d611fd1a3df385d0)
|
||||
- build(deps): bump apollo-server-express in /backend [`84df7b5`](https://github.com/Human-Connection/Human-Connection/commit/84df7b5a0a4845ab44d19946d877aef79691d38e)
|
||||
|
||||
#### [v0.2.0](https://github.com/Human-Connection/Human-Connection/compare/v0.1.13...v0.2.0)
|
||||
|
||||
@ -497,7 +678,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- first implementation [`aeae72f`](https://github.com/Human-Connection/Human-Connection/commit/aeae72f6918861aa2a4c64d0b32c847d9e857e93)
|
||||
- build(deps-dev): bump eslint-plugin-jest in /backend [`6c1bd53`](https://github.com/Human-Connection/Human-Connection/commit/6c1bd535ac482eb0a05d21e227a476800717a19e)
|
||||
|
||||
#### [v0.1.8](https://github.com/Human-Connection/Human-Connection/compare/0.1.7...v0.1.8)
|
||||
#### [v0.1.8](https://github.com/Human-Connection/Human-Connection/compare/v0.1.7...v0.1.8)
|
||||
|
||||
> 25 October 2019
|
||||
|
||||
@ -519,7 +700,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- build(deps): bump @nuxtjs/apollo in /webapp [`26c21b5`](https://github.com/Human-Connection/Human-Connection/commit/26c21b5b76c96206d98ff6bbfdbd1ca973ffcd4f)
|
||||
- Finish redesign of moderators report list [`15d28aa`](https://github.com/Human-Connection/Human-Connection/commit/15d28aa8ef84788aa640aac67838380bfacf63b7)
|
||||
|
||||
#### [0.1.7](https://github.com/Human-Connection/Human-Connection/compare/0.1.6...0.1.7)
|
||||
#### [v0.1.7](https://github.com/Human-Connection/Human-Connection/compare/v0.1.6...v0.1.7)
|
||||
|
||||
> 23 October 2019
|
||||
|
||||
@ -535,7 +716,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- build(deps-dev): bump @vue/cli-shared-utils in /webapp [`a5d993c`](https://github.com/Human-Connection/Human-Connection/commit/a5d993c761b2f92c3f44b6f83592ea4c1d822606)
|
||||
- Fix block user workflow [`44e5437`](https://github.com/Human-Connection/Human-Connection/commit/44e54372c4148fafae1095d172d1a52a87b3b1b2)
|
||||
|
||||
#### [0.1.6](https://github.com/Human-Connection/Human-Connection/compare/0.1.5...0.1.6)
|
||||
#### [v0.1.6](https://github.com/Human-Connection/Human-Connection/compare/v0.1.5...v0.1.6)
|
||||
|
||||
> 22 October 2019
|
||||
|
||||
@ -569,7 +750,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Refactor tests for querying reported resources [`4e42017`](https://github.com/Human-Connection/Human-Connection/commit/4e42017afaa97fa87ec726a5bbd1605cca911375)
|
||||
- Return pinnedAt date from pinPost resolver/clean up [`be0c804`](https://github.com/Human-Connection/Human-Connection/commit/be0c8044e87e211f2578df151d9d2d11795a135f)
|
||||
|
||||
#### [0.1.5](https://github.com/Human-Connection/Human-Connection/compare/0.1.4...0.1.5)
|
||||
#### [v0.1.5](https://github.com/Human-Connection/Human-Connection/compare/v0.1.4...v0.1.5)
|
||||
|
||||
> 17 October 2019
|
||||
|
||||
@ -627,7 +808,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Start adding missing portuguese translation [`33eb000`](https://github.com/Human-Connection/Human-Connection/commit/33eb000ee33e5aa513083450f0a00abd7240efb0)
|
||||
- refactor: restructure translations and components [`bb5d581`](https://github.com/Human-Connection/Human-Connection/commit/bb5d581906b5e6e723966c3dc687c7f309356841)
|
||||
|
||||
#### [0.1.4](https://github.com/Human-Connection/Human-Connection/compare/0.1.3...0.1.4)
|
||||
#### [v0.1.4](https://github.com/Human-Connection/Human-Connection/compare/v0.1.3...v0.1.4)
|
||||
|
||||
> 10 October 2019
|
||||
|
||||
@ -665,7 +846,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Fix lint, update tests [`bced698`](https://github.com/Human-Connection/Human-Connection/commit/bced6983ea1f51736e989eab6a41166723a6a6ca)
|
||||
- add test embeds and links [`7cc139e`](https://github.com/Human-Connection/Human-Connection/commit/7cc139e879ac7ea912e82ea7eff14f7b67eddb4a)
|
||||
|
||||
#### [0.1.3](https://github.com/Human-Connection/Human-Connection/compare/0.1.2...0.1.3)
|
||||
#### [v0.1.3](https://github.com/Human-Connection/Human-Connection/compare/v0.1.2...v0.1.3)
|
||||
|
||||
> 4 October 2019
|
||||
|
||||
@ -685,7 +866,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Set hasMore to false when returned Posts are equal to pageSize [`6f1c5e3`](https://github.com/Human-Connection/Human-Connection/commit/6f1c5e3efa3b77e72172592a0b5e4ea52158e642)
|
||||
- refactor: use named slot for additional text [`3912b21`](https://github.com/Human-Connection/Human-Connection/commit/3912b21ea2f24e2e25682060b7166d1511442e6e)
|
||||
|
||||
#### [0.1.2](https://github.com/Human-Connection/Human-Connection/compare/0.1.1...0.1.2)
|
||||
#### [v0.1.2](https://github.com/Human-Connection/Human-Connection/compare/v0.1.1...v0.1.2)
|
||||
|
||||
> 2 October 2019
|
||||
|
||||
@ -744,7 +925,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- build(deps): bump @nuxtjs/apollo in /webapp [`4648080`](https://github.com/Human-Connection/Human-Connection/commit/4648080a74fa6df60d6bb9b34d1db5030a9d4124)
|
||||
- Write and refactor backend test which are supposed to fail at first [`6ad9dc2`](https://github.com/Human-Connection/Human-Connection/commit/6ad9dc27e937eb263914846c073172906aa661e1)
|
||||
|
||||
#### [0.1.1](https://github.com/Human-Connection/Human-Connection/compare/0.1.0...0.1.1)
|
||||
#### [v0.1.1](https://github.com/Human-Connection/Human-Connection/compare/v0.1.0...v0.1.1)
|
||||
|
||||
> 27 September 2019
|
||||
|
||||
@ -818,7 +999,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
- Run with tag 0.1.0 [`c634ad2`](https://github.com/Human-Connection/Human-Connection/commit/c634ad264bd99dd1a87a86f870d7877aa751dc38)
|
||||
- zwischenspeichern [`e4c7c11`](https://github.com/Human-Connection/Human-Connection/commit/e4c7c1125da6f8fa259241b4d3838b1e7b1e24a2)
|
||||
|
||||
#### 0.1.0
|
||||
#### v0.1.0
|
||||
|
||||
> 18 September 2019
|
||||
|
||||
|
||||
@ -64,7 +64,7 @@ Regular pair programming sessions
|
||||
* we team up and work on an issue together (often using Visual Studio live sharing sessions)
|
||||
|
||||
Open-Source Community Meeting
|
||||
* every Thursday 13:00
|
||||
* bi-weekly on Mondays 13:00 (when there is no sprint retrospective)
|
||||
* the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
|
||||
* all contributors welcome!
|
||||
|
||||
@ -99,3 +99,34 @@ We believe in open source contributions as a learning experience – everyone is
|
||||
We use pair programming sessions as a tool for knowledge sharing. We can learn a lot from each other and only by sharing what we know and overcoming challenges together can we grow as a team and truly own this project collectively.
|
||||
|
||||
As a volunteeer you have no commitment except your own self development and your awesomeness by contributing to this free and open-source software project. Cheers to you!
|
||||
|
||||
|
||||
## Open-Source Bounties
|
||||
|
||||
There are so many good reasons to contribute to Human Connection
|
||||
* You learn state-of-the-art technologies
|
||||
* You build your portfolio
|
||||
* You contribute to a good cause
|
||||
|
||||
Now there is one more good reason: You can receive a small fincancial
|
||||
compensation for your contribution! :tada:
|
||||
|
||||
### How it works
|
||||
|
||||
Before you can benefit from the Open-Source bounty program you **must get one
|
||||
pull request approved and merged for free**. You can choose something really
|
||||
quick and easy. What's important is starting a working relationship with the
|
||||
team, learning the workflow, and understanding this contribution guide. You can
|
||||
filter issues by 'good first issue', to get an idea where to start. Please join
|
||||
our our [community chat](https://human-connection.org/discord), too.
|
||||
|
||||
You can filter Github issues with label [bounty](https://github.com/Human-Connection/Human-Connection/issues?q=is%3Aopen+is%3Aissue+label%3Abounty). These issues should have a second label `€<amount>`
|
||||
which indicate their respective financial compensation in Euros.
|
||||
|
||||
You can bill us after your pull request got approved and merged into `master`.
|
||||
Payment methods are up to you: Bank transfer or PayPal is fine for us. Just send
|
||||
us your invoice as .pdf file attached to an E-Mail once you are done.
|
||||
|
||||
Our Open-Source bounty program is a work-in-progress. Based on our future
|
||||
experience we will make changes and improvements. So keep an eye on this
|
||||
contribution guide.
|
||||
|
||||
@ -52,6 +52,10 @@ Check out the [contribution guideline](./CONTRIBUTING.md), too!
|
||||
|
||||
[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/0)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/1)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/2)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/3)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/4)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/5)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/6)[](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/7)
|
||||
|
||||
## Open-Source Bounties
|
||||
|
||||
You can get a small financial compensation for your contribution :moneybag: See
|
||||
details in our [Contribution Guidelines](./CONTRIBUTING.md#open-source-bounties).
|
||||
|
||||
## Attributions
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
* [Volume Snapshots](deployment/volumes/volume-snapshots/README.md)
|
||||
* [Reclaim Policy](deployment/volumes/reclaim-policy/README.md)
|
||||
* [Velero](deployment/volumes/velero/README.md)
|
||||
* [Metrics](deployment/monitoring/README.md)
|
||||
* [Legacy Migration](deployment/legacy-migration/README.md)
|
||||
* [Feature Specification](cypress/features.md)
|
||||
* [Code of conduct](CODE_OF_CONDUCT.md)
|
||||
|
||||
@ -53,6 +53,27 @@ can issue GraphQL requests or access GraphQL Playground in the browser.
|
||||
|
||||

|
||||
|
||||
### Database Indices and Constraints
|
||||
|
||||
Database indices and constraints need to be created when the database and the
|
||||
backend is running:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Docker" %}
|
||||
```bash
|
||||
docker-compose exec backend yarn run db:migrate init
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="Without Docker" %}
|
||||
```bash
|
||||
# in folder backend/
|
||||
# make sure your database is running on http://localhost:7474/browser/
|
||||
yarn run db:migrate init
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
|
||||
#### Seed Database
|
||||
|
||||
@ -73,7 +94,7 @@ $ docker-compose exec backend yarn run db:reset
|
||||
# you could also wipe out your neo4j database and delete all volumes with:
|
||||
$ docker-compose down -v
|
||||
# if container is not running, run this command to set up your database indeces and contstraints
|
||||
$ docker-compose run neo4j db_setup
|
||||
$ docker-compose run backend yarn run db:migrate init
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
@ -90,6 +111,38 @@ $ yarn run db:reset
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
### Data migrations
|
||||
|
||||
Although Neo4J is schema-less,you might find yourself in a situation in which
|
||||
you have to migrate your data e.g. because your data modeling has changed.
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="Docker" %}
|
||||
Generate a data migration file:
|
||||
```bash
|
||||
$ docker-compose exec backend yarn run db:migrate:create your_data_migration
|
||||
# Edit the file in ./src/db/migrations/
|
||||
```
|
||||
|
||||
To run the migration:
|
||||
```bash
|
||||
$ docker-compose exec backend yarn run db:migrate up
|
||||
```
|
||||
{% endtab %}
|
||||
{% tab title="Without Docker" %}
|
||||
Generate a data migration file:
|
||||
```bash
|
||||
$ yarn run db:migrate:create your_data_migration
|
||||
# Edit the file in ./src/db/migrations/
|
||||
```
|
||||
|
||||
To run the migration:
|
||||
```bash
|
||||
$ yarn run db:migrate up
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
# Testing
|
||||
|
||||
**Beware**: We have no multiple database setup at the moment. We clean the
|
||||
|
||||
@ -1,17 +1,22 @@
|
||||
{
|
||||
"name": "human-connection-backend",
|
||||
"version": "0.0.1",
|
||||
"version": "0.2.2",
|
||||
"description": "GraphQL Backend for Human Connection",
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"build": "babel src/ -d dist/ --copy-files",
|
||||
"__migrate": "migrate --compiler 'js:@babel/register' --migrations-dir ./src/db/migrations",
|
||||
"prod:migrate": "migrate --migrations-dir ./dist/db/migrations --store ./dist/db/migrate/store.js",
|
||||
"start": "node dist/",
|
||||
"build": "babel src/ -d dist/ --copy-files",
|
||||
"dev": "nodemon --exec babel-node src/ -e js,gql",
|
||||
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/index.js -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": "jest --forceExit --detectOpenHandles --runInBand",
|
||||
"db:reset": "babel-node src/seed/reset-db.js",
|
||||
"db:seed": "babel-node src/seed/seed-db.js"
|
||||
"db:clean": "babel-node src/db/clean.js",
|
||||
"db:reset": "yarn run db:clean",
|
||||
"db:seed": "babel-node src/db/seed.js",
|
||||
"db:migrate": "yarn run __migrate --store ./src/db/migrate/store.js",
|
||||
"db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.js --date-format 'yyyymmddHHmmss' create"
|
||||
},
|
||||
"author": "Human Connection gGmbH",
|
||||
"license": "MIT",
|
||||
@ -32,8 +37,8 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/joi": "^17.0.2",
|
||||
"@sentry/node": "^5.11.0",
|
||||
"@hapi/joi": "^17.1.0",
|
||||
"@sentry/node": "^5.12.0",
|
||||
"apollo-cache-inmemory": "~1.6.5",
|
||||
"apollo-client": "~2.6.8",
|
||||
"apollo-link-context": "~1.0.19",
|
||||
@ -44,42 +49,43 @@
|
||||
"bcryptjs": "~2.4.3",
|
||||
"cheerio": "~1.0.0-rc.3",
|
||||
"cors": "~2.8.5",
|
||||
"cross-env": "~6.0.3",
|
||||
"cross-env": "~7.0.0",
|
||||
"date-fns": "2.9.0",
|
||||
"debug": "~4.1.1",
|
||||
"dotenv": "~8.2.0",
|
||||
"express": "^4.17.1",
|
||||
"faker": "Marak/faker.js#master",
|
||||
"graphql": "^14.5.8",
|
||||
"graphql": "^14.6.0",
|
||||
"graphql-custom-directives": "~0.2.14",
|
||||
"graphql-iso-date": "~3.6.1",
|
||||
"graphql-middleware": "~4.0.2",
|
||||
"graphql-middleware-sentry": "^3.2.1",
|
||||
"graphql-shield": "~7.0.7",
|
||||
"graphql-shield": "~7.0.9",
|
||||
"graphql-tag": "~2.10.1",
|
||||
"helmet": "~3.21.2",
|
||||
"jsonwebtoken": "~8.5.1",
|
||||
"linkifyjs": "~2.1.8",
|
||||
"lodash": "~4.17.14",
|
||||
"merge-graphql-schemas": "^1.7.6",
|
||||
"metascraper": "^5.10.2",
|
||||
"metascraper-audio": "^5.9.5",
|
||||
"metascraper-author": "^5.9.5",
|
||||
"metascraper": "^5.11.0",
|
||||
"metascraper-audio": "^5.10.6",
|
||||
"metascraper-author": "^5.10.6",
|
||||
"metascraper-clearbit-logo": "^5.3.0",
|
||||
"metascraper-date": "^5.9.5",
|
||||
"metascraper-description": "^5.9.5",
|
||||
"metascraper-image": "^5.9.5",
|
||||
"metascraper-lang": "^5.9.5",
|
||||
"metascraper-date": "^5.10.7",
|
||||
"metascraper-description": "^5.11.0",
|
||||
"metascraper-image": "^5.10.7",
|
||||
"metascraper-lang": "^5.10.7",
|
||||
"metascraper-lang-detector": "^4.10.2",
|
||||
"metascraper-logo": "^5.9.5",
|
||||
"metascraper-publisher": "^5.9.5",
|
||||
"metascraper-soundcloud": "^5.9.5",
|
||||
"metascraper-title": "^5.9.5",
|
||||
"metascraper-url": "^5.9.5",
|
||||
"metascraper-video": "^5.9.5",
|
||||
"metascraper-youtube": "^5.9.5",
|
||||
"metascraper-logo": "^5.10.7",
|
||||
"metascraper-publisher": "^5.10.7",
|
||||
"metascraper-soundcloud": "^5.10.7",
|
||||
"metascraper-title": "^5.10.6",
|
||||
"metascraper-url": "^5.10.6",
|
||||
"metascraper-video": "^5.10.7",
|
||||
"metascraper-youtube": "^5.10.7",
|
||||
"migrate": "^1.6.2",
|
||||
"minimatch": "^3.0.4",
|
||||
"mustache": "^3.2.1",
|
||||
"mustache": "^4.0.0",
|
||||
"neo4j-driver": "^4.0.1",
|
||||
"neo4j-graphql-js": "^2.11.5",
|
||||
"neode": "^0.3.7",
|
||||
@ -88,37 +94,37 @@
|
||||
"nodemailer-html-to-text": "^3.1.0",
|
||||
"npm-run-all": "~4.1.5",
|
||||
"request": "~2.88.0",
|
||||
"sanitize-html": "~1.20.1",
|
||||
"slug": "~2.1.0",
|
||||
"sanitize-html": "~1.21.1",
|
||||
"slug": "~2.1.1",
|
||||
"trunc-html": "~1.1.2",
|
||||
"uuid": "~3.3.3",
|
||||
"validator": "^12.1.0",
|
||||
"wait-on": "~3.3.0",
|
||||
"uuid": "~3.4.0",
|
||||
"validator": "^12.2.0",
|
||||
"wait-on": "~4.0.0",
|
||||
"xregexp": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "~7.8.3",
|
||||
"@babel/core": "~7.8.3",
|
||||
"@babel/node": "~7.8.3",
|
||||
"@babel/cli": "~7.8.4",
|
||||
"@babel/core": "~7.8.4",
|
||||
"@babel/node": "~7.8.4",
|
||||
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
|
||||
"@babel/preset-env": "~7.8.3",
|
||||
"@babel/register": "~7.8.3",
|
||||
"@babel/preset-env": "~7.8.4",
|
||||
"@babel/register": "^7.8.3",
|
||||
"apollo-server-testing": "~2.9.16",
|
||||
"babel-core": "~7.0.0-0",
|
||||
"babel-eslint": "~10.0.3",
|
||||
"babel-jest": "~24.9.0",
|
||||
"babel-jest": "~25.1.0",
|
||||
"chai": "~4.2.0",
|
||||
"cucumber": "~6.0.5",
|
||||
"eslint": "~6.8.0",
|
||||
"eslint-config-prettier": "~6.9.0",
|
||||
"eslint-config-prettier": "~6.10.0",
|
||||
"eslint-config-standard": "~14.1.0",
|
||||
"eslint-plugin-import": "~2.20.0",
|
||||
"eslint-plugin-import": "~2.20.1",
|
||||
"eslint-plugin-jest": "~23.6.0",
|
||||
"eslint-plugin-node": "~11.0.0",
|
||||
"eslint-plugin-prettier": "~3.1.2",
|
||||
"eslint-plugin-promise": "~4.2.1",
|
||||
"eslint-plugin-standard": "~4.0.1",
|
||||
"jest": "~24.9.0",
|
||||
"jest": "~25.1.0",
|
||||
"nodemon": "~2.0.2",
|
||||
"prettier": "~1.19.1",
|
||||
"supertest": "~4.0.2"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { handler } from './webfinger'
|
||||
import Factory from '../../seed/factories'
|
||||
import { getDriver } from '../../bootstrap/neo4j'
|
||||
import Factory from '../../factories'
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
|
||||
let resource, res, json, status, contentType
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
import dotenv from 'dotenv'
|
||||
import path from 'path'
|
||||
|
||||
dotenv.config({ path: path.resolve(__dirname, '../../.env') })
|
||||
if (require.resolve) {
|
||||
// are we in a nodejs environment?
|
||||
dotenv.config({ path: require.resolve('../../.env') })
|
||||
}
|
||||
|
||||
const {
|
||||
MAPBOX_TOKEN,
|
||||
@ -27,6 +28,15 @@ export const requiredConfigs = {
|
||||
PRIVATE_KEY_PASSPHRASE,
|
||||
}
|
||||
|
||||
if (require.resolve) {
|
||||
// are we in a nodejs environment?
|
||||
Object.entries(requiredConfigs).map(entry => {
|
||||
if (!entry[1]) {
|
||||
throw new Error(`ERROR: "${entry[0]}" env variable is missing.`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const smtpConfigs = {
|
||||
SMTP_HOST,
|
||||
SMTP_PORT,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { cleanDatabase } from './factories'
|
||||
import { cleanDatabase } from '../factories'
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
throw new Error(`You cannot clean the database in production environment!`)
|
||||
97
backend/src/db/migrate/store.js
Normal file
97
backend/src/db/migrate/store.js
Normal file
@ -0,0 +1,97 @@
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
|
||||
class Store {
|
||||
async init(next) {
|
||||
const neode = getNeode()
|
||||
const { driver } = neode
|
||||
const session = driver.session()
|
||||
// eslint-disable-next-line no-console
|
||||
const writeTxResultPromise = session.writeTransaction(async txc => {
|
||||
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
|
||||
return Promise.all(
|
||||
[
|
||||
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
|
||||
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
|
||||
].map(statement => txc.run(statement)),
|
||||
)
|
||||
})
|
||||
try {
|
||||
await writeTxResultPromise
|
||||
await getNeode().schema.install()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Successfully created database indices and constraints!')
|
||||
next()
|
||||
} catch (error) {
|
||||
console.log(error) // eslint-disable-line no-console
|
||||
next(error, null)
|
||||
} finally {
|
||||
session.close()
|
||||
driver.close()
|
||||
}
|
||||
}
|
||||
|
||||
async load(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const readTxResultPromise = session.readTransaction(async txc => {
|
||||
const result = await txc.run(
|
||||
'MATCH (migration:Migration) RETURN migration {.*} ORDER BY migration.timestamp DESC',
|
||||
)
|
||||
return result.records.map(r => r.get('migration'))
|
||||
})
|
||||
try {
|
||||
const migrations = await readTxResultPromise
|
||||
if (migrations.length <= 0) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(
|
||||
"No migrations found in database. If it's the first time you run migrations, then this is normal.",
|
||||
)
|
||||
return next(null, {})
|
||||
}
|
||||
const [{ title: lastRun }] = migrations
|
||||
next(null, { lastRun, migrations })
|
||||
} catch (error) {
|
||||
console.log(error) // eslint-disable-line no-console
|
||||
next(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
|
||||
async save(set, next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const { migrations } = set
|
||||
const writeTxResultPromise = session.writeTransaction(txc => {
|
||||
return Promise.all(
|
||||
migrations.map(async migration => {
|
||||
const { title, description, timestamp } = migration
|
||||
const properties = { title, description, timestamp }
|
||||
const migrationResult = await txc.run(
|
||||
`
|
||||
MERGE (migration:Migration { title: $properties.title })
|
||||
ON MATCH SET
|
||||
migration += $properties
|
||||
ON CREATE SET
|
||||
migration += $properties,
|
||||
migration.migratedAt = toString(datetime())
|
||||
`,
|
||||
{ properties },
|
||||
)
|
||||
return migrationResult
|
||||
}),
|
||||
)
|
||||
})
|
||||
try {
|
||||
await writeTxResultPromise
|
||||
next()
|
||||
} catch (error) {
|
||||
console.log(error) // eslint-disable-line no-console
|
||||
next(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Store
|
||||
45
backend/src/db/migrate/template.js
Normal file
45
backend/src/db/migrate/template.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
|
||||
export const description = ''
|
||||
|
||||
export async function up(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(``)
|
||||
await transaction.commit()
|
||||
next()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(``)
|
||||
await transaction.commit()
|
||||
next()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,83 @@
|
||||
import { throwError, concat } from 'rxjs'
|
||||
import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators'
|
||||
import { getDriver } from '../neo4j'
|
||||
import normalizeEmail from '../../schema/resolvers//helpers/normalizeEmail'
|
||||
|
||||
export const description = `
|
||||
This migration merges duplicate :User and :EmailAddress nodes. It became
|
||||
necessary after we implemented the email normalization but forgot to migrate
|
||||
the existing data. Some (40) users decided to just register with a new account
|
||||
but the same email address. On signup our backend would normalize the email,
|
||||
which is good, but would also keep the existing unnormalized email address.
|
||||
|
||||
This led to about 40 duplicate user and email address nodes in our database.
|
||||
`
|
||||
export function up(next) {
|
||||
const driver = getDriver()
|
||||
const rxSession = driver.rxSession()
|
||||
rxSession
|
||||
.beginTransaction()
|
||||
.pipe(
|
||||
flatMap(txc =>
|
||||
concat(
|
||||
txc
|
||||
.run('MATCH (email:EmailAddress) RETURN email {.email}')
|
||||
.records()
|
||||
.pipe(
|
||||
map(record => {
|
||||
const { email } = record.get('email')
|
||||
const normalizedEmail = normalizeEmail(email)
|
||||
return { email, normalizedEmail }
|
||||
}),
|
||||
filter(({ email, normalizedEmail }) => email !== normalizedEmail),
|
||||
mergeMap(({ email, normalizedEmail }) => {
|
||||
return txc
|
||||
.run(
|
||||
`
|
||||
MATCH (oldUser:User)-[:PRIMARY_EMAIL]->(oldEmail:EmailAddress {email: $email})
|
||||
MATCH (user:User)-[:PRIMARY_EMAIL]->(email:EmailAddress {email: $normalizedEmail})
|
||||
WITH oldUser, oldEmail, user, email
|
||||
CALL apoc.refactor.mergeNodes([user, oldUser], { properties: { createdAt: 'overwrite', \`.*\`: 'discard' }, mergeRels: true }) YIELD node as mergedUser
|
||||
CALL apoc.refactor.mergeNodes([email, oldEmail], { properties: { createdAt: 'overwrite', verifiedAt: 'overwrite', \`.*\`: 'discard' }, mergeRels: true }) YIELD node as mergedEmail
|
||||
RETURN user {.*}, email {.*}
|
||||
`,
|
||||
{ email, normalizedEmail },
|
||||
)
|
||||
.records()
|
||||
.pipe(
|
||||
map(r => ({
|
||||
oldEmail: email,
|
||||
email: r.get('email'),
|
||||
user: r.get('user'),
|
||||
})),
|
||||
)
|
||||
}),
|
||||
),
|
||||
txc.commit(),
|
||||
).pipe(catchError(err => txc.rollback().pipe(throwError(err)))),
|
||||
),
|
||||
)
|
||||
.subscribe({
|
||||
next: ({ user, email, oldUser, oldEmail }) =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`
|
||||
Merged:
|
||||
=============================
|
||||
userId: ${user.id}
|
||||
email: ${oldEmail} => ${email.email}
|
||||
=============================
|
||||
`),
|
||||
complete: () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Merging of duplicate users completed')
|
||||
next()
|
||||
},
|
||||
error: error => {
|
||||
next(new Error(error), null)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function down(next) {
|
||||
next(new Error('Irreversible migration'))
|
||||
}
|
||||
@ -0,0 +1,77 @@
|
||||
import { throwError, concat } from 'rxjs'
|
||||
import { flatMap, mergeMap, map, catchError } from 'rxjs/operators'
|
||||
import { getDriver } from '../neo4j'
|
||||
|
||||
export const description = `
|
||||
This migration merges duplicate :Location nodes. It became
|
||||
necessary after we realized that we had not set up constraints for Location.id in production.
|
||||
`
|
||||
export function up(next) {
|
||||
const driver = getDriver()
|
||||
const rxSession = driver.rxSession()
|
||||
rxSession
|
||||
.beginTransaction()
|
||||
.pipe(
|
||||
flatMap(transaction =>
|
||||
concat(
|
||||
transaction
|
||||
.run(
|
||||
`
|
||||
MATCH (location:Location)
|
||||
RETURN location {.id}
|
||||
`,
|
||||
)
|
||||
.records()
|
||||
.pipe(
|
||||
map(record => {
|
||||
const { id: locationId } = record.get('location')
|
||||
return { locationId }
|
||||
}),
|
||||
mergeMap(({ locationId }) => {
|
||||
return transaction
|
||||
.run(
|
||||
`
|
||||
MATCH(location:Location {id: $locationId}), (location2:Location {id: $locationId})
|
||||
WHERE location.id = location2.id AND id(location) < id(location2)
|
||||
CALL apoc.refactor.mergeNodes([location, location2], { properties: 'combine', mergeRels: true }) YIELD node as updatedLocation
|
||||
RETURN location {.*},updatedLocation {.*}
|
||||
`,
|
||||
{ locationId },
|
||||
)
|
||||
.records()
|
||||
.pipe(
|
||||
map(record => ({
|
||||
location: record.get('location'),
|
||||
updatedLocation: record.get('updatedLocation'),
|
||||
})),
|
||||
)
|
||||
}),
|
||||
),
|
||||
transaction.commit(),
|
||||
).pipe(catchError(error => transaction.rollback().pipe(throwError(error)))),
|
||||
),
|
||||
)
|
||||
.subscribe({
|
||||
next: ({ updatedLocation, location }) =>
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`
|
||||
Merged:
|
||||
=============================
|
||||
locationId: ${location.id}
|
||||
updatedLocation: ${location.id} => ${updatedLocation.id}
|
||||
=============================
|
||||
`),
|
||||
complete: () => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('Merging of duplicate locations completed')
|
||||
next()
|
||||
},
|
||||
error: error => {
|
||||
next(new Error(error), null)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
export function down(next) {
|
||||
next(new Error('Irreversible migration'))
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
|
||||
export const description = `
|
||||
This migration creates a MUTED relationship between two edges(:User) that have a pre-existing BLOCKED relationship.
|
||||
It also sets the createdAt date for the BLOCKED relationship to the datetime the migration was run. This became
|
||||
necessary after we redefined what it means to block someone, and what it means to mute them. Muting is about filtering
|
||||
another user's content, whereas blocking means preventing that user from interacting with you/your contributions.
|
||||
A blocked user will still be able to see your contributions, but will not be able to interact with them and vice versa.
|
||||
`
|
||||
|
||||
export async function up(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
try {
|
||||
await transaction.run(
|
||||
`
|
||||
MATCH (blocker:User)-[blocked:BLOCKED]->(blockee:User)
|
||||
MERGE (blocker)-[muted:MUTED]->(blockee)
|
||||
SET muted.createdAt = toString(datetime()), blocked.createdAt = toString(datetime())
|
||||
`,
|
||||
)
|
||||
await transaction.commit()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export function down(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
try {
|
||||
// Rollback your migration here.
|
||||
next()
|
||||
} catch (err) {
|
||||
next(err)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,8 @@ import faker from 'faker'
|
||||
import sample from 'lodash/sample'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../server'
|
||||
import Factory from './factories'
|
||||
import { getNeode, getDriver } from '../bootstrap/neo4j'
|
||||
import Factory from '../factories'
|
||||
import { getNeode, getDriver } from '../db/neo4j'
|
||||
import { gql } from '../helpers/jest'
|
||||
|
||||
const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
@ -226,6 +226,10 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
dewey.relateTo(huey, 'following'),
|
||||
louie.relateTo(jennyRostock, 'following'),
|
||||
|
||||
huey.relateTo(dagobert, 'muted'),
|
||||
dewey.relateTo(dagobert, 'muted'),
|
||||
louie.relateTo(dagobert, 'muted'),
|
||||
|
||||
dagobert.relateTo(huey, 'blocked'),
|
||||
dagobert.relateTo(dewey, 'blocked'),
|
||||
dagobert.relateTo(louie, 'blocked'),
|
||||
@ -1,30 +1,18 @@
|
||||
import { getDriver, getNeode } from '../../bootstrap/neo4j'
|
||||
import createBadge from './badges.js'
|
||||
import createUser from './users.js'
|
||||
import createPost from './posts.js'
|
||||
import createComment from './comments.js'
|
||||
import createCategory from './categories.js'
|
||||
import createTag from './tags.js'
|
||||
import createSocialMedia from './socialMedia.js'
|
||||
import createLocation from './locations.js'
|
||||
import createEmailAddress from './emailAddresses.js'
|
||||
import createDonations from './donations.js'
|
||||
import createUnverifiedEmailAddresss from './unverifiedEmailAddresses.js'
|
||||
import createReport from './reports.js'
|
||||
import { getDriver, getNeode } from '../db/neo4j'
|
||||
|
||||
const factories = {
|
||||
Badge: createBadge,
|
||||
User: createUser,
|
||||
Post: createPost,
|
||||
Comment: createComment,
|
||||
Category: createCategory,
|
||||
Tag: createTag,
|
||||
SocialMedia: createSocialMedia,
|
||||
Location: createLocation,
|
||||
EmailAddress: createEmailAddress,
|
||||
UnverifiedEmailAddress: createUnverifiedEmailAddresss,
|
||||
Donations: createDonations,
|
||||
Report: createReport,
|
||||
Badge: require('./badges.js').default,
|
||||
User: require('./users.js').default,
|
||||
Post: require('./posts.js').default,
|
||||
Comment: require('./comments.js').default,
|
||||
Category: require('./categories.js').default,
|
||||
Tag: require('./tags.js').default,
|
||||
SocialMedia: require('./socialMedia.js').default,
|
||||
Location: require('./locations.js').default,
|
||||
EmailAddress: require('./emailAddresses.js').default,
|
||||
UnverifiedEmailAddress: require('./unverifiedEmailAddresses.js').default,
|
||||
Donations: require('./donations.js').default,
|
||||
Report: require('./reports.js').default,
|
||||
}
|
||||
|
||||
export const cleanDatabase = async (options = {}) => {
|
||||
@ -34,7 +22,7 @@ export const cleanDatabase = async (options = {}) => {
|
||||
await session.writeTransaction(transaction => {
|
||||
return transaction.run(
|
||||
`
|
||||
MATCH (everything)
|
||||
MATCH (everything)
|
||||
DETACH DELETE everything
|
||||
`,
|
||||
)
|
||||
@ -21,11 +21,15 @@ export default function create() {
|
||||
categoryIds: [],
|
||||
imageBlurred: false,
|
||||
imageAspectRatio: 1.333,
|
||||
pinned: null,
|
||||
}
|
||||
args = {
|
||||
...defaults,
|
||||
...args,
|
||||
}
|
||||
// Convert false to null
|
||||
args.pinned = args.pinned || null
|
||||
|
||||
args.slug = args.slug || slugify(args.title, { lower: true })
|
||||
args.contentExcerpt = args.contentExcerpt || args.content
|
||||
|
||||
@ -36,7 +40,6 @@ export default function create() {
|
||||
if (categoryIds)
|
||||
categories = await Promise.all(categoryIds.map(id => neodeInstance.find('Category', id)))
|
||||
categories = categories || (await Promise.all([factoryInstance.create('Category')]))
|
||||
|
||||
const { tagIds = [] } = args
|
||||
delete args.tags
|
||||
const tags = await Promise.all(
|
||||
@ -51,9 +54,33 @@ export default function create() {
|
||||
if (author && authorId) throw new Error('You provided both author and authorId')
|
||||
if (authorId) author = await neodeInstance.find('User', authorId)
|
||||
author = author || (await factoryInstance.create('User'))
|
||||
|
||||
const post = await neodeInstance.create('Post', args)
|
||||
|
||||
const { commentContent } = args
|
||||
let comment
|
||||
delete args.commentContent
|
||||
if (commentContent)
|
||||
comment = await factoryInstance.create('Comment', {
|
||||
contentExcerpt: commentContent,
|
||||
post,
|
||||
author,
|
||||
})
|
||||
|
||||
await post.relateTo(author, 'author')
|
||||
if (comment) await post.relateTo(comment, 'comments')
|
||||
|
||||
if (args.pinned) {
|
||||
args.pinnedAt = args.pinnedAt || new Date().toISOString()
|
||||
if (!args.pinnedBy) {
|
||||
const admin = await factoryInstance.create('User', {
|
||||
role: 'admin',
|
||||
updatedAt: new Date().toISOString(),
|
||||
})
|
||||
await admin.relateTo(post, 'pinned')
|
||||
args.pinnedBy = admin
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all(categories.map(c => c.relateTo(post, 'post')))
|
||||
await Promise.all(tags.map(t => t.relateTo(post, 'post')))
|
||||
return post
|
||||
@ -1,6 +1,6 @@
|
||||
import faker from 'faker'
|
||||
import uuid from 'uuid/v4'
|
||||
import encryptPassword from '../../helpers/encryptPassword'
|
||||
import encryptPassword from '../helpers/encryptPassword'
|
||||
import slugify from 'slug'
|
||||
|
||||
export default function create() {
|
||||
@ -1,5 +0,0 @@
|
||||
//* This is a fake ES2015 template string, just to benefit of syntax
|
||||
// highlighting of `gql` template strings in certain editors.
|
||||
export function gql(strings) {
|
||||
return strings.join('')
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import Factory from '../seed/factories/index'
|
||||
import { getDriver, getNeode } from '../bootstrap/neo4j'
|
||||
import Factory from '../factories/index'
|
||||
import { getDriver, getNeode } from '../db/neo4j'
|
||||
import decode from './decode'
|
||||
|
||||
const factory = Factory()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { gql } from '../../helpers/jest'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
let server
|
||||
|
||||
@ -50,7 +50,7 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
MATCH (post: Post { id: $id })<-[:WROTE]-(author: User)
|
||||
MATCH (user: User)
|
||||
WHERE user.id in $idsOfUsers
|
||||
AND NOT (user)<-[:BLOCKED]-(author)
|
||||
AND NOT (user)-[:BLOCKED]-(author)
|
||||
MERGE (post)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||
`
|
||||
break
|
||||
@ -60,8 +60,8 @@ const notifyUsersOfMention = async (label, id, idsOfUsers, reason, context) => {
|
||||
MATCH (postAuthor: User)-[:WROTE]->(post: Post)<-[:COMMENTS]-(comment: Comment { id: $id })<-[:WROTE]-(author: User)
|
||||
MATCH (user: User)
|
||||
WHERE user.id in $idsOfUsers
|
||||
AND NOT (user)<-[:BLOCKED]-(author)
|
||||
AND NOT (user)<-[:BLOCKED]-(postAuthor)
|
||||
AND NOT (user)-[:BLOCKED]-(author)
|
||||
AND NOT (user)-[:BLOCKED]-(postAuthor)
|
||||
MERGE (comment)-[notification:NOTIFIED {reason: $reason}]->(user)
|
||||
`
|
||||
break
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { gql } from '../../helpers/jest'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
let server, query, mutate, notifiedUser, authenticatedUser
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { gql } from '../helpers/jest'
|
||||
import Factory from '../seed/factories'
|
||||
import { getNeode, getDriver } from '../bootstrap/neo4j'
|
||||
import Factory from '../factories'
|
||||
import { getNeode, getDriver } from '../db/neo4j'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../server'
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { rule, shield, deny, allow, or } from 'graphql-shield'
|
||||
import { getNeode } from '../bootstrap/neo4j'
|
||||
import { getNeode } from '../db/neo4j'
|
||||
import CONFIG from '../config'
|
||||
|
||||
const debug = !!CONFIG.DEBUG
|
||||
@ -101,6 +101,7 @@ export default shield(
|
||||
Badge: allow,
|
||||
PostsEmotionsCountByEmotion: allow,
|
||||
PostsEmotionsByCurrentUser: isAuthenticated,
|
||||
mutedUsers: isAuthenticated,
|
||||
blockedUsers: isAuthenticated,
|
||||
notifications: isAuthenticated,
|
||||
Donations: isAuthenticated,
|
||||
@ -137,8 +138,10 @@ export default shield(
|
||||
resetPassword: allow,
|
||||
AddPostEmotions: isAuthenticated,
|
||||
RemovePostEmotions: isAuthenticated,
|
||||
block: isAuthenticated,
|
||||
unblock: isAuthenticated,
|
||||
muteUser: isAuthenticated,
|
||||
unmuteUser: isAuthenticated,
|
||||
blockUser: isAuthenticated,
|
||||
unblockUser: isAuthenticated,
|
||||
markAsRead: isAuthenticated,
|
||||
AddEmailAddress: isAuthenticated,
|
||||
VerifyEmailAddress: isAuthenticated,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../server'
|
||||
import Factory from '../seed/factories'
|
||||
import Factory from '../factories'
|
||||
import { gql } from '../helpers/jest'
|
||||
import { getDriver, getNeode } from '../bootstrap/neo4j'
|
||||
import { getDriver, getNeode } from '../db/neo4j'
|
||||
|
||||
const factory = Factory()
|
||||
const instance = getNeode()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../seed/factories'
|
||||
import Factory from '../factories'
|
||||
import { gql } from '../helpers/jest'
|
||||
import { getNeode, getDriver } from '../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../db/neo4j'
|
||||
import createServer from '../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { gql } from '../../helpers/jest'
|
||||
import Factory from '../../seed/factories'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import Factory from '../../factories'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../../server'
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@ function clean(dirty) {
|
||||
'hr',
|
||||
'b',
|
||||
'i',
|
||||
'u',
|
||||
'em',
|
||||
'strong',
|
||||
'a',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, lowercase: true },
|
||||
status: { type: 'string', valid: ['permanent', 'temporary'] },
|
||||
type: { type: 'string', valid: ['role', 'crowdfunding'] },
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
name: { type: 'string', required: true, default: false },
|
||||
slug: { type: 'string' },
|
||||
slug: { type: 'string', unique: 'true' },
|
||||
icon: { type: 'string', required: true, default: false },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
updatedAt: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
updatedAt: {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
goal: { type: 'number' },
|
||||
progress: { type: 'number' },
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
email: { type: 'string', primary: true, lowercase: true, email: true },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
verifiedAt: { type: 'string', isoDate: true },
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
token: { type: 'string', primary: true, token: true },
|
||||
generatedBy: {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true },
|
||||
lat: { type: 'number' },
|
||||
lng: { type: 'number' },
|
||||
|
||||
5
backend/src/models/Migration.js
Normal file
5
backend/src/models/Migration.js
Normal file
@ -0,0 +1,5 @@
|
||||
export default {
|
||||
title: { type: 'string', primary: true, token: true },
|
||||
description: { type: 'string' },
|
||||
migratedAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
activityId: { type: 'string', allow: [null] },
|
||||
objectId: { type: 'string', allow: [null] },
|
||||
@ -11,7 +11,7 @@ module.exports = {
|
||||
direction: 'in',
|
||||
},
|
||||
title: { type: 'string', disallow: [null], min: 3 },
|
||||
slug: { type: 'string', allow: [null] },
|
||||
slug: { type: 'string', allow: [null], unique: 'true' },
|
||||
content: { type: 'string', disallow: [null], min: 3 },
|
||||
contentExcerpt: { type: 'string', allow: [null] },
|
||||
image: { type: 'string', allow: [null] },
|
||||
@ -41,4 +41,16 @@ module.exports = {
|
||||
language: { type: 'string', allow: [null] },
|
||||
imageBlurred: { type: 'boolean', default: false },
|
||||
imageAspectRatio: { type: 'float', default: 1.0 },
|
||||
comments: {
|
||||
type: 'relationship',
|
||||
relationship: 'COMMENTS',
|
||||
target: 'Comment',
|
||||
direction: 'in',
|
||||
properties: {
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
updatedAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
},
|
||||
},
|
||||
pinned: { type: 'boolean', default: null, valid: [null, true] },
|
||||
pinnedAt: { type: 'string', isoDate: true },
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
updatedAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid },
|
||||
url: { type: 'string', uri: true, required: true },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true },
|
||||
deleted: { type: 'boolean', default: false },
|
||||
disabled: { type: 'boolean', default: false },
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
email: { type: 'string', primary: true, lowercase: true, email: true },
|
||||
export default {
|
||||
email: { type: 'string', lowercase: true, email: true },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
nonce: { type: 'string', token: true },
|
||||
belongsTo: {
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
id: { type: 'string', primary: true, default: uuid }, // TODO: should be type: 'uuid' but simplified for our tests
|
||||
actorId: { type: 'string', allow: [null] },
|
||||
name: { type: 'string', disallow: [null], min: 3 },
|
||||
slug: { type: 'string', regex: /^[a-z0-9_-]+$/, lowercase: true },
|
||||
slug: { type: 'string', unique: 'true', regex: /^[a-z0-9_-]+$/, lowercase: true },
|
||||
encryptedPassword: 'string',
|
||||
avatar: { type: 'string', allow: [null] },
|
||||
coverImg: { type: 'string', allow: [null] },
|
||||
@ -77,6 +77,18 @@ module.exports = {
|
||||
relationship: 'BLOCKED',
|
||||
target: 'User',
|
||||
direction: 'out',
|
||||
properties: {
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
},
|
||||
},
|
||||
muted: {
|
||||
type: 'relationship',
|
||||
relationship: 'MUTED',
|
||||
target: 'User',
|
||||
direction: 'out',
|
||||
properties: {
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
},
|
||||
},
|
||||
notifications: {
|
||||
type: 'relationship',
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import Factory from '../seed/factories'
|
||||
import { getNeode } from '../bootstrap/neo4j'
|
||||
import Factory from '../factories'
|
||||
import { getNeode } from '../db/neo4j'
|
||||
|
||||
const factory = Factory()
|
||||
const neode = getNeode()
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
// NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm
|
||||
// module that is not browser-compatible. Node's `fs` module is server-side only
|
||||
export default {
|
||||
Badge: require('./Badge.js'),
|
||||
User: require('./User.js'),
|
||||
EmailAddress: require('./EmailAddress.js'),
|
||||
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js'),
|
||||
SocialMedia: require('./SocialMedia.js'),
|
||||
Post: require('./Post.js'),
|
||||
Comment: require('./Comment.js'),
|
||||
Category: require('./Category.js'),
|
||||
Tag: require('./Tag.js'),
|
||||
Location: require('./Location.js'),
|
||||
Donations: require('./Donations.js'),
|
||||
Report: require('./Report.js'),
|
||||
Badge: require('./Badge.js').default,
|
||||
User: require('./User.js').default,
|
||||
EmailAddress: require('./EmailAddress.js').default,
|
||||
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js').default,
|
||||
SocialMedia: require('./SocialMedia.js').default,
|
||||
Post: require('./Post.js').default,
|
||||
Comment: require('./Comment.js').default,
|
||||
Category: require('./Category.js').default,
|
||||
Tag: require('./Tag.js').default,
|
||||
Location: require('./Location.js').default,
|
||||
Donations: require('./Donations.js').default,
|
||||
Report: require('./Report.js').default,
|
||||
Migration: require('./Migration.js').default,
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../../server'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
let mutate, query, authenticatedUser, variables
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getDriver, getNeode } from '../../bootstrap/neo4j'
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
|
||||
const neode = getNeode()
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import { getDriver, getNeode } from '../../bootstrap/neo4j'
|
||||
import Factory from '../../factories'
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { gql } from '../../helpers/jest'
|
||||
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
import { getBlockedUsers, getBlockedByUsers } from '../users.js'
|
||||
import { mergeWith, isArray } from 'lodash'
|
||||
|
||||
export const filterForBlockedUsers = async (params, context) => {
|
||||
if (!context.user) return params
|
||||
const [blockedUsers, blockedByUsers] = await Promise.all([
|
||||
getBlockedUsers(context),
|
||||
getBlockedByUsers(context),
|
||||
])
|
||||
const blockedUsersIds = [...blockedByUsers.map(b => b.id), ...blockedUsers.map(b => b.id)]
|
||||
if (!blockedUsersIds.length) return params
|
||||
|
||||
params.filter = mergeWith(
|
||||
params.filter,
|
||||
{
|
||||
author_not: { id_in: blockedUsersIds },
|
||||
},
|
||||
(objValue, srcValue) => {
|
||||
if (isArray(objValue)) {
|
||||
return objValue.concat(srcValue)
|
||||
}
|
||||
},
|
||||
)
|
||||
return params
|
||||
}
|
||||
22
backend/src/schema/resolvers/helpers/filterForMutedUsers.js
Normal file
22
backend/src/schema/resolvers/helpers/filterForMutedUsers.js
Normal file
@ -0,0 +1,22 @@
|
||||
import { getMutedUsers } from '../users.js'
|
||||
import { mergeWith, isArray } from 'lodash'
|
||||
|
||||
export const filterForMutedUsers = async (params, context) => {
|
||||
if (!context.user) return params
|
||||
const [mutedUsers] = await Promise.all([getMutedUsers(context)])
|
||||
const mutedUsersIds = [...mutedUsers.map(user => user.id)]
|
||||
if (!mutedUsersIds.length) return params
|
||||
|
||||
params.filter = mergeWith(
|
||||
params.filter,
|
||||
{
|
||||
author_not: { id_in: mutedUsersIds },
|
||||
},
|
||||
(objValue, srcValue) => {
|
||||
if (isArray(objValue)) {
|
||||
return objValue.concat(srcValue)
|
||||
}
|
||||
},
|
||||
)
|
||||
return params
|
||||
}
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
const factory = Factory()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getDriver } from '../../bootstrap/neo4j'
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../.././server'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createPasswordReset from './helpers/createPasswordReset'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import uuid from 'uuid/v4'
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import { isEmpty } from 'lodash'
|
||||
import fileUpload from './fileUpload'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
import fileUpload from './fileUpload'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import { filterForBlockedUsers } from './helpers/filterForBlockedUsers'
|
||||
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
|
||||
|
||||
const maintainPinnedPosts = params => {
|
||||
const pinnedPostFilter = { pinned: true }
|
||||
@ -19,16 +19,16 @@ const maintainPinnedPosts = params => {
|
||||
export default {
|
||||
Query: {
|
||||
Post: async (object, params, context, resolveInfo) => {
|
||||
params = await filterForBlockedUsers(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
params = await maintainPinnedPosts(params)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
findPosts: async (object, params, context, resolveInfo) => {
|
||||
params = await filterForBlockedUsers(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
profilePagePosts: async (object, params, context, resolveInfo) => {
|
||||
params = await filterForBlockedUsers(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
const driver = getDriver()
|
||||
@ -682,58 +682,62 @@ describe('UpdatePost', () => {
|
||||
})
|
||||
|
||||
describe('PostOrdering', () => {
|
||||
let pinnedPost, admin
|
||||
beforeEach(async () => {
|
||||
;[pinnedPost] = await Promise.all([
|
||||
neode.create('Post', {
|
||||
id: 'im-a-pinned-post',
|
||||
pinned: true,
|
||||
}),
|
||||
neode.create('Post', {
|
||||
id: 'i-was-created-after-pinned-post',
|
||||
createdAt: '2019-10-22T17:26:29.070Z', // this should always be 3rd
|
||||
}),
|
||||
])
|
||||
admin = await user.update({
|
||||
role: 'admin',
|
||||
name: 'Admin',
|
||||
updatedAt: new Date().toISOString(),
|
||||
await factory.create('Post', {
|
||||
id: 'im-a-pinned-post',
|
||||
createdAt: '2019-11-22T17:26:29.070Z',
|
||||
pinned: true,
|
||||
})
|
||||
await factory.create('Post', {
|
||||
id: 'i-was-created-before-pinned-post',
|
||||
// fairly old, so this should be 3rd
|
||||
createdAt: '2019-10-22T17:26:29.070Z',
|
||||
})
|
||||
await admin.relateTo(pinnedPost, 'pinned')
|
||||
})
|
||||
|
||||
it('pinned post appear first even when created before other posts', async () => {
|
||||
const postOrderingQuery = gql`
|
||||
query($orderBy: [_PostOrdering]) {
|
||||
Post(orderBy: $orderBy) {
|
||||
id
|
||||
pinnedAt
|
||||
describe('order by `pinned_asc` and `createdAt_desc`', () => {
|
||||
beforeEach(() => {
|
||||
// this is the ordering in the frontend
|
||||
variables = { orderBy: ['pinned_asc', 'createdAt_desc'] }
|
||||
})
|
||||
|
||||
it('pinned post appear first even when created before other posts', async () => {
|
||||
const postOrderingQuery = gql`
|
||||
query($orderBy: [_PostOrdering]) {
|
||||
Post(orderBy: $orderBy) {
|
||||
id
|
||||
pinned
|
||||
createdAt
|
||||
pinnedAt
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const expected = {
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'im-a-pinned-post',
|
||||
pinnedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 'p9876',
|
||||
pinnedAt: null,
|
||||
},
|
||||
{
|
||||
id: 'i-was-created-after-pinned-post',
|
||||
pinnedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
}
|
||||
variables = { orderBy: ['pinned_desc', 'createdAt_desc'] }
|
||||
await expect(query({ query: postOrderingQuery, variables })).resolves.toMatchObject(
|
||||
expected,
|
||||
)
|
||||
`
|
||||
await expect(query({ query: postOrderingQuery, variables })).resolves.toMatchObject({
|
||||
data: {
|
||||
Post: [
|
||||
{
|
||||
id: 'im-a-pinned-post',
|
||||
pinned: true,
|
||||
createdAt: '2019-11-22T17:26:29.070Z',
|
||||
pinnedAt: expect.any(String),
|
||||
},
|
||||
{
|
||||
id: 'p9876',
|
||||
pinned: null,
|
||||
createdAt: expect.any(String),
|
||||
pinnedAt: null,
|
||||
},
|
||||
{
|
||||
id: 'i-was-created-before-pinned-post',
|
||||
pinned: null,
|
||||
createdAt: '2019-10-22T17:26:29.070Z',
|
||||
pinnedAt: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
errors: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { UserInputError } from 'apollo-server'
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import fileUpload from './fileUpload'
|
||||
import encryptPassword from '../../helpers/encryptPassword'
|
||||
import generateNonce from './helpers/generateNonce'
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getDriver, getNeode } from '../../bootstrap/neo4j'
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../.././server'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getDriver, getNeode } from '../../bootstrap/neo4j'
|
||||
import { getDriver, getNeode } from '../../db/neo4j'
|
||||
|
||||
const factory = Factory()
|
||||
const instance = getNeode()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
|
||||
const neode = getNeode()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
const factory = Factory()
|
||||
|
||||
@ -20,7 +20,7 @@ export default {
|
||||
AND NOT (
|
||||
author.deleted = true OR author.disabled = true
|
||||
OR resource.deleted = true OR resource.disabled = true
|
||||
OR (:User { id: $thisUserId })-[:BLOCKED]-(author)
|
||||
OR (:User {id: $thisUserId})-[:MUTED]->(author)
|
||||
)
|
||||
WITH resource, author,
|
||||
[(resource)<-[:COMMENTS]-(comment:Comment) | comment] as comments,
|
||||
@ -40,8 +40,7 @@ export default {
|
||||
YIELD node as resource, score
|
||||
MATCH (resource)
|
||||
WHERE score >= 0.5
|
||||
AND NOT (resource.deleted = true OR resource.disabled = true
|
||||
OR (:User { id: $thisUserId })-[:BLOCKED]-(resource))
|
||||
AND NOT (resource.deleted = true OR resource.disabled = true)
|
||||
RETURN resource {.*, __typename: labels(resource)[0]}
|
||||
LIMIT $limit
|
||||
`
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
let mutate, query, authenticatedUser, variables
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import Resolver from './helpers/Resolver'
|
||||
|
||||
const neode = getNeode()
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../../server'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
|
||||
const driver = getDriver()
|
||||
const factory = Factory()
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
|
||||
let query, authenticatedUser
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import encode from '../../jwt/encode'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { AuthenticationError } from 'apollo-server'
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import normalizeEmail from './helpers/normalizeEmail'
|
||||
import log from './helpers/databaseLogger'
|
||||
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import jwt from 'jsonwebtoken'
|
||||
import CONFIG from './../../config'
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer, { context } from '../../server'
|
||||
import encode from '../../jwt/encode'
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
|
||||
const factory = Factory()
|
||||
const neode = getNeode()
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
import fileUpload from './fileUpload'
|
||||
import { getNeode } from '../../bootstrap/neo4j'
|
||||
import { getNeode } from '../../db/neo4j'
|
||||
import { UserInputError, ForbiddenError } from 'apollo-server'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import log from './helpers/databaseLogger'
|
||||
@ -8,6 +8,21 @@ import createOrUpdateLocations from './users/location'
|
||||
|
||||
const neode = getNeode()
|
||||
|
||||
export const getMutedUsers = async context => {
|
||||
const { neode } = context
|
||||
const userModel = neode.model('User')
|
||||
let mutedUsers = neode
|
||||
.query()
|
||||
.match('user', userModel)
|
||||
.where('user.id', context.user.id)
|
||||
.relationship(userModel.relationships().get('muted'))
|
||||
.to('muted', userModel)
|
||||
.return('muted')
|
||||
mutedUsers = await mutedUsers.execute()
|
||||
mutedUsers = mutedUsers.records.map(r => r.get('muted').properties)
|
||||
return mutedUsers
|
||||
}
|
||||
|
||||
export const getBlockedUsers = async context => {
|
||||
const { neode } = context
|
||||
const userModel = neode.model('User')
|
||||
@ -23,24 +38,15 @@ export const getBlockedUsers = async context => {
|
||||
return blockedUsers
|
||||
}
|
||||
|
||||
export const getBlockedByUsers = async context => {
|
||||
if (context.user.role === 'moderator' || context.user.role === 'admin') return []
|
||||
const { neode } = context
|
||||
const userModel = neode.model('User')
|
||||
let blockedByUsers = neode
|
||||
.query()
|
||||
.match('user', userModel)
|
||||
.relationship(userModel.relationships().get('blocked'))
|
||||
.to('blocked', userModel)
|
||||
.where('blocked.id', context.user.id)
|
||||
.return('user')
|
||||
blockedByUsers = await blockedByUsers.execute()
|
||||
blockedByUsers = blockedByUsers.records.map(r => r.get('user').properties)
|
||||
return blockedByUsers
|
||||
}
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
mutedUsers: async (object, args, context, resolveInfo) => {
|
||||
try {
|
||||
return getMutedUsers(context)
|
||||
} catch (e) {
|
||||
throw new UserInputError(e.message)
|
||||
}
|
||||
},
|
||||
blockedUsers: async (object, args, context, resolveInfo) => {
|
||||
try {
|
||||
return getBlockedUsers(context)
|
||||
@ -72,7 +78,37 @@ export default {
|
||||
},
|
||||
},
|
||||
Mutation: {
|
||||
block: async (object, args, context, resolveInfo) => {
|
||||
muteUser: async (_parent, params, context, _resolveInfo) => {
|
||||
const { user: currentUser } = context
|
||||
if (currentUser.id === params.id) return null
|
||||
await neode.cypher(
|
||||
`
|
||||
MATCH(u:User {id: $currentUser.id})-[previousRelationship:FOLLOWS]->(b:User {id: $params.id})
|
||||
DELETE previousRelationship
|
||||
`,
|
||||
{ currentUser, params },
|
||||
)
|
||||
const [user, mutedUser] = await Promise.all([
|
||||
neode.find('User', currentUser.id),
|
||||
neode.find('User', params.id),
|
||||
])
|
||||
await user.relateTo(mutedUser, 'muted')
|
||||
return mutedUser.toJson()
|
||||
},
|
||||
unmuteUser: async (_parent, params, context, _resolveInfo) => {
|
||||
const { user: currentUser } = context
|
||||
if (currentUser.id === params.id) return null
|
||||
await neode.cypher(
|
||||
`
|
||||
MATCH(u:User {id: $currentUser.id})-[previousRelationship:MUTED]->(b:User {id: $params.id})
|
||||
DELETE previousRelationship
|
||||
`,
|
||||
{ currentUser, params },
|
||||
)
|
||||
const unmutedUser = await neode.find('User', params.id)
|
||||
return unmutedUser.toJson()
|
||||
},
|
||||
blockUser: async (object, args, context, resolveInfo) => {
|
||||
const { user: currentUser } = context
|
||||
if (currentUser.id === args.id) return null
|
||||
await neode.cypher(
|
||||
@ -89,7 +125,7 @@ export default {
|
||||
await user.relateTo(blockedUser, 'blocked')
|
||||
return blockedUser.toJson()
|
||||
},
|
||||
unblock: async (object, args, context, resolveInfo) => {
|
||||
unblockUser: async (object, args, context, resolveInfo) => {
|
||||
const { user: currentUser } = context
|
||||
if (currentUser.id === args.id) return null
|
||||
await neode.cypher(
|
||||
@ -215,8 +251,10 @@ export default {
|
||||
boolean: {
|
||||
followedByCurrentUser:
|
||||
'MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
||||
isBlocked:
|
||||
blocked:
|
||||
'MATCH (this)<-[:BLOCKED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
||||
isMuted:
|
||||
'MATCH (this)<-[:MUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
|
||||
},
|
||||
count: {
|
||||
contributionsCount:
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import Factory from '../../seed/factories'
|
||||
import Factory from '../../factories'
|
||||
import { gql } from '../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../db/neo4j'
|
||||
import createServer from '../../server'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { gql } from '../../../helpers/jest'
|
||||
import Factory from '../../../seed/factories'
|
||||
import { getNeode, getDriver } from '../../../bootstrap/neo4j'
|
||||
import Factory from '../../../factories'
|
||||
import { getNeode, getDriver } from '../../../db/neo4j'
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../../../server'
|
||||
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
import createServer from '../../../server'
|
||||
import Factory from '../../../seed/factories'
|
||||
import Factory from '../../../factories'
|
||||
import { gql } from '../../../helpers/jest'
|
||||
import { getNeode, getDriver } from '../../../bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from '../../../db/neo4j'
|
||||
|
||||
const driver = getDriver()
|
||||
const factory = Factory()
|
||||
const neode = getNeode()
|
||||
|
||||
let currentUser
|
||||
let blockedUser
|
||||
let mutedUser
|
||||
let authenticatedUser
|
||||
let server
|
||||
|
||||
@ -33,15 +33,15 @@ afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('blockedUsers', () => {
|
||||
let blockedUserQuery
|
||||
describe('mutedUsers', () => {
|
||||
let mutedUserQuery
|
||||
beforeEach(() => {
|
||||
blockedUserQuery = gql`
|
||||
mutedUserQuery = gql`
|
||||
query {
|
||||
blockedUsers {
|
||||
mutedUsers {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
isMuted
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -49,34 +49,34 @@ describe('blockedUsers', () => {
|
||||
|
||||
it('throws permission error', async () => {
|
||||
const { query } = createTestClient(server)
|
||||
const result = await query({ query: blockedUserQuery })
|
||||
const result = await query({ query: mutedUserQuery })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
|
||||
describe('authenticated and given a blocked user', () => {
|
||||
describe('authenticated and given a muted user', () => {
|
||||
beforeEach(async () => {
|
||||
currentUser = await neode.create('User', {
|
||||
name: 'Current User',
|
||||
id: 'u1',
|
||||
})
|
||||
blockedUser = await neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
mutedUser = await neode.create('User', {
|
||||
name: 'Muted User',
|
||||
id: 'u2',
|
||||
})
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
await currentUser.relateTo(mutedUser, 'muted')
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
it('returns a list of blocked users', async () => {
|
||||
it('returns a list of muted users', async () => {
|
||||
const { query } = createTestClient(server)
|
||||
await expect(query({ query: blockedUserQuery })).resolves.toEqual(
|
||||
await expect(query({ query: mutedUserQuery })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
blockedUsers: [
|
||||
mutedUsers: [
|
||||
{
|
||||
name: 'Blocked User',
|
||||
name: 'Muted User',
|
||||
id: 'u2',
|
||||
isBlocked: true,
|
||||
isMuted: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -86,28 +86,28 @@ describe('blockedUsers', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('block', () => {
|
||||
let blockAction
|
||||
describe('muteUser', () => {
|
||||
let muteAction
|
||||
|
||||
beforeEach(() => {
|
||||
currentUser = undefined
|
||||
blockAction = variables => {
|
||||
muteAction = variables => {
|
||||
const { mutate } = createTestClient(server)
|
||||
const blockMutation = gql`
|
||||
const muteUserMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
block(id: $id) {
|
||||
muteUser(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
isMuted
|
||||
}
|
||||
}
|
||||
`
|
||||
return mutate({ mutation: blockMutation, variables })
|
||||
return mutate({ mutation: muteUserMutation, variables })
|
||||
}
|
||||
})
|
||||
|
||||
it('throws permission error', async () => {
|
||||
const result = await blockAction({ id: 'u2' })
|
||||
const result = await muteAction({ id: 'u2' })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
|
||||
@ -120,45 +120,47 @@ describe('block', () => {
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
describe('block yourself', () => {
|
||||
describe('mute yourself', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(blockAction({ id: 'u1' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { block: null } }),
|
||||
await expect(muteAction({ id: 'u1' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { muteUser: null } }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('block not existing user', () => {
|
||||
describe('mute not existing user', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(blockAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { block: null } }),
|
||||
await expect(muteAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { muteUser: null } }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a to-be-blocked user', () => {
|
||||
describe('given a to-be-muted user', () => {
|
||||
beforeEach(async () => {
|
||||
blockedUser = await neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
mutedUser = await neode.create('User', {
|
||||
name: 'Muted User',
|
||||
id: 'u2',
|
||||
})
|
||||
})
|
||||
|
||||
it('blocks a user', async () => {
|
||||
await expect(blockAction({ id: 'u2' })).resolves.toEqual(
|
||||
it('mutes a user', async () => {
|
||||
await expect(muteAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { block: { id: 'u2', name: 'Blocked User', isBlocked: true } },
|
||||
data: {
|
||||
muteUser: { id: 'u2', name: 'Muted User', isMuted: true },
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('unfollows the user', async () => {
|
||||
await currentUser.relateTo(blockedUser, 'following')
|
||||
await currentUser.relateTo(mutedUser, 'following')
|
||||
const queryUser = gql`
|
||||
query {
|
||||
User(id: "u2") {
|
||||
id
|
||||
isBlocked
|
||||
isMuted
|
||||
followedByCurrentUser
|
||||
}
|
||||
}
|
||||
@ -166,18 +168,18 @@ describe('block', () => {
|
||||
const { query } = createTestClient(server)
|
||||
await expect(query({ query: queryUser })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { User: [{ id: 'u2', isBlocked: false, followedByCurrentUser: true }] },
|
||||
data: { User: [{ id: 'u2', isMuted: false, followedByCurrentUser: true }] },
|
||||
}),
|
||||
)
|
||||
await blockAction({ id: 'u2' })
|
||||
await muteAction({ id: 'u2' })
|
||||
await expect(query({ query: queryUser })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { User: [{ id: 'u2', isBlocked: true, followedByCurrentUser: false }] },
|
||||
data: { User: [{ id: 'u2', isMuted: true, followedByCurrentUser: false }] },
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('given both the current user and the to-be-blocked user write a post', () => {
|
||||
describe('given both the current user and the to-be-muted user write a post', () => {
|
||||
let postQuery
|
||||
|
||||
beforeEach(async () => {
|
||||
@ -187,11 +189,11 @@ describe('block', () => {
|
||||
})
|
||||
const post2 = await neode.create('Post', {
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
title: 'A post written by the muted user',
|
||||
})
|
||||
await Promise.all([
|
||||
post1.relateTo(currentUser, 'author'),
|
||||
post2.relateTo(blockedUser, 'author'),
|
||||
post2.relateTo(mutedUser, 'author'),
|
||||
])
|
||||
postQuery = gql`
|
||||
query {
|
||||
@ -223,9 +225,9 @@ describe('block', () => {
|
||||
},
|
||||
{
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
title: 'A post written by the muted user',
|
||||
author: {
|
||||
name: 'Blocked User',
|
||||
name: 'Muted User',
|
||||
id: 'u2',
|
||||
},
|
||||
},
|
||||
@ -238,12 +240,12 @@ describe('block', () => {
|
||||
describe('from the perspective of the current user', () => {
|
||||
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
|
||||
|
||||
describe('but if the current user blocks the other user', () => {
|
||||
describe('but if the current user mutes the other user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
await currentUser.relateTo(mutedUser, 'muted')
|
||||
})
|
||||
|
||||
it("the blocked user's post won't show up in the newsfeed of the current user", async () => {
|
||||
it("the muted user's post won't show up in the newsfeed of the current user", async () => {
|
||||
const { query } = createTestClient(server)
|
||||
await expect(query({ query: postQuery })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
@ -262,29 +264,34 @@ describe('block', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('from the perspective of the blocked user', () => {
|
||||
describe('from the perspective of the muted user', () => {
|
||||
beforeEach(async () => {
|
||||
authenticatedUser = await blockedUser.toJson()
|
||||
authenticatedUser = await mutedUser.toJson()
|
||||
})
|
||||
|
||||
it('both posts are in the newsfeed', bothPostsAreInTheNewsfeed)
|
||||
describe('but if the current user blocks the other user', () => {
|
||||
describe('but if the current user mutes the other user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
await currentUser.relateTo(mutedUser, 'muted')
|
||||
})
|
||||
|
||||
it("the current user's post won't show up in the newsfeed of the blocked user", async () => {
|
||||
it("the current user's post will show up in the newsfeed of the muted user", async () => {
|
||||
const { query } = createTestClient(server)
|
||||
await expect(query({ query: postQuery })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
Post: [
|
||||
Post: expect.arrayContaining([
|
||||
{
|
||||
id: 'p23',
|
||||
title: 'A post written by the blocked user',
|
||||
author: { name: 'Blocked User', id: 'u2' },
|
||||
title: 'A post written by the muted user',
|
||||
author: { name: 'Muted User', id: 'u2' },
|
||||
},
|
||||
],
|
||||
{
|
||||
id: 'p12',
|
||||
title: 'A post written by the current user',
|
||||
author: { name: 'Current User', id: 'u1' },
|
||||
},
|
||||
]),
|
||||
},
|
||||
}),
|
||||
)
|
||||
@ -296,28 +303,28 @@ describe('block', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblock', () => {
|
||||
let unblockAction
|
||||
describe('unmuteUser', () => {
|
||||
let unmuteAction
|
||||
|
||||
beforeEach(() => {
|
||||
currentUser = undefined
|
||||
unblockAction = variables => {
|
||||
unmuteAction = variables => {
|
||||
const { mutate } = createTestClient(server)
|
||||
const unblockMutation = gql`
|
||||
const unmuteUserMutation = gql`
|
||||
mutation($id: ID!) {
|
||||
unblock(id: $id) {
|
||||
unmuteUser(id: $id) {
|
||||
id
|
||||
name
|
||||
isBlocked
|
||||
isMuted
|
||||
}
|
||||
}
|
||||
`
|
||||
return mutate({ mutation: unblockMutation, variables })
|
||||
return mutate({ mutation: unmuteUserMutation, variables })
|
||||
}
|
||||
})
|
||||
|
||||
it('throws permission error', async () => {
|
||||
const result = await unblockAction({ id: 'u2' })
|
||||
const result = await unmuteAction({ id: 'u2' })
|
||||
expect(result.errors[0]).toHaveProperty('message', 'Not Authorised!')
|
||||
})
|
||||
|
||||
@ -330,59 +337,69 @@ describe('unblock', () => {
|
||||
authenticatedUser = await currentUser.toJson()
|
||||
})
|
||||
|
||||
describe('unblock yourself', () => {
|
||||
describe('unmute yourself', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(unblockAction({ id: 'u1' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { unblock: null } }),
|
||||
await expect(unmuteAction({ id: 'u1' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { unmuteUser: null } }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblock not-existing user', () => {
|
||||
describe('unmute not-existing user', () => {
|
||||
it('returns null', async () => {
|
||||
await expect(unblockAction({ id: 'lksjdflksfdj' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { unblock: null } }),
|
||||
await expect(unmuteAction({ id: 'lksjdflksfdj' })).resolves.toEqual(
|
||||
expect.objectContaining({ data: { unmuteUser: null } }),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('given another user', () => {
|
||||
beforeEach(async () => {
|
||||
blockedUser = await neode.create('User', {
|
||||
name: 'Blocked User',
|
||||
mutedUser = await neode.create('User', {
|
||||
name: 'Muted User',
|
||||
id: 'u2',
|
||||
})
|
||||
})
|
||||
|
||||
describe('unblocking a not yet blocked user', () => {
|
||||
describe('unmuting a not yet muted user', () => {
|
||||
it('does not hurt', async () => {
|
||||
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
|
||||
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
|
||||
data: {
|
||||
unmuteUser: { id: 'u2', name: 'Muted User', isMuted: false },
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('given a blocked user', () => {
|
||||
describe('given a muted user', () => {
|
||||
beforeEach(async () => {
|
||||
await currentUser.relateTo(blockedUser, 'blocked')
|
||||
await currentUser.relateTo(mutedUser, 'muted')
|
||||
})
|
||||
|
||||
it('unblocks a user', async () => {
|
||||
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
|
||||
it('unmutes a user', async () => {
|
||||
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
|
||||
data: {
|
||||
unmuteUser: { id: 'u2', name: 'Muted User', isMuted: false },
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
describe('unblocking twice', () => {
|
||||
describe('unmuting twice', () => {
|
||||
it('has no effect', async () => {
|
||||
await unblockAction({ id: 'u2' })
|
||||
await expect(unblockAction({ id: 'u2' })).resolves.toEqual(
|
||||
await unmuteAction({ id: 'u2' })
|
||||
await expect(unmuteAction({ id: 'u2' })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: { unblock: { id: 'u2', name: 'Blocked User', isBlocked: false } },
|
||||
data: {
|
||||
unmuteUser: {
|
||||
id: 'u2',
|
||||
name: 'Muted User',
|
||||
isMuted: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
@ -1,30 +1,5 @@
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
import { mergeTypes } from 'merge-graphql-schemas'
|
||||
|
||||
const findGqlFiles = dir => {
|
||||
var results = []
|
||||
var list = fs.readdirSync(dir)
|
||||
list.forEach(file => {
|
||||
file = path.join(dir, file).toString('utf-8')
|
||||
var stat = fs.statSync(file)
|
||||
if (stat && stat.isDirectory()) {
|
||||
// Recurse into a subdirectory
|
||||
results = results.concat(findGqlFiles(file))
|
||||
} else {
|
||||
if (path.extname(file) === '.gql') {
|
||||
// Is a gql file
|
||||
results.push(file)
|
||||
}
|
||||
}
|
||||
})
|
||||
return results
|
||||
}
|
||||
|
||||
const typeDefs = []
|
||||
|
||||
findGqlFiles(__dirname).forEach(file => {
|
||||
typeDefs.push(fs.readFileSync(file).toString('utf-8'))
|
||||
})
|
||||
import { mergeTypes, fileLoader } from 'merge-graphql-schemas'
|
||||
|
||||
const typeDefs = fileLoader(path.join(__dirname, './**/*.gql'))
|
||||
export default mergeTypes(typeDefs, { all: true })
|
||||
|
||||
@ -68,10 +68,18 @@ type User {
|
||||
RETURN COUNT(u) >= 1
|
||||
"""
|
||||
)
|
||||
isBlocked: Boolean! @cypher(
|
||||
|
||||
blocked: Boolean! @cypher(
|
||||
statement: """
|
||||
MATCH (this)<-[: BLOCKED]-(u: User { id: $cypherParams.currentUserId})
|
||||
RETURN COUNT(u) >= 1
|
||||
MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
|
||||
RETURN COUNT(user) >= 1
|
||||
"""
|
||||
)
|
||||
|
||||
isMuted: Boolean! @cypher(
|
||||
statement: """
|
||||
MATCH (this)<-[:MUTED]-(user:User { id: $cypherParams.currentUserId})
|
||||
RETURN COUNT(user) >= 1
|
||||
"""
|
||||
)
|
||||
|
||||
@ -160,6 +168,7 @@ type Query {
|
||||
filter: _UserFilter
|
||||
): [User]
|
||||
|
||||
mutedUsers: [User]
|
||||
blockedUsers: [User]
|
||||
isLoggedIn: Boolean!
|
||||
currentUser: User
|
||||
@ -197,7 +206,8 @@ type Mutation {
|
||||
|
||||
DeleteUser(id: ID!, resource: [Deletable]): User
|
||||
|
||||
|
||||
block(id: ID!): User
|
||||
unblock(id: ID!): User
|
||||
muteUser(id: ID!): User
|
||||
unmuteUser(id: ID!): User
|
||||
blockUser(id: ID!): User
|
||||
unblockUser(id: ID!): User
|
||||
}
|
||||
|
||||
@ -1,134 +0,0 @@
|
||||
const _ = require('lodash')
|
||||
const faker = require('faker')
|
||||
const unsplashTopics = [
|
||||
'love',
|
||||
'family',
|
||||
'spring',
|
||||
'business',
|
||||
'nature',
|
||||
'travel',
|
||||
'happy',
|
||||
'landscape',
|
||||
'health',
|
||||
'friends',
|
||||
'computer',
|
||||
'autumn',
|
||||
'space',
|
||||
'animal',
|
||||
'smile',
|
||||
'face',
|
||||
'people',
|
||||
'portrait',
|
||||
'amazing',
|
||||
]
|
||||
let unsplashTopicsTmp = []
|
||||
|
||||
const ngoLogos = [
|
||||
'http://www.fetchlogos.com/wp-content/uploads/2015/11/Girl-Scouts-Of-The-Usa-Logo.jpg',
|
||||
'http://logos.textgiraffe.com/logos/logo-name/Ngo-designstyle-friday-m.png',
|
||||
'http://seeklogo.com/images/N/ngo-logo-BD53A3E024-seeklogo.com.png',
|
||||
'https://dcassetcdn.com/design_img/10133/25833/25833_303600_10133_image.jpg',
|
||||
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/20.jpg',
|
||||
'https://cdn.tutsplus.com/vector/uploads/legacy/articles/08bad_ngologos/33.jpg',
|
||||
null,
|
||||
]
|
||||
|
||||
const difficulties = ['easy', 'medium', 'hard']
|
||||
|
||||
export default {
|
||||
randomItem: (items, filter) => {
|
||||
const ids = filter
|
||||
? Object.keys(items).filter(id => {
|
||||
return filter(items[id])
|
||||
})
|
||||
: _.keys(items)
|
||||
const randomIds = _.shuffle(ids)
|
||||
return items[randomIds.pop()]
|
||||
},
|
||||
randomItems: (items, key = 'id', min = 1, max = 1) => {
|
||||
const randomIds = _.shuffle(_.keys(items))
|
||||
const res = []
|
||||
|
||||
const count = _.random(min, max)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
let r = items[randomIds.pop()][key]
|
||||
if (key === 'id') {
|
||||
r = r.toString()
|
||||
}
|
||||
res.push(r)
|
||||
}
|
||||
return res
|
||||
},
|
||||
random: items => {
|
||||
return _.shuffle(items).pop()
|
||||
},
|
||||
randomDifficulty: () => {
|
||||
return _.shuffle(difficulties).pop()
|
||||
},
|
||||
randomLogo: () => {
|
||||
return _.shuffle(ngoLogos).pop()
|
||||
},
|
||||
randomUnsplashUrl: () => {
|
||||
if (Math.random() < 0.6) {
|
||||
// do not attach images in 60 percent of the cases (faster seeding)
|
||||
return
|
||||
}
|
||||
if (unsplashTopicsTmp.length < 2) {
|
||||
unsplashTopicsTmp = _.shuffle(unsplashTopics)
|
||||
}
|
||||
return (
|
||||
'https://source.unsplash.com/daily?' + unsplashTopicsTmp.pop() + ',' + unsplashTopicsTmp.pop()
|
||||
)
|
||||
},
|
||||
randomCategories: (seederstore, allowEmpty = false) => {
|
||||
let count = Math.round(Math.random() * 3)
|
||||
if (allowEmpty === false && count === 0) {
|
||||
count = 1
|
||||
}
|
||||
const categorieIds = _.shuffle(_.keys(seederstore.categories))
|
||||
const ids = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
ids.push(categorieIds.pop())
|
||||
}
|
||||
return ids
|
||||
},
|
||||
randomAddresses: () => {
|
||||
const count = Math.round(Math.random() * 3)
|
||||
const addresses = []
|
||||
for (let i = 0; i < count; i++) {
|
||||
addresses.push({
|
||||
city: faker.address.city(),
|
||||
zipCode: faker.address.zipCode(),
|
||||
street: faker.address.streetAddress(),
|
||||
country: faker.address.countryCode(),
|
||||
lat: 54.032726 - Math.random() * 10,
|
||||
lng: 6.558838 + Math.random() * 10,
|
||||
})
|
||||
}
|
||||
return addresses
|
||||
},
|
||||
/**
|
||||
* Get array of ids from the given seederstore items after mapping them by the key in the values
|
||||
*
|
||||
* @param items items from the seederstore
|
||||
* @param values values for which you need the ids
|
||||
* @param key the field key that is represented in the values (slug, name, etc.)
|
||||
*/
|
||||
mapIdsByKey: (items, values, key) => {
|
||||
const res = []
|
||||
values.forEach(value => {
|
||||
res.push(_.find(items, [key, value]).id.toString())
|
||||
})
|
||||
return res
|
||||
},
|
||||
genInviteCode: () => {
|
||||
const chars = '23456789abcdefghkmnpqrstuvwxyzABCDEFGHJKLMNPRSTUVWXYZ'
|
||||
let code = ''
|
||||
for (let i = 0; i < 8; i++) {
|
||||
const n = _.random(0, chars.length - 1)
|
||||
code += chars.substr(n, 1)
|
||||
}
|
||||
return code
|
||||
},
|
||||
}
|
||||
@ -1,21 +1,13 @@
|
||||
import express from 'express'
|
||||
import helmet from 'helmet'
|
||||
import { ApolloServer } from 'apollo-server-express'
|
||||
import CONFIG, { requiredConfigs } from './config'
|
||||
import CONFIG from './config'
|
||||
import middleware from './middleware'
|
||||
import { getNeode, getDriver } from './bootstrap/neo4j'
|
||||
import { getNeode, getDriver } from './db/neo4j'
|
||||
import decode from './jwt/decode'
|
||||
import schema from './schema'
|
||||
import webfinger from './activitypub/routes/webfinger'
|
||||
|
||||
// check required configs and throw error
|
||||
// TODO check this directly in config file - currently not possible due to testsetup
|
||||
Object.entries(requiredConfigs).map(entry => {
|
||||
if (!entry[1]) {
|
||||
throw new Error(`ERROR: "${entry[0]}" env variable is missing.`)
|
||||
}
|
||||
})
|
||||
|
||||
const driver = getDriver()
|
||||
const neode = getNeode()
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { Given, When, Then, AfterAll } from 'cucumber'
|
||||
import { expect } from 'chai'
|
||||
// import { client } from '../../../src/activitypub/apollo-client'
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../../src/seed/factories'
|
||||
import Factory from '../../../src/factories'
|
||||
const debug = require('debug')('ea:test:steps')
|
||||
|
||||
const factory = Factory()
|
||||
|
||||
2395
backend/yarn.lock
2395
backend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -249,10 +249,12 @@ Shows automatically related actions for existing post.
|
||||
|
||||
### Administration
|
||||
|
||||
[Cucumber Features](https://github.com/Human-Connection/Human-Connection/tree/master/cypress/integration/administration)
|
||||
|
||||
* Provide Admin-Interface to send Users Invite Code
|
||||
* Static Pages for Data Privacy Statement ...
|
||||
* Create, edit and delete Announcements
|
||||
* Show Announcements on top of User Interface
|
||||
* Pin a post to inform users
|
||||
|
||||
### Invitation
|
||||
|
||||
|
||||
36
cypress/integration/administration/PinPost.feature
Normal file
36
cypress/integration/administration/PinPost.feature
Normal file
@ -0,0 +1,36 @@
|
||||
Feature: Pin a post
|
||||
As an admin
|
||||
I want to pin a post so that it always appears at the top
|
||||
In order to make sure all network users read it - e.g. notify people about security incidents, maintenance downtimes
|
||||
|
||||
|
||||
Background:
|
||||
Given we have the following posts in our database:
|
||||
| id | title | pinned | createdAt |
|
||||
| p1 | Some other post | | 2020-01-21 |
|
||||
| p2 | Houston we have a problem | x | 2020-01-20 |
|
||||
| p3 | Yet another post | | 2020-01-19 |
|
||||
|
||||
Scenario: Pinned post always appears on the top of the newsfeed
|
||||
Given I am logged in with a "user" role
|
||||
Then the first post on the landing page has the title:
|
||||
"""
|
||||
Houston we have a problem
|
||||
"""
|
||||
And the post with title "Houston we have a problem" has a ribbon for pinned posts
|
||||
|
||||
Scenario: Ordinary users cannot pin a post
|
||||
Given I am logged in with a "user" role
|
||||
When I open the content menu of post "Yet another post"
|
||||
Then there is no button to pin a post
|
||||
|
||||
Scenario: Admins are allowed to pin a post
|
||||
Given I am logged in with a "admin" role
|
||||
And I open the content menu of post "Yet another post"
|
||||
When I click on 'Pin post'
|
||||
Then I see a toaster with "Post pinned successfully"
|
||||
And the first post on the landing page has the title:
|
||||
"""
|
||||
Yet another post
|
||||
"""
|
||||
And the post with title "Yet another post" has a ribbon for pinned posts
|
||||
@ -17,17 +17,22 @@ Then("I click on the {string} button", text => {
|
||||
.click();
|
||||
});
|
||||
|
||||
Then("I click on the reply button", () => {
|
||||
cy.get(".reply-button")
|
||||
.click();
|
||||
});
|
||||
|
||||
Then("my comment should be successfully created", () => {
|
||||
cy.get(".iziToast-message").contains("Comment Submitted");
|
||||
cy.get(".iziToast-message").contains("Comment submitted!");
|
||||
});
|
||||
|
||||
Then("I should see my comment", () => {
|
||||
cy.get("div.comment p")
|
||||
.should("contain", "Human Connection rocks")
|
||||
.get(".ds-avatar img")
|
||||
.get(".user-avatar img")
|
||||
.should("have.attr", "src")
|
||||
.and("contain", narratorAvatar)
|
||||
.get("div p.ds-text span")
|
||||
.get(".user-teaser > .info > .text")
|
||||
.should("contain", "today at");
|
||||
});
|
||||
|
||||
@ -44,3 +49,37 @@ Then("I should see an abreviated version of my comment", () => {
|
||||
Then("the editor should be cleared", () => {
|
||||
cy.get(".ProseMirror p").should("have.class", "is-empty");
|
||||
});
|
||||
|
||||
Then("it should create a mention in the CommentForm", () => {
|
||||
cy.get(".ProseMirror a")
|
||||
.should('have.class', 'mention')
|
||||
.should('contain', '@peter-pan')
|
||||
})
|
||||
|
||||
When("I open the content menu of post {string}", (title)=> {
|
||||
cy.contains('.post-card', title)
|
||||
.find('.content-menu .base-button')
|
||||
.click()
|
||||
})
|
||||
|
||||
When("I click on 'Pin post'", (string)=> {
|
||||
cy.get("a.ds-menu-item-link").contains("Pin post")
|
||||
.click()
|
||||
})
|
||||
|
||||
Then("there is no button to pin a post", () => {
|
||||
cy.get("a.ds-menu-item-link")
|
||||
.should('contain', "Report Post") // sanity check
|
||||
.should('not.contain', "Pin post")
|
||||
})
|
||||
|
||||
And("the post with title {string} has a ribbon for pinned posts", (title) => {
|
||||
cy.get("article.post-card").contains(title)
|
||||
.parent()
|
||||
.find("div.ribbon.ribbon--pinned")
|
||||
.should("contain", "Announcement")
|
||||
})
|
||||
|
||||
Then("I see a toaster with {string}", (title) => {
|
||||
cy.get(".iziToast-message").should("contain", title);
|
||||
})
|
||||
|
||||
@ -32,5 +32,5 @@ Then("I cannot upload a picture", () => {
|
||||
cy.get(".ds-card-content")
|
||||
.children()
|
||||
.should("not.have.id", "customdropzone")
|
||||
.should("have.class", "ds-avatar");
|
||||
.should("have.class", "user-avatar");
|
||||
});
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user