Merge branch 'master' into migrate-styleguide-card

This commit is contained in:
Alina Beck 2020-02-03 19:23:22 +01:00
commit d2148bdd47
132 changed files with 4724 additions and 3559 deletions

View File

@ -18,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

View File

@ -4,10 +4,90 @@ 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.2.2](https://github.com/Human-Connection/Human-Connection/compare/v0.2.1...v0.2.2)
#### [v0.3.0](https://github.com/Human-Connection/Human-Connection/compare/v0.2.1...v0.3.0)
> 20 January 2020
> 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)
@ -97,12 +177,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- 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)
- build(deps-dev): bump @storybook/addon-actions in /webapp [`d0124bf`](https://github.com/Human-Connection/Human-Connection/commit/d0124bf2b4b4a641c9af76d6d2f7b5aa075ade90)
- refactor and use base-button in SearchableInput [`fcbe612`](https://github.com/Human-Connection/Human-Connection/commit/fcbe6125f35c0dd23e2ba1ae63f539f5ef5990ea)
- Update `vue-test-utils` and follow updated docs [`8c29ad9`](https://github.com/Human-Connection/Human-Connection/commit/8c29ad947b72fbaa173d070221cdf35b7ab6aaa5)
- 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)
@ -207,16 +288,16 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- build(deps): bump metascraper-publisher from 5.8.7 to 5.8.12 in /backend [`#2592`](https://github.com/Human-Connection/Human-Connection/pull/2592)
- build(deps-dev): bump @babel/preset-env from 7.7.6 to 7.7.7 in /backend [`#2568`](https://github.com/Human-Connection/Human-Connection/pull/2568)
- 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)
- manage button states and color schemes with mixin [`1b9249c`](https://github.com/Human-Connection/Human-Connection/commit/1b9249c685e34eb2e94b31ee0ec22421c6aa6a73)
- Specs for Searches [`bc3aa51`](https://github.com/Human-Connection/Human-Connection/commit/bc3aa519d0e7a6e0242ecd37d611fd1a3df385d0)
#### [v0.2.0](https://github.com/Human-Connection/Human-Connection/compare/v0.1.13...v0.2.0)
> 19 December 2019
- Update to version 0.2.0 [`#2584`](https://github.com/Human-Connection/Human-Connection/pull/2584)
- build(deps): bump metascraper-image from 5.8.10 to 5.8.12 in /backend [`#2556`](https://github.com/Human-Connection/Human-Connection/pull/2556)
- build(deps-dev): bump @babel/core from 7.7.5 to 7.7.7 [`#2569`](https://github.com/Human-Connection/Human-Connection/pull/2569)
- build(deps-dev): bump @babel/cli from 7.7.5 to 7.7.7 in /backend [`#2576`](https://github.com/Human-Connection/Human-Connection/pull/2576)
@ -303,7 +384,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Fix search by adding result id [`ebc5cf3`](https://github.com/Human-Connection/Human-Connection/commit/ebc5cf392d92acf3a9e22c8967d02ea2cf6fd7fb)
- Write test/refactor tests/resolvers/middleware [`d375ebe`](https://github.com/Human-Connection/Human-Connection/commit/d375ebe7d90e3251b17f59ffba8fb1470923ebe8)
#### [v0.1.12](https://github.com/Human-Connection/Human-Connection/compare/v0.1.11...v0.1.12)
#### [v0.1.12](https://github.com/Human-Connection/Human-Connection/compare/v0.1.10...v0.1.12)
> 10 December 2019
@ -413,16 +494,6 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- 2329 normalize emails in login form [`#2330`](https://github.com/Human-Connection/Human-Connection/pull/2330)
- Lokalise: Translations update [`#2327`](https://github.com/Human-Connection/Human-Connection/pull/2327)
- Changed translation must change test :( [`#2310`](https://github.com/Human-Connection/Human-Connection/pull/2310)
- Merge pull request #2443 from Human-Connection/2237-longer-comments [`#2237`](https://github.com/Human-Connection/Human-Connection/issues/2237)
- fix #2329: Normalize email on login in the backend [`#2329`](https://github.com/Human-Connection/Human-Connection/issues/2329)
- Lokalise: update of webapp/locales/ru.json [`3e52ee0`](https://github.com/Human-Connection/Human-Connection/commit/3e52ee090c88c357b796895370d126f8bb5529f0)
- Lokalise: update of webapp/locales/de.json [`d2b3396`](https://github.com/Human-Connection/Human-Connection/commit/d2b3396e9b44bac0e767ee970e083d1847426b26)
- Lokalise: update of webapp/locales/pt.json [`bcd9f0e`](https://github.com/Human-Connection/Human-Connection/commit/bcd9f0ec93cfab2661589d72a3b3f38455ec4d51)
#### [v0.1.11](https://github.com/Human-Connection/Human-Connection/compare/v0.1.10...v0.1.11)
> 22 November 2019
- build(deps-dev): bump apollo-server-testing from 2.9.9 to 2.9.12 in /backend [`#2318`](https://github.com/Human-Connection/Human-Connection/pull/2318)
- build(deps-dev): bump fuse.js from 3.4.5 to 3.4.6 in /webapp [`#2314`](https://github.com/Human-Connection/Human-Connection/pull/2314)
- build(deps-dev): bump eslint-config-prettier from 6.6.0 to 6.7.0 in /webapp [`#2302`](https://github.com/Human-Connection/Human-Connection/pull/2302)
@ -472,11 +543,13 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- build(deps-dev): bump eslint from 5.16.0 to 6.6.0 in /webapp [`#2205`](https://github.com/Human-Connection/Human-Connection/pull/2205)
- Add locale to undefined to null [`#2233`](https://github.com/Human-Connection/Human-Connection/pull/2233)
- Update to version 0.1.10 [`#2231`](https://github.com/Human-Connection/Human-Connection/pull/2231)
- Merge pull request #2443 from Human-Connection/2237-longer-comments [`#2237`](https://github.com/Human-Connection/Human-Connection/issues/2237)
- fix #2329: Normalize email on login in the backend [`#2329`](https://github.com/Human-Connection/Human-Connection/issues/2329)
- Fix #2294 [`#2294`](https://github.com/Human-Connection/Human-Connection/issues/2294)
- Merge pull request #2078 from Human-Connection/fix-2042-back-link [`#2042`](https://github.com/Human-Connection/Human-Connection/issues/2042)
- Tell github-linguists to ignore snapshots [`978347b`](https://github.com/Human-Connection/Human-Connection/commit/978347ba7b5a6aa1bc915ada972ffffa2816d37c)
- Lokalise: update of webapp/locales/ru.json [`906e851`](https://github.com/Human-Connection/Human-Connection/commit/906e8518bf060134150187fb1574ac50ffd502f6)
- set up global localVue [`77f4810`](https://github.com/Human-Connection/Human-Connection/commit/77f4810ddc963386bc68d3e8a5e078ef4cf270b2)
- Move components to components/features [`2357028`](https://github.com/Human-Connection/Human-Connection/commit/235702867d97b44dac37f8059f9194e23ba7f47d)
- Basic Search Is Working For Users And Posts [`72e4d0a`](https://github.com/Human-Connection/Human-Connection/commit/72e4d0abbcb9abab07f3fd12876453eb1de5da4c)
- Add missing unit tests/refactor code [`b364065`](https://github.com/Human-Connection/Human-Connection/commit/b3640659bb608cc34edc6f2aca350f07dd2b9ce6)
#### [v0.1.10](https://github.com/Human-Connection/Human-Connection/compare/v0.1.9...v0.1.10)
@ -536,8 +609,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Update feature template [`#2116`](https://github.com/Human-Connection/Human-Connection/pull/2116)
- Update to version 0.1.9 [`#2114`](https://github.com/Human-Connection/Human-Connection/pull/2114)
- remove package-lock.json [`3cf3c31`](https://github.com/Human-Connection/Human-Connection/commit/3cf3c31808dc6ae59fb9c6ec33e9e178c5556438)
- add current file [`26c0d4d`](https://github.com/Human-Connection/Human-Connection/commit/26c0d4d83e4418a2378e05b66b6b47461f82735f)
- Finish portuguese translations [`15c671c`](https://github.com/Human-Connection/Human-Connection/commit/15c671c4a8aae86317896ca30601389504bce9e1)
- Extract AvatarMenu into its own component [`994a0b0`](https://github.com/Human-Connection/Human-Connection/commit/994a0b049d1803784d9c06383872f1c9e33095a0)
- Add notifications page with Notifications in table [`7cdc12f`](https://github.com/Human-Connection/Human-Connection/commit/7cdc12f4b9943062e15a874dd39f8a50142b6c61)
#### [v0.1.9](https://github.com/Human-Connection/Human-Connection/compare/v0.1.8...v0.1.9)
@ -607,7 +680,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [v0.1.8](https://github.com/Human-Connection/Human-Connection/compare/v0.1.7...v0.1.8)
> 24 October 2019
> 25 October 2019
- add FAQ _blank-href in Footer [`#2028`](https://github.com/Human-Connection/Human-Connection/pull/2028)
- fix: Don't attempt to save locale if not authenticated [`#2025`](https://github.com/Human-Connection/Human-Connection/pull/2025)
@ -623,9 +696,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- build(deps-dev): bump @storybook/addon-a11y from 5.2.4 to 5.2.5 in /webapp [`#1989`](https://github.com/Human-Connection/Human-Connection/pull/1989)
- build(deps-dev): bump @vue/cli-shared-utils from 4.0.4 to 4.0.5 in /webapp [`#2002`](https://github.com/Human-Connection/Human-Connection/pull/2002)
- Update to version 0.1.7 [`#2015`](https://github.com/Human-Connection/Human-Connection/pull/2015)
- Update to version 0.1.8 [`d45264b`](https://github.com/Human-Connection/Human-Connection/commit/d45264b3afa1557c2205e7ca1b77c778ee37ab5a)
- 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)
- build(deps-dev): bump @storybook/addon-actions in /webapp [`7e95d37`](https://github.com/Human-Connection/Human-Connection/commit/7e95d376a311a5ede6351d577d30e25aea9cb65d)
#### [v0.1.7](https://github.com/Human-Connection/Human-Connection/compare/v0.1.6...v0.1.7)
@ -2064,5 +2137,5 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- Merge pull request #93 from Gerald1614/500_error_on_login [`#49`](https://github.com/Human-Connection/Human-Connection/issues/49)
- Update schema.graphql [`#7`](https://github.com/Human-Connection/Human-Connection/issues/7)
- Refactore the import and hashtags to all unicode characters [`0bc4c55`](https://github.com/Human-Connection/Human-Connection/commit/0bc4c558ae8f01d6d975b8ee1ea7f0f42b056d91)
- Remove package-lock.json [`25bd96e`](https://github.com/Human-Connection/Human-Connection/commit/25bd96eedf6be5b7ea6e94c8433d044e13d62e70)
- Remove Styleguide [`53ea934`](https://github.com/Human-Connection/Human-Connection/commit/53ea93492dcc7f861743cd50a4ddf7728c9d659b)
- Change strategy, only build docker image [`d6b7374`](https://github.com/Human-Connection/Human-Connection/commit/d6b7374ddbf497bdb5cbc935b88ae085c38b3237)
- Copy package.json from webapp/ [`f3a9996`](https://github.com/Human-Connection/Human-Connection/commit/f3a9996962e5dd8b2e365a032c1a5766fe666103)

View File

@ -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.

View File

@ -52,6 +52,10 @@ Check out the [contribution guideline](./CONTRIBUTING.md), too!
[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/0)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/0)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/1)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/1)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/2)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/2)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/3)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/3)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/4)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/4)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/5)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/5)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/6)](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/links/6)[![](https://sourcerer.io/fame/roschaefer/Human-Connection/Human-Connection/images/7)](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

View File

@ -53,6 +53,27 @@ can issue GraphQL requests or access GraphQL Playground in the browser.
![GraphQL Playground](../.gitbook/assets/graphql-playground.png)
### 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

View File

@ -4,14 +4,19 @@
"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/ -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",
@ -33,7 +38,7 @@
},
"dependencies": {
"@hapi/joi": "^17.1.0",
"@sentry/node": "^5.11.1",
"@sentry/node": "^5.11.2",
"apollo-cache-inmemory": "~1.6.5",
"apollo-client": "~2.6.8",
"apollo-link-context": "~1.0.19",
@ -44,40 +49,41 @@
"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.5",
"metascraper-audio": "^5.10.5",
"metascraper-author": "^5.10.5",
"metascraper": "^5.10.6",
"metascraper-audio": "^5.10.6",
"metascraper-author": "^5.10.6",
"metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.10.5",
"metascraper-description": "^5.10.5",
"metascraper-image": "^5.10.5",
"metascraper-lang": "^5.10.5",
"metascraper-date": "^5.10.6",
"metascraper-description": "^5.10.6",
"metascraper-image": "^5.10.6",
"metascraper-lang": "^5.10.6",
"metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.10.5",
"metascraper-publisher": "^5.10.5",
"metascraper-soundcloud": "^5.10.5",
"metascraper-title": "^5.10.5",
"metascraper-url": "^5.10.5",
"metascraper-video": "^5.10.5",
"metascraper-youtube": "^5.10.5",
"metascraper-logo": "^5.10.6",
"metascraper-publisher": "^5.10.6",
"metascraper-soundcloud": "^5.10.6",
"metascraper-title": "^5.10.6",
"metascraper-url": "^5.10.6",
"metascraper-video": "^5.10.6",
"metascraper-youtube": "^5.10.6",
"migrate": "^1.6.2",
"minimatch": "^3.0.4",
"mustache": "^4.0.0",
"neo4j-driver": "^4.0.1",
@ -89,28 +95,28 @@
"npm-run-all": "~4.1.5",
"request": "~2.88.0",
"sanitize-html": "~1.21.1",
"slug": "~2.1.0",
"slug": "~2.1.1",
"trunc-html": "~1.1.2",
"uuid": "~3.4.0",
"validator": "^12.1.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-jest": "~23.6.0",
@ -118,7 +124,7 @@
"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"

View File

@ -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

View File

@ -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!`)

View 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

View 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()
}
}

View File

@ -0,0 +1,84 @@
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}), (oldUser)-[previousRelationship]-(oldEmail)
MATCH (user:User)-[:PRIMARY_EMAIL]->(email:EmailAddress {email: $normalizedEmail})
DELETE previousRelationship
WITH oldUser, oldEmail, user, email
CALL apoc.refactor.mergeNodes([user, oldUser], { properties: 'discard', mergeRels: true }) YIELD node as mergedUser
CALL apoc.refactor.mergeNodes([email, oldEmail], { properties: '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'))
}

View File

@ -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'))
}

View File

@ -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()
}
}

View File

@ -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']

View File

@ -1,4 +1,4 @@
import { getDriver, getNeode } from '../../bootstrap/neo4j'
import { getDriver, getNeode } from '../db/neo4j'
const factories = {
Badge: require('./badges.js').default,

View File

@ -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
@ -50,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

View File

@ -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() {

View File

@ -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('')
}

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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
@ -102,6 +102,7 @@ export default shield(
PostsEmotionsCountByEmotion: allow,
PostsEmotionsByCurrentUser: isAuthenticated,
mutedUsers: isAuthenticated,
blockedUsers: isAuthenticated,
notifications: isAuthenticated,
Donations: isAuthenticated,
},
@ -139,6 +140,8 @@ export default shield(
RemovePostEmotions: isAuthenticated,
muteUser: isAuthenticated,
unmuteUser: isAuthenticated,
blockUser: isAuthenticated,
unblockUser: isAuthenticated,
markAsRead: isAuthenticated,
AddEmailAddress: isAuthenticated,
VerifyEmailAddress: isAuthenticated,

View File

@ -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()

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -20,6 +20,7 @@ function clean(dirty) {
'hr',
'b',
'i',
'u',
'em',
'strong',
'a',

View File

@ -3,7 +3,7 @@ import uuid from 'uuid/v4'
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: {

View 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() },
}

View File

@ -11,7 +11,7 @@ export default {
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 @@ export default {
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 },
}

View File

@ -1,5 +1,5 @@
export default {
email: { type: 'string', primary: true, lowercase: true, email: true },
email: { type: 'string', lowercase: true, email: true },
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
nonce: { type: 'string', token: true },
belongsTo: {

View File

@ -4,7 +4,7 @@ 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,12 +77,18 @@ export default {
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',

View File

@ -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()

View File

@ -13,4 +13,5 @@ export default {
Location: require('./Location.js').default,
Donations: require('./Donations.js').default,
Report: require('./Report.js').default,
Migration: require('./Migration.js').default,
}

View File

@ -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()

View File

@ -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

View File

@ -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'

View File

@ -1,4 +1,4 @@
import { getNeode } from '../../bootstrap/neo4j'
import { getNeode } from '../../db/neo4j'
const neode = getNeode()

View File

@ -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'

View File

@ -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'

View File

@ -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()

View File

@ -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'

View File

@ -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'

View File

@ -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,
})
})
})
})
})

View File

@ -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'

View File

@ -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'

View File

@ -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()

View File

@ -1,4 +1,4 @@
import { getNeode } from '../../bootstrap/neo4j'
import { getNeode } from '../../db/neo4j'
import { UserInputError } from 'apollo-server'
const neode = getNeode()

View File

@ -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()

View File

@ -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
`

View File

@ -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

View File

@ -1,4 +1,4 @@
import { getNeode } from '../../bootstrap/neo4j'
import { getNeode } from '../../db/neo4j'
import Resolver from './helpers/Resolver'
const neode = getNeode()

View File

@ -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()

View File

@ -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

View File

@ -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'

View File

@ -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()

View File

@ -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'
@ -23,6 +23,21 @@ export const getMutedUsers = async context => {
return mutedUsers
}
export const getBlockedUsers = async context => {
const { neode } = context
const userModel = neode.model('User')
let blockedUsers = neode
.query()
.match('user', userModel)
.where('user.id', context.user.id)
.relationship(userModel.relationships().get('blocked'))
.to('blocked', userModel)
.return('blocked')
blockedUsers = await blockedUsers.execute()
blockedUsers = blockedUsers.records.map(r => r.get('blocked').properties)
return blockedUsers
}
export default {
Query: {
mutedUsers: async (object, args, context, resolveInfo) => {
@ -32,6 +47,13 @@ export default {
throw new UserInputError(e.message)
}
},
blockedUsers: async (object, args, context, resolveInfo) => {
try {
return getBlockedUsers(context)
} catch (e) {
throw new UserInputError(e.message)
}
},
User: async (object, args, context, resolveInfo) => {
const { email } = args
if (email) {
@ -86,7 +108,7 @@ export default {
const unmutedUser = await neode.find('User', params.id)
return unmutedUser.toJson()
},
block: async (object, args, context, resolveInfo) => {
blockUser: async (object, args, context, resolveInfo) => {
const { user: currentUser } = context
if (currentUser.id === args.id) return null
await neode.cypher(
@ -103,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(
@ -229,7 +251,7 @@ 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',

View File

@ -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'

View File

@ -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'

View File

@ -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()

View File

@ -68,10 +68,11 @@ 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
"""
)
@ -207,6 +208,6 @@ type Mutation {
muteUser(id: ID!): User
unmuteUser(id: ID!): User
block(id: ID!): User
unblock(id: ID!): User
blockUser(id: ID!): User
unblockUser(id: ID!): User
}

View File

@ -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
},
}

View File

@ -3,7 +3,7 @@ import helmet from 'helmet'
import { ApolloServer } from 'apollo-server-express'
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'

View File

@ -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()

File diff suppressed because it is too large Load Diff

View File

@ -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

View 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

View File

@ -17,8 +17,13 @@ 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", () => {
@ -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);
})

View File

@ -170,5 +170,4 @@ When("they have a post someone has reported", () => {
authorId: 'annnoying-user',
title,
});
})

View File

@ -11,15 +11,24 @@ Then("I should have one item in the select dropdown", () => {
});
});
Then("the search has no results", () => {
Then("the search should not contain posts by the annoying user", () => {
cy.get(".searchable-input .ds-select-dropdown").should($li => {
expect($li).to.have.length(1);
});
cy.get(".ds-select-dropdown").should("contain", 'Nothing found');
})
cy.get(".ds-select-dropdown")
.should("not.have.class", '.search-post')
.should("not.contain", 'Spam')
});
Then("the search should contain the annoying user", () => {
cy.get(".searchable-input .ds-select-dropdown").should($li => {
expect($li).to.have.length(1);
})
cy.get(".ds-select-dropdown .user-teaser .slug").should("contain", '@spammy-spammer');
cy.get(".searchable-input .ds-select-search")
.focus()
.type("{esc}");
});
})
Then("I should see the following posts in the select dropdown:", table => {
table.hashes().forEach(({ title }) => {

View File

@ -31,6 +31,7 @@ const narratorParams = {
const annoyingParams = {
email: "spammy-spammer@example.org",
slug: 'spammy-spammer',
password: "1234",
...termsAndConditionsAgreedVersion
};
@ -39,8 +40,12 @@ Given("I am logged in", () => {
cy.login(loginCredentials);
});
Given("I am logged in as the muted user", () => {
cy.login({ email: annoyingParams.email, password: '1234' });
Given("the {string} user searches for {string}", (_, postTitle) => {
cy.logout()
.login({ email: annoyingParams.email, password: '1234' })
.get(".searchable-input .ds-select-search")
.focus()
.type(postTitle);
});
Given("we have a selection of categories", () => {
@ -123,6 +128,12 @@ When("I visit the {string} page", page => {
cy.openPage(page);
});
When("a blocked user visits the post page of one of my authored posts", () => {
cy.logout()
.login({ email: annoyingParams.email, password: annoyingParams.password })
.openPage('/post/previously-created-post')
})
Given("I am on the {string} page", page => {
cy.openPage(page);
});
@ -212,6 +223,7 @@ Given("we have the following posts in our database:", table => {
...postAttributes,
deleted: Boolean(postAttributes.deleted),
disabled: Boolean(postAttributes.disabled),
pinned: Boolean(postAttributes.pinned),
categoryIds: ['cat-456']
}
cy.factory().create("Post", postAttributes);
@ -485,7 +497,7 @@ Given("I follow the user {string}", name => {
});
});
Given('"Spammy Spammer" wrote a post {string}', title => {
Given('{string} wrote a post {string}', (_, title) => {
cy.createCategories("cat21")
.factory()
.create("Post", {
@ -500,7 +512,7 @@ Then("the list of posts of this user is empty", () => {
cy.get(".main-container").find(".ds-space.hc-empty");
});
Then("nobody is following the user profile anymore", () => {
Then("I get removed from his follower collection", () => {
cy.get(".ds-card-content").not(".post-link");
cy.get(".main-container").contains(
".ds-card-content",
@ -532,6 +544,20 @@ When("I mute the user {string}", name => {
});
});
When("I block the user {string}", name => {
cy.neode()
.first("User", {
name
})
.then(blockedUser => {
cy.neode()
.first("User", {
name: narratorParams.name
})
.relateTo(blockedUser, "blocked");
});
});
When("I log in with:", table => {
const [firstRow] = table.hashes();
const {
@ -550,3 +576,11 @@ Then("I see only one post with the title {string}", title => {
.should("have.length", 1);
cy.get(".main-container").contains(".post-link", title);
});
Then("they should not see the comment from", () => {
cy.get(".ds-card-footer").children().should('not.have.class', 'comment-form')
})
Then("they should see a text explaining commenting is not possible", () => {
cy.get('.ds-placeholder').should('contain', "Commenting is not possible at this time on this post.")
})

View File

@ -9,10 +9,10 @@ Feature: Report and Moderate
Background:
Given we have the following user accounts:
| id | name |
| u67 | David Irving |
| id | name |
| u67 | David Irving |
| annoying-user | I'm gonna mute Moderators and Admins HA HA HA |
Given we have the following posts in our database:
| authorId | id | title | content |
| u67 | p1 | The Truth about the Holocaust | It never existed! |

View File

@ -4,10 +4,10 @@ Feature: Post Comment
To be able to express my thoughts and emotions about these, discuss, and add give further information.
Background:
Given we have the following posts in our database:
| id | title | slug |
| bWBjpkTKZp | 101 Essays that will change the way you think | 101-essays |
And I have a user account
Given I have a user account
And we have the following posts in our database:
| id | title | slug | authorId | commentContent |
| bWBjpkTKZp | 101 Essays that will change the way you think | 101-essays | id-of-peter-pan | @peter-pan reply to me |
And I am logged in
Scenario: Comment creation
@ -36,3 +36,8 @@ Feature: Post Comment
Then my comment should be successfully created
And I should see an abreviated version of my comment
And the editor should be cleared
Scenario: Direct reply to Comment
Given I visit "post/bWBjpkTKZp/101-essays"
And I click on the reply button
Then it should create a mention in the CommentForm

View File

@ -0,0 +1,46 @@
Feature: Block a User
As a user
I'd like to have a button to block another user
To prevent him from seeing and interacting with my contributions
Background:
Given I have a user account
And there is an annoying user called "Harassing User"
And I am logged in
Scenario: Block a user
Given I am on the profile page of the annoying user
When I click on "Block user" from the content menu in the user info box
And I navigate to my "Blocked users" settings page
Then I can see the following table:
| Avatar | Name |
| | Harassing User |
Scenario: Blocked user cannot interact with my contributions
Given I block the user "Harassing User"
And I previously created a post
And a blocked user visits the post page of one of my authored posts
Then they should not see the comment from
And they should see a text explaining commenting is not possible
Scenario: Block a previously followed user
Given I follow the user "Harassing User"
When I visit the profile page of the annoying user
And I click on "Block user" from the content menu in the user info box
And I get removed from his follower collection
Scenario: Posts of blocked users are not filtered from search results
Given "Harassing User" wrote a post "You can still see my posts"
And I block the user "Harassing User"
When I search for "see"
Then I should see the following posts in the select dropdown:
| title |
| You can still see my posts |
Scenario: Blocked users can still see my posts
Given I previously created a post
And I block the user "Harassing User"
And the "blocked" user searches for "previously created"
Then I should see the following posts in the select dropdown:
| title |
| previously created post |

View File

@ -1,8 +1,7 @@
Feature: Mute a User
As a user
I'd like to have a button to mute another user
To prevent him from seeing and interacting with my contributions and also to avoid seeing his/her posts
To prevent him from seeing and interacting with my contributions
Background:
Given I have a user account
And there is an annoying user called "Spammy Spammer"
@ -22,9 +21,9 @@ Feature: Mute a User
When I visit the profile page of the annoying user
And I click on "Mute user" from the content menu in the user info box
Then the list of posts of this user is empty
And nobody is following the user profile anymore
And I get removed from his follower collection
Scenario: Posts of muted users are filtered from search results
Scenario: Posts of muted users are filtered from search results, users are not
Given we have the following posts in our database:
| id | title | content |
| im-not-muted | Post that should be seen | cause I'm not muted |
@ -36,18 +35,17 @@ Feature: Mute a User
When I mute the user "Spammy Spammer"
And I refresh the page
And I search for "Spam"
Then the search has no results
Then the search should not contain posts by the annoying user
But the search should contain the annoying user
But I search for "not muted"
Then I should see the following posts in the select dropdown:
| title |
| Post that should be seen |
Scenario: Muted users can still see my posts
Given I previously created a post
And I mute the user "Spammy Spammer"
Given I log out
And I am logged in as the muted user
When I search for "previously created"
And the "muted" user searches for "previously created"
Then I should see the following posts in the select dropdown:
| title |
| previously created post |

View File

@ -15,7 +15,6 @@
/* globals Cypress cy */
import "cypress-file-upload";
import helpers from "./helpers";
import users from "../fixtures/users.json";
import { GraphQLClient, request } from 'graphql-request'
import { gql } from '../../backend/src/helpers/jest'
import config from '../../backend/src/config'

View File

@ -1,5 +1,5 @@
import Factory from '../../backend/src/seed/factories'
import { getDriver, getNeode } from '../../backend/src/bootstrap/neo4j'
import Factory from '../../backend/src/factories'
import { getDriver, getNeode } from '../../backend/src/db/neo4j'
const neo4jConfigs = {
uri: Cypress.env('NEO4J_URI'),

View File

@ -1,6 +1,6 @@
// features/support/steps.js
import { Given, When, Then, After, AfterAll } from 'cucumber'
import Factory from '../../backend/src/seed/factories'
import Factory from '../../backend/src/factories'
import dotenv from 'dotenv'
import expect from 'expect'

View File

@ -23,7 +23,6 @@ DELETE disabled
CREATE (moderator)-[review:REVIEWED]->(report:Report)-[:BELONGS_TO]->(disabledResource)
SET review.createdAt = toString(datetime()), review.updatedAt = review.createdAt, review.disable = true
SET report.id = randomUUID(), report.createdAt = toString(datetime()), report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.closed = false
// if disabledResource has no filed report, then create a moderators default filed report
WITH moderator, disabledResource, report
OPTIONAL MATCH (disabledResourceReporter:User)-[existingFiledReport:FILED]->(disabledResource)
@ -36,7 +35,6 @@ FOREACH(disabledResource IN CASE WHEN existingFiledReport IS NOT NULL THEN [1] E
SET moveModeratorReport = existingFiledReport
DELETE existingFiledReport
)
RETURN disabledResource {.id};
" | cypher-shell
@ -49,7 +47,5 @@ ON CREATE SET report.id = randomUUID(), report.createdAt = toString(datetime()),
CREATE (reporter)-[filed:FILED]->(report)
SET report = oldReport
DELETE oldReport
RETURN notDisabledResource {.id};
" | cypher-shell

View File

@ -4,7 +4,5 @@ LABEL Description="Neo4J database of the Social Network Human-Connection.org wit
ARG BUILD_COMMIT
ENV BUILD_COMMIT=$BUILD_COMMIT
COPY db_setup.sh /usr/local/bin/db_setup
RUN apt-get update && apt-get -y install wget htop
RUN wget https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases/download/3.5.0.4/apoc-3.5.0.4-all.jar -P plugins/

View File

@ -18,15 +18,6 @@ docker-compose up
You can access Neo4J through [http://localhost:7474/](http://localhost:7474/)
for an interactive cypher shell and a visualization of the graph.
### Database Indices and Constraints
Database indices and constraints need to be created when the database is
running. So start the container with the command above and run:
```bash
docker-compose exec neo4j db_setup
```
## Installation without Docker
@ -45,20 +36,6 @@ Then make sure to allow Apoc procedures by adding the following line to your Neo
```
dbms.security.procedures.unrestricted=apoc.*
```
### Database Indices and Constraints
If you have `cypher-shell` available with your local installation of neo4j you
can run:
```bash
# in folder neo4j/
$ cp .env.template .env
$ ./db_setup.sh
```
Otherwise, if you don't have `cypher-shell` available, copy the cypher
statements [from the `db_setup.sh` script](https://github.com/Human-Connection/Human-Connection/blob/master/neo4j/db_setup.sh) and paste the scripts into your
[database browser frontend](http://localhost:7474).
### Alternatives

View File

@ -1,51 +0,0 @@
#!/usr/bin/env bash
ENV_FILE=$(dirname "$0")/.env
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
if [ -z "$NEO4J_USERNAME" ] || [ -z "$NEO4J_PASSWORD" ]; then
echo "Please set NEO4J_USERNAME and NEO4J_PASSWORD environment variables."
echo "Database manipulation is not possible without connecting to the database."
echo "E.g. you could \`cp .env.template .env\` unless you run the script in a docker container"
fi
until echo 'RETURN "Connection successful" as info;' | cypher-shell
do
echo "Connecting to neo4j failed, trying again..."
sleep 1
done
echo "
:begin
MATCH(user)-[reported:REPORTED]->(resource)
WITH reported, resource, COLLECT(user) as users
MERGE(report:Report)-[:BELONGS_TO]->(resource)
SET report.id = randomUUID(), report.createdAt = toString(datetime()), report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.closed = false
WITH report, users, reported
UNWIND users as user
MERGE (user)-[filed:FILED]->(report)
SET filed = reported
DELETE reported;
MATCH(moderator)-[disabled:DISABLED]->(resource)
MATCH(report:Report)-[:BELONGS_TO]->(resource)
WITH disabled, resource, COLLECT(moderator) as moderators, report
DELETE disabled
WITH report, moderators, disabled
UNWIND moderators as moderator
MERGE (moderator)-[review:REVIEWED {disable: true}]->(report)
SET review.createdAt = toString(datetime()), review.updatedAt = review.createdAt, review.disable = true;
MATCH(moderator)-[disabled:DISABLED]->(resource)
WITH disabled, resource, COLLECT(moderator) as moderators
MERGE(report:Report)-[:BELONGS_TO]->(resource)
SET report.id = randomUUID(), report.createdAt = toString(datetime()), report.updatedAt = report.createdAt, report.rule = 'latestReviewUpdatedAtRules', report.closed = false
DELETE disabled
WITH report, moderators, disabled
UNWIND moderators as moderator
MERGE(moderator)-[filed:FILED]->(report)
SET filed.createdAt = toString(datetime()), filed.reasonCategory = 'other', filed.reasonDescription = 'Old DISABLED relations didn\'t enforce mandatory reporting !!! Created automatically to ensure database consistency! Creation date is when the database manipulation happened.'
MERGE (moderator)-[review:REVIEWED {disable: true}]->(report)
SET review.createdAt = toString(datetime()), review.updatedAt = review.createdAt, review.disable = true;
:commit
" | cypher-shell

View File

@ -1,41 +0,0 @@
#!/usr/bin/env bash
ENV_FILE=$(dirname "$0")/.env
[[ -f "$ENV_FILE" ]] && source "$ENV_FILE"
if [ -z "$NEO4J_USERNAME" ] || [ -z "$NEO4J_PASSWORD" ]; then
echo "Please set NEO4J_USERNAME and NEO4J_PASSWORD environment variables."
echo "Setting up database constraints and indexes will probably fail because of authentication errors."
echo "E.g. you could \`cp .env.template .env\` unless you run the script in a docker container"
fi
until echo 'RETURN "Connection successful" as info;' | cypher-shell
do
echo "Connecting to neo4j failed, trying again..."
sleep 1
done
echo '
RETURN "Here is a list of indexes and constraints BEFORE THE SETUP:" as info;
CALL db.indexes();
' | cypher-shell
echo '
CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"]);
CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"]);
CREATE CONSTRAINT ON (p:Post) ASSERT p.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Comment) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.id IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.id IS UNIQUE;
CREATE CONSTRAINT ON (t:Tag) ASSERT t.id IS UNIQUE;
CREATE CONSTRAINT ON (p:Post) ASSERT p.slug IS UNIQUE;
CREATE CONSTRAINT ON (c:Category) ASSERT c.slug IS UNIQUE;
CREATE CONSTRAINT ON (u:User) ASSERT u.slug IS UNIQUE;
CREATE CONSTRAINT ON (e:EmailAddress) ASSERT e.email IS UNIQUE;
' | cypher-shell
echo '
RETURN "Setting up all the indexes and constraints seems to have been successful. Here is a list AFTER THE SETUP:" as info;
CALL db.indexes();
' | cypher-shell

View File

@ -1,6 +1,6 @@
{
"name": "human-connection",
"version": "0.2.2",
"version": "0.3.0",
"description": "Fullstack and API tests with cypress and cucumber for Human Connection",
"author": "Human Connection gGmbh",
"license": "MIT",
@ -22,30 +22,31 @@
"cypress:open": "cross-env cypress open",
"cucumber:setup": "cd backend && yarn run dev",
"cucumber": "wait-on tcp:4000 && cucumber-js --require-module @babel/register --exit",
"release": "standard-version"
"release": "standard-version",
"generate:changelog": "yarn version && auto-changelog"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/preset-env": "^7.8.3",
"@babel/core": "^7.8.4",
"@babel/preset-env": "^7.8.4",
"@babel/register": "^7.8.3",
"auto-changelog": "^1.16.2",
"bcryptjs": "^2.4.3",
"codecov": "^3.6.1",
"codecov": "^3.6.2",
"cross-env": "^6.0.3",
"cucumber": "^6.0.5",
"cypress": "^3.8.2",
"cypress": "^3.8.3",
"cypress-cucumber-preprocessor": "^2.0.1",
"cypress-file-upload": "^3.5.3",
"cypress-plugin-retries": "^1.5.2",
"date-fns": "^2.9.0",
"dotenv": "^8.2.0",
"expect": "^24.9.0",
"expect": "^25.1.0",
"faker": "Marak/faker.js#master",
"graphql-request": "^1.8.2",
"neo4j-driver": "^4.0.1",
"neode": "^0.3.7",
"npm-run-all": "^4.1.5",
"slug": "^2.1.0",
"slug": "^2.1.1",
"standard-version": "^7.1.0"
},
"resolutions": {

View File

@ -1,6 +1,6 @@
#!/usr/bin/env bash
sed -i "s/<COMMIT>/${TRAVIS_COMMIT}/g" $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml
sed -i "s/<COMMIT>/${TRAVIS_COMMIT}/g" $TRAVIS_BUILD_DIR/scripts/patches/patch-configmap.yaml
kubectl --namespace=human-connection patch configmap configmap -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-configmap.yaml)"
kubectl --namespace=human-connection patch deployment backend -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"
kubectl --namespace=human-connection patch deployment web -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"
kubectl --namespace=human-connection patch configmap develop-configmap -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-configmap.yaml)"
kubectl --namespace=human-connection patch deployment develop-backend -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"
kubectl --namespace=human-connection patch deployment develop-webapp -p "$(cat $TRAVIS_BUILD_DIR/scripts/patches/patch-deployment.yaml)"

View File

@ -0,0 +1,5 @@
<!-- Generated by IcoMoon.io -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32">
<title>level-down</title>
<path d="M5 5h17v19.063l4.281-4.281 1.438 1.438-6 6-0.719 0.688-0.719-0.688-6-6 1.438-1.438 4.281 4.281v-17.063h-15v-2z"></path>
</svg>

After

Width:  |  Height:  |  Size: 293 B

Some files were not shown because too many files have changed in this diff Show More