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

View File

@ -16,7 +16,7 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# TODO: DO STUFF ??? #####################################################
##########################################################################
@ -37,7 +37,7 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# NEO4J ##################################################################
##########################################################################
@ -46,7 +46,7 @@ jobs:
docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/
docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: docker-neo4j-image
path: /tmp/neo4j.tar
@ -63,7 +63,7 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# BUILD BACKEND DOCKER IMAGE (build) #####################################
##########################################################################
@ -72,7 +72,7 @@ jobs:
docker build --target test -t "ocelotsocialnetwork/backend:test" backend/
docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: docker-backend-test
path: /tmp/backend.tar
@ -89,7 +89,7 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
##########################################################################
@ -98,7 +98,7 @@ jobs:
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: docker-webapp-test
path: /tmp/webapp.tar
@ -115,12 +115,12 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
@ -144,12 +144,12 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGE ##################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
@ -173,19 +173,19 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-neo4j-image
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
@ -230,12 +230,12 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
@ -290,26 +290,26 @@ jobs:
# CHECKOUT CODE ##########################################################
##########################################################################
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v3
##########################################################################
# DOWNLOAD DOCKER IMAGES #################################################
##########################################################################
- name: Download Docker Image (Neo4J)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-neo4j-image
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/neo4j.tar
- name: Download Docker Image (Backend)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-backend-test
path: /tmp
- name: Load Docker Image
run: docker load < /tmp/backend.tar
- name: Download Docker Image (Webapp)
uses: actions/download-artifact@v2
uses: actions/download-artifact@v3
with:
name: docker-webapp-test
path: /tmp
@ -332,12 +332,12 @@ jobs:
# UPLOAD SCREENSHOTS & VIDEO #############################################
##########################################################################
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: cypress-screenshots
path: cypress/screenshots/
- name: Upload Artifact
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: 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).
#### [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)
- 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)
@ -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)
- 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)
- 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)
- 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)
- 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: 🍰 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)
@ -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: 🍰 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: 🍰 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: 🍰 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: 🍰 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: 🍰 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 `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)
- 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)
- 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: 🍰 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)
- 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)
@ -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)
- 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 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)
- 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)
- comment out LanguagesFilter, EmotionsFilter, fix tests, fix lint [`52dcd77`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/52dcd772fa81e02a0d95e89a9fc8232e70a09d28)
- add new yunite icons [`bb0d632`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/bb0d6329e7e36ea03671318ea8dd128a6d5a5a7a)
- cleanup refactor rebranding [`5f5c0fa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5f5c0faa1f28cd4df7681eba335ae5998b2d9cca)
- 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)

View File

