Merge branch 'master' into cypress-migrate-to-v10

This commit is contained in:
mahula 2023-02-22 09:14:11 +01:00
commit 4afe3e05a5
173 changed files with 6235 additions and 2730 deletions

71
.github/workflows/lint_pr.yml vendored Normal file
View File

@ -0,0 +1,71 @@
name: "ocelot.social lint pull request CI"
on:
pull_request:
pull_request_target:
types:
- opened
- edited
- synchronize
jobs:
main:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure which types are allowed (newline delimited).
# Default: https://github.com/commitizen/conventional-commit-types
#types: |
# fix
# feat
# Configure which scopes are allowed (newline delimited).
scopes: |
backend
webapp
database
release
other
# Configure that a scope must always be provided.
requireScope: true
# Configure which scopes (newline delimited) are disallowed in PR
# titles. For instance by setting # the value below, `chore(release):
# ...` and `ci(e2e,release): ...` will be rejected.
#disallowScopes: |
# release
# Configure additional validation for the subject based on a regex.
# This example ensures the subject doesn't start with an uppercase character.
subjectPattern: ^(?![A-Z]).+$
# If `subjectPattern` is configured, you can use this property to override
# the default error message that is shown when the pattern doesn't match.
# The variables `subject` and `title` can be used within the message.
subjectPatternError: |
The subject "{subject}" found in the pull request title "{title}"
didn't match the configured pattern. Please ensure that the subject
doesn't start with an uppercase character.
# If you use GitHub Enterprise, you can set this to the URL of your server
#githubBaseUrl: https://github.myorg.com/api/v3
# If the PR contains one of these labels (newline delimited), the
# validation is skipped.
# If you want to rerun the validation when labels change, you might want
# to use the `labeled` and `unlabeled` event triggers in your workflow.
#ignoreLabels: |
# bot
# ignore-semantic-pull-request
# If you're using a format for the PR title that differs from the traditional Conventional
# Commits spec, you can use these options to customize the parsing of the type, scope and
# subject. The `headerPattern` should contain a regex where the capturing groups in parentheses
# correspond to the parts listed in `headerPatternCorrespondence`.
# See: https://github.com/conventional-changelog/conventional-changelog/tree/master/packages/conventional-commits-parser#headerpattern
headerPattern: '^(\w*)(?:\(([\w$.\-*/ ]*)\))?: (.*)$'
headerPatternCorrespondence: type, scope, subject
# For work-in-progress PRs you can typically use draft pull requests
# from GitHub. However, private repositories on the free plan don't have
# this option and therefore this action allows you to opt-in to using the
# special "[WIP]" prefix to indicate this state. This will avoid the
# validation of the PR title and the pull request checks remain pending.
# Note that a second check will be reported if this is enabled.
wip: true

View File

@ -21,7 +21,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# TODO: DO STUFF ??? ##################################################### # TODO: DO STUFF ??? #####################################################
########################################################################## ##########################################################################
@ -42,7 +42,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# SET ENVS ############################################################### # SET ENVS ###############################################################
########################################################################## ##########################################################################
@ -62,7 +62,7 @@ jobs:
- name: Neo4J | Save docker image - name: Neo4J | Save docker image
run: docker save "ocelotsocialnetwork/neo4j-community" > /tmp/neo4j.tar run: docker save "ocelotsocialnetwork/neo4j-community" > /tmp/neo4j.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-neo4j-community name: docker-neo4j-community
path: /tmp/neo4j.tar path: /tmp/neo4j.tar
@ -79,7 +79,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# SET ENVS ############################################################### # SET ENVS ###############################################################
########################################################################## ##########################################################################
@ -102,7 +102,7 @@ jobs:
- name: Backend | Save docker image - name: Backend | Save docker image
run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-backend-production name: docker-backend-production
path: /tmp/backend.tar path: /tmp/backend.tar
@ -119,7 +119,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# SET ENVS ############################################################### # SET ENVS ###############################################################
########################################################################## ##########################################################################
@ -142,7 +142,7 @@ jobs:
- name: Webapp | Save docker image - name: Webapp | Save docker image
run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-webapp-production name: docker-webapp-production
path: /tmp/webapp.tar path: /tmp/webapp.tar
@ -159,7 +159,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# SET ENVS ############################################################### # SET ENVS ###############################################################
########################################################################## ##########################################################################
@ -182,7 +182,7 @@ jobs:
- name: Maintenance | Save docker image - name: Maintenance | Save docker image
run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-maintenance-production name: docker-maintenance-production
path: /tmp/maintenance.tar path: /tmp/maintenance.tar
@ -202,33 +202,33 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGES ################################################# # DOWNLOAD DOCKER IMAGES #################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Neo4J) - name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-neo4j-community name: docker-neo4j-community
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/neo4j.tar run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend) - name: Download Docker Image (Backend)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-backend-production name: docker-backend-production
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/backend.tar run: docker load < /tmp/backend.tar
- name: Download Docker Image (WebApp) - name: Download Docker Image (WebApp)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-webapp-production name: docker-webapp-production
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/webapp.tar run: docker load < /tmp/webapp.tar
- name: Download Docker Image (Maintenance) - name: Download Docker Image (Maintenance)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-maintenance-production name: docker-maintenance-production
path: /tmp path: /tmp
@ -262,7 +262,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# SET ENVS ############################################################### # SET ENVS ###############################################################
########################################################################## ##########################################################################
@ -335,7 +335,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
with: with:
fetch-depth: 0 # Fetch full History for changelog fetch-depth: 0 # Fetch full History for changelog
########################################################################## ##########################################################################

View File

@ -16,7 +16,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# TODO: DO STUFF ??? ##################################################### # TODO: DO STUFF ??? #####################################################
########################################################################## ##########################################################################
@ -37,7 +37,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# NEO4J ################################################################## # NEO4J ##################################################################
########################################################################## ##########################################################################
@ -46,7 +46,7 @@ jobs:
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/ docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-neo4j-image name: docker-neo4j-image
path: /tmp/neo4j.tar path: /tmp/neo4j.tar
@ -63,7 +63,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# BUILD BACKEND DOCKER IMAGE (build) ##################################### # BUILD BACKEND DOCKER IMAGE (build) #####################################
########################################################################## ##########################################################################
@ -72,7 +72,7 @@ jobs:
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/ docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-backend-test name: docker-backend-test
path: /tmp/backend.tar path: /tmp/backend.tar
@ -89,7 +89,7 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# BUILD WEBAPP DOCKER IMAGE (build) ###################################### # BUILD WEBAPP DOCKER IMAGE (build) ######################################
########################################################################## ##########################################################################
@ -98,7 +98,7 @@ jobs:
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/ docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: docker-webapp-test name: docker-webapp-test
path: /tmp/webapp.tar path: /tmp/webapp.tar
@ -115,12 +115,12 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGE ################################################## # DOWNLOAD DOCKER IMAGE ##################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Backend) - name: Download Docker Image (Backend)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-backend-test name: docker-backend-test
path: /tmp path: /tmp
@ -144,12 +144,12 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGE ################################################## # DOWNLOAD DOCKER IMAGE ##################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Webapp) - name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-webapp-test name: docker-webapp-test
path: /tmp path: /tmp
@ -173,19 +173,19 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGES ################################################# # DOWNLOAD DOCKER IMAGES #################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Neo4J) - name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-neo4j-image name: docker-neo4j-image
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/neo4j.tar run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend) - name: Download Docker Image (Backend)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-backend-test name: docker-backend-test
path: /tmp path: /tmp
@ -230,12 +230,12 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGES ################################################# # DOWNLOAD DOCKER IMAGES #################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Webapp) - name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-webapp-test name: docker-webapp-test
path: /tmp path: /tmp
@ -290,26 +290,26 @@ jobs:
# CHECKOUT CODE ########################################################## # CHECKOUT CODE ##########################################################
########################################################################## ##########################################################################
- name: Checkout code - name: Checkout code
uses: actions/checkout@v2 uses: actions/checkout@v3
########################################################################## ##########################################################################
# DOWNLOAD DOCKER IMAGES ################################################# # DOWNLOAD DOCKER IMAGES #################################################
########################################################################## ##########################################################################
- name: Download Docker Image (Neo4J) - name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-neo4j-image name: docker-neo4j-image
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/neo4j.tar run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend) - name: Download Docker Image (Backend)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-backend-test name: docker-backend-test
path: /tmp path: /tmp
- name: Load Docker Image - name: Load Docker Image
run: docker load < /tmp/backend.tar run: docker load < /tmp/backend.tar
- name: Download Docker Image (Webapp) - name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2 uses: actions/download-artifact@v3
with: with:
name: docker-webapp-test name: docker-webapp-test
path: /tmp path: /tmp
@ -332,12 +332,12 @@ jobs:
# UPLOAD SCREENSHOTS & VIDEO ############################################# # UPLOAD SCREENSHOTS & VIDEO #############################################
########################################################################## ##########################################################################
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: cypress-screenshots name: cypress-screenshots
path: cypress/screenshots/ path: cypress/screenshots/
- name: Upload Artifact - name: Upload Artifact
uses: actions/upload-artifact@v2 uses: actions/upload-artifact@v3
with: with:
name: cypress-videos name: cypress-videos
path: cypress/videos/ path: cypress/videos/

2
.nvmrc
View File

@ -1 +1 @@
v17.9.0 v19.4.0

View File