@ -105,7 +105,7 @@ Sprint retrospective
## 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)
* 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.
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
@ -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
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) ###################################
##################################################################################
FROM node:12.19.0-alpine3.10 as base
FROM node:19.4.0-alpine3.17 as base
# ENVs
## 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
For the local installation you need a recent version of
[node](https://nodejs.org/en/) (&gt;= `v10.12.0`). We are using
`12.19.0` and therefore we recommend to use the same version
[Node](https://nodejs.org/en/) (&gt;= `v16.19.0`). We are using
`v19.4.0` and therefore we recommend to use the same version
([see](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4082)
some known problems with more recent node versions). You can use the
[node version manager](https://github.com/nvm-sh/nvm) to switch
between different local node versions.
[node version manager](https://github.com/nvm-sh/nvm) `nvm` to switch
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/):
@ -32,6 +39,10 @@ Install node dependencies with [yarn](https://yarnpkg.com/en/):
# in main folder
$ cd backend
$ yarn install
# or just
$ yarn
# or just later on to use version of ".nvmrc" file
$ nvm use && yarn
```
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",
"version": "2.2.0",
"version": "2.4.0",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
@ -15,29 +15,13 @@
"dev": "nodemon --exec babel-node src/ -e js,gql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql",
"lint": "eslint src --config .eslintrc.js",
"test": "cross-env NODE_ENV=test 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:reset": "yarn run db:clean",
"db:seed": "babel-node src/db/seed.js",
"db:migrate": "yarn run __migrate --store ./src/db/migrate/store.js",
"db:migrate:create": "yarn run __migrate --template-file ./src/db/migrate/template.js --date-format 'yyyymmddHHmmss' create"
},
"jest": {
"verbose": true,
"collectCoverageFrom": [
"**/*.js",
"!**/node_modules/**",
"!**/test/**",
"!**/dist/**",
"!**/src/**/?(*.)+(spec|test).js?(x)"
],
"coverageReporters": [
"lcov"
],
"testMatch": [
"**/src/**/?(*.)+(spec|test).js?(x)"
]
},
"dependencies": {
"@babel/cli": "~7.8.4",
"@babel/core": "~7.9.0",
@ -81,22 +65,22 @@
"linkifyjs": "~2.1.8",
"lodash": "~4.17.14",
"merge-graphql-schemas": "^1.7.8",
"metascraper": "^5.11.8",
"metascraper-audio": "^5.14.26",
"metascraper-author": "^5.14.22",
"metascraper": "^5.33.5",
"metascraper-audio": "^5.33.5",
"metascraper-author": "^5.33.5",
"metascraper-clearbit-logo": "^5.3.0",
"metascraper-date": "^5.11.8",
"metascraper-description": "^5.23.1",
"metascraper-image": "^5.11.8",
"metascraper-lang": "^5.23.1",
"metascraper-date": "^5.33.5",
"metascraper-description": "^5.33.5",
"metascraper-image": "^5.33.5",
"metascraper-lang": "^5.33.5",
"metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.14.26",
"metascraper-publisher": "^5.23.0",
"metascraper-soundcloud": "^5.23.0",
"metascraper-title": "^5.11.8",
"metascraper-url": "^5.14.26",
"metascraper-video": "^5.11.8",
"metascraper-youtube": "^5.23.0",
"metascraper-logo": "^5.33.5",
"metascraper-publisher": "^5.33.5",
"metascraper-soundcloud": "^5.33.5",
"metascraper-title": "^5.33.5",
"metascraper-url": "^5.33.5",
"metascraper-video": "^5.33.5",
"metascraper-youtube": "^5.33.5",
"migrate": "^1.7.0",
"mime-types": "^2.1.26",
"minimatch": "^3.0.4",
@ -132,13 +116,15 @@
"eslint-plugin-prettier": "~3.4.1",
"eslint-plugin-promise": "~4.3.1",
"eslint-plugin-standard": "~4.0.1",
"jest": "~25.3.0",
"jest": "29.4",
"nodemon": "~2.0.2",
"prettier": "~2.3.2",
"rosie": "^2.0.1",
"supertest": "~4.0.2"
},
"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 () => {
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

View File

@ -1,3 +1,3 @@
// 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

View File

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

View File

@ -9,9 +9,9 @@ import {
createGroupMutation,
joinGroupMutation,
changeGroupMemberRoleMutation,
} from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
} from '../graphql/groups'
import { createPostMutation } from '../graphql/posts'
import { createCommentMutation } from '../graphql/comments'
import { categories } from '../constants/categories'
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
* @property data Array
* @property fields Array
* @property fieldName String
* @property callback Function
*/
function walkRecursive(data, fields, callback, _key) {
function walkRecursive(data, fields, fieldName, callback, _key) {
if (!Array.isArray(fields)) {
throw new Error('please provide an fields array for the walkRecursive helper')
}
if (data && typeof data === 'string' && fields.includes(_key)) {
// 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)) {
// go into the rabbit hole and dig through that array
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') {
// lets get some keys and stir them
Object.keys(data).forEach((k) => {
data[k] = walkRecursive(data[k], fields, callback, k)
data[k] = walkRecursive(data[k], fields, fieldName, callback, k)
})
}
return data

View File

@ -36,6 +36,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,37 +122,41 @@ const isAllowedToChangeGroupMemberRole = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
const adminId = user.id
const currentUserId = user.id
const { groupId, userId, roleInGroup } = args
if (adminId === userId) return false
if (currentUserId === userId) return false
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
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})
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 {
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],
member: transactionResponse.records.map((record) => record.get('member'))[0],
}
})
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 (
!!group &&
!!admin &&
(!member ||
(!!member &&
(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)))
groupExists &&
currentUserExists &&
(!userIsMember || (userIsMember && (sameUserRoleInGroup || !userIsOwner))) &&
((currentUserIsAdmin && adminCanSetRole) || (currentUserIsOwner && ownerCanSetRole))
)
} catch (error) {
throw new Error(error)

View File

@ -28,6 +28,7 @@ describe('authorization', () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,15 +1,22 @@
import walkRecursive from '../helpers/walkRecursive'
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 {
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)
},
Query: async (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 { getNeode } from '../db/neo4j'
import { getNeode, getDriver } from '../db/neo4j'
const driver = getDriver()
const neode = getNeode()
beforeAll(async () => {
@ -9,6 +10,7 @@ beforeAll(async () => {
afterAll(async () => {
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
@ -37,11 +39,10 @@ describe('slug', () => {
)
})
it('must be unique', async (done) => {
it('must be unique', async () => {
await neode.create('User', { slug: 'Matt' })
try {
await expect(neode.create('User', { slug: 'Matt' })).rejects.toThrow('already exists')
done()
} catch (error) {
throw new Error(`
${error}

View File

@ -11,7 +11,7 @@ export default makeAugmentedSchema({
'Badge',
'Embed',
'EmailAddress',
'Notfication',
'Notification',
'Statistics',
'LoggedInUser',
'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 () => {
await cleanDatabase()
driver.close()
})
beforeEach(async () => {

View File

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

View File

@ -40,7 +40,9 @@ export default {
}
// 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 {

View File

@ -30,6 +30,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
})
beforeEach(async () => {
@ -134,11 +135,17 @@ describe('AddEmailAddress', () => {
})
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 expect(mutate({ mutation, variables })).resolves.toMatchObject({
data: { AddEmailAddress: null },
errors: [{ message: 'A user account with this email already exists.' }],
data: {
AddEmailAddress: {
createdAt: expect.any(String),
verifiedAt: null,
email: 'new-email@example.org',
},
},
errors: undefined,
})
})
})

View File

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

View File

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

View File

@ -97,7 +97,7 @@ export default {
if (isMember) {
cypher = `MATCH (user:User)-[membership:MEMBER_OF]->(group:Group)
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`
} else {
cypher = `MATCH (group:Group)

View File

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

View File

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

View File

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

View File

@ -15,6 +15,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
})
beforeEach(async () => {
@ -78,7 +79,7 @@ describe('deleteImage', () => {
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)
const session = driver.session()
try {
@ -93,7 +94,6 @@ describe('deleteImage', () => {
// nothing has been deleted
await expect(neode.all('Image')).resolves.toHaveLength(1)
// all good
done()
} finally {
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()
try {
await session.writeTransaction(async (transaction) => {
@ -254,7 +254,6 @@ describe('mergeImage', () => {
// nothing has been created
await expect(neode.all('Image')).resolves.toHaveLength(0)
// all good
done()
} finally {
session.close()
}

View File

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

View File

@ -26,6 +26,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -29,6 +29,7 @@ beforeAll(async () => {
afterAll(async () => {
await cleanDatabase()
driver.close()
})
beforeEach(async () => {
@ -118,9 +119,9 @@ describe('Signup', () => {
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({
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 () => {
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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,6 +39,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

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

View File

@ -81,6 +81,7 @@ beforeAll(async () => {
afterAll(async () => {
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

View File

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

View File

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

View File

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

View File

@ -12,6 +12,7 @@ import { RedisPubSub } from 'graphql-redis-subscriptions'
import { PubSub } from 'graphql-subscriptions'
import Redis from 'ioredis'
import bodyParser from 'body-parser'
import { graphqlUploadExpress } from 'graphql-upload'
export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED'
const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG
@ -67,6 +68,7 @@ const createServer = (options) => {
},
},
debug: !!CONFIG.DEBUG,
uploads: false,
tracing: !!CONFIG.DEBUG,
formatError: (error) => {
if (error.message === 'ERROR_VALIDATION') {
@ -85,6 +87,7 @@ const createServer = (options) => {
app.use(express.static('public'))
app.use(bodyParser.json({ limit: '10mb' }))
app.use(bodyParser.urlencoded({ limit: '10mb', extended: true }))
app.use(graphqlUploadExpress())
server.applyMiddleware({ app, path: '/' })
const httpServer = http.createServer(app)
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"
Then I am on page "/settings/my-social-media"
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
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"
Then they should be able to see my social media links
Scenario: Deleting Social Media
When I navigate to page "/settings/my-social-media"
Then I am on page "/settings/my-social-media"
Given I have added a social media link
When I delete a social media link
Then I see a toaster with "Deleted social media"
Given I have added the social media link "https://freeradical.zone/peter-pan"
When I delete the social media link "https://freeradical.zone/peter-pan"
Then I see a toaster with status "success"
Scenario: Editing Social Media
When I navigate to 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
Then I can cancel editing
When I start editing a social media 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
But the old url is not displayed

View File

@ -1,6 +1,12 @@
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 => {
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-popover').contains(name)
cy.get('.avatar-menu').click() // close again

View File

@ -1,12 +1,10 @@
import { When } from "@badeball/cypress-cucumber-preprocessor";
When('I add a social media link', () => {
cy.get('button')
.contains('Add link')
cy.get('[data-test="add-save-button"]')
.click()
.get('#editSocialMedia')
.type('https://freeradical.zone/peter-pan')
.get('button')
.contains('Save')
.get('[data-test="add-save-button"]')
.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')
.clear()
.type('https://freeradical.zone/tinkerbell')
.get('button')
.contains('Save')
.get('[data-test="add-save-button"]')
.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";
When('I start editing a social media link', () => {
cy.get(".base-button[title='Edit']")
cy.get('[data-test="edit-button"]')
.click()
})

View File

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

View File

@ -1,6 +1,12 @@
import { When } from "@badeball/cypress-cucumber-preprocessor";
When("I log out", () => {
cy.get(".avatar-menu").then(($menu) => {
if (!$menu.is(':visible')){
cy.scrollTo("top");
cy.wait(500);
}
})
cy.get(".avatar-menu")
.click();
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
- CLIENT_URI=http://localhost:3000
- JWT_SECRET=b/&&7b78BF&fv/Vd
- MAPBOX_TOKEN=pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g
- PRIVATE_KEY_PASSPHRASE=a7dsf78sadg87ad87sfagsadg78
- NEO4J_apoc_import_file_enabled=true
- "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"
```
***Cypher commands to show indexes and contraints***
***Cypher commands to show indexes and constraints***
```bash
# in browser command line or cypher shell
# show all indexes and contraints
# show all indexes and constraints
$ :schema
# show all indexes
$ CALL db.indexes();
# show all contraints
# show all constraints
$ CALL db.constraints();
```
***Cypher commands to create and drop indexes and contraints***
***Cypher commands to create and drop indexes and constraints***
```bash
# 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 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 * ;
```

View File

@ -1,6 +1,6 @@
{
"name": "ocelot-social",
"version": "2.2.0",
"version": "2.4.0",
"description": "Free and open source software program code available to run social networks.",
"author": "ocelot.social Community",
"license": "MIT",
@ -15,8 +15,8 @@
"scripts": {
"db:seed": "cd backend && yarn run db:seed",
"db:reset": "cd backend && yarn run db:reset",
"cypress:run": "cypress run --browser electron --config-file ./cypress/cypress.config.js",
"cypress:open": "cypress open --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.json",
"cucumber:setup": "cd backend && yarn run dev",
"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\")"
@ -25,15 +25,15 @@
"@babel/core": "^7.9.0",
"@babel/preset-env": "^7.12.7",
"@babel/register": "^7.12.10",
"@badeball/cypress-cucumber-preprocessor": "^13.0.3",
"@cypress/browserify-preprocessor": "^3.0.2",
"@faker-js/faker": "5.1.0",
"auto-changelog": "^2.3.0",
"bcryptjs": "^2.4.3",
"codecov": "^3.8.2",
"cross-env": "^7.0.3",
"cypress": "^10.11.0",
"cypress-file-upload": "^5.0.8",
"cucumber": "^6.0.5",
"cypress": "^7.0.1",
"cypress-cucumber-preprocessor": "^2.2.1",
"cypress-file-upload": "^3.5.3",
"date-fns": "^2.25.0",
"dotenv": "^8.2.0",
"expect": "^25.3.0",
@ -48,7 +48,8 @@
"slug": "^6.0.0"
},
"resolutions": {
"set-value": "^2.0.1"
"set-value": "^2.0.1",
"nan": "2.17.0"
},
"dependencies": {}
}

View File

@ -1,7 +1,8 @@
SENTRY_DSN_WEBAPP=
COMMIT=
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
WEBSOCKETS_URI=ws://localhost:3000/api/graphql
GRAPHQL_URI=http://localhost:4000/
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
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) ###################################
##################################################################################
FROM node:12.19.0-alpine3.10 as base
FROM node:19.4.0-alpine3.17 as base
# ENVs
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame

View File

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

View File

@ -4,18 +4,32 @@
## 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
# install all dependencies
$ cd webapp/
$ cd webapp
$ yarn install
# or just
$ yarn
# or just later on to use version of ".nvmrc" file
$ nvm use && yarn
```
Copy:
```text
# in webapp/
# in webapp
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
*/
$color-primary: rgb(23, 181, 63);
$color-primary-light: rgb(96, 214, 98);
$color-primary-dark: rgb(25, 122, 49);
$color-primary-active: rgb(25, 194, 67);
$color-primary-inverse: rgb(241, 253, 244);
$color-secondary: rgb(0, 142, 230);
$color-secondary-active: rgb(10, 161, 255);
$color-secondary-inverse: rgb(240, 249, 255);
$color-success: rgb(23, 181, 63);
$color-success-active: rgb(26, 203, 71);
$color-success-inverse: rgb(241, 253, 244);
$color-danger: rgb(219, 57, 36);
$color-danger-light: rgb(242, 97, 65);
$color-danger-dark: rgb(158, 43, 28);
$color-danger-active: rgb(224, 81, 62);
$color-danger-inverse: rgb(253, 243, 242);
$color-warning: rgb(230, 121, 25);
$color-warning-active: rgb(233, 137, 53);
$color-warning-inverse: rgb(253, 247, 241);
$color-yellow: rgb(245, 196, 0);
$color-yellow-active: rgb(255, 206, 10);
$color-yellow-inverse: rgb(255, 252, 240);
$color-primary: rgb(23, 181, 63);
$color-primary-light: rgb(96, 214, 98);
$color-primary-dark: rgb(25, 122, 49);
$color-primary-active: rgb(25, 194, 67);
$color-primary-inverse: rgb(241, 253, 244);
$color-secondary: rgb(0, 142, 230);
$color-secondary-active: rgb(10, 161, 255);
$color-secondary-inverse: rgb(240, 249, 255);
$color-success: rgb(23, 181, 63);
$color-success-active: rgb(26, 203, 71);
$color-success-inverse: rgb(241, 253, 244);
$color-danger: rgb(219, 57, 36);
$color-danger-light: rgb(242, 97, 65);
$color-danger-dark: rgb(158, 43, 28);
$color-danger-active: rgb(224, 81, 62);
$color-danger-inverse: rgb(253, 243, 242);
$color-warning: rgb(230, 121, 25);
$color-warning-active: rgb(233, 137, 53);
$color-warning-inverse: rgb(253, 247, 241);
$color-yellow: rgb(245, 196, 0);
$color-yellow-active: rgb(255, 206, 10);
$color-yellow-inverse: rgb(255, 252, 240);
/**
* @tokens Color Neutral

View File

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

View File

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

View File

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

View File

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

View File

@ -11,15 +11,7 @@
<base-icon class="dropdown-arrow" name="angle-down" />
</base-button>
<template slot="popover">
<div class="filter-menu-options">
<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>
<filter-menu-component />
</template>
</dropdown>
</template>
@ -27,26 +19,16 @@
<script>
import Dropdown from '~/components/Dropdown'
import { mapGetters } from 'vuex'
import FollowingFilter from './FollowingFilter'
import OrderByFilter from './OrderByFilter'
import CategoriesFilter from './CategoriesFilter'
import FilterMenuComponent from './FilterMenuComponent'
export default {
components: {
Dropdown,
FollowingFilter,
OrderByFilter,
CategoriesFilter,
},
data() {
return {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
}
FilterMenuComponent,
},
props: {
placement: { type: String },
offset: { type: [String, Number] },
showMobileMenu: { type: Boolean, default: false },
},
computed: {
...mapGetters({
@ -55,14 +37,3 @@ export default {
},
}
</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