@ -4,8 +4,54 @@ 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). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [2.2.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.1.0...2.2.0) #### [2.4.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.3.0...2.4.0)
- fix(webapp): post teaser width in mobile view [`#5958`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5958)
- fix(webapp): navbar disappears when scrolling to top [`#5957`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5957)
- fix(webapp): close mobile menu on all links [`#5956`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5956)
- feat(webapp): mark active filters on the posts [`#5952`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5952)
- refactor(webapp): refacchange postion add content button, refactor mobile filter menu [`#5943`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5943)
- refactor(webapp): set legend to map [`#5950`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5950)
- fix(webapp): small changes for deploy [`#5949`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5949)
- feat(webapp): hide navbar by scroll, filter button for content [`#5926`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5926)
- feat(webapp): map [`#5843`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5843)
- refactor(webapp): upgrade node version to 19.4 [`#5910`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5910)
- fix(webapp): texts for groups [`#5923`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5923)
- fix(webapp): add category name in tooltip [`#5884`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5884)
- refactor(webapp): 🍰 Refactor Social Media And `MySomethingList` [`#5138`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5138)
- fix(backend): do not expose registered emails on registration [`#5909`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5909)
- refactor(backend): node 19 with fixed image upload [`#5897`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5897)
- refactor(webapp): nump docker version to `16.19.0` in webapp & maintenance [`#5842`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5842)
- updated required packages for node19, fix fs-capacitator [`11087cb`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/11087cbaefde604668ac192b710666df09cb813c)
- fixed build error [`3889204`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3889204f871df97b307401032900db7940913038)
- linting [`8bfe486`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8bfe486034badd55a8096982f81aba08207b9e83)
#### [2.3.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.2.0...2.3.0)
> 5 December 2022
- chore(release): v2.3.0 [`#5768`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5768)
- feat(webapp): 🍰 Sort Categories By Name [`#5709`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5709)
- fix(backend): sanitize group description [`#5707`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5707)
- fix(webapp): clean up webapp unit tests [`#5711`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5711)
- feat(webapp): 🍰 Search Users To Add To Group [`#5652`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5652)
- feat(webapp): 🍰 Params For Category Filter [`#5699`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5699)
- fix(backend): 🍰 Misspelled Notification [`#5703`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5703)
- test(other): jest coverage report in console [`#5637`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5637)
- fix(webapp): centralize mobile hamburger menu [`#5674`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5674)
- refactor(backend): 🍰 Move `db/graphql` Folder Content To `graphql` Folder [`#5661`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5661)
- feat(other): semantic pullrequest workflow [`#5634`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5634)
- fix: Cannot Add Group Members as New Members to Group [`#5635`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5635)
- fix: My Groups Count Includes Pending Membership [`#5631`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5631)
- frontend: jest coverage [`f57e11d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f57e11dd021dbb156b33fbd5538cf5ca32df7334)
- reverted yarn lock [`a01aee8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a01aee89a92bbc9885e2895c577eb3c63042ba22)
- reverted all package updates [`a520089`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a5200893f4508d45619447231b6789178a51daf7)
#### [2.2.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.1.0...2.2.0)
> 28 October 2022
- chore: 🍰 Release v2.2.0 [`#5610`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5610)
- refactor: 🍰 Refactor Design Of Category Filter In Filter Menu [`#5597`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5597) - refactor: 🍰 Refactor Design Of Category Filter In Filter Menu [`#5597`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5597)
- feat: 🍰 Footer And Header Links Configurable To Have External Link Target [`#5590`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5590) - feat: 🍰 Footer And Header Links Configurable To Have External Link Target [`#5590`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5590)
- feat: 🍰 Header Menu To Component And Other Refinements [`#5546`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5546) - feat: 🍰 Header Menu To Component And Other Refinements [`#5546`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5546)
@ -19,10 +65,26 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- fix: 🍰 Fix Group Teaser [`#5584`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5584) - fix: 🍰 Fix Group Teaser [`#5584`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5584)
- feat: 🍰 List All Groups [`#5582`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5582) - feat: 🍰 List All Groups [`#5582`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5582)
- feat: 🍰 Header Logo Routing Update [`#5579`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5579) - feat: 🍰 Header Logo Routing Update [`#5579`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5579)
- add header menu to component, central variabl for screen width [`401f59a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/401f59ae8de5b1c27f0e26e1f71778d3257d2180)
- comment out LanguagesFilter, EmotionsFilter, fix tests, fix lint [`52dcd77`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/52dcd772fa81e02a0d95e89a9fc8232e70a09d28)
- fix lint [`15561cb`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/15561cb94f8768e93846c25945c935ae83977553)
#### [2.1.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/2.0.0...2.1.0)
> 25 October 2022
- chore: 🍰 Release v2.1.0 [`#5574`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5574) - chore: 🍰 Release v2.1.0 [`#5574`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5574)
- feat: 🍰 EPIC Groups [`#5132`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5132) - feat: 🍰 EPIC Groups [`#5132`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5132)
- chore: 🍰 Remove Group Branchs `5059-epic-groups` Separate Auto-Deployment [`#5552`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5552) - chore: 🍰 Remove Group Branchs `5059-epic-groups` Separate Auto-Deployment [`#5552`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5552)
- fix: [WIP] 🍰 Long Words Are Being Wrapped Now [`#5559`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5559) - fix: [WIP] 🍰 Long Words Are Being Wrapped Now [`#5559`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5559)
- Remove groups separate auto-deployment [`c8d8168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/c8d816887b2d49293d1b8ee2805d452fe10d907e)
- Release v2.1.0 [`dc085e9`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/dc085e9e69b288fce6dd06e8d7eb05ef34bd9a7b)
- Add database migration to auto-deployment on publish [`ef06f1a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/ef06f1a67d2654aaeb55d0434d3324a3ac37a380)
### [2.0.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.1.1...2.0.0)
> 23 October 2022
- feat: 🍰 Search For Groups [`#5543`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5543) - feat: 🍰 Search For Groups [`#5543`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5543)
- feat: 🍰 Mobile Footer Menu To Header Menu [`#5524`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5524) - feat: 🍰 Mobile Footer Menu To Header Menu [`#5524`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5524)
- feat: 🍰 Implement `LOGO_HEADER_CLICK` As Configuration [`#5525`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5525) - feat: 🍰 Implement `LOGO_HEADER_CLICK` As Configuration [`#5525`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5525)
@ -39,25 +101,35 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- feat: 🍰 Group Members Management [`#5345`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5345) - feat: 🍰 Group Members Management [`#5345`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5345)
- feat: 🍰 Have My Groups In The User Menu And Configure Groups Button In Header [`#5411`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5411) - feat: 🍰 Have My Groups In The User Menu And Configure Groups Button In Header [`#5411`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5411)
- chore: 🍰 Implement Automatic Deployment For Groups Branch '5059-epic-groups' [`#5408`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5408) - chore: 🍰 Implement Automatic Deployment For Groups Branch '5059-epic-groups' [`#5408`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5408)
- Chore: 🍰 Release v1.1.1 Refactor Rebranding [`#5392`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5392)
- chore: 🍰 Refactor Rebranding [`#5390`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5390)
- feat: 🍰 Group Profile Description Etc [`#5368`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5368) - feat: 🍰 Group Profile Description Etc [`#5368`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5368)
- feat: 🍰 Tooltips For Topics [`#5350`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5350)
- feat: 🍰 Group Profile Members List Etc [`#5335`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5335) - feat: 🍰 Group Profile Members List Etc [`#5335`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5335)
- feat: 🍰 Implement Group Profile Visibility [`#5332`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5332) - feat: 🍰 Implement Group Profile Visibility [`#5332`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5332)
- feat: 🍰 Save Categories In Frontend [`#5284`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5284)
- feat: 🍰 Add New Yunite Icons [`#5319`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5319)
- chore: 🍰 Update Neode From v^0.4.7 To v^0.4.8 In Backend [`#5334`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5334)
- feat: 🍰 My Groups Page [`#5148`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5148) - feat: 🍰 My Groups Page [`#5148`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5148)
- feat: 🍰 Hidden Groups Shall Not Be Visible For None Or Pending Members In Backend [`#5317`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5317) - feat: 🍰 Hidden Groups Shall Not Be Visible For None Or Pending Members In Backend [`#5317`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5317)
- feat: 🍰 Implement Group Profile [`#5197`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5197) - feat: 🍰 Implement Group Profile [`#5197`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5197)
- feat: 🍰 Implement `UpdateGroup` Resolver [`#5224`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5224)
- feat: 🍰 Implement `JoinGroup`, `GroupMember`, `SwitchGroupMemberRole` Resolvers [`#5199`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5199)
- chore: 🍰 Add Groups To Seeding [`#5185`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5185)
- feat: 🍰 Implement Group GQL Model And CRUD Resolvers First Step [`#5139`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5139)
- Refine design and functionality of group list and create, edit group [`7b11122`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/7b11122bea4868624dd1c1641219e71070412e20)
- improved code and tests as suggested by @tirokk, thanks for the great review! [`631f34a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/631f34a2e5224d68279337a92e7535794b670d70)
- implement and test post visibilty when leaving or changing the role in a group [`76bfe48`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/76bfe484768cf9b20b2dced865d5d3e3eb999235)
#### [1.1.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.1.0...1.1.1)
> 22 September 2022
- Chore: 🍰 Release v1.1.1 Refactor Rebranding [`#5392`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5392)
- chore: 🍰 Refactor Rebranding [`#5390`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5390)
- feat: 🍰 Tooltips For Topics [`#5350`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5350)
- feat: 🍰 Save Categories In Frontend [`#5284`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5284)
- feat: 🍰 Add New Yunite Icons [`#5319`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5319)
- chore: 🍰 Update Neode From v^0.4.7 To v^0.4.8 In Backend [`#5334`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5334)
- fix: Category Filter Menu Client Only [`#5301`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5301) - fix: Category Filter Menu Client Only [`#5301`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5301)
- feat: Save Category Settings [`#5261`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5261) - feat: Save Category Settings [`#5261`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5261)
- feat: 🍰 Implement `UpdateGroup` Resolver [`#5224`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5224)
- feat: Topics Menu [`#5248`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5248) - feat: Topics Menu [`#5248`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5248)
- docs: 🍰 Document GraqhQL Playground [`#5253`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5253) - docs: 🍰 Document GraqhQL Playground [`#5253`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5253)
- feat: Categories Filter Menu [`#5198`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5198) - feat: Categories Filter Menu [`#5198`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5198)
- feat: 🍰 Implement `JoinGroup`, `GroupMember`, `SwitchGroupMemberRole` Resolvers [`#5199`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5199)
- fix: 🍰 Fix Test Description From `enter-nonce.vue` To `change-password` [`#5217`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5217) - fix: 🍰 Fix Test Description From `enter-nonce.vue` To `change-password` [`#5217`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5217)
- Bump cookie-universal-nuxt from 2.1.5 to 2.2.2 in /webapp [`#5218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5218) - Bump cookie-universal-nuxt from 2.1.5 to 2.2.2 in /webapp [`#5218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5218)
- Bump prettier from 2.2.1 to 2.7.1 in /webapp [`#5170`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5170) - Bump prettier from 2.2.1 to 2.7.1 in /webapp [`#5170`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5170)
@ -69,12 +141,10 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- feat: 🍰 Change Error Message With `Authorised` To `Authorized` All Over The Place To Have American English [`#5206`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5206) - feat: 🍰 Change Error Message With `Authorised` To `Authorized` All Over The Place To Have American English [`#5206`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5206)
- Bump cross-env from 7.0.2 to 7.0.3 in /webapp [`#5168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5168) - Bump cross-env from 7.0.2 to 7.0.3 in /webapp [`#5168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5168)
- chore: 🍰 Add `--logHeapUsage` To Jest Test Call [`#5182`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5182) - chore: 🍰 Add `--logHeapUsage` To Jest Test Call [`#5182`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5182)
- chore: 🍰 Add Groups To Seeding [`#5185`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5185)
- feat: 🍰 Implement Group GQL Model And CRUD Resolvers First Step [`#5139`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5139)
- refactor: 🍰 Rename `UserGroup` To `UserRole` [`#5143`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5143) - refactor: 🍰 Rename `UserGroup` To `UserRole` [`#5143`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/5143)
- improved code and tests as suggested by @tirokk, thanks for the great review! [`631f34a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/631f34a2e5224d68279337a92e7535794b670d70) - add new yunite icons [`bb0d632`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/bb0d6329e7e36ea03671318ea8dd128a6d5a5a7a)
- implement and test post visibilty when leaving or changing the role in a group [`76bfe48`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/76bfe484768cf9b20b2dced865d5d3e3eb999235) - cleanup refactor rebranding [`5f5c0fa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5f5c0faa1f28cd4df7681eba335ae5998b2d9cca)
- comment out LanguagesFilter, EmotionsFilter, fix tests, fix lint [`52dcd77`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/52dcd772fa81e02a0d95e89a9fc8232e70a09d28) - change color and scss in branding [`52070b8`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/52070b8c570970bf48df561134bf67cb4111b640)
#### [1.1.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.9...1.1.0) #### [1.1.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.9...1.1.0)

View File

@ -105,7 +105,7 @@ Sprint retrospective
## Philosophy ## Philosophy
We practise [collective code ownership](http://www.extremeprogramming.org/rules/collective.html) rather than strong code ownership, which means that: We practice [collective code ownership](http://www.extremeprogramming.org/rules/collective.html) rather than strong code ownership, which means that:
* developers can make contributions to other people's PRs (after checking in with them) * developers can make contributions to other people's PRs (after checking in with them)
* we avoid blocking because someone else isn't working, so we sometimes take over PRs from other developers * we avoid blocking because someone else isn't working, so we sometimes take over PRs from other developers
@ -115,7 +115,7 @@ 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. 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! As a volunteer 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 ## Open-Source Bounties
@ -149,3 +149,106 @@ 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 experience we will make changes and improvements. So keep an eye on this
contribution guide. contribution guide.
--> -->
## Programming
### Localization
#### Quotation Marks
The following characters are different from the programming quotation mark:
`"` or `\"`
Please copy and paste the following quotes for the languages:
* de: „Dies ist ein Beispielsatz.“
* en: “This is a sample sentence.”
* See <https://grammar.collinsdictionary.com/easy-learning/when-do-you-use-quotation-marks-or-in-english>
## Docker More Closely
### Apple M1 Platform
***Attention:** For using Docker commands in Apple M1 environments!*
#### Environment Variable For Apple M1 Platform
To set the Docker platform environment variable in your terminal tab, run:
```bash
# set env variable for your shell
$ export DOCKER_DEFAULT_PLATFORM=linux/amd64
```
#### Docker Compose Override File For Apple M1 Platform
For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform:
```bash
# in main folder
# for development
$ docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend yarn prod:migrate init
# clean db
$ docker compose exec backend yarn db:reset
# seed db
$ docker compose exec backend yarn db:seed
# for production
$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend /bin/sh -c "yarn prod:migrate init"
```
### Analyzing Docker Builds
To analyze a Docker build, there is a wonderful tool called [dive](https://github.com/wagoodman/dive). Please sponsor if you're using it!
The `dive build` command is exactly the right one to fulfill what we are looking for.
We can use it just like the `docker build` command and get an analysis afterwards.
So, in our main folder, we use it in the following way:
```bash
# in main folder
$ dive build --target <layer-name> -t "ocelotsocialnetwork/<app-name>:local-<layer-name>" --build-arg BBUILD_DATE="<build-date>" --build-arg BBUILD_VERSION="<build-version>" --build-arg BBUILD_COMMIT="<build-commit>" <app-folder-name-or-dot>/
```
The build arguments are optional.
For the specific applications, we use them as follows.
#### Backend
##### Production For Backend
```bash
# in main folder
$ dive build --target production -t "ocelotsocialnetwork/backend:local-production" backend/
```
##### Development For Backend
```bash
# in main folder
$ dive build --target development -t "ocelotsocialnetwork/backend:local-development" backend/
```
#### Webapp
##### Production For Webapp
```bash
# in main folder
$ dive build --target production -t "ocelotsocialnetwork/webapp:local-production" webapp/
```
##### Development For Webapp
```bash
# in main folder
$ dive build --target development -t "ocelotsocialnetwork/webapp:local-development" webapp/
```

View File

@ -1,86 +0,0 @@
# Docker More Closely
## Apple M1 Platform
***Attention:** For using Docker commands in Apple M1 environments!*
### Enviroment Variable For Apple M1 Platform
To set the Docker platform environment variable in your terminal tab, run:
```bash
# set env variable for your shell
$ export DOCKER_DEFAULT_PLATFORM=linux/amd64
```
### Docker Compose Override File For Apple M1 Platform
For Docker compose `up` or `build` commands, you can use our Apple M1 override file that specifies the M1 platform:
```bash
# in main folder
# for development
$ docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend yarn prod:migrate init
# clean db
$ docker compose exec backend yarn db:reset
# seed db
$ docker compose exec backend yarn db:seed
# for production
$ docker compose -f docker-compose.yml -f docker-compose.apple-m1.override.yml up
# only once: init admin user and create indexes and contraints in Neo4j database
$ docker compose exec backend /bin/sh -c "yarn prod:migrate init"
```
## Analysing Docker Builds
To analyze a Docker build, there is a wonderful tool called [dive](https://github.com/wagoodman/dive). Please sponsor if you're using it!
The `dive build` command is exactly the right one to fulfill what we are looking for.
We can use it just like the `docker build` command and get an analysis afterwards.
So, in our main folder, we use it in the following way:
```bash
# in main folder
$ dive build --target <layer-name> -t "ocelotsocialnetwork/<app-name>:local-<layer-name>" --build-arg BBUILD_DATE="<build-date>" --build-arg BBUILD_VERSION="<build-version>" --build-arg BBUILD_COMMIT="<build-commit>" <app-folder-name-or-dot>/
```
The build arguments are optional.
For the specific applications, we use them as follows.
### Backend
#### Production For Backend
```bash
# in main folder
$ dive build --target production -t "ocelotsocialnetwork/backend:local-production" backend/
```
#### Development For Backend
```bash
# in main folder
$ dive build --target development -t "ocelotsocialnetwork/backend:local-development" backend/
```
### Webapp
#### Production For Webapp
```bash
# in main folder
$ dive build --target production -t "ocelotsocialnetwork/webapp:local-production" webapp/
```
#### Development For Webapp
```bash
# in main folder
$ dive build --target development -t "ocelotsocialnetwork/webapp:local-development" webapp/
```

View File

@ -1 +1 @@
v12.19.0 v19.4.0

View File

@ -1,7 +1,7 @@
################################################################################## ##################################################################################
# BASE (Is pushed to DockerHub for rebranding) ################################### # BASE (Is pushed to DockerHub for rebranding) ###################################
################################################################################## ##################################################################################
FROM node:12.19.0-alpine3.10 as base FROM node:19.4.0-alpine3.17 as base
# ENVs # ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame ## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame

View File

@ -19,12 +19,19 @@ Wait a little until your backend is up and running at [http://localhost:4000/](h
## Installation without Docker ## Installation without Docker
For the local installation you need a recent version of For the local installation you need a recent version of
[node](https://nodejs.org/en/) (&gt;= `v10.12.0`). We are using [Node](https://nodejs.org/en/) (&gt;= `v16.19.0`). We are using
`12.19.0` and therefore we recommend to use the same version `v19.4.0` and therefore we recommend to use the same version
([see](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4082) ([see](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4082)
some known problems with more recent node versions). You can use the some known problems with more recent node versions). You can use the
[node version manager](https://github.com/nvm-sh/nvm) to switch [node version manager](https://github.com/nvm-sh/nvm) `nvm` to switch
between different local node versions. between different local Node versions:
```bash
# install Node
$ cd backend
$ nvm install v19.4.0
$ nvm use v19.4.0
```
Install node dependencies with [yarn](https://yarnpkg.com/en/): Install node dependencies with [yarn](https://yarnpkg.com/en/):
@ -32,6 +39,10 @@ Install node dependencies with [yarn](https://yarnpkg.com/en/):
# in main folder # in main folder
$ cd backend $ cd backend
$ yarn install $ yarn install
# or just
$ yarn
# or just later on to use version of ".nvmrc" file
$ nvm use && yarn
``` ```
Copy Environment Variables: Copy Environment Variables:

14
backend/jest.config.js Normal file
View File

@ -0,0 +1,14 @@
module.exports = {
verbose: true,
collectCoverage: true,
collectCoverageFrom: [
'**/*.js',
'!**/node_modules/**',
'!**/test/**',
'!**/dist/**',
'!**/src/**/?(*.)+(spec|test).js?(x)'
],
coverageReporters: ['lcov', 'text'],
testMatch: ['**/src/**/?(*.)+(spec|test).js?(x)'],
setupFilesAfterEnv: ['<rootDir>/test/setup.js']
}

View File

@ -1,6 +1,6 @@
{ {
"name": "ocelot-social-backend", "name": "ocelot-social-backend",
"version": "2.2.0", "version": "2.4.0",
"description": "GraphQL Backend for ocelot.social", "description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community", "author": "ocelot.social Community",
@ -15,29 +15,13 @@
"dev": "nodemon --exec babel-node src/ -e js,gql", "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", "dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql",
"lint": "eslint src --config .eslintrc.js", "lint": "eslint src --config .eslintrc.js",
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --forceExit --detectOpenHandles --runInBand --coverage --logHeapUsage", "test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles",
"db:clean": "babel-node src/db/clean.js", "db:clean": "babel-node src/db/clean.js",
"db:reset": "yarn run db:clean", "db:reset": "yarn run db:clean",
"db:seed": "babel-node src/db/seed.js", "db:seed": "babel-node src/db/seed.js",
"db:migrate": "yarn run __migrate --store ./src/db/migrate/store.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" "db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.js --date-format 'yyyymmddHHmmss' create"
}, },
"jest": {
"verbose": true,
"collectCoverageFrom": [
"**/*.js",
"!**/node_modules/**",
"!**/test/**",
"!**/dist/**",
"!**/src/**/?(*.)+(spec|test).js?(x)"
],
"coverageReporters": [
"lcov"
],
"testMatch": [
"**/src/**/?(*.)+(spec|test).js?(x)"
]
},
"dependencies": { "dependencies": {
"@babel/cli": "~7.8.4", "@babel/cli": "~7.8.4",
"@babel/core": "~7.9.0", "@babel/core": "~7.9.0",
@ -81,22 +65,22 @@
"linkifyjs": "~2.1.8", "linkifyjs": "~2.1.8",
"lodash": "~4.17.14", "lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.8", "merge-graphql-schemas": "^1.7.8",
"metascraper": "^5.11.8", "metascraper": "^5.33.5",
"metascraper-audio": "^5.14.26", "metascraper-audio": "^5.33.5",
"metascraper-author": "^5.14.22", "metascraper-author": "^5.33.5",
"metascraper-clearbit-logo": "^5.3.0", "metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.11.8", "metascraper-date": "^5.33.5",
"metascraper-description": "^5.23.1", "metascraper-description": "^5.33.5",
"metascraper-image": "^5.11.8", "metascraper-image": "^5.33.5",
"metascraper-lang": "^5.23.1", "metascraper-lang": "^5.33.5",
"metascraper-lang-detector": "^4.10.2", "metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.14.26", "metascraper-logo": "^5.33.5",
"metascraper-publisher": "^5.23.0", "metascraper-publisher": "^5.33.5",
"metascraper-soundcloud": "^5.23.0", "metascraper-soundcloud": "^5.33.5",
"metascraper-title": "^5.11.8", "metascraper-title": "^5.33.5",
"metascraper-url": "^5.14.26", "metascraper-url": "^5.33.5",
"metascraper-video": "^5.11.8", "metascraper-video": "^5.33.5",
"metascraper-youtube": "^5.23.0", "metascraper-youtube": "^5.33.5",
"migrate": "^1.7.0", "migrate": "^1.7.0",
"mime-types": "^2.1.26", "mime-types": "^2.1.26",
"minimatch": "^3.0.4", "minimatch": "^3.0.4",
@ -132,13 +116,15 @@
"eslint-plugin-prettier": "~3.4.1", "eslint-plugin-prettier": "~3.4.1",
"eslint-plugin-promise": "~4.3.1", "eslint-plugin-promise": "~4.3.1",
"eslint-plugin-standard": "~4.0.1", "eslint-plugin-standard": "~4.0.1",
"jest": "~25.3.0", "jest": "29.4",
"nodemon": "~2.0.2", "nodemon": "~2.0.2",
"prettier": "~2.3.2", "prettier": "~2.3.2",
"rosie": "^2.0.1", "rosie": "^2.0.1",
"supertest": "~4.0.2" "supertest": "~4.0.2"
}, },
"resolutions": { "resolutions": {
"fs-capacitor": "6.0.0" "**/**/fs-capacitor": "^6.2.0",
"**/graphql-upload": "^11.0.0",
"nan": "2.17.0"
} }
} }

View File

@ -33,6 +33,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -1,3 +1,3 @@
// this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js` // this file is duplicated in `backend/src/constants/group.js` and `webapp/constants/group.js`
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 50 // with removed HTML tags
export const DESCRIPTION_EXCERPT_HTML_LENGTH = 250 // with removed HTML tags export const DESCRIPTION_EXCERPT_HTML_LENGTH = 250 // with removed HTML tags

View File

@ -85,7 +85,7 @@ class Store {
await createDefaultAdminUser(session) await createDefaultAdminUser(session)
if (CONFIG.CATEGORIES_ACTIVE) await createCategories(session) if (CONFIG.CATEGORIES_ACTIVE) await createCategories(session)
const writeTxResultPromise = session.writeTransaction(async (txc) => { const writeTxResultPromise = session.writeTransaction(async (txc) => {
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices and contraints await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices and constraints
return Promise.all( return Promise.all(
[ [
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])', 'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',

View File

@ -9,9 +9,9 @@ import {
createGroupMutation, createGroupMutation,
joinGroupMutation, joinGroupMutation,
changeGroupMemberRoleMutation, changeGroupMemberRoleMutation,
} from './graphql/groups' } from '../graphql/groups'
import { createPostMutation } from './graphql/posts' import { createPostMutation } from '../graphql/posts'
import { createCommentMutation } from './graphql/comments' import { createCommentMutation } from '../graphql/comments'
import { categories } from '../constants/categories' import { categories } from '../constants/categories'
if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {

View File

@ -2,24 +2,26 @@
* iterate through all fields and replace it with the callback result * iterate through all fields and replace it with the callback result
* @property data Array * @property data Array
* @property fields Array * @property fields Array
* @property fieldName String
* @property callback Function * @property callback Function
*/ */
function walkRecursive(data, fields, callback, _key) { function walkRecursive(data, fields, fieldName, callback, _key) {
if (!Array.isArray(fields)) { if (!Array.isArray(fields)) {
throw new Error('please provide an fields array for the walkRecursive helper') throw new Error('please provide an fields array for the walkRecursive helper')
} }
if (data && typeof data === 'string' && fields.includes(_key)) { if (data && typeof data === 'string' && fields.includes(_key)) {
// well we found what we searched for, lets replace the value with our callback result // well we found what we searched for, lets replace the value with our callback result
data = callback(data, _key) const key = _key.split('!')
if (key.length === 1 || key[1] !== fieldName) data = callback(data, key[0])
} else if (data && Array.isArray(data)) { } else if (data && Array.isArray(data)) {
// go into the rabbit hole and dig through that array // go into the rabbit hole and dig through that array
data.forEach((res, index) => { data.forEach((res, index) => {
data[index] = walkRecursive(data[index], fields, callback, index) data[index] = walkRecursive(data[index], fields, fieldName, callback, index)
}) })
} else if (data && typeof data === 'object') { } else if (data && typeof data === 'object') {
// lets get some keys and stir them // lets get some keys and stir them
Object.keys(data).forEach((k) => { Object.keys(data).forEach((k) => {
data[k] = walkRecursive(data[k], fields, callback, k) data[k] = walkRecursive(data[k], fields, fieldName, callback, k)
}) })
} }
return data return data

View File

@ -36,6 +36,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -50,6 +50,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -80,14 +80,6 @@ const testEmailData = (emailTemplate, templateBuilder, templateData, texts) => {
return emailTemplate return emailTemplate
} }
// beforeAll(async () => {
// await cleanDatabase()
// })
// afterAll(async () => {
// await cleanDatabase()
// })
describe('templateBuilder', () => { describe('templateBuilder', () => {
describe('signupTemplate', () => { describe('signupTemplate', () => {
describe('multi language', () => { describe('multi language', () => {

View File

@ -28,6 +28,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
const createPostMutation = gql` const createPostMutation = gql`

View File

@ -10,10 +10,13 @@ const sendSignupMail = async (resolve, root, args, context, resolveInfo) => {
const { inviteCode } = args const { inviteCode } = args
const response = await resolve(root, args, context, resolveInfo) const response = await resolve(root, args, context, resolveInfo)
const { email, nonce } = response const { email, nonce } = response
if (inviteCode) { if (nonce) {
await sendMail(signupTemplate({ email, variables: { nonce, inviteCode } })) // emails that already exist do not have a nonce
} else { if (inviteCode) {
await sendMail(signupTemplate({ email, variables: { nonce } })) await sendMail(signupTemplate({ email, variables: { nonce, inviteCode } }))
} else {
await sendMail(signupTemplate({ email, variables: { nonce } }))
}
} }
delete response.nonce delete response.nonce
return response return response
@ -30,7 +33,9 @@ const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo)
const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => { const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => {
const response = await resolve(root, args, context, resolveInfo) const response = await resolve(root, args, context, resolveInfo)
const { email, nonce, name } = response const { email, nonce, name } = response
await sendMail(emailVerificationTemplate({ email, variables: { nonce, name } })) if (nonce) {
await sendMail(emailVerificationTemplate({ email, variables: { nonce, name } }))
}
delete response.nonce delete response.nonce
return response return response
} }

View File

@ -56,6 +56,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -24,6 +24,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -122,37 +122,41 @@ const isAllowedToChangeGroupMemberRole = rule({
cache: 'no_cache', cache: 'no_cache',
})(async (_parent, args, { user, driver }) => { })(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false if (!(user && user.id)) return false
const adminId = user.id const currentUserId = user.id
const { groupId, userId, roleInGroup } = args const { groupId, userId, roleInGroup } = args
if (adminId === userId) return false if (currentUserId === userId) return false
const session = driver.session() const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => { const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run( const transactionResponse = await transaction.run(
` `
MATCH (admin:User {id: $adminId})-[adminMembership:MEMBER_OF]->(group:Group {id: $groupId}) MATCH (currentUser:User {id: $currentUserId})-[currentUserMembership:MEMBER_OF]->(group:Group {id: $groupId})
OPTIONAL MATCH (group)<-[userMembership:MEMBER_OF]-(member:User {id: $userId}) OPTIONAL MATCH (group)<-[userMembership:MEMBER_OF]-(member:User {id: $userId})
RETURN group {.*}, admin {.*, myRoleInGroup: adminMembership.role}, member {.*, myRoleInGroup: userMembership.role} RETURN group {.*}, currentUser {.*, myRoleInGroup: currentUserMembership.role}, member {.*, myRoleInGroup: userMembership.role}
`, `,
{ groupId, adminId, userId }, { groupId, currentUserId, userId },
) )
return { return {
admin: transactionResponse.records.map((record) => record.get('admin'))[0], currentUser: transactionResponse.records.map((record) => record.get('currentUser'))[0],
group: transactionResponse.records.map((record) => record.get('group'))[0], group: transactionResponse.records.map((record) => record.get('group'))[0],
member: transactionResponse.records.map((record) => record.get('member'))[0], member: transactionResponse.records.map((record) => record.get('member'))[0],
} }
}) })
try { try {
const { admin, group, member } = await readTxPromise const { currentUser, group, member } = await readTxPromise
const groupExists = !!group
const currentUserExists = !!currentUser
const userIsMember = !!member
const sameUserRoleInGroup = member && member.myRoleInGroup === roleInGroup
const userIsOwner = member && ['owner'].includes(member.myRoleInGroup)
const currentUserIsAdmin = currentUser && ['admin'].includes(currentUser.myRoleInGroup)
const adminCanSetRole = ['pending', 'usual', 'admin'].includes(roleInGroup)
const currentUserIsOwner = currentUser && ['owner'].includes(currentUser.myRoleInGroup)
const ownerCanSetRole = ['pending', 'usual', 'admin', 'owner'].includes(roleInGroup)
return ( return (
!!group && groupExists &&
!!admin && currentUserExists &&
(!member || (!userIsMember || (userIsMember && (sameUserRoleInGroup || !userIsOwner))) &&
(!!member && ((currentUserIsAdmin && adminCanSetRole) || (currentUserIsOwner && ownerCanSetRole))
(member.myRoleInGroup === roleInGroup || !['owner'].includes(member.myRoleInGroup)))) &&
((['admin'].includes(admin.myRoleInGroup) &&
['pending', 'usual', 'admin'].includes(roleInGroup)) ||
(['owner'].includes(admin.myRoleInGroup) &&
['pending', 'usual', 'admin', 'owner'].includes(roleInGroup)))
) )
} catch (error) { } catch (error) {
throw new Error(error) throw new Error(error)

View File

@ -28,6 +28,7 @@ describe('authorization', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -2,9 +2,9 @@ import { getNeode, getDriver } from '../db/neo4j'
import createServer from '../server' import createServer from '../server'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../db/factories' import Factory, { cleanDatabase } from '../db/factories'
import { createGroupMutation, updateGroupMutation } from '../db/graphql/groups' import { createGroupMutation, updateGroupMutation } from '../graphql/groups'
import { createPostMutation } from '../db/graphql/posts' import { createPostMutation } from '../graphql/posts'
import { signupVerificationMutation } from '../db/graphql/authentications' import { signupVerificationMutation } from '../graphql/authentications'
let authenticatedUser let authenticatedUser
let variables let variables
@ -33,6 +33,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -152,7 +153,7 @@ describe('slugifyMiddleware', () => {
}) })
describe('but if the client specifies a slug', () => { describe('but if the client specifies a slug', () => {
it('rejects CreateGroup', async (done) => { it('rejects CreateGroup', async () => {
try { try {
await expect( await expect(
mutate({ mutate({
@ -171,7 +172,6 @@ describe('slugifyMiddleware', () => {
}, },
], ],
}) })
done()
} catch (error) { } catch (error) {
throw new Error(` throw new Error(`
${error} ${error}
@ -258,7 +258,7 @@ describe('slugifyMiddleware', () => {
}) })
describe('setting slug explicitly', () => { describe('setting slug explicitly', () => {
it('rejects UpdateGroup', async (done) => { it('rejects UpdateGroup', async () => {
try { try {
await expect( await expect(
mutate({ mutate({
@ -275,7 +275,6 @@ describe('slugifyMiddleware', () => {
}, },
], ],
}) })
done()
} catch (error) { } catch (error) {
throw new Error(` throw new Error(`
${error} ${error}
@ -382,7 +381,7 @@ describe('slugifyMiddleware', () => {
}) })
describe('but if the client specifies a slug', () => { describe('but if the client specifies a slug', () => {
it('rejects CreatePost', async (done) => { it('rejects CreatePost', async () => {
try { try {
await expect( await expect(
mutate({ mutate({
@ -402,7 +401,6 @@ describe('slugifyMiddleware', () => {
}, },
], ],
}) })
done()
} catch (error) { } catch (error) {
throw new Error(` throw new Error(`
${error} ${error}

View File

@ -195,6 +195,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('softDeleteMiddleware', () => { describe('softDeleteMiddleware', () => {

View File

@ -42,6 +42,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('middleware/userInteractions', () => { describe('middleware/userInteractions', () => {

View File

@ -75,6 +75,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -1,15 +1,22 @@
import walkRecursive from '../helpers/walkRecursive' import walkRecursive from '../helpers/walkRecursive'
import { cleanHtml } from '../middleware/helpers/cleanHtml.js' import { cleanHtml } from '../middleware/helpers/cleanHtml.js'
const fields = ['content', 'contentExcerpt', 'reasonDescription'] // exclamation mark separetes field names, that should not be sanitized
const fields = [
'content',
'contentExcerpt',
'reasonDescription',
'description!embed',
'descriptionExcerpt',
]
export default { export default {
Mutation: async (resolve, root, args, context, info) => { Mutation: async (resolve, root, args, context, info) => {
args = walkRecursive(args, fields, cleanHtml) args = walkRecursive(args, fields, info.fieldName, cleanHtml)
return resolve(root, args, context, info) return resolve(root, args, context, info)
}, },
Query: async (resolve, root, args, context, info) => { Query: async (resolve, root, args, context, info) => {
const result = await resolve(root, args, context, info) const result = await resolve(root, args, context, info)
return walkRecursive(result, fields, cleanHtml) return walkRecursive(result, fields, info.fieldName, cleanHtml)
}, },
} }

View File

@ -1,6 +1,7 @@
import { cleanDatabase } from '../db/factories' import { cleanDatabase } from '../db/factories'
import { getNeode } from '../db/neo4j' import { getNeode, getDriver } from '../db/neo4j'
const driver = getDriver()
const neode = getNeode() const neode = getNeode()
beforeAll(async () => { beforeAll(async () => {
@ -9,6 +10,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543
@ -37,11 +39,10 @@ describe('slug', () => {
) )
}) })
it('must be unique', async (done) => { it('must be unique', async () => {
await neode.create('User', { slug: 'Matt' }) await neode.create('User', { slug: 'Matt' })
try { try {
await expect(neode.create('User', { slug: 'Matt' })).rejects.toThrow('already exists') await expect(neode.create('User', { slug: 'Matt' })).rejects.toThrow('already exists')
done()
} catch (error) { } catch (error) {
throw new Error(` throw new Error(`
${error} ${error}

View File

@ -11,7 +11,7 @@ export default makeAugmentedSchema({
'Badge', 'Badge',
'Embed', 'Embed',
'EmailAddress', 'EmailAddress',
'Notfication', 'Notification',
'Statistics', 'Statistics',
'LoggedInUser', 'LoggedInUser',
'Location', 'Location',

View File

@ -0,0 +1,7 @@
import { GraphQLUpload } from 'graphql-upload'
export default {
// This maps the `Upload` scalar to the implementation provided
// by the `graphql-upload` package.
Upload: GraphQLUpload,
}

View File

@ -25,6 +25,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -37,6 +37,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('donations', () => { describe('donations', () => {

View File

@ -40,7 +40,9 @@ export default {
} }
// check email does not belong to anybody // check email does not belong to anybody
await existingEmailAddress({ args, context }) const existingEmail = await existingEmailAddress({ args, context })
if (existingEmail && existingEmail.alreadyExistingEmail && existingEmail.user)
return existingEmail.alreadyExistingEmail
const nonce = generateNonce() const nonce = generateNonce()
const { const {

View File

@ -30,6 +30,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -134,11 +135,17 @@ describe('AddEmailAddress', () => {
}) })
describe('but if another user owns an `EmailAddress` already with that email', () => { describe('but if another user owns an `EmailAddress` already with that email', () => {
it('throws UserInputError because of unique constraints', async () => { it('does not throw UserInputError', async () => {
await Factory.build('user', {}, { email: 'new-email@example.org' }) await Factory.build('user', {}, { email: 'new-email@example.org' })
await expect(mutate({ mutation, variables })).resolves.toMatchObject({ await expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null }, data: {
errors: [{ message: 'A user account with this email already exists.' }], AddEmailAddress: {
createdAt: expect.any(String),
verifiedAt: null,
email: 'new-email@example.org',
},
},
errors: undefined,
}) })
}) })
}) })

View File

@ -96,7 +96,7 @@ describe('Query', () => {
description: null, description: null,
html: null, html: null,
image: null, image: null,
lang: null, lang: 'false',
publisher: null, publisher: null,
sources: ['resource'], sources: ['resource'],
title: null, title: null,

View File

@ -71,6 +71,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -97,7 +97,7 @@ export default {
if (isMember) { if (isMember) {
cypher = `MATCH (user:User)-[membership:MEMBER_OF]->(group:Group) cypher = `MATCH (user:User)-[membership:MEMBER_OF]->(group:Group)
WHERE user.id = $userId WHERE user.id = $userId
AND membership.role IN ['usual', 'admin', 'owner'] AND membership.role IN ['usual', 'admin', 'owner', 'pending']
RETURN toString(count(group)) AS count` RETURN toString(count(group)) AS count`
} else { } else {
cypher = `MATCH (group:Group) cypher = `MATCH (group:Group)

View File

@ -8,7 +8,7 @@ import {
changeGroupMemberRoleMutation, changeGroupMemberRoleMutation,
groupMembersQuery, groupMembersQuery,
groupQuery, groupQuery,
} from '../../db/graphql/groups' } from '../../graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j' import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server' import createServer from '../../server'
import CONFIG from '../../config' import CONFIG from '../../config'
@ -252,6 +252,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('in mode', () => { describe('in mode', () => {

View File

@ -1,5 +1,3 @@
import { UserInputError } from 'apollo-server'
export default async function alreadyExistingMail({ args, context }) { export default async function alreadyExistingMail({ args, context }) {
const session = context.driver.session() const session = context.driver.session()
try { try {
@ -20,9 +18,11 @@ export default async function alreadyExistingMail({ args, context }) {
}) })
}) })
const [emailBelongsToUser] = await existingEmailAddressTxPromise const [emailBelongsToUser] = await existingEmailAddressTxPromise
const { alreadyExistingEmail, user } = emailBelongsToUser || {} /*
if (user) throw new UserInputError('A user account with this email already exists.') const { alreadyExistingEmail, user } =
return alreadyExistingEmail if (user) throw new UserInputError('A user account with this email already exists.')
*/
return emailBelongsToUser || {}
} finally { } finally {
session.close() session.close()
} }

View File

@ -113,10 +113,11 @@ const sanitizeRelationshipType = (relationshipType) => {
const localFileUpload = ({ createReadStream, uniqueFilename }) => { const localFileUpload = ({ createReadStream, uniqueFilename }) => {
const destination = `/uploads/${uniqueFilename}` const destination = `/uploads/${uniqueFilename}`
return new Promise((resolve, reject) => return new Promise((resolve, reject) =>
createReadStream() createReadStream().pipe(
.pipe(createWriteStream(`public${destination}`)) createWriteStream(`public${destination}`)
.on('finish', () => resolve(destination)) .on('finish', () => resolve(destination))
.on('error', reject), .on('error', (error) => reject(error)),
),
) )
} }

View File

@ -15,6 +15,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -78,7 +79,7 @@ describe('deleteImage', () => {
await expect(someString).toEqual('Hello') await expect(someString).toEqual('Hello')
}) })
it('rolls back the transaction in case of errors', async (done) => { it('rolls back the transaction in case of errors', async () => {
await expect(neode.all('Image')).resolves.toHaveLength(1) await expect(neode.all('Image')).resolves.toHaveLength(1)
const session = driver.session() const session = driver.session()
try { try {
@ -93,7 +94,6 @@ describe('deleteImage', () => {
// nothing has been deleted // nothing has been deleted
await expect(neode.all('Image')).resolves.toHaveLength(1) await expect(neode.all('Image')).resolves.toHaveLength(1)
// all good // all good
done()
} finally { } finally {
session.close() session.close()
} }
@ -239,7 +239,7 @@ describe('mergeImage', () => {
}) })
}) })
it('rolls back the transaction in case of errors', async (done) => { it('rolls back the transaction in case of errors', async () => {
const session = driver.session() const session = driver.session()
try { try {
await session.writeTransaction(async (transaction) => { await session.writeTransaction(async (transaction) => {
@ -254,7 +254,6 @@ describe('mergeImage', () => {
// nothing has been created // nothing has been created
await expect(neode.all('Image')).resolves.toHaveLength(0) await expect(neode.all('Image')).resolves.toHaveLength(0)
// all good // all good
done()
} finally { } finally {
session.close() session.close()
} }

View File

@ -52,6 +52,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('inviteCodes', () => { describe('inviteCodes', () => {

View File

@ -26,6 +26,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -70,6 +70,7 @@ describe('moderate resources', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -29,6 +29,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -38,6 +38,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(() => { beforeEach(() => {

View File

@ -56,6 +56,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -6,19 +6,19 @@ import {
createGroupMutation, createGroupMutation,
changeGroupMemberRoleMutation, changeGroupMemberRoleMutation,
leaveGroupMutation, leaveGroupMutation,
} from '../../db/graphql/groups' } from '../../graphql/groups'
import { import {
createPostMutation, createPostMutation,
postQuery, postQuery,
filterPosts, filterPosts,
profilePagePosts, profilePagePosts,
searchPosts, searchPosts,
} from '../../db/graphql/posts' } from '../../graphql/posts'
import { createCommentMutation } from '../../db/graphql/comments' import { createCommentMutation } from '../../graphql/comments'
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups' import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '../../constants/groups'
import CONFIG from '../../config' import CONFIG from '../../config'
import { signupVerificationMutation } from '../../db/graphql/authentications' import { signupVerificationMutation } from '../../graphql/authentications'
CONFIG.CATEGORIES_ACTIVE = false CONFIG.CATEGORIES_ACTIVE = false
@ -61,6 +61,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('Posts in Groups', () => { describe('Posts in Groups', () => {

View File

@ -13,7 +13,12 @@ export default {
args.nonce = generateNonce() args.nonce = generateNonce()
args.email = normalizeEmail(args.email) args.email = normalizeEmail(args.email)
let emailAddress = await existingEmailAddress({ args, context }) let emailAddress = await existingEmailAddress({ args, context })
if (emailAddress) return emailAddress /*
if (emailAddress.user) {
// what to do?
}
*/
if (emailAddress.alreadyExistingEmail) return emailAddress.alreadyExistingEmail
try { try {
emailAddress = await neode.create('EmailAddress', args) emailAddress = await neode.create('EmailAddress', args)
return emailAddress.toJson() return emailAddress.toJson()

View File

@ -29,6 +29,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {
@ -118,9 +119,9 @@ describe('Signup', () => {
await emailAddress.relateTo(user, 'belongsTo') await emailAddress.relateTo(user, 'belongsTo')
}) })
it('throws UserInputError error because of unique constraint violation', async () => { it('does not throw UserInputError error', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({ await expect(mutate({ mutation, variables })).resolves.toMatchObject({
errors: [{ message: 'A user account with this email already exists.' }], data: { Signup: { email: 'someuser@example.org' } },
}) })
}) })
}) })

View File

@ -117,6 +117,7 @@ describe('file a report on a resource', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -33,6 +33,7 @@ describe('rewards', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -26,6 +26,8 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
neode.close()
}) })
const searchQuery = gql` const searchQuery = gql`

View File

@ -51,6 +51,7 @@ describe('shout and unshout posts', () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(async () => { beforeEach(async () => {

View File

@ -12,6 +12,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('SocialMedia', () => { describe('SocialMedia', () => {

View File

@ -39,6 +39,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -60,6 +60,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('resolvers/userData', () => { describe('resolvers/userData', () => {

View File

@ -2,14 +2,16 @@ import jwt from 'jsonwebtoken'
import CONFIG from './../../config' import CONFIG from './../../config'
import Factory, { cleanDatabase } from '../../db/factories' import Factory, { cleanDatabase } from '../../db/factories'
import { gql } from '../../helpers/jest' import { gql } from '../../helpers/jest'
import { loginMutation } from '../../db/graphql/userManagement' import { loginMutation } from '../../graphql/userManagement'
import { createTestClient } from 'apollo-server-testing' import { createTestClient } from 'apollo-server-testing'
import createServer, { context } from '../../server' import createServer, { context } from '../../server'
import encode from '../../jwt/encode' import encode from '../../jwt/encode'
import { getNeode } from '../../db/neo4j' import { getNeode, getDriver } from '../../db/neo4j'
import { categories } from '../../constants/categories' import { categories } from '../../constants/categories'
const neode = getNeode() const neode = getNeode()
const driver = getDriver()
let query, mutate, variables, req, user let query, mutate, variables, req, user
const disable = async (id) => { const disable = async (id) => {
@ -47,6 +49,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(() => { beforeEach(() => {
@ -137,7 +140,12 @@ describe('currentUser', () => {
describe('authenticated', () => { describe('authenticated', () => {
describe('and corresponding user in the database', () => { describe('and corresponding user in the database', () => {
let avatar
beforeEach(async () => { beforeEach(async () => {
avatar = await Factory.build('image', {
url: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
})
await Factory.build( await Factory.build(
'user', 'user',
{ {
@ -149,9 +157,7 @@ describe('currentUser', () => {
}, },
{ {
email: 'test@example.org', email: 'test@example.org',
avatar: Factory.build('image', { avatar,
url: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
}),
}, },
) )
const userBearerToken = encode({ id: 'u3' }) const userBearerToken = encode({ id: 'u3' })
@ -163,9 +169,11 @@ describe('currentUser', () => {
data: { data: {
currentUser: { currentUser: {
id: 'u3', id: 'u3',
avatar: Factory.build('image', { avatar: {
url: 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg', url: expect.stringContaining(
}), 'https://s3.amazonaws.com/uifaces/faces/twitter/jimmuirhead/128.jpg',
),
},
email: 'test@example.org', email: 'test@example.org',
name: 'Matilde Hermiston', name: 'Matilde Hermiston',
slug: 'matilde-hermiston', slug: 'matilde-hermiston',
@ -243,7 +251,7 @@ describe('login', () => {
describe('ask for a `token`', () => { describe('ask for a `token`', () => {
describe('with a valid email/password combination', () => { describe('with a valid email/password combination', () => {
it('responds with a JWT bearer token', async (done) => { it('responds with a JWT bearer token', async () => {
const { const {
data: { login: token }, data: { login: token },
} = await mutate({ mutation: loginMutation, variables }) } = await mutate({ mutation: loginMutation, variables })
@ -252,7 +260,6 @@ describe('login', () => {
id: 'acb2d923-f3af-479e-9f00-61b12e864666', id: 'acb2d923-f3af-479e-9f00-61b12e864666',
}) })
expect(err).toBeNull() expect(err).toBeNull()
done()
}) })
}) })

View File

@ -81,6 +81,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543

View File

@ -90,6 +90,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(() => { beforeEach(() => {

View File

@ -18,6 +18,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
beforeEach(() => { beforeEach(() => {

View File

@ -28,6 +28,7 @@ beforeAll(async () => {
afterAll(async () => { afterAll(async () => {
await cleanDatabase() await cleanDatabase()
driver.close()
}) })
describe('count post teaser views', () => { describe('count post teaser views', () => {

View File

@ -12,6 +12,7 @@ import { RedisPubSub } from 'graphql-redis-subscriptions'
import { PubSub } from 'graphql-subscriptions' import { PubSub } from 'graphql-subscriptions'
import Redis from 'ioredis' import Redis from 'ioredis'
import bodyParser from 'body-parser' import bodyParser from 'body-parser'
import { graphqlUploadExpress } from 'graphql-upload'
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG
@ -67,6 +68,7 @@ const createServer = (options) => {
}, },
}, },
debug: !!CONFIG.DEBUG, debug: !!CONFIG.DEBUG,
uploads: false,
tracing: !!CONFIG.DEBUG, tracing: !!CONFIG.DEBUG,
formatError: (error) => { formatError: (error) => {
if (error.message === 'ERROR_VALIDATION') { if (error.message === 'ERROR_VALIDATION') {
@ -85,6 +87,7 @@ const createServer = (options) => {
app.use(express.static('public')) app.use(express.static('public'))
app.use(bodyParser.json({ limit: '10mb' })) app.use(bodyParser.json({ limit: '10mb' }))
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true })) app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }))
app.use(graphqlUploadExpress())
server.applyMiddleware({ app, path: '/' }) server.applyMiddleware({ app, path: '/' })
const httpServer = http.createServer(app) const httpServer = http.createServer(app)
server.installSubscriptionHandlers(httpServer) server.installSubscriptionHandlers(httpServer)

8
backend/test/setup.js Normal file
View File

@ -0,0 +1,8 @@
// Polyfill missing encoders in jsdom
// https://stackoverflow.com/questions/68468203/why-am-i-getting-textencoder-is-not-defined-in-jest
import { TextEncoder, TextDecoder } from 'util'
global.TextEncoder = TextEncoder
global.TextDecoder = TextDecoder
// Metascraper takes longer nowadays, double time
jest.setTimeout(10000)

File diff suppressed because it is too large Load Diff

View File

@ -13,29 +13,29 @@ Feature: User profile - list social media accounts
When I navigate to page "/settings/my-social-media" When I navigate to page "/settings/my-social-media"
Then I am on page "/settings/my-social-media" Then I am on page "/settings/my-social-media"
When I add a social media link When I add a social media link
Then I see a toaster with "Added social media" Then I see a toaster with status "success"
And the new social media link shows up on the page And the new social media link shows up on the page
Scenario: Other users viewing my Social Media Scenario: Other users viewing my Social Media
Given I have added a social media link Given I have added the social media link "https://freeradical.zone/peter-pan"
When I navigate to page "/profile/peter-pan" When I navigate to page "/profile/peter-pan"
Then they should be able to see my social media links Then they should be able to see my social media links
Scenario: Deleting Social Media Scenario: Deleting Social Media
When I navigate to page "/settings/my-social-media" When I navigate to page "/settings/my-social-media"
Then I am on page "/settings/my-social-media" Then I am on page "/settings/my-social-media"
Given I have added a social media link Given I have added the social media link "https://freeradical.zone/peter-pan"
When I delete a social media link When I delete the social media link "https://freeradical.zone/peter-pan"
Then I see a toaster with "Deleted social media" Then I see a toaster with status "success"
Scenario: Editing Social Media Scenario: Editing Social Media
When I navigate to page "/settings/my-social-media" When I navigate to page "/settings/my-social-media"
Then I am on page "/settings/my-social-media" Then I am on page "/settings/my-social-media"
Given I have added a social media link Given I have added the social media link "https://freeradical.zone/peter-pan"
When I start editing a social media link When I start editing a social media link
Then I can cancel editing Then I can cancel editing
When I start editing a social media link When I start editing a social media link
And I edit and save the link And I edit and save the link
Then I see a toaster with "Added social media" Then I see a toaster with status "success"
And the new url is displayed And the new url is displayed
But the old url is not displayed But the old url is not displayed

View File

@ -1,6 +1,12 @@
import { Then } from "@badeball/cypress-cucumber-preprocessor"; import { Then } from "@badeball/cypress-cucumber-preprocessor";
Then('I can see my new name {string} when I click on my profile picture in the top right', name => { Then('I can see my new name {string} when I click on my profile picture in the top right', name => {
cy.get(".avatar-menu").then(($menu) => {
if (!$menu.is(':visible')){
cy.scrollTo("top");
cy.wait(500);
}
})
cy.get('.avatar-menu').click() // open cy.get('.avatar-menu').click() // open
cy.get('.avatar-menu-popover').contains(name) cy.get('.avatar-menu-popover').contains(name)
cy.get('.avatar-menu').click() // close again cy.get('.avatar-menu').click() // close again

View File

@ -1,12 +1,10 @@
import { When } from "@badeball/cypress-cucumber-preprocessor"; import { When } from "@badeball/cypress-cucumber-preprocessor";
When('I add a social media link', () => { When('I add a social media link', () => {
cy.get('button') cy.get('[data-test="add-save-button"]')
.contains('Add link')
.click() .click()
.get('#editSocialMedia') .get('#editSocialMedia')
.type('https://freeradical.zone/peter-pan') .type('https://freeradical.zone/peter-pan')
.get('button') .get('[data-test="add-save-button"]')
.contains('Save')
.click() .click()
}) })

View File

@ -0,0 +1,12 @@
import { When } from "cypress-cucumber-preprocessor/steps";
When('I delete the social media link {string}', (link) => {
cy.get('[data-test="delete-button"]')
.click()
cy.get('[data-test="confirm-modal"]')
.should("be.visible")
cy.get('[data-test="confirm-button"]')
.click()
cy.get('.ds-list-item-content > a')
.contains(link).should('not.exist')
})

View File

@ -4,7 +4,6 @@ When('I edit and save the link', () => {
cy.get('input#editSocialMedia') cy.get('input#editSocialMedia')
.clear() .clear()
.type('https://freeradical.zone/tinkerbell') .type('https://freeradical.zone/tinkerbell')
.get('button') .get('[data-test="add-save-button"]')
.contains('Save')
.click() .click()
}) })

View File

@ -0,0 +1,13 @@
import { Given } from "cypress-cucumber-preprocessor/steps";
Given('I have added the social media link {string}', (link) => {
cy.visit('/settings/my-social-media')
.get('[data-test="add-save-button"]')
.click()
.get('#editSocialMedia')
.type(link)
.get('[data-test="add-save-button"]')
.click()
cy.get('.ds-list-item-content > a')
.contains(link)
})

View File

@ -1,6 +1,6 @@
import { When } from "@badeball/cypress-cucumber-preprocessor"; import { When } from "@badeball/cypress-cucumber-preprocessor";
When('I start editing a social media link', () => { When('I start editing a social media link', () => {
cy.get(".base-button[title='Edit']") cy.get('[data-test="edit-button"]')
.click() .click()
}) })

View File

@ -1,8 +1,8 @@
import { Then } from "@badeball/cypress-cucumber-preprocessor"; import { Then } from "@badeball/cypress-cucumber-preprocessor";
Then('they should be able to see my social media links', () => { Then('they should be able to see my social media links', () => {
cy.get('.base-card') cy.get('[data-test="social-media-list-headline"]')
.contains('Where else can I find Peter Pan?') .contains('Peter Pan')
.get('a[href="https://freeradical.zone/peter-pan"]') .get('a[href="https://freeradical.zone/peter-pan"]')
.should('have.length', 1) .should('have.length', 1)
}) })

View File

@ -1,6 +1,12 @@
import { When } from "@badeball/cypress-cucumber-preprocessor"; import { When } from "@badeball/cypress-cucumber-preprocessor";
When("I log out", () => { When("I log out", () => {
cy.get(".avatar-menu").then(($menu) => {
if (!$menu.is(':visible')){
cy.scrollTo("top");
cy.wait(500);
}
})
cy.get(".avatar-menu") cy.get(".avatar-menu")
.click(); .click();
cy.get(".avatar-menu-popover") cy.get(".avatar-menu-popover")

View File

@ -0,0 +1,9 @@
import { Then } from "cypress-cucumber-preprocessor/steps";
Then("I see a toaster with status {string}", (status) => {
switch (status) {
case "success":
cy.get(".iziToast.iziToast-color-green").should("be.visible");
break;
}
})

View File

@ -20,7 +20,6 @@ services:
- GRAPHQL_URI=http://localhost:4000 - GRAPHQL_URI=http://localhost:4000
- CLIENT_URI=http://localhost:3000 - CLIENT_URI=http://localhost:3000
- JWT_SECRET=b/&&7b78BF&fv/Vd - JWT_SECRET=b/&&7b78BF&fv/Vd
- MAPBOX_TOKEN=pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g
- PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78 - PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78
- NEO4J_apoc_import_file_enabled=true - NEO4J_apoc_import_file_enabled=true
- "SSH_USERNAME=${SSH_USERNAME}" - "SSH_USERNAME=${SSH_USERNAME}"

View File

@ -98,22 +98,22 @@ On a server with Kubernetes cluster:
$ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "yarn prod:migrate init" $ kubectl -n default exec -it $(kubectl -n default get pods | grep ocelot-backend | awk '{ print $1 }') -- /bin/sh -c "yarn prod:migrate init"
``` ```
***Cypher commands to show indexes and contraints*** ***Cypher commands to show indexes and constraints***
```bash ```bash
# in browser command line or cypher shell # in browser command line or cypher shell
# show all indexes and contraints # show all indexes and constraints
$ :schema $ :schema
# show all indexes # show all indexes
$ CALL db.indexes(); $ CALL db.indexes();
# show all contraints # show all constraints
$ CALL db.constraints(); $ CALL db.constraints();
``` ```
***Cypher commands to create and drop indexes and contraints*** ***Cypher commands to create and drop indexes and constraints***
```bash ```bash
# in browser command line or cypher shell # in browser command line or cypher shell
@ -126,6 +126,6 @@ $ CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"]);
# drop an index # drop an index
$ DROP CONSTRAINT ON ( image:Image ) ASSERT image.url IS UNIQUE $ DROP CONSTRAINT ON ( image:Image ) ASSERT image.url IS UNIQUE
# drop all indexes and contraints # drop all indexes and constraints
$ CALL apoc.schema.assert({},{},true) YIELD label, key RETURN * ; $ CALL apoc.schema.assert({},{},true) YIELD label, key RETURN * ;
``` ```

View File

@ -1,6 +1,6 @@
{ {
"name": "ocelot-social", "name": "ocelot-social",
"version": "2.2.0", "version": "2.4.0",
"description": "Free and open source software program code available to run social networks.", "description": "Free and open source software program code available to run social networks.",
"author": "ocelot.social Community", "author": "ocelot.social Community",
"license": "MIT", "license": "MIT",
@ -15,8 +15,8 @@
"scripts": { "scripts": {
"db:seed": "cd backend && yarn run db:seed", "db:seed": "cd backend && yarn run db:seed",
"db:reset": "cd backend && yarn run db:reset", "db:reset": "cd backend && yarn run db:reset",
"cypress:run": "cypress run --browser electron --config-file ./cypress/cypress.config.js", "cypress:run": "cypress run --browser electron --config-file ./cypress/cypress.json",
"cypress:open": "cypress open --browser electron --config-file ./cypress/cypress.config.js", "cypress:open": "cypress open --browser electron --config-file ./cypress/cypress.json",
"cucumber:setup": "cd backend && yarn run dev", "cucumber:setup": "cd backend && yarn run dev",
"cucumber": "wait-on tcp:4000 && cucumber-js --require-module @babel/register --exit", "cucumber": "wait-on tcp:4000 && cucumber-js --require-module @babel/register --exit",
"release": "yarn version --no-git-tag-version --no-commit-hooks --no-commit && auto-changelog --latest-version $(node -p -e \"require('./package.json').version\") && cd backend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp/maintenance/source && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../../../package.json').version\")" "release": "yarn version --no-git-tag-version --no-commit-hooks --no-commit && auto-changelog --latest-version $(node -p -e \"require('./package.json').version\") && cd backend && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../package.json').version\") && cd ../webapp/maintenance/source && yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version $(node -p -e \"require('./../../../package.json').version\")"
@ -25,15 +25,15 @@
"@babel/core": "^7.9.0", "@babel/core": "^7.9.0",
"@babel/preset-env": "^7.12.7", "@babel/preset-env": "^7.12.7",
"@babel/register": "^7.12.10", "@babel/register": "^7.12.10",
"@badeball/cypress-cucumber-preprocessor": "^13.0.3",
"@cypress/browserify-preprocessor": "^3.0.2",
"@faker-js/faker": "5.1.0", "@faker-js/faker": "5.1.0",
"auto-changelog": "^2.3.0", "auto-changelog": "^2.3.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"codecov": "^3.8.2", "codecov": "^3.8.2",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"cypress": "^10.11.0", "cucumber": "^6.0.5",
"cypress-file-upload": "^5.0.8", "cypress": "^7.0.1",
"cypress-cucumber-preprocessor": "^2.2.1",
"cypress-file-upload": "^3.5.3",
"date-fns": "^2.25.0", "date-fns": "^2.25.0",
"dotenv": "^8.2.0", "dotenv": "^8.2.0",
"expect": "^25.3.0", "expect": "^25.3.0",
@ -48,7 +48,8 @@
"slug": "^6.0.0" "slug": "^6.0.0"
}, },
"resolutions": { "resolutions": {
"set-value": "^2.0.1" "set-value": "^2.0.1",
"nan": "2.17.0"
}, },
"dependencies": {} "dependencies": {}
} }

View File

@ -1,7 +1,8 @@
SENTRY_DSN_WEBAPP= SENTRY_DSN_WEBAPP=
COMMIT= COMMIT=
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
WEBSOCKETS_URI=ws://localhost:3000/api/graphql WEBSOCKETS_URI=ws://localhost:3000/api/graphql
GRAPHQL_URI=http://localhost:4000/ GRAPHQL_URI=http://localhost:4000/
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
CATEGORIES_ACTIVE=false CATEGORIES_ACTIVE=false

View File

@ -1 +1 @@
v12.19.0 v19.4.0

View File

@ -1,7 +1,7 @@
################################################################################## ##################################################################################
# BASE (Is pushed to DockerHub for rebranding) ################################### # BASE (Is pushed to DockerHub for rebranding) ###################################
################################################################################## ##################################################################################
FROM node:12.19.0-alpine3.10 as base FROM node:19.4.0-alpine3.17 as base
# ENVs # ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame ## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame

View File

@ -1,7 +1,7 @@
################################################################################## ##################################################################################
# BASE ########################################################################### # BASE ###########################################################################
################################################################################## ##################################################################################
FROM node:12.19.0-alpine3.10 as base FROM node:19.4.0-alpine3.17 as base
# ENVs # ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame ## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame

View File

@ -4,18 +4,32 @@
## Installation ## Installation
For preparation we need Node and recommend to use [node version manager](https://github.com/nvm-sh/nvm) `nvm` to switch
between different local Node versions:
```bash
# install Node
$ cd webapp
$ nvm install v16.19.0
$ nvm use v16.19.0
```
Install node dependencies with [yarn](https://yarnpkg.com/en/):
```bash ```bash
# install all dependencies # install all dependencies
$ cd webapp/ $ cd webapp
$ yarn install $ yarn install
# or just # or just
$ yarn $ yarn
# or just later on to use version of ".nvmrc" file
$ nvm use && yarn
``` ```
Copy: Copy:
```text ```text
# in webapp/ # in webapp
cp .env.template .env cp .env.template .env
``` ```

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>angle-up</title>
<path d="M16 6.594l0.719 0.688 12.5 12.5-1.438 1.438-11.781-11.781-11.781 11.781-1.438-1.438 12.5-12.5z"></path>
</svg>

After

Width:  |  Height:  |  Size: 275 B

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 121 KiB

View File

@ -3,28 +3,28 @@
* @presenter Color * @presenter Color
*/ */
$color-primary: rgb(23, 181, 63); $color-primary: rgb(23, 181, 63);
$color-primary-light: rgb(96, 214, 98); $color-primary-light: rgb(96, 214, 98);
$color-primary-dark: rgb(25, 122, 49); $color-primary-dark: rgb(25, 122, 49);
$color-primary-active: rgb(25, 194, 67); $color-primary-active: rgb(25, 194, 67);
$color-primary-inverse: rgb(241, 253, 244); $color-primary-inverse: rgb(241, 253, 244);
$color-secondary: rgb(0, 142, 230); $color-secondary: rgb(0, 142, 230);
$color-secondary-active: rgb(10, 161, 255); $color-secondary-active: rgb(10, 161, 255);
$color-secondary-inverse: rgb(240, 249, 255); $color-secondary-inverse: rgb(240, 249, 255);
$color-success: rgb(23, 181, 63); $color-success: rgb(23, 181, 63);
$color-success-active: rgb(26, 203, 71); $color-success-active: rgb(26, 203, 71);
$color-success-inverse: rgb(241, 253, 244); $color-success-inverse: rgb(241, 253, 244);
$color-danger: rgb(219, 57, 36); $color-danger: rgb(219, 57, 36);
$color-danger-light: rgb(242, 97, 65); $color-danger-light: rgb(242, 97, 65);
$color-danger-dark: rgb(158, 43, 28); $color-danger-dark: rgb(158, 43, 28);
$color-danger-active: rgb(224, 81, 62); $color-danger-active: rgb(224, 81, 62);
$color-danger-inverse: rgb(253, 243, 242); $color-danger-inverse: rgb(253, 243, 242);
$color-warning: rgb(230, 121, 25); $color-warning: rgb(230, 121, 25);
$color-warning-active: rgb(233, 137, 53); $color-warning-active: rgb(233, 137, 53);
$color-warning-inverse: rgb(253, 247, 241); $color-warning-inverse: rgb(253, 247, 241);
$color-yellow: rgb(245, 196, 0); $color-yellow: rgb(245, 196, 0);
$color-yellow-active: rgb(255, 206, 10); $color-yellow-active: rgb(255, 206, 10);
$color-yellow-inverse: rgb(255, 252, 240); $color-yellow-inverse: rgb(255, 252, 240);
/** /**
* @tokens Color Neutral * @tokens Color Neutral

View File

@ -31,7 +31,10 @@
slot-scope="item" slot-scope="item"
:route="item.route" :route="item.route"
:parents="item.parents" :parents="item.parents"
@click.native="closeMenu(false)" @click.native="
closeMenu(false)
$emit('toggle-Mobile-Menu-view')
"
> >
<base-icon :name="item.route.icon" /> <base-icon :name="item.route.icon" />
{{ item.route.name }} {{ item.route.name }}

View File

@ -23,6 +23,7 @@
import CategoryQuery from '~/graphql/CategoryQuery' import CategoryQuery from '~/graphql/CategoryQuery'
import { CATEGORIES_MAX } from '~/constants/categories.js' import { CATEGORIES_MAX } from '~/constants/categories.js'
import xor from 'lodash/xor' import xor from 'lodash/xor'
import SortCategories from '~/mixins/sortCategoriesMixin.js'
export default { export default {
inject: { inject: {
@ -30,6 +31,7 @@ export default {
default: null, default: null,
}, },
}, },
mixins: [SortCategories],
props: { props: {
existingCategoryIds: { type: Array, default: () => [] }, existingCategoryIds: { type: Array, default: () => [] },
model: { type: String, required: true }, model: { type: String, required: true },
@ -72,7 +74,7 @@ export default {
return CategoryQuery() return CategoryQuery()
}, },
result({ data: { Category } }) { result({ data: { Category } }) {
this.categories = Category this.categories = this.sortCategories(Category)
}, },
}, },
}, },

View File

@ -1,5 +1,5 @@
<template> <template>
<ds-tag class="category-tag"> <ds-tag class="category-tag" :class="filterActive ? 'filterActive' : ''">
<base-icon :name="icon" /> <base-icon :name="icon" />
{{ name }} {{ name }}
</ds-tag> </ds-tag>
@ -11,6 +11,7 @@ export default {
props: { props: {
icon: { type: String, required: true }, icon: { type: String, required: true },
name: { type: String, default: '' }, name: { type: String, default: '' },
filterActive: { type: Boolean, default: false, required: false },
}, },
} }
</script> </script>
@ -28,4 +29,7 @@ export default {
font-size: $font-size-base; font-size: $font-size-base;
} }
} }
.filterActive {
background-color: $color-success-active;
}
</style> </style>

View File

@ -6,7 +6,7 @@
:filled="!filteredCategoryIds.length" :filled="!filteredCategoryIds.length"
:label="$t('filter-menu.all')" :label="$t('filter-menu.all')"
icon="check" icon="check"
@click="resetCategories" @click="setResetCategories"
/> />
</li> </li>
<li class="item item-save-topics"> <li class="item item-save-topics">
@ -39,15 +39,14 @@ import CategoryQuery from '~/graphql/CategoryQuery.js'
import SaveCategories from '~/graphql/SaveCategories.js' import SaveCategories from '~/graphql/SaveCategories.js'
import FilterMenuSection from '~/components/FilterMenu/FilterMenuSection' import FilterMenuSection from '~/components/FilterMenu/FilterMenuSection'
import LabeledButton from '~/components/_new/generic/LabeledButton/LabeledButton' import LabeledButton from '~/components/_new/generic/LabeledButton/LabeledButton'
import SortCategories from '~/mixins/sortCategoriesMixin.js'
export default { export default {
components: { components: {
FilterMenuSection, FilterMenuSection,
LabeledButton, LabeledButton,
}, },
props: { mixins: [SortCategories],
showMobileMenu: { type: Boolean, default: false },
},
data() { data() {
return { return {
categories: [], categories: [],
@ -63,6 +62,10 @@ export default {
resetCategories: 'posts/RESET_CATEGORIES', resetCategories: 'posts/RESET_CATEGORIES',
toggleCategory: 'posts/TOGGLE_CATEGORY', toggleCategory: 'posts/TOGGLE_CATEGORY',
}), }),
setResetCategories() {
this.resetCategories()
this.$emit('showFilterMenu')
},
saveCategories() { saveCategories() {
this.$apollo this.$apollo
.mutate({ .mutate({
@ -70,6 +73,7 @@ export default {
variables: { activeCategories: this.filteredCategoryIds }, variables: { activeCategories: this.filteredCategoryIds },
}) })
.then(() => { .then(() => {
this.$emit('showFilterMenu')
this.$toast.success(this.$t('filter-menu.save.success')) this.$toast.success(this.$t('filter-menu.save.success'))
}) })
.catch(() => { .catch(() => {
@ -84,7 +88,7 @@ export default {
}, },
update({ Category }) { update({ Category }) {
if (!Category) return [] if (!Category) return []
this.categories = Category this.categories = this.sortCategories(Category)
}, },
fetchPolicy: 'cache-and-network', fetchPolicy: 'cache-and-network',
}, },

View File

@ -11,15 +11,7 @@
<base-icon class="dropdown-arrow" name="angle-down" /> <base-icon class="dropdown-arrow" name="angle-down" />
</base-button> </base-button>
<template slot="popover"> <template slot="popover">
<div class="filter-menu-options"> <filter-menu-component />
<h2 class="title">{{ $t('filter-menu.filter-by') }}</h2>
<following-filter />
<categories-filter v-if="categoriesActive" :showMobileMenu="showMobileMenu" />
</div>
<div class="filter-menu-options">
<h2 class="title">{{ $t('filter-menu.order-by') }}</h2>
<order-by-filter />
</div>
</template> </template>
</dropdown> </dropdown>
</template> </template>
@ -27,26 +19,16 @@
<script> <script>
import Dropdown from '~/components/Dropdown' import Dropdown from '~/components/Dropdown'
import { mapGetters } from 'vuex' import { mapGetters } from 'vuex'
import FollowingFilter from './FollowingFilter' import FilterMenuComponent from './FilterMenuComponent'
import OrderByFilter from './OrderByFilter'
import CategoriesFilter from './CategoriesFilter'
export default { export default {
components: { components: {
Dropdown, Dropdown,
FollowingFilter, FilterMenuComponent,
OrderByFilter,
CategoriesFilter,
},
data() {
return {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
}
}, },
props: { props: {
placement: { type: String }, placement: { type: String },
offset: { type: [String, Number] }, offset: { type: [String, Number] },
showMobileMenu: { type: Boolean, default: false },
}, },
computed: { computed: {
...mapGetters({ ...mapGetters({
@ -55,14 +37,3 @@ export default {
}, },
} }
</script> </script>
<style lang="scss">
.filter-menu-options {
max-width: $size-max-width-filter-menu;
padding: $space-small $space-x-small;
> .title {
font-size: $font-size-large;
}
}
</style>

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