mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge branch 'master' of https://github.com/Ocelot-Social-Community/Ocelot-Social into dependabot/npm_and_yarn/webapp/linkify-it-3.0.2
# Conflicts: # webapp/package-lock.json
This commit is contained in:
commit
ae206e8302
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
22
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,25 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: 🐛 Bug report
|
name: 🐛 Bug Report
|
||||||
about: Create a report to help us improve
|
about: Create a report to help us to improve.
|
||||||
labels: bug
|
labels: bug
|
||||||
title: 🐛 [Bug]
|
title: 🐛 [Bug]
|
||||||
---
|
---
|
||||||
|
|
||||||
## :bug: Bugreport
|
## :bug: Bug Report
|
||||||
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the bug is.-->
|
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the bug is.-->
|
||||||
|
|
||||||
### Steps to reproduce the behavior
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
4. ...
|
|
||||||
5. Profit
|
|
||||||
|
|
||||||
### Expected behavior
|
|
||||||
<!-- A clear and concise description of what you expected to happen. -->
|
|
||||||
|
|
||||||
### Version & Environment
|
|
||||||
<!-- Add context about your environment and used version here. -->
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
<!-- Add any other context about the problem here. -->
|
|
||||||
|
|||||||
21
.github/ISSUE_TEMPLATE/devops_ticket.md
vendored
21
.github/ISSUE_TEMPLATE/devops_ticket.md
vendored
@ -1,24 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: 💥 DevOps ticket
|
name: 💥 DevOps Ticket
|
||||||
about: Help us manage our deployed App.
|
about: Help us manage our deployed app.
|
||||||
labels: devops
|
labels: devops
|
||||||
title: 💥 [DevOps]
|
title: 💥 [DevOps]
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💥 DevOps ticket
|
## 💥 DevOps Ticket
|
||||||
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
||||||
|
|
||||||
### Motive
|
|
||||||
<!-- Why does this task need to be done? What can we benefit from this? -->
|
|
||||||
|
|
||||||
### Related issues
|
|
||||||
<!-- Are there any related issues to link to? Please paste them below for reference. -->
|
|
||||||
|
|
||||||
### Implementation
|
|
||||||
<!-- Please, document any ideas of how the task can be performed. -->
|
|
||||||
|
|
||||||
### Validation
|
|
||||||
<!-- How can we make sure that this task was successful? -->
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
<!-- Add other context or background about the feature request here.-->
|
|
||||||
|
|||||||
12
.github/ISSUE_TEMPLATE/epic.md
vendored
Normal file
12
.github/ISSUE_TEMPLATE/epic.md
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
---
|
||||||
|
name: 🌟 Epic
|
||||||
|
about: Define a big development step.
|
||||||
|
labels: epic
|
||||||
|
title: 🌟 [EPIC]
|
||||||
|
---
|
||||||
|
<!-- THIS ISSUE-TYPE IS NOT FOR YOU! -->
|
||||||
|
<!-- If you need an answer right away, visit the ocelot.social Discord:
|
||||||
|
https://discord.gg/AJSX9DCSUA -->
|
||||||
|
|
||||||
|
## 🌟 EPIC
|
||||||
|
<!-- Describe your Epic in detail. Include screenshots and drawings -->
|
||||||
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
21
.github/ISSUE_TEMPLATE/feature_request.md
vendored
@ -1,24 +1,9 @@
|
|||||||
---
|
---
|
||||||
name: 🚀 Feature request
|
name: 🚀 Feature Request
|
||||||
about: Suggest an idea for this project
|
about: Suggest an idea for this project.
|
||||||
labels: feature
|
labels: feature
|
||||||
title: 🚀 [Feature]
|
title: 🚀 [Feature]
|
||||||
---
|
---
|
||||||
|
|
||||||
## :rocket: Feature
|
## :rocket: Feature Request
|
||||||
<!-- Give a short summary of the Feature. Use Screenshots if you want. -->
|
<!-- Give a short summary of the Feature. Use Screenshots if you want. -->
|
||||||
|
|
||||||
### User Problem
|
|
||||||
<!-- Which problem is this solving? Why do you think this is important? Who will benefit from it and how? -->
|
|
||||||
|
|
||||||
### Implementation
|
|
||||||
<!-- How do you think this feature should be implemented? How will it be used? Where in the network should it be located? Which steps and screens are involved? -->
|
|
||||||
|
|
||||||
### Design & Layout
|
|
||||||
<!-- Attach Screenshots and Sketches to illustrate your idea. -->
|
|
||||||
|
|
||||||
### Validation
|
|
||||||
<!-- How can we make sure that this feature indeed solves the above problem? How do we know if it has been accepted by the users of the network, once released? -->
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
<!-- Add other context or background about the feature request here.-->
|
|
||||||
|
|||||||
10
.github/ISSUE_TEMPLATE/question.md
vendored
10
.github/ISSUE_TEMPLATE/question.md
vendored
@ -1,12 +1,12 @@
|
|||||||
---
|
---
|
||||||
name: 💬 Question
|
name: 💬 Question
|
||||||
about: If you need help understanding HumanConnection.
|
about: If you need help understanding ocelot.social.
|
||||||
labels: question
|
labels: question
|
||||||
title: 💬 [Question]
|
title: 💬 [Question]
|
||||||
---
|
---
|
||||||
<!-- Chat with Team HumanConnection -->
|
<!-- Chat with ocelot.social team -->
|
||||||
<!-- If you need an answer right away, visit the HumanConnection Discord:
|
<!-- If you need an answer right away, visit the ocelot.social Discord:
|
||||||
https://discord.gg/Q3mpcgr -->
|
https://discord.gg/AJSX9DCSUA -->
|
||||||
|
|
||||||
## :speech_balloon: Question
|
## 💬 Question
|
||||||
<!-- Describe your Question in detail. Include screenshots and drawings if needed. -->
|
<!-- Describe your Question in detail. Include screenshots and drawings if needed. -->
|
||||||
|
|||||||
15
.github/ISSUE_TEMPLATE/refactor_tickets.md
vendored
15
.github/ISSUE_TEMPLATE/refactor_tickets.md
vendored
@ -1,21 +1,10 @@
|
|||||||
---
|
---
|
||||||
name: 🔧 Refactor ticket
|
name: 🔧 Refactor
|
||||||
about: Help us improve our code by refactoring it.
|
about: Help us improve our code by refactoring it.
|
||||||
labels: refactor
|
labels: refactor
|
||||||
title: 🔧 [Refactor]
|
title: 🔧 [Refactor]
|
||||||
---
|
---
|
||||||
|
|
||||||
## :zap: Refactor ticket
|
## 🔧 Refactor
|
||||||
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
<!-- Describe your issue in detail. Include screenshots if needed. Give us as much information as possible. Use a clear and concise description of what the problem is.-->
|
||||||
|
|
||||||
### Motive
|
|
||||||
<!-- What is the purpose of this refactoring? If it's removing depcrecated code, please link to the deprecation notice. -->
|
|
||||||
|
|
||||||
### Related issues
|
|
||||||
<!-- Are there any related issues to link to? Please paste them below for reference. -->
|
|
||||||
|
|
||||||
### Implementation
|
|
||||||
<!-- Please, document any ideas of how the code should be refactored. -->
|
|
||||||
|
|
||||||
### Additional context
|
|
||||||
<!-- Add other context or background about the feature request here.-->
|
|
||||||
|
|||||||
179
.github/dependabot.yml
vendored
Normal file
179
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: cypress
|
||||||
|
versions:
|
||||||
|
- 6.3.0
|
||||||
|
- 6.4.0
|
||||||
|
- 6.5.0
|
||||||
|
- 6.6.0
|
||||||
|
- 6.7.1
|
||||||
|
- 6.8.0
|
||||||
|
- 7.0.0
|
||||||
|
- 7.0.1
|
||||||
|
- 7.1.0
|
||||||
|
- dependency-name: cypress-cucumber-preprocessor
|
||||||
|
versions:
|
||||||
|
- 4.0.0
|
||||||
|
- 4.0.1
|
||||||
|
- 4.0.3
|
||||||
|
- dependency-name: date-fns
|
||||||
|
versions:
|
||||||
|
- 2.16.1
|
||||||
|
- 2.17.0
|
||||||
|
- 2.18.0
|
||||||
|
- 2.19.0
|
||||||
|
- 2.20.0
|
||||||
|
- 2.20.1
|
||||||
|
- 2.20.2
|
||||||
|
- 2.20.3
|
||||||
|
- 2.21.0
|
||||||
|
- dependency-name: cypress-file-upload
|
||||||
|
versions:
|
||||||
|
- 5.0.2
|
||||||
|
- 5.0.3
|
||||||
|
- 5.0.4
|
||||||
|
- 5.0.5
|
||||||
|
- dependency-name: neo4j-driver
|
||||||
|
versions:
|
||||||
|
- 4.2.2
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/backend"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: y18n
|
||||||
|
versions:
|
||||||
|
- 4.0.1
|
||||||
|
- 4.0.2
|
||||||
|
- dependency-name: metascraper-publisher
|
||||||
|
versions:
|
||||||
|
- 5.16.16
|
||||||
|
- 5.18.1
|
||||||
|
- 5.18.12
|
||||||
|
- 5.18.2
|
||||||
|
- 5.18.4
|
||||||
|
- 5.18.5
|
||||||
|
- 5.18.6
|
||||||
|
- 5.18.9
|
||||||
|
- 5.20.0
|
||||||
|
- 5.21.0
|
||||||
|
- 5.21.2
|
||||||
|
- 5.21.3
|
||||||
|
- 5.21.4
|
||||||
|
- 5.21.5
|
||||||
|
- dependency-name: metascraper-author
|
||||||
|
versions:
|
||||||
|
- 5.16.16
|
||||||
|
- 5.18.1
|
||||||
|
- 5.18.12
|
||||||
|
- 5.18.2
|
||||||
|
- 5.18.4
|
||||||
|
- 5.18.5
|
||||||
|
- 5.18.6
|
||||||
|
- 5.18.9
|
||||||
|
- 5.20.0
|
||||||
|
- 5.21.0
|
||||||
|
- 5.21.2
|
||||||
|
- 5.21.3
|
||||||
|
- 5.21.4
|
||||||
|
- 5.21.5
|
||||||
|
- dependency-name: neo4j-driver
|
||||||
|
versions:
|
||||||
|
- 4.2.2
|
||||||
|
- dependency-name: neo4j-graphql-js
|
||||||
|
versions:
|
||||||
|
- 2.19.1
|
||||||
|
- dependency-name: mustache
|
||||||
|
versions:
|
||||||
|
- 4.1.0
|
||||||
|
- package-ecosystem: npm
|
||||||
|
directory: "/webapp"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: nuxt
|
||||||
|
versions:
|
||||||
|
- 2.14.12
|
||||||
|
- 2.15.0
|
||||||
|
- 2.15.1
|
||||||
|
- 2.15.2
|
||||||
|
- 2.15.3
|
||||||
|
- dependency-name: v-tooltip
|
||||||
|
versions:
|
||||||
|
- 2.1.2
|
||||||
|
- dependency-name: "@vue/server-test-utils"
|
||||||
|
versions:
|
||||||
|
- 1.1.2
|
||||||
|
- 1.1.3
|
||||||
|
- dependency-name: node-notifier
|
||||||
|
versions:
|
||||||
|
- 8.0.1
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: "/webapp"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: node
|
||||||
|
versions:
|
||||||
|
- ">= 15.5.a, < 15.6"
|
||||||
|
- dependency-name: node
|
||||||
|
versions:
|
||||||
|
- 15.10.0.pre.alpine3.10
|
||||||
|
- 15.11.0.pre.alpine3.10
|
||||||
|
- 15.12.0.pre.alpine3.10
|
||||||
|
- 15.13.0.pre.alpine3.10
|
||||||
|
- 15.7.0.pre.alpine3.10
|
||||||
|
- 15.8.0.pre.alpine3.10
|
||||||
|
- 15.9.0.pre.alpine3.10
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: "/backend"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: node
|
||||||
|
versions:
|
||||||
|
- ">= 15.4.a, < 15.5"
|
||||||
|
- dependency-name: node
|
||||||
|
versions:
|
||||||
|
- ">= 15.5.a, < 15.6"
|
||||||
|
- dependency-name: node
|
||||||
|
versions:
|
||||||
|
- 15.10.0.pre.alpine3.10
|
||||||
|
- 15.11.0.pre.alpine3.10
|
||||||
|
- 15.12.0.pre.alpine3.10
|
||||||
|
- 15.13.0.pre.alpine3.10
|
||||||
|
- 15.7.0.pre.alpine3.10
|
||||||
|
- 15.8.0.pre.alpine3.10
|
||||||
|
- 15.9.0.pre.alpine3.10
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: "/neo4j"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
|
ignore:
|
||||||
|
- dependency-name: neo4j
|
||||||
|
versions:
|
||||||
|
- 4.2.3
|
||||||
|
- 4.2.4
|
||||||
|
- package-ecosystem: docker
|
||||||
|
directory: "/deployment/legacy-migration/maintenance-worker"
|
||||||
|
schedule:
|
||||||
|
interval: daily
|
||||||
|
time: "04:00"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
35
.github/workflows/publish.yml
vendored
35
.github/workflows/publish.yml
vendored
@ -4,6 +4,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
|
# - 4451-new-deployment-with-base-and-code # for testing while developing
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -55,9 +56,9 @@ jobs:
|
|||||||
# NEO4J ##################################################################
|
# NEO4J ##################################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Neo4J | Build `community` image
|
- name: Neo4J | Build `community` image
|
||||||
run: |
|
run: docker build --target community -t "ocelotsocialnetwork/neo4j:latest" -t "ocelotsocialnetwork/neo4j:community" -t "ocelotsocialnetwork/neo4j:${VERSION}" -t "ocelotsocialnetwork/neo4j:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT neo4j/
|
||||||
docker build --target community -t "ocelotsocialnetwork/neo4j:latest" -t "ocelotsocialnetwork/neo4j:community" -t "ocelotsocialnetwork/neo4j:${VERSION}" -t "ocelotsocialnetwork/neo4j:${BUILD_VERSION}" neo4j/
|
- name: Neo4J | Save docker image
|
||||||
docker save "ocelotsocialnetwork/neo4j" > /tmp/neo4j.tar
|
run: docker save "ocelotsocialnetwork/neo4j" > /tmp/neo4j.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
@ -91,10 +92,13 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# BUILD BACKEND DOCKER IMAGE (production) ################################
|
# BUILD BACKEND DOCKER IMAGE (production) ################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: backend | Build `production` image
|
- name: Backend | Build `production` image
|
||||||
run: |
|
run: |
|
||||||
docker build --target production -t "ocelotsocialnetwork/backend:latest" -t "ocelotsocialnetwork/backend:${VERSION}" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}" backend/
|
docker build --target base -t "ocelotsocialnetwork/backend:latest-base" -t "ocelotsocialnetwork/backend:${VERSION}-base" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
|
||||||
docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
|
docker build --target code -t "ocelotsocialnetwork/backend:latest-code" -t "ocelotsocialnetwork/backend:${VERSION}-code" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
|
||||||
|
docker build --target production -t "ocelotsocialnetwork/backend:latest" -t "ocelotsocialnetwork/backend:${VERSION}" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
|
||||||
|
- name: Backend | Save docker image
|
||||||
|
run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
@ -128,10 +132,13 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
|
# BUILD WEBAPP DOCKER IMAGE (build) ######################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: webapp | Build `production` image
|
- name: Webapp | Build `production` image
|
||||||
run: |
|
run: |
|
||||||
docker build --target production -t "ocelotsocialnetwork/webapp:latest" -t "ocelotsocialnetwork/webapp:${VERSION}" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}" webapp/
|
docker build --target base -t "ocelotsocialnetwork/webapp:latest-base" -t "ocelotsocialnetwork/webapp:${VERSION}-base" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
|
||||||
docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
|
docker build --target code -t "ocelotsocialnetwork/webapp:latest-code" -t "ocelotsocialnetwork/webapp:${VERSION}-code" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
|
||||||
|
docker build --target production -t "ocelotsocialnetwork/webapp:latest" -t "ocelotsocialnetwork/webapp:${VERSION}" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
|
||||||
|
- name: Webapp | Save docker image
|
||||||
|
run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
@ -165,11 +172,13 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# BUILD MAINTENANCE DOCKER IMAGE (build) #################################
|
# BUILD MAINTENANCE DOCKER IMAGE (build) #################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: maintenance | Build `production` image
|
- name: Maintenance | Build `production` image
|
||||||
# TODO: --target production
|
|
||||||
run: |
|
run: |
|
||||||
docker build -t "ocelotsocialnetwork/maintenance:latest" -t "ocelotsocialnetwork/maintenance:${VERSION}" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" webapp/ -f webapp/Dockerfile.maintenance
|
docker build --target base -t "ocelotsocialnetwork/maintenance:latest-base" -t "ocelotsocialnetwork/maintenance:${VERSION}-base" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
|
||||||
docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
|
docker build --target code -t "ocelotsocialnetwork/maintenance:latest-code" -t "ocelotsocialnetwork/maintenance:${VERSION}-code" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
|
||||||
|
docker build --target production -t "ocelotsocialnetwork/maintenance:latest" -t "ocelotsocialnetwork/maintenance:${VERSION}" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
|
||||||
|
- name: Maintenance | Save docker image
|
||||||
|
run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
|
||||||
- name: Upload Artifact
|
- name: Upload Artifact
|
||||||
uses: actions/upload-artifact@v2
|
uses: actions/upload-artifact@v2
|
||||||
with:
|
with:
|
||||||
|
|||||||
107
.github/workflows/test.yml
vendored
107
.github/workflows/test.yml
vendored
@ -194,7 +194,6 @@ jobs:
|
|||||||
##########################################################################
|
##########################################################################
|
||||||
# UNIT TESTS BACKEND #####################################################
|
# UNIT TESTS BACKEND #####################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# TODO: Why do we need those .envs?
|
|
||||||
- name: backend | copy env files webapp
|
- name: backend | copy env files webapp
|
||||||
run: cp webapp/.env.template webapp/.env
|
run: cp webapp/.env.template webapp/.env
|
||||||
- name: backend | copy env files backend
|
- name: backend | copy env files backend
|
||||||
@ -205,6 +204,17 @@ jobs:
|
|||||||
run: docker-compose exec -T backend yarn db:migrate init
|
run: docker-compose exec -T backend yarn db:migrate init
|
||||||
- name: backend | Unit test
|
- name: backend | Unit test
|
||||||
run: docker-compose exec -T backend yarn test
|
run: docker-compose exec -T backend yarn test
|
||||||
|
##########################################################################
|
||||||
|
# COVERAGE CHECK BACKEND #################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: backend | Coverage check
|
||||||
|
uses: webcraftmedia/coverage-check-action@master
|
||||||
|
with:
|
||||||
|
report_name: Coverage Backend
|
||||||
|
type: lcov
|
||||||
|
result_path: ./coverage/lcov.info
|
||||||
|
min_coverage: 58
|
||||||
|
token: ${{ github.token }}
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST WEBAPP ######################################################
|
# JOB: UNIT TEST WEBAPP ######################################################
|
||||||
@ -230,9 +240,8 @@ jobs:
|
|||||||
- name: Load Docker Image
|
- name: Load Docker Image
|
||||||
run: docker load < /tmp/webapp.tar
|
run: docker load < /tmp/webapp.tar
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# UNIT TESTS WEBAPP #####################################################
|
# UNIT TESTS WEBAPP ######################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# TODO: Why do we need those .envs?
|
|
||||||
- name: backend | copy env files webapp
|
- name: backend | copy env files webapp
|
||||||
run: cp webapp/.env.template webapp/.env
|
run: cp webapp/.env.template webapp/.env
|
||||||
- name: backend | copy env files backend
|
- name: backend | copy env files backend
|
||||||
@ -240,5 +249,93 @@ jobs:
|
|||||||
- name: backend | docker-compose
|
- name: backend | docker-compose
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp
|
||||||
- name: webapp | Unit tests
|
- name: webapp | Unit tests
|
||||||
#run: docker run --rm ocelotsocialnetwork/webapp:build yarn run test
|
run: docker-compose exec -T webapp yarn test
|
||||||
run: docker-compose exec -T webapp yarn test
|
##########################################################################
|
||||||
|
# COVERAGE REPORT FRONTEND ################################################
|
||||||
|
##########################################################################
|
||||||
|
#- name: frontend | Coverage report
|
||||||
|
# uses: romeovs/lcov-reporter-action@v0.2.21
|
||||||
|
# with:
|
||||||
|
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
# lcov-file: ./coverage/lcov.info
|
||||||
|
##########################################################################
|
||||||
|
# COVERAGE CHECK WEBAPP ##################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: webapp | Coverage check
|
||||||
|
uses: webcraftmedia/coverage-check-action@master
|
||||||
|
with:
|
||||||
|
report_name: Coverage Webapp
|
||||||
|
type: lcov
|
||||||
|
result_path: ./coverage/lcov.info
|
||||||
|
min_coverage: 65
|
||||||
|
token: ${{ github.token }}
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# JOB: FULLSTACK TESTS #######################################################
|
||||||
|
##############################################################################
|
||||||
|
fullstack_tests:
|
||||||
|
name: Fullstack tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build_test_webapp, build_test_backend, build_test_neo4j]
|
||||||
|
env:
|
||||||
|
jobs: 8
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
# run copies of the current job in parallel
|
||||||
|
job: [1, 2, 3, 4, 5, 6, 7, 8]
|
||||||
|
steps:
|
||||||
|
##########################################################################
|
||||||
|
# CHECKOUT CODE ##########################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
##########################################################################
|
||||||
|
# DOWNLOAD DOCKER IMAGES #################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Download Docker Image (Neo4J)
|
||||||
|
uses: actions/download-artifact@v2
|
||||||
|
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
|
||||||
|
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
|
||||||
|
with:
|
||||||
|
name: docker-webapp-test
|
||||||
|
path: /tmp
|
||||||
|
- name: Load Docker Image
|
||||||
|
run: docker load < /tmp/webapp.tar
|
||||||
|
##########################################################################
|
||||||
|
# FULLSTACK TESTS CYPRESS ################################################
|
||||||
|
##########################################################################
|
||||||
|
- name: webapp | copy env files webapp
|
||||||
|
run: cp webapp/.env.template webapp/.env
|
||||||
|
- name: backend | copy env files backend
|
||||||
|
run: cp backend/.env.template backend/.env
|
||||||
|
- name: backend | docker-compose
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend
|
||||||
|
- name: cypress | Fullstack tests
|
||||||
|
run: |
|
||||||
|
yarn install
|
||||||
|
yarn run cypress:run --spec $(cypress/parallel-features.sh ${{ matrix.job }} ${{ env.jobs }} )
|
||||||
|
##########################################################################
|
||||||
|
# UPLOAD SCREENSHOTS & VIDEO #############################################
|
||||||
|
##########################################################################
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: cypress-screenshots
|
||||||
|
path: cypress/screenshots/
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: cypress-videos
|
||||||
|
path: cypress/videos/
|
||||||
|
|||||||
99
CHANGELOG.md
99
CHANGELOG.md
@ -4,16 +4,85 @@ 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).
|
||||||
|
|
||||||
#### [v0.6.15](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.14...v0.6.15)
|
#### [v1.0.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.3...v1.0.4)
|
||||||
|
|
||||||
|
- fixed wrong env variable [`#4474`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4474)
|
||||||
|
- chore: [WIP] 🍰 New Deployment With 'base' And 'code' Docker Images [`#4452`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4452)
|
||||||
|
- feat: 🍰 Flexible Footer Links [`#4468`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4468)
|
||||||
|
- docs: 🍰 Correct 'Contribution.md' [`#4466`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4466)
|
||||||
|
- docs: 🍰 Correct Discord Links And Divers [`#4461`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4461)
|
||||||
|
- Implement flexible page footer links [`1bd4af6`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/1bd4af6fd3b5db167575910948a0a72461a1129a)
|
||||||
|
- Implement tests for flexible page footer links [`627a20f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/627a20f66a65450996a5fe3128fd37769fdfd629)
|
||||||
|
- Correct Discord links and divers [`0318910`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/0318910488e245c4a1d09181265de63d05a89cf1)
|
||||||
|
|
||||||
|
#### [1.0.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.2...1.0.3)
|
||||||
|
|
||||||
|
> 19 May 2021
|
||||||
|
|
||||||
|
- chore: 🍰 Release v1.0.3 [`#4435`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4435)
|
||||||
|
- chore: 🍰 Refactor Logos [`#4433`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4433)
|
||||||
|
- feat: 🍰 Show Password Component [`#4370`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4370)
|
||||||
|
- chore: 🍰 Replace Ocelot Logos 619x593 With 600x570 [`#4428`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4428)
|
||||||
|
- Coverage [`#4393`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4393)
|
||||||
|
- correct_docker_tagging [`#4391`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4391)
|
||||||
|
- 🍰 Get Cypress Tests Running Again [`#4338`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4338)
|
||||||
|
- frontend + backend coverage tests [`#4367`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4367)
|
||||||
|
- refactor: 🍰 Refactor E-Mail Templates [`#4350`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4350)
|
||||||
|
- feat: 🍰 Remove More-Info Of Post [`#4316`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4316)
|
||||||
|
- have cypress running locally - the tests still fail [`e3e0341`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/e3e03415e1bd9e0be88f33930a52e63d8af64ee1)
|
||||||
|
- have cypress running locally - the tests still fail [`0ec0574`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/0ec05743751cfef0cb86c17b87e9e4ef9a2c9e47)
|
||||||
|
- Refactor logos, first step [`ff6cc30`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/ff6cc306aff6150a924f1a647387e498d050ea9f)
|
||||||
|
|
||||||
|
#### [1.0.2](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.1...1.0.2)
|
||||||
|
|
||||||
|
> 6 April 2021
|
||||||
|
|
||||||
|
- fix: Email Confirmation-Link When An Invite-Code Is Given [`#4336`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4336)
|
||||||
|
- release v1.0.2 [`e583010`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/e5830101e4b449905fe2d0018627d75af62b2a20)
|
||||||
|
- slider jumps to enter-nonce when link contains invite-code, email and nonce and method is invite-code. Thanks @tirokk [`c80b3a2`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/c80b3a212835f76cfc2f11542345d6c3b226995b)
|
||||||
|
- fix enail confirmation link when an invite-code is given [`27f0de9`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/27f0de9464685c8f960bde9d07986fdc5b20f8f9)
|
||||||
|
|
||||||
|
#### [1.0.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/1.0.0...1.0.1)
|
||||||
|
|
||||||
|
> 4 April 2021
|
||||||
|
|
||||||
|
- readme_update [`#4331`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4331)
|
||||||
|
- jwt_expiretime [`#4330`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4330)
|
||||||
|
- Clean env, docker & workflow [`#4337`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4337)
|
||||||
|
- fix: 🍰 Fixing The Avatars unwanted Border [`#4320`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4320)
|
||||||
|
- Change background color of avatar image to white [`e48a99a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/e48a99afe63e96cffafe16db7bf5ae35cfdebd7a)
|
||||||
|
- removed config warning [`f6c070a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f6c070a3cb36c5593123b27b4d1b6a5b7a10aba3)
|
||||||
|
- include env files in build process [`dfe6f67`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/dfe6f679b3bb0f440aee986d6d12925cccee9050)
|
||||||
|
|
||||||
|
### [1.0.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.15...1.0.0)
|
||||||
|
|
||||||
|
> 31 March 2021
|
||||||
|
|
||||||
|
- v1.0.0 [`#4321`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4321)
|
||||||
|
- fix: 🐛 Fix Wrong Truncation Of Hashtags In Admin List Via Adding 'truncateStr' [`#4314`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4314)
|
||||||
|
- feat: 🍰Implement Registration Slider [`#4270`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4270)
|
||||||
|
- feat: 🍰 Invite Button [`#4301`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4301)
|
||||||
|
- fix: 🍰 Suggestion List Filter [`#4296`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4296)
|
||||||
|
- fix: JWT Expires In 2 Years [`#4278`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4278)
|
||||||
|
- Deployment [`#4263`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4263)
|
||||||
|
- feat: 🍰 Redesign Registration Process Frontend [`#4168`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4168)
|
||||||
|
- Delete unnecessary code [`3fa7e04`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/3fa7e04d4895161db1f764ccad58e35188c9d065)
|
||||||
|
- basic invite button in frontend [`356f026`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/356f02622655a57561fcdecfcc8a735d23cac79f)
|
||||||
|
- setting up invite button [`e6dc3f4`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/e6dc3f42cedaf9953d737cf30cf7ed317b634be7)
|
||||||
|
|
||||||
|
#### [0.6.15](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.14...0.6.15)
|
||||||
|
|
||||||
|
> 1 March 2021
|
||||||
|
|
||||||
|
- feat: Count Post Teaser Views [`#4255`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4255)
|
||||||
- feat: 🍰 Count Post Clicks [`#4248`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4248)
|
- feat: 🍰 Count Post Clicks [`#4248`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4248)
|
||||||
- Correct version style [`#4218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4218)
|
- Correct version style [`#4218`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4218)
|
||||||
- dashboard restructuring image [`#4266`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4266)
|
- dashboard restructuring image [`#4266`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4266)
|
||||||
- fix: 🐛 Adapted Editor List Styles [`#4239`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4239)
|
- fix: 🐛 Adapted Editor List Styles [`#4239`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4239)
|
||||||
- fix: Scrolling On Profile Page [`#4234`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4234)
|
- fix: Scrolling On Profile Page [`#4234`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4234)
|
||||||
|
- release 0.6.15 [`ef4265d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/ef4265d9387d94aa09db2b80461c2ec90b4623ae)
|
||||||
- count views of post teaser [`1c3f628`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/1c3f628fb2e161400319b32da274952c1b57836e)
|
- count views of post teaser [`1c3f628`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/1c3f628fb2e161400319b32da274952c1b57836e)
|
||||||
- tests fixed for clickedCount [`96066ea`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/96066eae02e659a00bce280f9f97a28ac1446ce0)
|
- tests fixed for clickedCount [`96066ea`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/96066eae02e659a00bce280f9f97a28ac1446ce0)
|
||||||
- add clickedCount to PostTeaser [`d3eafc9`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/d3eafc9b69c70e31021c6e2723bd5bcfa6b9d17f)
|
|
||||||
|
|
||||||
#### [0.6.14](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.13...0.6.14)
|
#### [0.6.14](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.13...0.6.14)
|
||||||
|
|
||||||
@ -99,21 +168,11 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|||||||
- file upload: refactored [`650e83f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/650e83f4c250389477933a2e7d21d8245b0ce882)
|
- file upload: refactored [`650e83f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/650e83f4c250389477933a2e7d21d8245b0ce882)
|
||||||
- change user role: tests are working [`14dfe2a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/14dfe2ae2cd4a24c06c9229893b33586dfceae4f)
|
- change user role: tests are working [`14dfe2a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/14dfe2ae2cd4a24c06c9229893b33586dfceae4f)
|
||||||
|
|
||||||
#### [0.6.5](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.4...0.6.5)
|
#### [0.6.5](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.4...0.6.5)
|
||||||
|
|
||||||
> 8 February 2021
|
> 8 February 2021
|
||||||
|
|
||||||
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
- updated CHANGELOG.md [`9d9075f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9d9075f2117b2eb4b607e7d59ab18c7e655c6ea7)
|
||||||
- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a)
|
|
||||||
- - deploy structure image [`a60400b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a60400b4fe6f59bbb80e1073db4def3ba205e1a7)
|
|
||||||
|
|
||||||
#### [v0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.4...v0.6.4)
|
|
||||||
|
|
||||||
> 9 February 2021
|
|
||||||
|
|
||||||
- chore(release): 0.6.4 [`8b7570d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8b7570dc35d0ea431f673a711ac051f1e1320acb)
|
|
||||||
- change user roles is working, test fails [`8c3310a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8c3310abaf87c0e5597fec4f93fb37d27122c9e7)
|
|
||||||
- change user role: tests are working [`f10da4b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f10da4b09388fe1e2b85abd53f6ffc67c785d4c1)
|
|
||||||
|
|
||||||
#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...0.6.4)
|
#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...0.6.4)
|
||||||
|
|
||||||
@ -123,15 +182,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|||||||
- fetch full history [`5ecee4d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5ecee4d73a92d2e5c5ae971d79848ed27f65a72c)
|
- fetch full history [`5ecee4d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5ecee4d73a92d2e5c5ae971d79848ed27f65a72c)
|
||||||
- don't fail if tag exists (release) [`39c82fc`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/39c82fcb37d5c8e7e78a79288e1ef6280f8d0892)
|
- don't fail if tag exists (release) [`39c82fc`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/39c82fcb37d5c8e7e78a79288e1ef6280f8d0892)
|
||||||
|
|
||||||
#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.3...0.6.3)
|
#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...0.6.3)
|
||||||
|
|
||||||
> 8 February 2021
|
|
||||||
|
|
||||||
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
|
||||||
- - fixed changelog [`cf70b12`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/cf70b12ed74011924ea788ab932fc9d7ac0e6bd9)
|
|
||||||
- - yarn install to allow yarn auto-changelog [`fc496aa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/fc496aa04cb7e804da4335da0cb5cda26f874ea2)
|
|
||||||
|
|
||||||
#### [v0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...v0.6.3)
|
|
||||||
|
|
||||||
> 8 February 2021
|
> 8 February 2021
|
||||||
|
|
||||||
@ -140,9 +191,9 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
|||||||
- feat: 🍰 Allow Only Supported Image File Formats [`#3928`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/3928)
|
- feat: 🍰 Allow Only Supported Image File Formats [`#3928`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/3928)
|
||||||
- refactor: Disbale Emoji, Language And Catgeory Filter [`#4193`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4193)
|
- refactor: Disbale Emoji, Language And Catgeory Filter [`#4193`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4193)
|
||||||
- refactor: Remove Catgeories From Post Teaser [`#4191`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4191)
|
- refactor: Remove Catgeories From Post Teaser [`#4191`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4191)
|
||||||
|
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
||||||
- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a)
|
- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a)
|
||||||
- - lots of additional tests [`0ba37aa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/0ba37aab18f537d722aede7b87fa0b8e79f80e66)
|
- - lots of additional tests [`0ba37aa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/0ba37aab18f537d722aede7b87fa0b8e79f80e66)
|
||||||
- - deploy structure image [`a60400b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a60400b4fe6f59bbb80e1073db4def3ba205e1a7)
|
|
||||||
|
|
||||||
#### [0.6.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.0...0.6.0)
|
#### [0.6.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.0...0.6.0)
|
||||||
|
|
||||||
|
|||||||
@ -4,31 +4,33 @@ Thank you so much for thinking of contributing to the Human Connection project!
|
|||||||
|
|
||||||
## Getting Set Up
|
## Getting Set Up
|
||||||
|
|
||||||
Instructions for how to install all the necessary software and some code guidelines can be found in our [documentation](https://docs.human-connection.org/human-connection/).
|
Instructions for how to install all the necessary software and some code guidelines can be found in our main [Readme](/README.md) or in our [documentation](https://docs.human-connection.org/human-connection/).
|
||||||
|
|
||||||
To get you started we recommend that you join forces with a regular contributor. Please join [our discord instance](https://human-connection.org/discord) to chat with developers or just get in touch directly on an issue on either [Github](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues) or [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089):
|
To get you started we recommend that you join forces with a regular contributor. Please join [our Discord instance](https://discord.gg/AJSX9DCSUA) to chat with developers or just get in touch directly on an issue on either [Github](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues) or [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089):
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
We also have regular pair programming sessions that you are very welcome to join! We feel this is often the best way to get to know both the project and the team. Most developers are also available for spontaneous sessions if the times listed below don't work for you – just ping us on discord.
|
We also have regular pair programming sessions that you are very welcome to join! We feel this is often the best way to get to know both the project and the team. Most developers are also available for spontaneous sessions if the times listed below don't work for you – just ping us on Discord.
|
||||||
|
|
||||||
## Development Flow
|
## Development Flow
|
||||||
|
|
||||||
We operate in two week sprints that are planned, estimated and prioritised on [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089). All issues are also linked to and synced with [Github](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues). Look for the `good first issue` label if you're not sure where to start!
|
We operate in two week sprints that are planned, estimated and prioritised on [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089). All issues are also linked to and synced with [Github](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues). Look for the `good first issue` label if you're not sure where to start!
|
||||||
|
|
||||||
We try to discuss all questions directly related to a feature or bug in the respective issue, in order to preserve it for the future and for other developers. We use discord for real-time communication.
|
We try to discuss all questions directly related to a feature or bug in the respective issue, in order to preserve it for the future and for other developers. We use Discord for real-time communication.
|
||||||
|
|
||||||
This is how we solve bugs and implement features, step by step:
|
This is how we solve bugs and implement features, step by step:
|
||||||
|
|
||||||
1. We find an issue we want to work on, usually during the sprint planning but as an open source contributor this can happen at any time.
|
1. We find an issue we want to work on, usually during the sprint planning but as an open source contributor this can happen at any time.
|
||||||
2. We communicate with the team to see if the issue is still available. (When you comment on an issue but don't get an answer there within 1-2 days try to mention @Human-Connection/hc-dev-team to make sure we check in.)
|
2. We communicate with the team to see if the issue is still available. (When you comment on an issue but don't get an answer there within 1-2 days try to mention @Human-Connection/hc-dev-team to make sure we check in.)
|
||||||
3. We make sure we understand the issue in detail – what problem is it solving and how should it be implemented?
|
3. We make sure we understand the issue in detail – what problem is it solving and how should it be implemented?
|
||||||
4. We assign ourselves to the issue and move it to `In Progress` on [Zenhub](https://app.zenhub.com/workspaces/human-connection-nitro-5c0154ecc699f60fc92cf11f).
|
4. We assign ourselves to the issue and move it to `In Progress` on [Zenhub](https://app.zenhub.com/workspaces/ocelotsocial-5fb21ff922cb410015dd6535/board?filterLogic=any&repos=301151089).
|
||||||
5. We start working on it in a `new branch` and open a `pull request` prefixed with `[WIP]` (work in progress) to which we regularly push our changes.
|
5. We start working on it in a `new branch` and open a `pull request` prefixed with `[WIP]` (work in progress) to which we regularly push our changes.
|
||||||
6. When questions come up we clarify them with the team (directly in the issue on Github).
|
6. When questions come up we clarify them with the team (directly in the issue on Github).
|
||||||
7. When we are happy with our work and our PR is passing all tests we remove the `[WIP]` from the PR description and ask for reviews (if you're not sure who to ask there is @Human-Connection/hc-dev-team which pings all core developers).
|
7. When we are happy with our work and our PR is passing all tests we remove the `[WIP]` from the PR description and ask for reviews (if you're not sure who to ask there is @Human-Connection/hc-dev-team which pings all core developers).
|
||||||
8. We then incorporate the suggestions from the reviews into our work and once it has been approved it can be merged into master!
|
8. We then incorporate the suggestions from the reviews into our work and once it has been approved it can be merged into master!
|
||||||
|
|
||||||
Every pull request needs to:
|
Every pull request needs to:
|
||||||
|
|
||||||
* fix an issue (if there is something you want to work on but there is no issue for it, create one first and discuss it with the team)
|
* fix an issue (if there is something you want to work on but there is no issue for it, create one first and discuss it with the team)
|
||||||
* include tests for the code that is added or changed
|
* include tests for the code that is added or changed
|
||||||
* pass all tests (linter, backend, frontend, end-to-end)
|
* pass all tests (linter, backend, frontend, end-to-end)
|
||||||
@ -38,37 +40,46 @@ Every pull request needs to:
|
|||||||
|
|
||||||
There are many volunteers all around the world helping us build this network and without their contributions we wouldn't be where we are today. Big thank you to all of you!
|
There are many volunteers all around the world helping us build this network and without their contributions we wouldn't be where we are today. Big thank you to all of you!
|
||||||
|
|
||||||
You can see the core team behind Human Connection [on our website](https://human-connection.org/en/the-team/). On Github you will mostly run into our developers:
|
You can talk to our core team on [Discord](https://discord.gg/AJSX9DCSUA). And on Github you will mostly run into our core developers:
|
||||||
* Robert (@roschaefer)
|
|
||||||
* Matt (@mattwr18)
|
* Ulf (@ulfgebhardt)
|
||||||
|
* Moriz (@Mogge)
|
||||||
* Wolle (@Tirokk)
|
* Wolle (@Tirokk)
|
||||||
* Alex (@ogerly)
|
* Alex (@ogerly)
|
||||||
|
|
||||||
|
<!-- * Robert (@roschaefer)
|
||||||
|
* Matt (@mattwr18)
|
||||||
* Alina (@alina-beck)
|
* Alina (@alina-beck)
|
||||||
* Martin (@datenbrei), our head of IT
|
* Martin (@datenbrei), our head of IT
|
||||||
* and sometimes Dennis (@DennisHack), the founder of Human Connection
|
* and sometimes Dennis (@DennisHack), the founder of Human Connection -->
|
||||||
|
|
||||||
## Meetings and Pair Programming Sessions
|
## Meetings and Pair Programming Sessions
|
||||||
|
|
||||||
Times below refer to **German Time** – that's CET (GMT+1) in winter and CEST (GMT+2) in summer – because most Human Connection core team members are living in Germany.
|
Times below refer to **German Time** – that's CET (GMT+1) in winter and CEST (GMT+2) in summer – because most Human Connection core team members are living in Germany.
|
||||||
|
|
||||||
Daily standup
|
Daily standup
|
||||||
* every Monday–Friday 11:30
|
|
||||||
* in the discord `Conference Room`
|
* every Monday–Thursday 11:30 am (german time see above 👆🏼)
|
||||||
|
* in our [Discord](https://discord.gg/AJSX9DCSUA) `Office Cube`
|
||||||
* all contributors welcome!
|
* all contributors welcome!
|
||||||
* everybody shares what they are working on and asks for help if they are blocked
|
* everybody shares what they are working on and asks for help if they are blocked
|
||||||
|
|
||||||
|
<!--
|
||||||
Regular pair programming sessions
|
Regular pair programming sessions
|
||||||
|
|
||||||
* every Monday, Wednesday and Thursday 15:00
|
* every Monday, Wednesday and Thursday 15:00
|
||||||
* the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
|
* the link will be posted in the [Discord chat](https://discord.gg/AJSX9DCSUA) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
|
||||||
* all contributors welcome!
|
* all contributors welcome!
|
||||||
* we team up and work on an issue together (often using Visual Studio live sharing sessions)
|
* we team up and work on an issue together (often using Visual Studio live sharing sessions)
|
||||||
|
|
||||||
Open-Source Community Meeting
|
Open-Source Community Meeting
|
||||||
|
|
||||||
* bi-weekly on Mondays 13:00 (when there is no sprint retrospective)
|
* bi-weekly on Mondays 13:00 (when there is no sprint retrospective)
|
||||||
* the link will be posted in the [discord chat](https://discord.gg/6ub73U3) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
|
* the link will be posted in the [Discord chat](https://discord.gg/AJSX9DCSUA) and on the [Agile Ventures website](https://www.agileventures.org/events?utf8=%E2%9C%93&project_id=220&commit=Filter+by+Project)
|
||||||
* all contributors welcome!
|
* all contributors welcome!
|
||||||
|
|
||||||
Meet the team
|
Meet the team
|
||||||
|
|
||||||
* every Monday 21:00 (at the moment only in German)
|
* every Monday 21:00 (at the moment only in German)
|
||||||
* details here https://human-connection.org/veranstaltungen/
|
* details here https://human-connection.org/veranstaltungen/
|
||||||
* via this [zoom link](https://zoom.us/j/936943532)
|
* via this [zoom link](https://zoom.us/j/936943532)
|
||||||
@ -76,6 +87,7 @@ Meet the team
|
|||||||
* users of the network chat with the Human Connection team and discuss current questions and issues
|
* users of the network chat with the Human Connection team and discuss current questions and issues
|
||||||
|
|
||||||
Sprint planning
|
Sprint planning
|
||||||
|
|
||||||
* bi-weekly on Tuesday 13:00
|
* bi-weekly on Tuesday 13:00
|
||||||
* via this [zoom link](https://zoom.us/j/7743582385)
|
* via this [zoom link](https://zoom.us/j/7743582385)
|
||||||
* all contributors welcome (recommended for those who want to work on an issue in this sprint)
|
* all contributors welcome (recommended for those who want to work on an issue in this sprint)
|
||||||
@ -87,6 +99,7 @@ Sprint retrospective
|
|||||||
* via this [zoom link](https://zoom.us/j/7743582385)
|
* via this [zoom link](https://zoom.us/j/7743582385)
|
||||||
* all contributors welcome (most interesting for those who participated in the sprint)
|
* all contributors welcome (most interesting for those who participated in the sprint)
|
||||||
* we review the past sprint and talk about what went well and what we could improve
|
* we review the past sprint and talk about what went well and what we could improve
|
||||||
|
-->
|
||||||
|
|
||||||
## Philosophy
|
## Philosophy
|
||||||
|
|
||||||
@ -102,10 +115,9 @@ We use pair programming sessions as a tool for knowledge sharing. We can learn a
|
|||||||
|
|
||||||
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 volunteeer you have no commitment except your own self development and your awesomeness by contributing to this free and open-source software project. Cheers to you!
|
||||||
|
|
||||||
|
|
||||||
## Open-Source Bounties
|
## Open-Source Bounties
|
||||||
|
|
||||||
There are so many good reasons to contribute to Human Connection
|
There are so many good reasons to contribute to ocelot.social
|
||||||
|
|
||||||
* You learn state-of-the-art technologies
|
* You learn state-of-the-art technologies
|
||||||
* You build your portfolio
|
* You build your portfolio
|
||||||
@ -121,7 +133,7 @@ pull request approved and merged for free**. You can choose something really
|
|||||||
quick and easy. What's important is starting a working relationship with the
|
quick and easy. What's important is starting a working relationship with the
|
||||||
team, learning the workflow, and understanding this contribution guide. You can
|
team, learning the workflow, and understanding this contribution guide. You can
|
||||||
filter issues by 'good first issue', to get an idea where to start. Please join
|
filter issues by 'good first issue', to get an idea where to start. Please join
|
||||||
our our [community chat](https://human-connection.org/discord), too.
|
our our [Discord community chat](https://discord.gg/AJSX9DCSUA), too.
|
||||||
|
|
||||||
You can filter Github issues with label [bounty](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues?q=is%3Aopen+is%3Aissue+label%3Abounty). These issues should have a second label `€<amount>`
|
You can filter Github issues with label [bounty](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues?q=is%3Aopen+is%3Aissue+label%3Abounty). These issues should have a second label `€<amount>`
|
||||||
which indicate their respective financial compensation in Euros.
|
which indicate their respective financial compensation in Euros.
|
||||||
|
|||||||
32
README.md
32
README.md
@ -12,11 +12,13 @@ ocelot.social is a nonprofit social, action and knowledge network that connects
|
|||||||
* **Knowledge**: Read articles about interesting topics and find related posts in the **More Info** tab or by **Filtering** based on **Categories** and **Tagging** or by using the **Fulltext Search**.
|
* **Knowledge**: Read articles about interesting topics and find related posts in the **More Info** tab or by **Filtering** based on **Categories** and **Tagging** or by using the **Fulltext Search**.
|
||||||
* **Action**: Don't just read about how to make the world a better place, but come into **Action** by following provided suggestions on the **Action** tab provided by other people or **Organisations**.
|
* **Action**: Don't just read about how to make the world a better place, but come into **Action** by following provided suggestions on the **Action** tab provided by other people or **Organisations**.
|
||||||
|
|
||||||
[](https://ocelot.social)
|
<p align="center">
|
||||||
|
<img src="webapp/static/img/custom/logo-squared.svg" alt="ocelot.social" width="40%" height="40%">
|
||||||
|
</p>
|
||||||
|
|
||||||
## Live demo
|
## Live demo
|
||||||
|
|
||||||
Try out our deployed [development environment](https://develop.human-connection.org/).
|
__Try out our deployed [development environment](https://develop.human-connection.org/).__
|
||||||
|
|
||||||
Logins:
|
Logins:
|
||||||
|
|
||||||
@ -29,9 +31,9 @@ Logins:
|
|||||||
## Directory Layout
|
## Directory Layout
|
||||||
|
|
||||||
There are four important directories:
|
There are four important directories:
|
||||||
|
|
||||||
* [Backend](./backend) runs on the server and is a middleware between database and frontend
|
* [Backend](./backend) runs on the server and is a middleware between database and frontend
|
||||||
* [Frontend](./webapp) is a server-side-rendered and client-side-rendered web frontend
|
* [Frontend](./webapp) is a server-side-rendered and client-side-rendered web frontend
|
||||||
* [Deployment](./deployment) configuration for kubernetes
|
|
||||||
* [Cypress](./cypress) contains end-to-end tests and executable feature specifications
|
* [Cypress](./cypress) contains end-to-end tests and executable feature specifications
|
||||||
|
|
||||||
In order to setup the application and start to develop features you have to
|
In order to setup the application and start to develop features you have to
|
||||||
@ -45,16 +47,19 @@ There are two approaches:
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Clone the Repository
|
### Clone the Repository
|
||||||
|
|
||||||
Clone the repository, this will create a new folder called `Ocelot-Social`:
|
Clone the repository, this will create a new folder called `Ocelot-Social`:
|
||||||
|
|
||||||
Using HTTPS:
|
Using HTTPS:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/Ocelot-Social-Community/Ocelot-Social.git
|
$ git clone https://github.com/Ocelot-Social-Community/Ocelot-Social.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Using SSH:
|
Using SSH:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone git@github.com:Human-Connection/Human-Connection.git
|
$ git clone git@github.com:Ocelot-Social-Community/Ocelot-Social.git
|
||||||
```
|
```
|
||||||
|
|
||||||
Change into the new folder.
|
Change into the new folder.
|
||||||
@ -87,25 +92,33 @@ docker-compose version 1.23.2
|
|||||||
#### Start Ocelot-Social via Docker-Compose
|
#### Start Ocelot-Social via Docker-Compose
|
||||||
|
|
||||||
For Development:
|
For Development:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose up
|
$ docker-compose up
|
||||||
```
|
```
|
||||||
|
|
||||||
For Production
|
For Production:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose -f docker-compose.yml up
|
$ docker-compose -f docker-compose.yml up
|
||||||
```
|
```
|
||||||
|
|
||||||
This will start all required Docker containers
|
This will start all required Docker containers
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Deployment methods can be found in the [Ocelot-Social-Deploy-Rebranding](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding) repository.
|
||||||
|
|
||||||
|
The only deployment method in this repository is `docker-compose` for development purposes as described above.
|
||||||
|
|
||||||
## Developer Chat
|
## Developer Chat
|
||||||
|
|
||||||
Join our friendly open-source community on [Discord](https://discordapp.com/invite/DFSjPaX) :heart_eyes_cat:
|
Join our friendly open-source community on [Discord](https://discord.gg/AJSX9DCSUA) :heart_eyes_cat:
|
||||||
Just introduce yourself at `#introduce-yourself` and mention `@@Mentor` to get you onboard :neckbeard:
|
Just introduce yourself at `#introduce-yourself` and mention `@@Mentor` to get you onboard :neckbeard:
|
||||||
Check out the [contribution guideline](./CONTRIBUTING.md), too!
|
Check out the [contribution guideline](./CONTRIBUTING.md), too!
|
||||||
|
|
||||||
We give write permissions to every developer who asks for it. Just text us on
|
We give write permissions to every developer who asks for it. Just text us on
|
||||||
[Discord](https://discord.gg/6ub73U3).
|
[Discord](https://discord.gg/AJSX9DCSUA).
|
||||||
|
|
||||||
## Technology Stack
|
## Technology Stack
|
||||||
|
|
||||||
@ -124,4 +137,5 @@ Browser compatibility testing with [BrowserStack](https://www.browserstack.com/)
|
|||||||
<img alt="BrowserStack Logo" src=".gitbook/assets/browserstack-logo.svg" width="256">
|
<img alt="BrowserStack Logo" src=".gitbook/assets/browserstack-logo.svg" width="256">
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT).
|
See the [LICENSE](LICENSE.md) file for license rights and limitations (MIT).
|
||||||
|
|||||||
18
SUMMARY.md
18
SUMMARY.md
@ -15,24 +15,8 @@
|
|||||||
* [End-to-end tests](cypress/README.md)
|
* [End-to-end tests](cypress/README.md)
|
||||||
* [Frontend tests](webapp/testing.md)
|
* [Frontend tests](webapp/testing.md)
|
||||||
* [Backend tests](backend/testing.md)
|
* [Backend tests](backend/testing.md)
|
||||||
|
* [Deployment](https://github.com/Ocelot-Social-Community/Ocelot-Social-Deploy-Rebranding/blob/master/deployment/README.md)
|
||||||
* [Contributing](CONTRIBUTING.md)
|
* [Contributing](CONTRIBUTING.md)
|
||||||
* [Kubernetes Deployment](deployment/README.md)
|
|
||||||
* [Minikube](deployment/minikube/README.md)
|
|
||||||
* [Digital Ocean](deployment/digital-ocean/README.md)
|
|
||||||
* [Kubernetes Dashboard](deployment/digital-ocean/dashboard/README.md)
|
|
||||||
* [HTTPS](deployment/digital-ocean/https/README.md)
|
|
||||||
* [ocelot.social](deployment/ocelot-social/README.md)
|
|
||||||
* [Error Reporting](deployment/ocelot-social/error-reporting/README.md)
|
|
||||||
* [Mailserver](deployment/ocelot-social/mailserver/README.md)
|
|
||||||
* [Maintenance](deployment/ocelot-social/maintenance/README.md)
|
|
||||||
* [Volumes](deployment/volumes/README.md)
|
|
||||||
* [Neo4J Offline-Backups](deployment/volumes/neo4j-offline-backup/README.md)
|
|
||||||
* [Neo4J Online-Backups](deployment/volumes/neo4j-online-backup/README.md)
|
|
||||||
* [Volume Snapshots](deployment/volumes/volume-snapshots/README.md)
|
|
||||||
* [Reclaim Policy](deployment/volumes/reclaim-policy/README.md)
|
|
||||||
* [Velero](deployment/volumes/velero/README.md)
|
|
||||||
* [Metrics](deployment/monitoring/README.md)
|
|
||||||
* [Legacy Migration](deployment/legacy-migration/README.md)
|
|
||||||
* [Feature Specification](cypress/features.md)
|
* [Feature Specification](cypress/features.md)
|
||||||
* [Code of conduct](CODE_OF_CONDUCT.md)
|
* [Code of conduct](CODE_OF_CONDUCT.md)
|
||||||
* [License](LICENSE.md)
|
* [License](LICENSE.md)
|
||||||
|
|||||||
@ -10,6 +10,7 @@ SMTP_USERNAME=
|
|||||||
SMTP_PASSWORD=
|
SMTP_PASSWORD=
|
||||||
|
|
||||||
JWT_SECRET="b/&&7b78BF&fv/Vd"
|
JWT_SECRET="b/&&7b78BF&fv/Vd"
|
||||||
|
JWT_EXPIRES="2y"
|
||||||
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
|
MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g"
|
||||||
|
|
||||||
PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
||||||
@ -17,6 +18,7 @@ PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78"
|
|||||||
SENTRY_DSN_BACKEND=
|
SENTRY_DSN_BACKEND=
|
||||||
COMMIT=
|
COMMIT=
|
||||||
PUBLIC_REGISTRATION=false
|
PUBLIC_REGISTRATION=false
|
||||||
|
INVITE_REGISTRATION=true
|
||||||
|
|
||||||
AWS_ACCESS_KEY_ID=
|
AWS_ACCESS_KEY_ID=
|
||||||
AWS_SECRET_ACCESS_KEY=
|
AWS_SECRET_ACCESS_KEY=
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
##################################################################################
|
##################################################################################
|
||||||
# BASE ###########################################################################
|
# BASE (Is pushed to DockerHub for rebranding) ###################################
|
||||||
##################################################################################
|
##################################################################################
|
||||||
FROM node:12.19.0-alpine3.10 as base
|
FROM node:12.19.0-alpine3.10 as base
|
||||||
|
|
||||||
# ENVs (available in production aswell, can be overwritten by commandline or env file)
|
# 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
|
||||||
ENV DOCKER_WORKDIR="/app"
|
ENV DOCKER_WORKDIR="/app"
|
||||||
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
|
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
|
||||||
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
|
ARG BBUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||||
|
ENV BUILD_DATE=$BBUILD_DATE
|
||||||
## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0
|
## We cannot do $(yarn run version)-${BUILD_NUMBER} here so we default to 0.0.0-0
|
||||||
ENV BUILD_VERSION="0.0.0-0"
|
ARG BBUILD_VERSION="0.0.0-0"
|
||||||
|
ENV BUILD_VERSION=$BBUILD_VERSION
|
||||||
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||||
ENV BUILD_COMMIT="0000000"
|
ARG BBUILD_COMMIT="0000000"
|
||||||
|
ENV BUILD_COMMIT=$BBUILD_COMMIT
|
||||||
## SET NODE_ENV
|
## SET NODE_ENV
|
||||||
ENV NODE_ENV="production"
|
ENV NODE_ENV="production"
|
||||||
## App relevant Envs
|
## App relevant Envs
|
||||||
@ -56,12 +59,18 @@ FROM base as development
|
|||||||
CMD /bin/sh -c "yarn install && yarn run dev"
|
CMD /bin/sh -c "yarn install && yarn run dev"
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# BUILD (Does contain all files and is therefore bloated) ########################
|
# CODE (Does contain all code files and is pushed to DockerHub for rebranding) ###
|
||||||
##################################################################################
|
##################################################################################
|
||||||
FROM base as build
|
FROM base as code
|
||||||
|
|
||||||
# Copy everything
|
# copy everything, but do not build.
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# BUILD (Does contain all files and the compilate and is therefore bloated) ######
|
||||||
|
##################################################################################
|
||||||
|
FROM code as build
|
||||||
|
|
||||||
# yarn install
|
# yarn install
|
||||||
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||||
# yarn build
|
# yarn build
|
||||||
@ -82,7 +91,6 @@ FROM base as production
|
|||||||
|
|
||||||
# Copy "binary"-files from build image
|
# Copy "binary"-files from build image
|
||||||
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
|
COPY --from=build ${DOCKER_WORKDIR}/dist ./dist
|
||||||
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
|
|
||||||
# Copy static files
|
# Copy static files
|
||||||
# TODO - externalize the uploads so we can copy the whole folder
|
# TODO - externalize the uploads so we can copy the whole folder
|
||||||
COPY --from=build ${DOCKER_WORKDIR}/public/img/ ./public/img/
|
COPY --from=build ${DOCKER_WORKDIR}/public/img/ ./public/img/
|
||||||
@ -91,4 +99,4 @@ COPY --from=build ${DOCKER_WORKDIR}/public/providers.json ./public/providers.jso
|
|||||||
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
|
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
|
||||||
|
|
||||||
# Run command
|
# Run command
|
||||||
CMD /bin/sh -c "yarn run start"
|
CMD /bin/sh -c "yarn run start"
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "ocelot-social-backend",
|
"name": "ocelot-social-backend",
|
||||||
"version": "0.6.15",
|
"version": "1.0.4",
|
||||||
"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,7 +15,7 @@
|
|||||||
"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 jest --forceExit --detectOpenHandles --runInBand",
|
"test": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --runInBand --coverage",
|
||||||
"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",
|
||||||
@ -39,6 +39,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@babel/cli": "~7.8.4",
|
||||||
|
"@babel/core": "~7.9.0",
|
||||||
|
"@babel/node": "~7.8.7",
|
||||||
|
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
|
||||||
|
"@babel/preset-env": "~7.9.5",
|
||||||
|
"@babel/register": "^7.9.0",
|
||||||
"@hapi/joi": "^17.1.1",
|
"@hapi/joi": "^17.1.1",
|
||||||
"@sentry/node": "^5.15.4",
|
"@sentry/node": "^5.15.4",
|
||||||
"apollo-cache-inmemory": "~1.6.5",
|
"apollo-cache-inmemory": "~1.6.5",
|
||||||
@ -48,12 +54,15 @@
|
|||||||
"apollo-server": "~2.14.2",
|
"apollo-server": "~2.14.2",
|
||||||
"apollo-server-express": "^2.14.2",
|
"apollo-server-express": "^2.14.2",
|
||||||
"aws-sdk": "^2.652.0",
|
"aws-sdk": "^2.652.0",
|
||||||
|
"babel-core": "~7.0.0-0",
|
||||||
|
"babel-eslint": "~10.1.0",
|
||||||
|
"babel-jest": "~25.2.6",
|
||||||
"babel-plugin-transform-runtime": "^6.23.0",
|
"babel-plugin-transform-runtime": "^6.23.0",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"cheerio": "~1.0.0-rc.3",
|
"cheerio": "~1.0.0-rc.3",
|
||||||
"cors": "~2.8.5",
|
"cors": "~2.8.5",
|
||||||
"cross-env": "~7.0.2",
|
"cross-env": "~7.0.2",
|
||||||
"date-fns": "2.11.1",
|
"date-fns": "2.22.1",
|
||||||
"debug": "~4.1.1",
|
"debug": "~4.1.1",
|
||||||
"dotenv": "~8.2.0",
|
"dotenv": "~8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
@ -72,7 +81,7 @@
|
|||||||
"languagedetect": "^2.0.0",
|
"languagedetect": "^2.0.0",
|
||||||
"linkifyjs": "~2.1.8",
|
"linkifyjs": "~2.1.8",
|
||||||
"lodash": "~4.17.14",
|
"lodash": "~4.17.14",
|
||||||
"merge-graphql-schemas": "^1.7.7",
|
"merge-graphql-schemas": "^1.7.8",
|
||||||
"metascraper": "^5.11.8",
|
"metascraper": "^5.11.8",
|
||||||
"metascraper-audio": "^5.14.26",
|
"metascraper-audio": "^5.14.26",
|
||||||
"metascraper-author": "^5.14.22",
|
"metascraper-author": "^5.14.22",
|
||||||
@ -92,7 +101,7 @@
|
|||||||
"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",
|
||||||
"mustache": "^4.0.1",
|
"mustache": "^4.2.0",
|
||||||
"neo4j-driver": "^4.0.2",
|
"neo4j-driver": "^4.0.2",
|
||||||
"neo4j-graphql-js": "^2.11.5",
|
"neo4j-graphql-js": "^2.11.5",
|
||||||
"neode": "^0.3.7",
|
"neode": "^0.3.7",
|
||||||
@ -111,16 +120,7 @@
|
|||||||
"xregexp": "^4.3.0"
|
"xregexp": "^4.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/cli": "~7.8.4",
|
|
||||||
"@babel/core": "~7.9.0",
|
|
||||||
"@babel/node": "~7.8.7",
|
|
||||||
"@babel/plugin-proposal-throw-expressions": "^7.8.3",
|
|
||||||
"@babel/preset-env": "~7.9.5",
|
|
||||||
"@babel/register": "^7.9.0",
|
|
||||||
"apollo-server-testing": "~2.11.0",
|
"apollo-server-testing": "~2.11.0",
|
||||||
"babel-core": "~7.0.0-0",
|
|
||||||
"babel-eslint": "~10.1.0",
|
|
||||||
"babel-jest": "~25.2.6",
|
|
||||||
"chai": "~4.2.0",
|
"chai": "~4.2.0",
|
||||||
"cucumber": "~6.0.5",
|
"cucumber": "~6.0.5",
|
||||||
"eslint": "~6.8.0",
|
"eslint": "~6.8.0",
|
||||||
@ -134,7 +134,7 @@
|
|||||||
"eslint-plugin-standard": "~4.0.1",
|
"eslint-plugin-standard": "~4.0.1",
|
||||||
"jest": "~25.3.0",
|
"jest": "~25.3.0",
|
||||||
"nodemon": "~2.0.2",
|
"nodemon": "~2.0.2",
|
||||||
"prettier": "~2.2.0",
|
"prettier": "~2.3.2",
|
||||||
"rosie": "^2.0.1",
|
"rosie": "^2.0.1",
|
||||||
"supertest": "~4.0.2"
|
"supertest": "~4.0.2"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,9 +7,8 @@ if (require.resolve) {
|
|||||||
try {
|
try {
|
||||||
dotenv.config({ path: require.resolve('../../.env') })
|
dotenv.config({ path: require.resolve('../../.env') })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code === 'MODULE_NOT_FOUND') {
|
// This error is thrown when the .env is not found
|
||||||
console.log('WARN: No `.env` file found in `/app` (docker) or `/backend` (no docker)') // eslint-disable-line no-console
|
if (error.code !== 'MODULE_NOT_FOUND') {
|
||||||
} else {
|
|
||||||
throw error
|
throw error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -35,13 +34,14 @@ const required = {
|
|||||||
const server = {
|
const server = {
|
||||||
CLIENT_URI: env.CLIENT_URI || 'http://localhost:3000',
|
CLIENT_URI: env.CLIENT_URI || 'http://localhost:3000',
|
||||||
GRAPHQL_URI: env.GRAPHQL_URI || 'http://localhost:4000',
|
GRAPHQL_URI: env.GRAPHQL_URI || 'http://localhost:4000',
|
||||||
|
JWT_EXPIRES: env.JWT_EXPIRES || '2y',
|
||||||
}
|
}
|
||||||
|
|
||||||
const smtp = {
|
const smtp = {
|
||||||
SMTP_HOST: env.SMTP_HOST,
|
SMTP_HOST: env.SMTP_HOST,
|
||||||
SMTP_PORT: env.SMTP_PORT,
|
SMTP_PORT: env.SMTP_PORT,
|
||||||
SMTP_IGNORE_TLS: env.SMTP_IGNORE_TLS === 'true' || true,
|
SMTP_IGNORE_TLS: env.SMTP_IGNORE_TLS !== 'false', // default = true
|
||||||
SMTP_SECURE: env.SMTP_IGNORE_TLS === 'true' || false,
|
SMTP_SECURE: env.SMTP_SECURE === 'true',
|
||||||
SMTP_USERNAME: env.SMTP_USERNAME,
|
SMTP_USERNAME: env.SMTP_USERNAME,
|
||||||
SMTP_PASSWORD: env.SMTP_PASSWORD,
|
SMTP_PASSWORD: env.SMTP_PASSWORD,
|
||||||
}
|
}
|
||||||
@ -82,7 +82,8 @@ const options = {
|
|||||||
SUPPORT_URL: links.SUPPORT,
|
SUPPORT_URL: links.SUPPORT,
|
||||||
APPLICATION_NAME: metadata.APPLICATION_NAME,
|
APPLICATION_NAME: metadata.APPLICATION_NAME,
|
||||||
ORGANIZATION_URL: links.ORGANIZATION,
|
ORGANIZATION_URL: links.ORGANIZATION,
|
||||||
PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true',
|
PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true' || false,
|
||||||
|
INVITE_REGISTRATION: env.INVITE_REGISTRATION !== 'false', // default = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if all required configs are present
|
// Check if all required configs are present
|
||||||
|
|||||||
@ -1,6 +1,13 @@
|
|||||||
|
// this file is duplicated in `backend/src/config/links.js` and `webapp/constants/links.js` and replaced on rebranding
|
||||||
export default {
|
export default {
|
||||||
ORGANIZATION: 'https://ocelot.social',
|
ORGANIZATION: 'https://ocelot.social',
|
||||||
DONATE: 'https://ocelot-social.herokuapp.com/donations',
|
|
||||||
FAQ: 'https://ocelot.social',
|
|
||||||
SUPPORT: 'https://ocelot.social',
|
SUPPORT: 'https://ocelot.social',
|
||||||
|
|
||||||
|
// on null or empty strings internal imprint is used, see 'webapp/locales/html/'
|
||||||
|
DONATE: 'https://ocelot-social.herokuapp.com/donations', // we use 'ocelot-social.herokuapp.com' at the moment, because redirections of 'ocelot.social' subpages are not working correctly
|
||||||
|
IMPRINT: 'https://ocelot-social.herokuapp.com/imprint', // we use 'ocelot-social.herokuapp.com' at the moment, because redirections of 'ocelot.social' subpages are not working correctly
|
||||||
|
TERMS_AND_CONDITIONS: null,
|
||||||
|
CODE_OF_CONDUCT: null,
|
||||||
|
DATA_PRIVACY: null,
|
||||||
|
FAQ: 'https://ocelot.social',
|
||||||
}
|
}
|
||||||
|
|||||||
10
backend/src/config/logos.js
Normal file
10
backend/src/config/logos.js
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
// this file is duplicated in `backend/src/config/logos.js` and `webapp/constants/logos.js` and replaced on rebranding
|
||||||
|
// this are the paths in the webapp
|
||||||
|
export default {
|
||||||
|
LOGO_HEADER_PATH: '/img/custom/logo-horizontal.svg',
|
||||||
|
LOGO_SIGNUP_PATH: '/img/custom/logo-squared.svg',
|
||||||
|
LOGO_WELCOME_PATH: '/img/custom/logo-squared.svg',
|
||||||
|
LOGO_LOGOUT_PATH: '/img/custom/logo-squared.svg',
|
||||||
|
LOGO_PASSWORD_RESET_PATH: '/img/custom/logo-squared.svg',
|
||||||
|
LOGO_MAINTENACE_RESET_PATH: '/img/custom/logo-squared.svg',
|
||||||
|
}
|
||||||
@ -1,7 +1,9 @@
|
|||||||
|
// this file is duplicated in `backend/src/config/metadata.js` and `webapp/constants/metadata.js` and replaced on rebranding
|
||||||
export default {
|
export default {
|
||||||
APPLICATION_NAME: 'ocelot.social',
|
APPLICATION_NAME: 'ocelot.social',
|
||||||
APPLICATION_SHORT_NAME: 'ocelot',
|
APPLICATION_SHORT_NAME: 'ocelot',
|
||||||
APPLICATION_DESCRIPTION: 'ocelot.social Community Network',
|
APPLICATION_DESCRIPTION: 'ocelot.social Community Network',
|
||||||
|
COOKIE_NAME: 'ocelot-social-token',
|
||||||
ORGANIZATION_NAME: 'ocelot.social Community',
|
ORGANIZATION_NAME: 'ocelot.social Community',
|
||||||
ORGANIZATION_JURISDICTION: 'City of Angels',
|
ORGANIZATION_JURISDICTION: 'City of Angels',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,12 +105,12 @@ Factory.define('user')
|
|||||||
})
|
})
|
||||||
|
|
||||||
Factory.define('post')
|
Factory.define('post')
|
||||||
.option('categoryIds', [])
|
/* .option('categoryIds', [])
|
||||||
.option('categories', ['categoryIds'], (categoryIds) => {
|
.option('categories', ['categoryIds'], (categoryIds) => {
|
||||||
if (categoryIds.length) return Promise.all(categoryIds.map((id) => neode.find('Category', id)))
|
if (categoryIds.length) return Promise.all(categoryIds.map((id) => neode.find('Category', id)))
|
||||||
// there must be at least one category
|
// there must be at least one category
|
||||||
return Promise.all([Factory.build('category')])
|
return Promise.all([Factory.build('category')])
|
||||||
})
|
}) */
|
||||||
.option('tagIds', [])
|
.option('tagIds', [])
|
||||||
.option('tags', ['tagIds'], (tagIds) => {
|
.option('tags', ['tagIds'], (tagIds) => {
|
||||||
return Promise.all(tagIds.map((id) => neode.find('Tag', id)))
|
return Promise.all(tagIds.map((id) => neode.find('Tag', id)))
|
||||||
@ -147,16 +147,16 @@ Factory.define('post')
|
|||||||
return language || 'en'
|
return language || 'en'
|
||||||
})
|
})
|
||||||
.after(async (buildObject, options) => {
|
.after(async (buildObject, options) => {
|
||||||
const [post, author, image, categories, tags] = await Promise.all([
|
const [post, author, image, /* categories, */ tags] = await Promise.all([
|
||||||
neode.create('Post', buildObject),
|
neode.create('Post', buildObject),
|
||||||
options.author,
|
options.author,
|
||||||
options.image,
|
options.image,
|
||||||
options.categories,
|
// options.categories,
|
||||||
options.tags,
|
options.tags,
|
||||||
])
|
])
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
post.relateTo(author, 'author'),
|
post.relateTo(author, 'author'),
|
||||||
Promise.all(categories.map((c) => c.relateTo(post, 'post'))),
|
// Promise.all(categories.map((c) => c.relateTo(post, 'post'))),
|
||||||
Promise.all(tags.map((t) => t.relateTo(post, 'post'))),
|
Promise.all(tags.map((t) => t.relateTo(post, 'post'))),
|
||||||
])
|
])
|
||||||
if (image) await post.relateTo(image, 'image')
|
if (image) await post.relateTo(image, 'image')
|
||||||
|
|||||||
@ -137,100 +137,93 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
const [
|
const [peterLustig, bobDerBaumeister, jennyRostock, huey, dewey, louie, dagobert] =
|
||||||
peterLustig,
|
await Promise.all([
|
||||||
bobDerBaumeister,
|
Factory.build(
|
||||||
jennyRostock,
|
'user',
|
||||||
huey,
|
{
|
||||||
dewey,
|
id: 'u1',
|
||||||
louie,
|
name: 'Peter Lustig',
|
||||||
dagobert,
|
slug: 'peter-lustig',
|
||||||
] = await Promise.all([
|
role: 'admin',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'admin@example.org',
|
||||||
id: 'u1',
|
},
|
||||||
name: 'Peter Lustig',
|
),
|
||||||
slug: 'peter-lustig',
|
Factory.build(
|
||||||
role: 'admin',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u2',
|
||||||
email: 'admin@example.org',
|
name: 'Bob der Baumeister',
|
||||||
},
|
slug: 'bob-der-baumeister',
|
||||||
),
|
role: 'moderator',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'moderator@example.org',
|
||||||
id: 'u2',
|
},
|
||||||
name: 'Bob der Baumeister',
|
),
|
||||||
slug: 'bob-der-baumeister',
|
Factory.build(
|
||||||
role: 'moderator',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u3',
|
||||||
email: 'moderator@example.org',
|
name: 'Jenny Rostock',
|
||||||
},
|
slug: 'jenny-rostock',
|
||||||
),
|
role: 'user',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'user@example.org',
|
||||||
id: 'u3',
|
},
|
||||||
name: 'Jenny Rostock',
|
),
|
||||||
slug: 'jenny-rostock',
|
Factory.build(
|
||||||
role: 'user',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u4',
|
||||||
email: 'user@example.org',
|
name: 'Huey',
|
||||||
},
|
slug: 'huey',
|
||||||
),
|
role: 'user',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'huey@example.org',
|
||||||
id: 'u4',
|
},
|
||||||
name: 'Huey',
|
),
|
||||||
slug: 'huey',
|
Factory.build(
|
||||||
role: 'user',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u5',
|
||||||
email: 'huey@example.org',
|
name: 'Dewey',
|
||||||
},
|
slug: 'dewey',
|
||||||
),
|
role: 'user',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'dewey@example.org',
|
||||||
id: 'u5',
|
},
|
||||||
name: 'Dewey',
|
),
|
||||||
slug: 'dewey',
|
Factory.build(
|
||||||
role: 'user',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u6',
|
||||||
email: 'dewey@example.org',
|
name: 'Louie',
|
||||||
},
|
slug: 'louie',
|
||||||
),
|
role: 'user',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'louie@example.org',
|
||||||
id: 'u6',
|
},
|
||||||
name: 'Louie',
|
),
|
||||||
slug: 'louie',
|
Factory.build(
|
||||||
role: 'user',
|
'user',
|
||||||
},
|
{
|
||||||
{
|
id: 'u7',
|
||||||
email: 'louie@example.org',
|
name: 'Dagobert',
|
||||||
},
|
slug: 'dagobert',
|
||||||
),
|
role: 'user',
|
||||||
Factory.build(
|
},
|
||||||
'user',
|
{
|
||||||
{
|
email: 'dagobert@example.org',
|
||||||
id: 'u7',
|
},
|
||||||
name: 'Dagobert',
|
),
|
||||||
slug: 'dagobert',
|
])
|
||||||
role: 'user',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: 'dagobert@example.org',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
])
|
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
peterLustig.relateTo(Berlin, 'isIn'),
|
peterLustig.relateTo(Berlin, 'isIn'),
|
||||||
@ -561,7 +554,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
|||||||
const hashtagAndMention1 =
|
const hashtagAndMention1 =
|
||||||
'The new physics of <a class="hashtag" data-hashtag-id="QuantenFlussTheorie" href="/?hashtag=QuantenFlussTheorie">#QuantenFlussTheorie</a> can explain <a class="hashtag" data-hashtag-id="QuantumGravity" href="/?hashtag=QuantumGravity">#QuantumGravity</a>! <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> got that already. ;-)'
|
'The new physics of <a class="hashtag" data-hashtag-id="QuantenFlussTheorie" href="/?hashtag=QuantenFlussTheorie">#QuantenFlussTheorie</a> can explain <a class="hashtag" data-hashtag-id="QuantumGravity" href="/?hashtag=QuantumGravity">#QuantumGravity</a>! <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> got that already. ;-)'
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($id: ID, $title: String!, $content: String!, $categoryIds: [ID]) {
|
mutation ($id: ID, $title: String!, $content: String!, $categoryIds: [ID]) {
|
||||||
CreatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
|
CreatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
@ -618,7 +611,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
|||||||
const mentionInComment2 =
|
const mentionInComment2 =
|
||||||
'Did <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> tell you?'
|
'Did <a class="mention" data-mention-id="u1" href="/profile/u1">@peter-lustig</a> tell you?'
|
||||||
const createCommentMutation = gql`
|
const createCommentMutation = gql`
|
||||||
mutation($id: ID, $postId: ID!, $content: String!) {
|
mutation ($id: ID, $postId: ID!, $content: String!) {
|
||||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import CONFIG from './../config'
|
|||||||
export default function encode(user) {
|
export default function encode(user) {
|
||||||
const { id, name, slug } = user
|
const { id, name, slug } = user
|
||||||
const token = jwt.sign({ id, name, slug }, CONFIG.JWT_SECRET, {
|
const token = jwt.sign({ id, name, slug }, CONFIG.JWT_SECRET, {
|
||||||
expiresIn: '1d',
|
expiresIn: CONFIG.JWT_EXPIRES,
|
||||||
issuer: CONFIG.GRAPHQL_URI,
|
issuer: CONFIG.GRAPHQL_URI,
|
||||||
audience: CONFIG.CLIENT_URI,
|
audience: CONFIG.CLIENT_URI,
|
||||||
subject: user.id.toString(),
|
subject: user.id.toString(),
|
||||||
|
|||||||
@ -43,9 +43,14 @@ if (!hasEmailConfig) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sendSignupMail = async (resolve, root, args, context, resolveInfo) => {
|
const sendSignupMail = async (resolve, root, args, context, resolveInfo) => {
|
||||||
|
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
|
||||||
await sendMail(signupTemplate({ email, nonce }))
|
if (inviteCode) {
|
||||||
|
await sendMail(signupTemplate({ email, nonce, inviteCode }))
|
||||||
|
} else {
|
||||||
|
await sendMail(signupTemplate({ email, nonce }))
|
||||||
|
}
|
||||||
delete response.nonce
|
delete response.nonce
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
@ -71,6 +76,5 @@ export default {
|
|||||||
AddEmailAddress: sendEmailVerificationMail,
|
AddEmailAddress: sendEmailVerificationMail,
|
||||||
requestPasswordReset: sendPasswordResetMail,
|
requestPasswordReset: sendPasswordResetMail,
|
||||||
Signup: sendSignupMail,
|
Signup: sendSignupMail,
|
||||||
SignupByInvitation: sendSignupMail,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import mustache from 'mustache'
|
import mustache from 'mustache'
|
||||||
import CONFIG from '../../config'
|
import CONFIG from '../../config'
|
||||||
|
import logosWebapp from '../../config/logos.js'
|
||||||
|
|
||||||
import * as templates from './templates'
|
import * as templates from './templates'
|
||||||
|
|
||||||
const from = CONFIG.EMAIL_DEFAULT_SENDER
|
const from = CONFIG.EMAIL_DEFAULT_SENDER
|
||||||
const welcomeImageUrl = new URL(`/img/custom/welcome.svg`, CONFIG.CLIENT_URI)
|
const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI)
|
||||||
|
|
||||||
const defaultParams = {
|
const defaultParams = {
|
||||||
supportUrl: CONFIG.SUPPORT_URL,
|
supportUrl: CONFIG.SUPPORT_URL,
|
||||||
@ -13,11 +14,18 @@ const defaultParams = {
|
|||||||
welcomeImageUrl,
|
welcomeImageUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
export const signupTemplate = ({ email, nonce }) => {
|
export const signupTemplate = ({ email, nonce, inviteCode = null }) => {
|
||||||
const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!`
|
const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!`
|
||||||
const actionUrl = new URL('/registration/create-user-account', CONFIG.CLIENT_URI)
|
// dev format example: http://localhost:3000/registration?method=invite-mail&email=wolle.huss%40pjannto.com&nonce=64853
|
||||||
actionUrl.searchParams.set('nonce', nonce)
|
const actionUrl = new URL('/registration', CONFIG.CLIENT_URI)
|
||||||
actionUrl.searchParams.set('email', email)
|
actionUrl.searchParams.set('email', email)
|
||||||
|
actionUrl.searchParams.set('nonce', nonce)
|
||||||
|
if (inviteCode) {
|
||||||
|
actionUrl.searchParams.set('inviteCode', inviteCode)
|
||||||
|
actionUrl.searchParams.set('method', 'invite-code')
|
||||||
|
} else {
|
||||||
|
actionUrl.searchParams.set('method', 'invite-mail')
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from,
|
from,
|
||||||
@ -34,8 +42,8 @@ export const signupTemplate = ({ email, nonce }) => {
|
|||||||
export const emailVerificationTemplate = ({ email, nonce, name }) => {
|
export const emailVerificationTemplate = ({ email, nonce, name }) => {
|
||||||
const subject = 'Neue E-Mail Adresse | New E-Mail Address'
|
const subject = 'Neue E-Mail Adresse | New E-Mail Address'
|
||||||
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
|
const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI)
|
||||||
actionUrl.searchParams.set('nonce', nonce)
|
|
||||||
actionUrl.searchParams.set('email', email)
|
actionUrl.searchParams.set('email', email)
|
||||||
|
actionUrl.searchParams.set('nonce', nonce)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
from,
|
from,
|
||||||
@ -77,7 +85,7 @@ export const wrongAccountTemplate = ({ email }) => {
|
|||||||
subject,
|
subject,
|
||||||
html: mustache.render(
|
html: mustache.render(
|
||||||
templates.layout,
|
templates.layout,
|
||||||
{ actionUrl, supportUrl: CONFIG.SUPPORT_URL, welcomeImageUrl },
|
{ ...defaultParams, actionUrl, supportUrl: CONFIG.SUPPORT_URL, welcomeImageUrl },
|
||||||
{ content: templates.wrongAccount },
|
{ content: templates.wrongAccount },
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -105,8 +105,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -105,8 +105,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -118,8 +118,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -105,8 +105,8 @@
|
|||||||
<td style="background-color: #ffffff;">
|
<td style="background-color: #ffffff;">
|
||||||
<img
|
<img
|
||||||
src="{{{ welcomeImageUrl }}}"
|
src="{{{ welcomeImageUrl }}}"
|
||||||
width="600" height="" alt="Welcome image" border="0"
|
width="300" height="" alt="Welcome image" border="0"
|
||||||
style="width: 100%; max-width: 600px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block;"
|
style="width: 100%; max-width: 300px; height: auto; background: #ffffff; font-family: Lato, sans-serif; font-size: 16px; line-height: 15px; color: #555555; margin: auto; display: block; padding: 20px;"
|
||||||
class="g-img">
|
class="g-img">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@ -13,7 +13,7 @@ const driver = getDriver()
|
|||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
const categoryIds = ['cat9']
|
const categoryIds = ['cat9']
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
||||||
CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) {
|
CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
@ -22,7 +22,7 @@ const createPostMutation = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const updatePostMutation = gql`
|
const updatePostMutation = gql`
|
||||||
mutation($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
mutation ($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
||||||
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
|
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
|
||||||
title
|
title
|
||||||
content
|
content
|
||||||
@ -95,7 +95,7 @@ describe('hashtags', () => {
|
|||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
const postWithHastagsQuery = gql`
|
const postWithHastagsQuery = gql`
|
||||||
query($id: ID) {
|
query ($id: ID) {
|
||||||
Post(id: $id) {
|
Post(id: $id) {
|
||||||
tags {
|
tags {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -29,7 +29,7 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($title: String!, $content: String!, $categoryIds: [ID]) {
|
mutation ($title: String!, $content: String!, $categoryIds: [ID]) {
|
||||||
CreatePost(title: $title, content: $content, categoryIds: $categoryIds) {
|
CreatePost(title: $title, content: $content, categoryIds: $categoryIds) {
|
||||||
language
|
language
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const driver = getDriver()
|
|||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
const categoryIds = ['cat9']
|
const categoryIds = ['cat9']
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
||||||
CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) {
|
CreatePost(id: $id, title: $title, content: $postContent, categoryIds: $categoryIds) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
@ -19,7 +19,7 @@ const createPostMutation = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const updatePostMutation = gql`
|
const updatePostMutation = gql`
|
||||||
mutation($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
mutation ($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
|
||||||
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
|
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
|
||||||
title
|
title
|
||||||
content
|
content
|
||||||
@ -27,7 +27,7 @@ const updatePostMutation = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const createCommentMutation = gql`
|
const createCommentMutation = gql`
|
||||||
mutation($id: ID, $postId: ID!, $commentContent: String!) {
|
mutation ($id: ID, $postId: ID!, $commentContent: String!) {
|
||||||
CreateComment(id: $id, postId: $postId, content: $commentContent) {
|
CreateComment(id: $id, postId: $postId, content: $commentContent) {
|
||||||
id
|
id
|
||||||
content
|
content
|
||||||
@ -80,7 +80,7 @@ afterEach(async () => {
|
|||||||
|
|
||||||
describe('notifications', () => {
|
describe('notifications', () => {
|
||||||
const notificationQuery = gql`
|
const notificationQuery = gql`
|
||||||
query($read: Boolean) {
|
query ($read: Boolean) {
|
||||||
notifications(read: $read, orderBy: updatedAt_desc) {
|
notifications(read: $read, orderBy: updatedAt_desc) {
|
||||||
read
|
read
|
||||||
reason
|
reason
|
||||||
@ -367,7 +367,7 @@ describe('notifications', () => {
|
|||||||
describe('if the notification was marked as read earlier', () => {
|
describe('if the notification was marked as read earlier', () => {
|
||||||
const markAsReadAction = async () => {
|
const markAsReadAction = async () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
markAsRead(id: $id) {
|
markAsRead(id: $id) {
|
||||||
read
|
read
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import { rule, shield, deny, allow, or } from 'graphql-shield'
|
import { rule, shield, deny, allow, or } from 'graphql-shield'
|
||||||
import { getNeode } from '../db/neo4j'
|
import { getNeode } from '../db/neo4j'
|
||||||
import CONFIG from '../config'
|
import CONFIG from '../config'
|
||||||
|
import { validateInviteCode } from '../schema/resolvers/transactions/inviteCodes'
|
||||||
|
|
||||||
const debug = !!CONFIG.DEBUG
|
const debug = !!CONFIG.DEBUG
|
||||||
const allowExternalErrors = true
|
const allowExternalErrors = true
|
||||||
@ -87,7 +88,14 @@ const noEmailFilter = rule({
|
|||||||
return !('email' in args)
|
return !('email' in args)
|
||||||
})
|
})
|
||||||
|
|
||||||
const publicRegistration = rule()(() => !!CONFIG.PUBLIC_REGISTRATION)
|
const publicRegistration = rule()(() => CONFIG.PUBLIC_REGISTRATION)
|
||||||
|
|
||||||
|
const inviteRegistration = rule()(async (_parent, args, { user, driver }) => {
|
||||||
|
if (!CONFIG.INVITE_REGISTRATION) return false
|
||||||
|
const { inviteCode } = args
|
||||||
|
const session = driver.session()
|
||||||
|
return validateInviteCode(session, inviteCode)
|
||||||
|
})
|
||||||
|
|
||||||
// Permissions
|
// Permissions
|
||||||
export default shield(
|
export default shield(
|
||||||
@ -121,14 +129,15 @@ export default shield(
|
|||||||
userData: isAuthenticated,
|
userData: isAuthenticated,
|
||||||
MyInviteCodes: isAuthenticated,
|
MyInviteCodes: isAuthenticated,
|
||||||
isValidInviteCode: allow,
|
isValidInviteCode: allow,
|
||||||
|
VerifyNonce: allow,
|
||||||
queryLocations: isAuthenticated,
|
queryLocations: isAuthenticated,
|
||||||
availableRoles: isAdmin,
|
availableRoles: isAdmin,
|
||||||
|
getInviteCode: isAuthenticated, // and inviteRegistration
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
'*': deny,
|
'*': deny,
|
||||||
login: allow,
|
login: allow,
|
||||||
SignupByInvitation: allow,
|
Signup: or(publicRegistration, inviteRegistration, isAdmin),
|
||||||
Signup: or(publicRegistration, isAdmin),
|
|
||||||
SignupVerification: allow,
|
SignupVerification: allow,
|
||||||
UpdateUser: onlyYourself,
|
UpdateUser: onlyYourself,
|
||||||
CreatePost: isAuthenticated,
|
CreatePost: isAuthenticated,
|
||||||
|
|||||||
@ -3,11 +3,13 @@ import createServer from '../server'
|
|||||||
import Factory, { cleanDatabase } from '../db/factories'
|
import Factory, { cleanDatabase } from '../db/factories'
|
||||||
import { gql } from '../helpers/jest'
|
import { gql } from '../helpers/jest'
|
||||||
import { getDriver, getNeode } from '../db/neo4j'
|
import { getDriver, getNeode } from '../db/neo4j'
|
||||||
|
import CONFIG from '../config'
|
||||||
|
|
||||||
const instance = getNeode()
|
const instance = getNeode()
|
||||||
const driver = getDriver()
|
const driver = getDriver()
|
||||||
|
|
||||||
let query, authenticatedUser, owner, anotherRegularUser, administrator, variables, moderator
|
let query, mutate, variables
|
||||||
|
let authenticatedUser, owner, anotherRegularUser, administrator, moderator
|
||||||
|
|
||||||
describe('authorization', () => {
|
describe('authorization', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -20,6 +22,7 @@ describe('authorization', () => {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
query = createTestClient(server).query
|
query = createTestClient(server).query
|
||||||
|
mutate = createTestClient(server).mutate
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@ -77,7 +80,7 @@ describe('authorization', () => {
|
|||||||
|
|
||||||
describe('access email address', () => {
|
describe('access email address', () => {
|
||||||
const userQuery = gql`
|
const userQuery = gql`
|
||||||
query($name: String) {
|
query ($name: String) {
|
||||||
User(name: $name) {
|
User(name: $name) {
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
@ -159,5 +162,132 @@ describe('authorization', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('access Signup', () => {
|
||||||
|
const signupMutation = gql`
|
||||||
|
mutation ($email: String!, $inviteCode: String) {
|
||||||
|
Signup(email: $email, inviteCode: $inviteCode) {
|
||||||
|
email
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
describe('admin invite only', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'some@email.org',
|
||||||
|
inviteCode: 'AAAAAA',
|
||||||
|
}
|
||||||
|
CONFIG.INVITE_REGISTRATION = false
|
||||||
|
CONFIG.PUBLIC_REGISTRATION = false
|
||||||
|
await Factory.build('inviteCode', {
|
||||||
|
code: 'AAAAAA',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('as user', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await anotherRegularUser.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('denies permission', async () => {
|
||||||
|
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||||
|
errors: [{ message: 'Not Authorised!' }],
|
||||||
|
data: { Signup: null },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('as admin', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = await administrator.toJson()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an email', async () => {
|
||||||
|
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||||
|
errors: undefined,
|
||||||
|
data: {
|
||||||
|
Signup: { email: 'some@email.org' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('public registration', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'some@email.org',
|
||||||
|
inviteCode: 'AAAAAA',
|
||||||
|
}
|
||||||
|
CONFIG.INVITE_REGISTRATION = false
|
||||||
|
CONFIG.PUBLIC_REGISTRATION = true
|
||||||
|
await Factory.build('inviteCode', {
|
||||||
|
code: 'AAAAAA',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('as anyone', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
authenticatedUser = null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an email', async () => {
|
||||||
|
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||||
|
errors: undefined,
|
||||||
|
data: {
|
||||||
|
Signup: { email: 'some@email.org' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('invite registration', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
CONFIG.INVITE_REGISTRATION = true
|
||||||
|
CONFIG.PUBLIC_REGISTRATION = false
|
||||||
|
await Factory.build('inviteCode', {
|
||||||
|
code: 'AAAAAA',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('as anyone with valid invite code', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'some@email.org',
|
||||||
|
inviteCode: 'AAAAAA',
|
||||||
|
}
|
||||||
|
authenticatedUser = null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns an email', async () => {
|
||||||
|
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||||
|
errors: undefined,
|
||||||
|
data: {
|
||||||
|
Signup: { email: 'some@email.org' },
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('as anyone without valid invite', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'some@email.org',
|
||||||
|
inviteCode: 'no valid invite code',
|
||||||
|
}
|
||||||
|
authenticatedUser = null
|
||||||
|
})
|
||||||
|
|
||||||
|
it('denies permission', async () => {
|
||||||
|
await expect(mutate({ mutation: signupMutation, variables })).resolves.toMatchObject({
|
||||||
|
errors: [{ message: 'Not Authorised!' }],
|
||||||
|
data: { Signup: null },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@ -54,7 +54,7 @@ describe('slugifyMiddleware', () => {
|
|||||||
describe('CreatePost', () => {
|
describe('CreatePost', () => {
|
||||||
const categoryIds = ['cat9']
|
const categoryIds = ['cat9']
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
|
mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
|
||||||
CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
|
CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
@ -164,7 +164,7 @@ describe('slugifyMiddleware', () => {
|
|||||||
|
|
||||||
describe('SignupVerification', () => {
|
describe('SignupVerification', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation(
|
mutation (
|
||||||
$password: String!
|
$password: String!
|
||||||
$email: String!
|
$email: String!
|
||||||
$name: String!
|
$name: String!
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const driver = getDriver()
|
|||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
|
|
||||||
const postQuery = gql`
|
const postQuery = gql`
|
||||||
query($id: ID) {
|
query ($id: ID) {
|
||||||
Post(id: $id) {
|
Post(id: $id) {
|
||||||
clickedCount
|
clickedCount
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,14 +17,14 @@ let authenticatedUser,
|
|||||||
commentingUser
|
commentingUser
|
||||||
|
|
||||||
const createCommentMutation = gql`
|
const createCommentMutation = gql`
|
||||||
mutation($id: ID, $postId: ID!, $content: String!) {
|
mutation ($id: ID, $postId: ID!, $content: String!) {
|
||||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const updateCommentMutation = gql`
|
const updateCommentMutation = gql`
|
||||||
mutation($content: String!, $id: ID!) {
|
mutation ($content: String!, $id: ID!) {
|
||||||
UpdateComment(content: $content, id: $id) {
|
UpdateComment(content: $content, id: $id) {
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
@ -32,7 +32,7 @@ const updateCommentMutation = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const reportMutation = gql`
|
const reportMutation = gql`
|
||||||
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
mutation ($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
fileReport(
|
fileReport(
|
||||||
resourceId: $resourceId
|
resourceId: $resourceId
|
||||||
reasonCategory: $reasonCategory
|
reasonCategory: $reasonCategory
|
||||||
@ -43,7 +43,7 @@ const reportMutation = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const reviewMutation = gql`
|
const reviewMutation = gql`
|
||||||
mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
||||||
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
@ -52,7 +52,7 @@ const reviewMutation = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const updateUserMutation = gql`
|
const updateUserMutation = gql`
|
||||||
mutation($id: ID!, $name: String) {
|
mutation ($id: ID!, $name: String) {
|
||||||
UpdateUser(id: $id, name: $name) {
|
UpdateUser(id: $id, name: $name) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ afterEach(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const createCommentMutation = gql`
|
const createCommentMutation = gql`
|
||||||
mutation($id: ID, $postId: ID!, $content: String!) {
|
mutation ($id: ID, $postId: ID!, $content: String!) {
|
||||||
CreateComment(id: $id, postId: $postId, content: $content) {
|
CreateComment(id: $id, postId: $postId, content: $content) {
|
||||||
id
|
id
|
||||||
content
|
content
|
||||||
@ -128,7 +128,7 @@ describe('CreateComment', () => {
|
|||||||
|
|
||||||
describe('UpdateComment', () => {
|
describe('UpdateComment', () => {
|
||||||
const updateCommentMutation = gql`
|
const updateCommentMutation = gql`
|
||||||
mutation($content: String!, $id: ID!) {
|
mutation ($content: String!, $id: ID!) {
|
||||||
UpdateComment(content: $content, id: $id) {
|
UpdateComment(content: $content, id: $id) {
|
||||||
id
|
id
|
||||||
content
|
content
|
||||||
@ -220,7 +220,7 @@ describe('UpdateComment', () => {
|
|||||||
|
|
||||||
describe('DeleteComment', () => {
|
describe('DeleteComment', () => {
|
||||||
const deleteCommentMutation = gql`
|
const deleteCommentMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
DeleteComment(id: $id) {
|
DeleteComment(id: $id) {
|
||||||
id
|
id
|
||||||
content
|
content
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const instance = getNeode()
|
|||||||
const driver = getDriver()
|
const driver = getDriver()
|
||||||
|
|
||||||
const updateDonationsMutation = gql`
|
const updateDonationsMutation = gql`
|
||||||
mutation($goal: Int, $progress: Int) {
|
mutation ($goal: Int, $progress: Int) {
|
||||||
UpdateDonations(goal: $goal, progress: $progress) {
|
UpdateDonations(goal: $goal, progress: $progress) {
|
||||||
id
|
id
|
||||||
goal
|
goal
|
||||||
|
|||||||
@ -6,6 +6,27 @@ import Validator from 'neode/build/Services/Validator.js'
|
|||||||
import normalizeEmail from './helpers/normalizeEmail'
|
import normalizeEmail from './helpers/normalizeEmail'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
Query: {
|
||||||
|
VerifyNonce: async (_parent, args, context, _resolveInfo) => {
|
||||||
|
const session = context.driver.session()
|
||||||
|
const readTxResultPromise = session.readTransaction(async (txc) => {
|
||||||
|
const result = await txc.run(
|
||||||
|
`
|
||||||
|
MATCH (email:EmailAddress {email: $email, nonce: $nonce})
|
||||||
|
RETURN count(email) > 0 AS result
|
||||||
|
`,
|
||||||
|
{ email: args.email, nonce: args.nonce },
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const txResult = await readTxResultPromise
|
||||||
|
return txResult.records[0].get('result')
|
||||||
|
} finally {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
AddEmailAddress: async (_parent, args, context, _resolveInfo) => {
|
AddEmailAddress: async (_parent, args, context, _resolveInfo) => {
|
||||||
let response
|
let response
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import { createTestClient } from 'apollo-server-testing'
|
|||||||
|
|
||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
|
|
||||||
let mutate
|
let mutate, query
|
||||||
let authenticatedUser
|
let authenticatedUser
|
||||||
let user
|
let user
|
||||||
let variables
|
let variables
|
||||||
@ -16,7 +16,8 @@ beforeEach(async () => {
|
|||||||
variables = {}
|
variables = {}
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(async () => {
|
||||||
|
await cleanDatabase()
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
context: () => {
|
context: () => {
|
||||||
return {
|
return {
|
||||||
@ -27,6 +28,7 @@ beforeAll(() => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
mutate = createTestClient(server).mutate
|
mutate = createTestClient(server).mutate
|
||||||
|
query = createTestClient(server).query
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(async () => {
|
afterEach(async () => {
|
||||||
@ -35,7 +37,7 @@ afterEach(async () => {
|
|||||||
|
|
||||||
describe('AddEmailAddress', () => {
|
describe('AddEmailAddress', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($email: String!) {
|
mutation ($email: String!) {
|
||||||
AddEmailAddress(email: $email) {
|
AddEmailAddress(email: $email) {
|
||||||
email
|
email
|
||||||
verifiedAt
|
verifiedAt
|
||||||
@ -140,7 +142,7 @@ describe('AddEmailAddress', () => {
|
|||||||
|
|
||||||
describe('VerifyEmailAddress', () => {
|
describe('VerifyEmailAddress', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($email: String!, $nonce: String!) {
|
mutation ($email: String!, $nonce: String!) {
|
||||||
VerifyEmailAddress(email: $email, nonce: $nonce) {
|
VerifyEmailAddress(email: $email, nonce: $nonce) {
|
||||||
email
|
email
|
||||||
createdAt
|
createdAt
|
||||||
@ -185,7 +187,7 @@ describe('VerifyEmailAddress', () => {
|
|||||||
let emailAddress
|
let emailAddress
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
emailAddress = await Factory.build('unverifiedEmailAddress', {
|
emailAddress = await Factory.build('unverifiedEmailAddress', {
|
||||||
nonce: 'abcdef',
|
nonce: '12345',
|
||||||
verifiedAt: null,
|
verifiedAt: null,
|
||||||
createdAt: new Date().toISOString(),
|
createdAt: new Date().toISOString(),
|
||||||
email: 'to-be-verified@example.org',
|
email: 'to-be-verified@example.org',
|
||||||
@ -204,7 +206,7 @@ describe('VerifyEmailAddress', () => {
|
|||||||
|
|
||||||
describe('given valid nonce for `UnverifiedEmailAddress` node', () => {
|
describe('given valid nonce for `UnverifiedEmailAddress` node', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
variables = { ...variables, nonce: 'abcdef' }
|
variables = { ...variables, nonce: '12345' }
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('but the address does not belong to the authenticated user', () => {
|
describe('but the address does not belong to the authenticated user', () => {
|
||||||
@ -295,3 +297,40 @@ describe('VerifyEmailAddress', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('VerifyNonce', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await Factory.build('emailAddress', {
|
||||||
|
nonce: '12345',
|
||||||
|
verifiedAt: null,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
email: 'to-be-verified@example.org',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const verifyNonceQuery = gql`
|
||||||
|
query ($email: String!, $nonce: String!) {
|
||||||
|
VerifyNonce(email: $email, nonce: $nonce)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
it('returns true when nonce and email match', async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'to-be-verified@example.org',
|
||||||
|
nonce: '12345',
|
||||||
|
}
|
||||||
|
await expect(query({ query: verifyNonceQuery, variables })).resolves.toMatchObject({
|
||||||
|
data: { VerifyNonce: true },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns false when nonce and email do not match', async () => {
|
||||||
|
variables = {
|
||||||
|
email: 'to-be-verified@example.org',
|
||||||
|
nonce: '---',
|
||||||
|
}
|
||||||
|
await expect(query({ query: verifyNonceQuery, variables })).resolves.toMatchObject({
|
||||||
|
data: { VerifyNonce: false },
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -37,8 +37,7 @@ const babyLovesCatEmbedResponse = new Response(
|
|||||||
thumbnail_height: 360,
|
thumbnail_height: 360,
|
||||||
provider_url: 'https://www.youtube.com/',
|
provider_url: 'https://www.youtube.com/',
|
||||||
thumbnail_width: 480,
|
thumbnail_width: 480,
|
||||||
html:
|
html: '<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
|
||||||
thumbnail_url: 'https://i.ytimg.com/vi/qkdXAtO40Fo/hqdefault.jpg',
|
thumbnail_url: 'https://i.ytimg.com/vi/qkdXAtO40Fo/hqdefault.jpg',
|
||||||
version: '1.0',
|
version: '1.0',
|
||||||
author_name: 'Merkley Family',
|
author_name: 'Merkley Family',
|
||||||
@ -57,7 +56,7 @@ describe('Query', () => {
|
|||||||
})
|
})
|
||||||
const { query } = createTestClient(server)
|
const { query } = createTestClient(server)
|
||||||
const embed = gql`
|
const embed = gql`
|
||||||
query($url: String!) {
|
query ($url: String!) {
|
||||||
embed(url: $url) {
|
embed(url: $url) {
|
||||||
type
|
type
|
||||||
title
|
title
|
||||||
@ -204,8 +203,7 @@ Have all the information for the brand in separate config files. Set these defau
|
|||||||
video: null,
|
video: null,
|
||||||
lang: 'de',
|
lang: 'de',
|
||||||
sources: ['resource', 'oembed'],
|
sources: ['resource', 'oembed'],
|
||||||
html:
|
html: '<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
||||||
'<iframe width="480" height="270" src="https://www.youtube.com/embed/qkdXAtO40Fo?start=18&feature=oembed" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
errors: undefined,
|
errors: undefined,
|
||||||
|
|||||||
@ -16,7 +16,7 @@ let user2
|
|||||||
let variables
|
let variables
|
||||||
|
|
||||||
const mutationFollowUser = gql`
|
const mutationFollowUser = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
followUser(id: $id) {
|
followUser(id: $id) {
|
||||||
name
|
name
|
||||||
followedBy {
|
followedBy {
|
||||||
@ -29,7 +29,7 @@ const mutationFollowUser = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const mutationUnfollowUser = gql`
|
const mutationUnfollowUser = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
unfollowUser(id: $id) {
|
unfollowUser(id: $id) {
|
||||||
name
|
name
|
||||||
followedBy {
|
followedBy {
|
||||||
@ -42,7 +42,7 @@ const mutationUnfollowUser = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const userQuery = gql`
|
const userQuery = gql`
|
||||||
query($id: ID) {
|
query ($id: ID) {
|
||||||
User(id: $id) {
|
User(id: $id) {
|
||||||
followedBy {
|
followedBy {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { v4 as uuid } from 'uuid'
|
|
||||||
export default function generateNonce() {
|
export default function generateNonce() {
|
||||||
return uuid().substring(0, 6)
|
return Array.from({ length: 5 }, (n = Math.floor(Math.random() * 10)) => {
|
||||||
|
return String.fromCharCode(n + 48)
|
||||||
|
}).join('')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import generateInviteCode from './helpers/generateInviteCode'
|
import generateInviteCode from './helpers/generateInviteCode'
|
||||||
import Resolver from './helpers/Resolver'
|
import Resolver from './helpers/Resolver'
|
||||||
|
import { validateInviteCode } from './transactions/inviteCodes'
|
||||||
|
|
||||||
const uniqueInviteCode = async (session, code) => {
|
const uniqueInviteCode = async (session, code) => {
|
||||||
return session.readTransaction(async (txc) => {
|
return session.readTransaction(async (txc) => {
|
||||||
@ -12,6 +13,52 @@ const uniqueInviteCode = async (session, code) => {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
Query: {
|
Query: {
|
||||||
|
getInviteCode: async (_parent, args, context, _resolveInfo) => {
|
||||||
|
const {
|
||||||
|
user: { id: userId },
|
||||||
|
} = context
|
||||||
|
const session = context.driver.session()
|
||||||
|
const readTxResultPromise = session.readTransaction(async (txc) => {
|
||||||
|
const result = await txc.run(
|
||||||
|
`MATCH (user:User {id: $userId})-[:GENERATED]->(ic:InviteCode)
|
||||||
|
WHERE ic.expiresAt IS NULL
|
||||||
|
OR datetime(ic.expiresAt) >= datetime()
|
||||||
|
RETURN properties(ic) AS inviteCodes`,
|
||||||
|
{
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return result.records.map((record) => record.get('inviteCodes'))
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const inviteCode = await readTxResultPromise
|
||||||
|
if (inviteCode && inviteCode.length > 0) return inviteCode[0]
|
||||||
|
let code = generateInviteCode()
|
||||||
|
while (!(await uniqueInviteCode(session, code))) {
|
||||||
|
code = generateInviteCode()
|
||||||
|
}
|
||||||
|
const writeTxResultPromise = session.writeTransaction(async (txc) => {
|
||||||
|
const result = await txc.run(
|
||||||
|
`MATCH (user:User {id: $userId})
|
||||||
|
MERGE (user)-[:GENERATED]->(ic:InviteCode { code: $code })
|
||||||
|
ON CREATE SET
|
||||||
|
ic.createdAt = toString(datetime()),
|
||||||
|
ic.expiresAt = $expiresAt
|
||||||
|
RETURN ic AS inviteCode`,
|
||||||
|
{
|
||||||
|
userId,
|
||||||
|
code,
|
||||||
|
expiresAt: null,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return result.records.map((record) => record.get('inviteCode').properties)
|
||||||
|
})
|
||||||
|
const txResult = await writeTxResultPromise
|
||||||
|
return txResult[0]
|
||||||
|
} finally {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
},
|
||||||
MyInviteCodes: async (_parent, args, context, _resolveInfo) => {
|
MyInviteCodes: async (_parent, args, context, _resolveInfo) => {
|
||||||
const {
|
const {
|
||||||
user: { id: userId },
|
user: { id: userId },
|
||||||
@ -36,28 +83,9 @@ export default {
|
|||||||
},
|
},
|
||||||
isValidInviteCode: async (_parent, args, context, _resolveInfo) => {
|
isValidInviteCode: async (_parent, args, context, _resolveInfo) => {
|
||||||
const { code } = args
|
const { code } = args
|
||||||
if (!code) return false
|
|
||||||
const session = context.driver.session()
|
const session = context.driver.session()
|
||||||
const readTxResultPromise = session.readTransaction(async (txc) => {
|
if (!code) return false
|
||||||
const result = await txc.run(
|
return validateInviteCode(session, code)
|
||||||
`MATCH (ic:InviteCode { code: toUpper($code) })
|
|
||||||
RETURN
|
|
||||||
CASE
|
|
||||||
WHEN ic.expiresAt IS NULL THEN true
|
|
||||||
WHEN datetime(ic.expiresAt) >= datetime() THEN true
|
|
||||||
ELSE false END AS result`,
|
|
||||||
{
|
|
||||||
code,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return result.records.map((record) => record.get('result'))
|
|
||||||
})
|
|
||||||
try {
|
|
||||||
const txResult = await readTxResultPromise
|
|
||||||
return !!txResult[0]
|
|
||||||
} finally {
|
|
||||||
session.close()
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
|
|||||||
@ -11,7 +11,7 @@ let mutate
|
|||||||
const driver = getDriver()
|
const driver = getDriver()
|
||||||
|
|
||||||
const generateInviteCodeMutation = gql`
|
const generateInviteCodeMutation = gql`
|
||||||
mutation($expiresAt: String = null) {
|
mutation ($expiresAt: String = null) {
|
||||||
GenerateInviteCode(expiresAt: $expiresAt) {
|
GenerateInviteCode(expiresAt: $expiresAt) {
|
||||||
code
|
code
|
||||||
createdAt
|
createdAt
|
||||||
@ -31,7 +31,7 @@ const myInviteCodesQuery = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const isValidInviteCodeQuery = gql`
|
const isValidInviteCodeQuery = gql`
|
||||||
query($code: ID!) {
|
query ($code: ID!) {
|
||||||
isValidInviteCode(code: $code)
|
isValidInviteCode(code: $code)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -31,7 +31,7 @@ describe('resolvers', () => {
|
|||||||
describe('custom mutation, not handled by neo4j-graphql-js', () => {
|
describe('custom mutation, not handled by neo4j-graphql-js', () => {
|
||||||
let variables
|
let variables
|
||||||
const updateUserMutation = gql`
|
const updateUserMutation = gql`
|
||||||
mutation($id: ID!, $name: String) {
|
mutation ($id: ID!, $name: String) {
|
||||||
UpdateUser(id: $id, name: $name) {
|
UpdateUser(id: $id, name: $name) {
|
||||||
name
|
name
|
||||||
location {
|
location {
|
||||||
|
|||||||
@ -16,7 +16,7 @@ let mutate,
|
|||||||
closeReportVariables
|
closeReportVariables
|
||||||
|
|
||||||
const reviewMutation = gql`
|
const reviewMutation = gql`
|
||||||
mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
||||||
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
|
|||||||
@ -139,7 +139,7 @@ describe('given some notifications', () => {
|
|||||||
|
|
||||||
describe('notifications', () => {
|
describe('notifications', () => {
|
||||||
const notificationQuery = gql`
|
const notificationQuery = gql`
|
||||||
query($read: Boolean, $orderBy: NotificationOrdering) {
|
query ($read: Boolean, $orderBy: NotificationOrdering) {
|
||||||
notifications(read: $read, orderBy: $orderBy) {
|
notifications(read: $read, orderBy: $orderBy) {
|
||||||
from {
|
from {
|
||||||
__typename
|
__typename
|
||||||
@ -249,7 +249,7 @@ describe('given some notifications', () => {
|
|||||||
const deletePostAction = async () => {
|
const deletePostAction = async () => {
|
||||||
authenticatedUser = await author.toJson()
|
authenticatedUser = await author.toJson()
|
||||||
const deletePostMutation = gql`
|
const deletePostMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
DeletePost(id: $id) {
|
DeletePost(id: $id) {
|
||||||
id
|
id
|
||||||
deleted
|
deleted
|
||||||
@ -284,7 +284,7 @@ describe('given some notifications', () => {
|
|||||||
|
|
||||||
describe('markAsRead', () => {
|
describe('markAsRead', () => {
|
||||||
const markAsReadMutation = gql`
|
const markAsReadMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
markAsRead(id: $id) {
|
markAsRead(id: $id) {
|
||||||
from {
|
from {
|
||||||
__typename
|
__typename
|
||||||
|
|||||||
@ -55,7 +55,7 @@ describe('passwordReset', () => {
|
|||||||
|
|
||||||
describe('requestPasswordReset', () => {
|
describe('requestPasswordReset', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($email: String!) {
|
mutation ($email: String!) {
|
||||||
requestPasswordReset(email: $email)
|
requestPasswordReset(email: $email)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -116,7 +116,7 @@ describe('resetPassword', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($nonce: String!, $email: String!, $newPassword: String!) {
|
mutation ($nonce: String!, $email: String!, $newPassword: String!) {
|
||||||
resetPassword(nonce: $nonce, email: $email, newPassword: $newPassword)
|
resetPassword(nonce: $nonce, email: $email, newPassword: $newPassword)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -196,7 +196,7 @@ describe('resetPassword', () => {
|
|||||||
it('updates password of the user', async () => {
|
it('updates password of the user', async () => {
|
||||||
await mutate({ mutation, variables })
|
await mutate({ mutation, variables })
|
||||||
const checkLoginMutation = gql`
|
const checkLoginMutation = gql`
|
||||||
mutation($email: String!, $password: String!) {
|
mutation ($email: String!, $password: String!) {
|
||||||
login(email: $email, password: $password)
|
login(email: $email, password: $password)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -348,7 +348,7 @@ export default {
|
|||||||
undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'],
|
undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'],
|
||||||
hasMany: {
|
hasMany: {
|
||||||
tags: '-[:TAGGED]->(related:Tag)',
|
tags: '-[:TAGGED]->(related:Tag)',
|
||||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
// categories: '-[:CATEGORIZED]->(related:Category)',
|
||||||
comments: '<-[:COMMENTS]-(related:Comment)',
|
comments: '<-[:COMMENTS]-(related:Comment)',
|
||||||
shoutedBy: '<-[:SHOUTED]-(related:User)',
|
shoutedBy: '<-[:SHOUTED]-(related:User)',
|
||||||
emotions: '<-[related:EMOTED]',
|
emotions: '<-[related:EMOTED]',
|
||||||
|
|||||||
@ -16,7 +16,7 @@ const categoryIds = ['cat9', 'cat4', 'cat15']
|
|||||||
let variables
|
let variables
|
||||||
|
|
||||||
const createPostMutation = gql`
|
const createPostMutation = gql`
|
||||||
mutation($id: ID, $title: String!, $content: String!, $language: String, $categoryIds: [ID]) {
|
mutation ($id: ID, $title: String!, $content: String!, $language: String, $categoryIds: [ID]) {
|
||||||
CreatePost(
|
CreatePost(
|
||||||
id: $id
|
id: $id
|
||||||
title: $title
|
title: $title
|
||||||
@ -147,7 +147,7 @@ describe('Post', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('by categories', async () => {
|
/* it('by categories', async () => {
|
||||||
const postQueryFilteredByCategories = gql`
|
const postQueryFilteredByCategories = gql`
|
||||||
query Post($filter: _PostFilter) {
|
query Post($filter: _PostFilter) {
|
||||||
Post(filter: $filter) {
|
Post(filter: $filter) {
|
||||||
@ -172,7 +172,7 @@ describe('Post', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
query({ query: postQueryFilteredByCategories, variables }),
|
query({ query: postQueryFilteredByCategories, variables }),
|
||||||
).resolves.toMatchObject(expected)
|
).resolves.toMatchObject(expected)
|
||||||
})
|
}) */
|
||||||
|
|
||||||
describe('by emotions', () => {
|
describe('by emotions', () => {
|
||||||
const postQueryFilteredByEmotions = gql`
|
const postQueryFilteredByEmotions = gql`
|
||||||
@ -323,14 +323,8 @@ describe('CreatePost', () => {
|
|||||||
describe('UpdatePost', () => {
|
describe('UpdatePost', () => {
|
||||||
let author, newlyCreatedPost
|
let author, newlyCreatedPost
|
||||||
const updatePostMutation = gql`
|
const updatePostMutation = gql`
|
||||||
mutation($id: ID!, $title: String!, $content: String!, $categoryIds: [ID], $image: ImageInput) {
|
mutation ($id: ID!, $title: String!, $content: String!, $image: ImageInput) {
|
||||||
UpdatePost(
|
UpdatePost(id: $id, title: $title, content: $content, image: $image) {
|
||||||
id: $id
|
|
||||||
title: $title
|
|
||||||
content: $content
|
|
||||||
categoryIds: $categoryIds
|
|
||||||
image: $image
|
|
||||||
) {
|
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
content
|
content
|
||||||
@ -338,9 +332,6 @@ describe('UpdatePost', () => {
|
|||||||
name
|
name
|
||||||
slug
|
slug
|
||||||
}
|
}
|
||||||
categories {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
createdAt
|
createdAt
|
||||||
updatedAt
|
updatedAt
|
||||||
}
|
}
|
||||||
@ -428,7 +419,7 @@ describe('UpdatePost', () => {
|
|||||||
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
|
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('no new category ids provided for update', () => {
|
/* describe('no new category ids provided for update', () => {
|
||||||
it('resolves and keeps current categories', async () => {
|
it('resolves and keeps current categories', async () => {
|
||||||
const expected = {
|
const expected = {
|
||||||
data: {
|
data: {
|
||||||
@ -443,9 +434,9 @@ describe('UpdatePost', () => {
|
|||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}) */
|
||||||
|
|
||||||
describe('given category ids', () => {
|
/* describe('given category ids', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
variables = { ...variables, categoryIds: ['cat27'] }
|
variables = { ...variables, categoryIds: ['cat27'] }
|
||||||
})
|
})
|
||||||
@ -464,7 +455,7 @@ describe('UpdatePost', () => {
|
|||||||
expected,
|
expected,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
}) */
|
||||||
|
|
||||||
describe('params.image', () => {
|
describe('params.image', () => {
|
||||||
describe('is object', () => {
|
describe('is object', () => {
|
||||||
@ -506,7 +497,7 @@ describe('UpdatePost', () => {
|
|||||||
describe('pin posts', () => {
|
describe('pin posts', () => {
|
||||||
let author
|
let author
|
||||||
const pinPostMutation = gql`
|
const pinPostMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
pinPost(id: $id) {
|
pinPost(id: $id) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
@ -782,7 +773,7 @@ describe('pin posts', () => {
|
|||||||
|
|
||||||
it('pinned post appear first even when created before other posts', async () => {
|
it('pinned post appear first even when created before other posts', async () => {
|
||||||
const postOrderingQuery = gql`
|
const postOrderingQuery = gql`
|
||||||
query($orderBy: [_PostOrdering]) {
|
query ($orderBy: [_PostOrdering]) {
|
||||||
Post(orderBy: $orderBy) {
|
Post(orderBy: $orderBy) {
|
||||||
id
|
id
|
||||||
pinned
|
pinned
|
||||||
@ -825,7 +816,7 @@ describe('pin posts', () => {
|
|||||||
describe('unpin posts', () => {
|
describe('unpin posts', () => {
|
||||||
let pinnedPost
|
let pinnedPost
|
||||||
const unpinPostMutation = gql`
|
const unpinPostMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
unpinPost(id: $id) {
|
unpinPost(id: $id) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
@ -937,7 +928,7 @@ describe('unpin posts', () => {
|
|||||||
describe('DeletePost', () => {
|
describe('DeletePost', () => {
|
||||||
let author
|
let author
|
||||||
const deletePostMutation = gql`
|
const deletePostMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
DeletePost(id: $id) {
|
DeletePost(id: $id) {
|
||||||
id
|
id
|
||||||
deleted
|
deleted
|
||||||
@ -1061,14 +1052,14 @@ describe('DeletePost', () => {
|
|||||||
describe('emotions', () => {
|
describe('emotions', () => {
|
||||||
let author, postToEmote
|
let author, postToEmote
|
||||||
const PostsEmotionsCountQuery = gql`
|
const PostsEmotionsCountQuery = gql`
|
||||||
query($id: ID!) {
|
query ($id: ID!) {
|
||||||
Post(id: $id) {
|
Post(id: $id) {
|
||||||
emotionsCount
|
emotionsCount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const PostsEmotionsQuery = gql`
|
const PostsEmotionsQuery = gql`
|
||||||
query($id: ID!) {
|
query ($id: ID!) {
|
||||||
Post(id: $id) {
|
Post(id: $id) {
|
||||||
emotions {
|
emotions {
|
||||||
emotion
|
emotion
|
||||||
@ -1102,7 +1093,7 @@ describe('emotions', () => {
|
|||||||
|
|
||||||
describe('AddPostEmotions', () => {
|
describe('AddPostEmotions', () => {
|
||||||
const addPostEmotionsMutation = gql`
|
const addPostEmotionsMutation = gql`
|
||||||
mutation($to: _PostInput!, $data: _EMOTEDInput!) {
|
mutation ($to: _PostInput!, $data: _EMOTEDInput!) {
|
||||||
AddPostEmotions(to: $to, data: $data) {
|
AddPostEmotions(to: $to, data: $data) {
|
||||||
from {
|
from {
|
||||||
id
|
id
|
||||||
@ -1219,7 +1210,7 @@ describe('emotions', () => {
|
|||||||
describe('RemovePostEmotions', () => {
|
describe('RemovePostEmotions', () => {
|
||||||
let removePostEmotionsVariables, postsEmotionsQueryVariables
|
let removePostEmotionsVariables, postsEmotionsQueryVariables
|
||||||
const removePostEmotionsMutation = gql`
|
const removePostEmotionsMutation = gql`
|
||||||
mutation($to: _PostInput!, $data: _EMOTEDInput!) {
|
mutation ($to: _PostInput!, $data: _EMOTEDInput!) {
|
||||||
RemovePostEmotions(to: $to, data: $data) {
|
RemovePostEmotions(to: $to, data: $data) {
|
||||||
from {
|
from {
|
||||||
id
|
id
|
||||||
@ -1318,13 +1309,13 @@ describe('emotions', () => {
|
|||||||
let PostsEmotionsByCurrentUserVariables
|
let PostsEmotionsByCurrentUserVariables
|
||||||
|
|
||||||
const PostsEmotionsCountByEmotionQuery = gql`
|
const PostsEmotionsCountByEmotionQuery = gql`
|
||||||
query($postId: ID!, $data: _EMOTEDInput!) {
|
query ($postId: ID!, $data: _EMOTEDInput!) {
|
||||||
PostsEmotionsCountByEmotion(postId: $postId, data: $data)
|
PostsEmotionsCountByEmotion(postId: $postId, data: $data)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
const PostsEmotionsByCurrentUserQuery = gql`
|
const PostsEmotionsByCurrentUserQuery = gql`
|
||||||
query($postId: ID!) {
|
query ($postId: ID!) {
|
||||||
PostsEmotionsByCurrentUser(postId: $postId)
|
PostsEmotionsByCurrentUser(postId: $postId)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -29,34 +29,22 @@ export default {
|
|||||||
}
|
}
|
||||||
args.termsAndConditionsAgreedAt = new Date().toISOString()
|
args.termsAndConditionsAgreedAt = new Date().toISOString()
|
||||||
|
|
||||||
let { nonce, email } = args
|
let { nonce, email, inviteCode } = args
|
||||||
email = normalizeEmail(email)
|
email = normalizeEmail(email)
|
||||||
delete args.nonce
|
delete args.nonce
|
||||||
delete args.email
|
delete args.email
|
||||||
|
delete args.inviteCode
|
||||||
args = encryptPassword(args)
|
args = encryptPassword(args)
|
||||||
|
|
||||||
const { driver } = context
|
const { driver } = context
|
||||||
const session = driver.session()
|
const session = driver.session()
|
||||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||||
const createUserTransactionResponse = await transaction.run(
|
const createUserTransactionResponse = await transaction.run(signupCypher(inviteCode), {
|
||||||
`
|
args,
|
||||||
MATCH(email:EmailAddress {nonce: $nonce, email: $email})
|
nonce,
|
||||||
WHERE NOT (email)-[:BELONGS_TO]->()
|
email,
|
||||||
CREATE (user:User)
|
inviteCode,
|
||||||
MERGE(user)-[:PRIMARY_EMAIL]->(email)
|
})
|
||||||
MERGE(user)<-[:BELONGS_TO]-(email)
|
|
||||||
SET user += $args
|
|
||||||
SET user.id = randomUUID()
|
|
||||||
SET user.role = 'user'
|
|
||||||
SET user.createdAt = toString(datetime())
|
|
||||||
SET user.updatedAt = toString(datetime())
|
|
||||||
SET user.allowEmbedIframes = FALSE
|
|
||||||
SET user.showShoutsPublicly = FALSE
|
|
||||||
SET email.verifiedAt = toString(datetime())
|
|
||||||
RETURN user {.*}
|
|
||||||
`,
|
|
||||||
{ args, nonce, email },
|
|
||||||
)
|
|
||||||
const [user] = createUserTransactionResponse.records.map((record) => record.get('user'))
|
const [user] = createUserTransactionResponse.records.map((record) => record.get('user'))
|
||||||
if (!user) throw new UserInputError('Invalid email or nonce')
|
if (!user) throw new UserInputError('Invalid email or nonce')
|
||||||
return user
|
return user
|
||||||
@ -74,3 +62,39 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const signupCypher = (inviteCode) => {
|
||||||
|
let optionalMatch = ''
|
||||||
|
let optionalMerge = ''
|
||||||
|
if (inviteCode) {
|
||||||
|
optionalMatch = `
|
||||||
|
OPTIONAL MATCH
|
||||||
|
(inviteCode:InviteCode {code: $inviteCode})<-[:GENERATED]-(host:User)
|
||||||
|
`
|
||||||
|
optionalMerge = `
|
||||||
|
MERGE(user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode)
|
||||||
|
MERGE(host)-[:INVITED { createdAt: toString(datetime()) }]->(user)
|
||||||
|
MERGE(user)-[:FOLLOWS { createdAt: toString(datetime()) }]->(host)
|
||||||
|
MERGE(host)-[:FOLLOWS { createdAt: toString(datetime()) }]->(user)
|
||||||
|
`
|
||||||
|
}
|
||||||
|
const cypher = `
|
||||||
|
MATCH(email:EmailAddress {nonce: $nonce, email: $email})
|
||||||
|
WHERE NOT (email)-[:BELONGS_TO]->()
|
||||||
|
${optionalMatch}
|
||||||
|
CREATE (user:User)
|
||||||
|
MERGE(user)-[:PRIMARY_EMAIL]->(email)
|
||||||
|
MERGE(user)<-[:BELONGS_TO]-(email)
|
||||||
|
${optionalMerge}
|
||||||
|
SET user += $args
|
||||||
|
SET user.id = randomUUID()
|
||||||
|
SET user.role = 'user'
|
||||||
|
SET user.createdAt = toString(datetime())
|
||||||
|
SET user.updatedAt = toString(datetime())
|
||||||
|
SET user.allowEmbedIframes = FALSE
|
||||||
|
SET user.showShoutsPublicly = FALSE
|
||||||
|
SET email.verifiedAt = toString(datetime())
|
||||||
|
RETURN user {.*}
|
||||||
|
`
|
||||||
|
return cypher
|
||||||
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { gql } from '../../helpers/jest'
|
|||||||
import { getDriver, getNeode } from '../../db/neo4j'
|
import { getDriver, getNeode } from '../../db/neo4j'
|
||||||
import createServer from '../../server'
|
import createServer from '../../server'
|
||||||
import { createTestClient } from 'apollo-server-testing'
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
|
import CONFIG from '../../config'
|
||||||
|
|
||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
|
|
||||||
@ -15,7 +16,8 @@ beforeEach(async () => {
|
|||||||
variables = {}
|
variables = {}
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(async () => {
|
||||||
|
await cleanDatabase()
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
context: () => {
|
context: () => {
|
||||||
return {
|
return {
|
||||||
@ -34,8 +36,8 @@ afterEach(async () => {
|
|||||||
|
|
||||||
describe('Signup', () => {
|
describe('Signup', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation($email: String!) {
|
mutation ($email: String!, $inviteCode: String) {
|
||||||
Signup(email: $email) {
|
Signup(email: $email, inviteCode: $inviteCode) {
|
||||||
email
|
email
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -50,6 +52,8 @@ describe('Signup', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('throws AuthorizationError', async () => {
|
it('throws AuthorizationError', async () => {
|
||||||
|
CONFIG.INVITE_REGISTRATION = false
|
||||||
|
CONFIG.PUBLIC_REGISTRATION = false
|
||||||
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
|
||||||
errors: [{ message: 'Not Authorised!' }],
|
errors: [{ message: 'Not Authorised!' }],
|
||||||
})
|
})
|
||||||
@ -141,7 +145,7 @@ describe('Signup', () => {
|
|||||||
|
|
||||||
describe('SignupVerification', () => {
|
describe('SignupVerification', () => {
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
mutation(
|
mutation (
|
||||||
$name: String!
|
$name: String!
|
||||||
$password: String!
|
$password: String!
|
||||||
$email: String!
|
$email: String!
|
||||||
|
|||||||
@ -11,7 +11,7 @@ describe('file a report on a resource', () => {
|
|||||||
let authenticatedUser, currentUser, mutate, query, moderator, abusiveUser, otherReportingUser
|
let authenticatedUser, currentUser, mutate, query, moderator, abusiveUser, otherReportingUser
|
||||||
const categoryIds = ['cat9']
|
const categoryIds = ['cat9']
|
||||||
const fileReportMutation = gql`
|
const fileReportMutation = gql`
|
||||||
mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
mutation ($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
fileReport(
|
fileReport(
|
||||||
resourceId: $resourceId
|
resourceId: $resourceId
|
||||||
reasonCategory: $reasonCategory
|
reasonCategory: $reasonCategory
|
||||||
@ -42,7 +42,7 @@ describe('file a report on a resource', () => {
|
|||||||
reasonDescription: 'Violates code of conduct !!!',
|
reasonDescription: 'Violates code of conduct !!!',
|
||||||
}
|
}
|
||||||
const reportsQuery = gql`
|
const reportsQuery = gql`
|
||||||
query($closed: Boolean) {
|
query ($closed: Boolean) {
|
||||||
reports(orderBy: createdAt_desc, closed: $closed) {
|
reports(orderBy: createdAt_desc, closed: $closed) {
|
||||||
id
|
id
|
||||||
createdAt
|
createdAt
|
||||||
@ -74,7 +74,7 @@ describe('file a report on a resource', () => {
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
const reviewMutation = gql`
|
const reviewMutation = gql`
|
||||||
mutation($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
mutation ($resourceId: ID!, $disable: Boolean, $closed: Boolean) {
|
||||||
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
review(resourceId: $resourceId, disable: $disable, closed: $closed) {
|
||||||
createdAt
|
createdAt
|
||||||
resource {
|
resource {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ describe('rewards', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
|
await cleanDatabase()
|
||||||
const { server } = createServer({
|
const { server } = createServer({
|
||||||
context: () => {
|
context: () => {
|
||||||
return {
|
return {
|
||||||
@ -75,7 +76,7 @@ describe('rewards', () => {
|
|||||||
|
|
||||||
describe('reward', () => {
|
describe('reward', () => {
|
||||||
const rewardMutation = gql`
|
const rewardMutation = gql`
|
||||||
mutation($from: ID!, $to: ID!) {
|
mutation ($from: ID!, $to: ID!) {
|
||||||
reward(badgeKey: $from, userId: $to) {
|
reward(badgeKey: $from, userId: $to) {
|
||||||
id
|
id
|
||||||
badges {
|
badges {
|
||||||
@ -265,7 +266,7 @@ describe('rewards', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const unrewardMutation = gql`
|
const unrewardMutation = gql`
|
||||||
mutation($from: ID!, $to: ID!) {
|
mutation ($from: ID!, $to: ID!) {
|
||||||
unreward(badgeKey: $from, userId: $to) {
|
unreward(badgeKey: $from, userId: $to) {
|
||||||
id
|
id
|
||||||
badges {
|
badges {
|
||||||
|
|||||||
@ -28,7 +28,7 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const searchQuery = gql`
|
const searchQuery = gql`
|
||||||
query($query: String!) {
|
query ($query: String!) {
|
||||||
searchResults(query: $query, limit: 5) {
|
searchResults(query: $query, limit: 5) {
|
||||||
__typename
|
__typename
|
||||||
... on Post {
|
... on Post {
|
||||||
@ -49,7 +49,7 @@ const searchQuery = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const searchPostQuery = gql`
|
const searchPostQuery = gql`
|
||||||
query($query: String!, $firstPosts: Int, $postsOffset: Int) {
|
query ($query: String!, $firstPosts: Int, $postsOffset: Int) {
|
||||||
searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) {
|
searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) {
|
||||||
postCount
|
postCount
|
||||||
posts {
|
posts {
|
||||||
|
|||||||
@ -9,17 +9,17 @@ const instance = getNeode()
|
|||||||
const driver = getDriver()
|
const driver = getDriver()
|
||||||
|
|
||||||
const mutationShoutPost = gql`
|
const mutationShoutPost = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
shout(id: $id, type: Post)
|
shout(id: $id, type: Post)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const mutationUnshoutPost = gql`
|
const mutationUnshoutPost = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
unshout(id: $id, type: Post)
|
unshout(id: $id, type: Post)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
const queryPost = gql`
|
const queryPost = gql`
|
||||||
query($id: ID!) {
|
query ($id: ID!) {
|
||||||
Post(id: $id) {
|
Post(id: $id) {
|
||||||
id
|
id
|
||||||
shoutedBy {
|
shoutedBy {
|
||||||
|
|||||||
@ -70,7 +70,7 @@ describe('SocialMedia', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mutation = gql`
|
mutation = gql`
|
||||||
mutation($url: String!) {
|
mutation ($url: String!) {
|
||||||
CreateSocialMedia(url: $url) {
|
CreateSocialMedia(url: $url) {
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
@ -131,7 +131,7 @@ describe('SocialMedia', () => {
|
|||||||
describe('ownedBy', () => {
|
describe('ownedBy', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mutation = gql`
|
mutation = gql`
|
||||||
mutation($url: String!) {
|
mutation ($url: String!) {
|
||||||
CreateSocialMedia(url: $url) {
|
CreateSocialMedia(url: $url) {
|
||||||
url
|
url
|
||||||
ownedBy {
|
ownedBy {
|
||||||
@ -162,7 +162,7 @@ describe('SocialMedia', () => {
|
|||||||
const socialMedia = await setUpSocialMedia()
|
const socialMedia = await setUpSocialMedia()
|
||||||
|
|
||||||
mutation = gql`
|
mutation = gql`
|
||||||
mutation($id: ID!, $url: String!) {
|
mutation ($id: ID!, $url: String!) {
|
||||||
UpdateSocialMedia(id: $id, url: $url) {
|
UpdateSocialMedia(id: $id, url: $url) {
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
@ -225,7 +225,7 @@ describe('SocialMedia', () => {
|
|||||||
const socialMedia = await setUpSocialMedia()
|
const socialMedia = await setUpSocialMedia()
|
||||||
|
|
||||||
mutation = gql`
|
mutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
DeleteSocialMedia(id: $id) {
|
DeleteSocialMedia(id: $id) {
|
||||||
id
|
id
|
||||||
url
|
url
|
||||||
|
|||||||
22
backend/src/schema/resolvers/transactions/inviteCodes.js
Normal file
22
backend/src/schema/resolvers/transactions/inviteCodes.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export async function validateInviteCode(session, inviteCode) {
|
||||||
|
const readTxResultPromise = session.readTransaction(async (txc) => {
|
||||||
|
const result = await txc.run(
|
||||||
|
`MATCH (ic:InviteCode { code: toUpper($inviteCode) })
|
||||||
|
RETURN
|
||||||
|
CASE
|
||||||
|
WHEN ic.expiresAt IS NULL THEN true
|
||||||
|
WHEN datetime(ic.expiresAt) >= datetime() THEN true
|
||||||
|
ELSE false END AS result`,
|
||||||
|
{
|
||||||
|
inviteCode,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return result.records.map((record) => record.get('result'))
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
const txResult = await readTxResultPromise
|
||||||
|
return !!txResult[0]
|
||||||
|
} finally {
|
||||||
|
session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,7 +39,7 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
const userDataQuery = gql`
|
const userDataQuery = gql`
|
||||||
query($id: ID!) {
|
query ($id: ID!) {
|
||||||
userData(id: $id) {
|
userData(id: $id) {
|
||||||
user {
|
user {
|
||||||
id
|
id
|
||||||
|
|||||||
@ -171,7 +171,7 @@ describe('currentUser', () => {
|
|||||||
|
|
||||||
describe('login', () => {
|
describe('login', () => {
|
||||||
const loginMutation = gql`
|
const loginMutation = gql`
|
||||||
mutation($email: String!, $password: String!) {
|
mutation ($email: String!, $password: String!) {
|
||||||
login(email: $email, password: $password)
|
login(email: $email, password: $password)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
@ -287,7 +287,7 @@ describe('login', () => {
|
|||||||
|
|
||||||
describe('change password', () => {
|
describe('change password', () => {
|
||||||
const changePasswordMutation = gql`
|
const changePasswordMutation = gql`
|
||||||
mutation($oldPassword: String!, $newPassword: String!) {
|
mutation ($oldPassword: String!, $newPassword: String!) {
|
||||||
changePassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
changePassword(oldPassword: $oldPassword, newPassword: $newPassword)
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const driver = getDriver()
|
|||||||
const neode = getNeode()
|
const neode = getNeode()
|
||||||
|
|
||||||
const deleteUserMutation = gql`
|
const deleteUserMutation = gql`
|
||||||
mutation($id: ID!, $resource: [Deletable]) {
|
mutation ($id: ID!, $resource: [Deletable]) {
|
||||||
DeleteUser(id: $id, resource: $resource) {
|
DeleteUser(id: $id, resource: $resource) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@ -46,7 +46,7 @@ const deleteUserMutation = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const switchUserRoleMutation = gql`
|
const switchUserRoleMutation = gql`
|
||||||
mutation($role: UserGroup!, $id: ID!) {
|
mutation ($role: UserGroup!, $id: ID!) {
|
||||||
switchUserRole(role: $role, id: $id) {
|
switchUserRole(role: $role, id: $id) {
|
||||||
name
|
name
|
||||||
role
|
role
|
||||||
@ -81,7 +81,7 @@ describe('User', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
userQuery = gql`
|
userQuery = gql`
|
||||||
query($email: String) {
|
query ($email: String) {
|
||||||
User(email: $email) {
|
User(email: $email) {
|
||||||
name
|
name
|
||||||
}
|
}
|
||||||
@ -144,7 +144,7 @@ describe('UpdateUser', () => {
|
|||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
updateUserMutation = gql`
|
updateUserMutation = gql`
|
||||||
mutation(
|
mutation (
|
||||||
$id: ID!
|
$id: ID!
|
||||||
$name: String
|
$name: String
|
||||||
$termsAndConditionsAgreedVersion: String
|
$termsAndConditionsAgreedVersion: String
|
||||||
|
|||||||
@ -9,7 +9,7 @@ const driver = getDriver()
|
|||||||
let authenticatedUser, mutate, query, variables
|
let authenticatedUser, mutate, query, variables
|
||||||
|
|
||||||
const updateUserMutation = gql`
|
const updateUserMutation = gql`
|
||||||
mutation($id: ID!, $name: String!, $locationName: String) {
|
mutation ($id: ID!, $name: String!, $locationName: String) {
|
||||||
UpdateUser(id: $id, name: $name, locationName: $locationName) {
|
UpdateUser(id: $id, name: $name, locationName: $locationName) {
|
||||||
locationName
|
locationName
|
||||||
}
|
}
|
||||||
@ -17,7 +17,7 @@ const updateUserMutation = gql`
|
|||||||
`
|
`
|
||||||
|
|
||||||
const queryLocations = gql`
|
const queryLocations = gql`
|
||||||
query($place: String!, $lang: String!) {
|
query ($place: String!, $lang: String!) {
|
||||||
queryLocations(place: $place, lang: $lang) {
|
queryLocations(place: $place, lang: $lang) {
|
||||||
place_name
|
place_name
|
||||||
id
|
id
|
||||||
@ -114,10 +114,22 @@ describe('Location Service', () => {
|
|||||||
const result = await query({ query: queryLocations, variables })
|
const result = await query({ query: queryLocations, variables })
|
||||||
expect(result.data.queryLocations).toEqual([
|
expect(result.data.queryLocations).toEqual([
|
||||||
{ id: 'place.14094307404564380', place_name: 'Berlin, Germany' },
|
{ id: 'place.14094307404564380', place_name: 'Berlin, Germany' },
|
||||||
{ id: 'place.15095411613564380', place_name: 'Berlin, Maryland, United States' },
|
{
|
||||||
{ id: 'place.5225018734564380', place_name: 'Berlin, Connecticut, United States' },
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
{ id: 'place.16922023226564380', place_name: 'Berlin, New Jersey, United States' },
|
place_name: 'Berlin, Maryland, United States',
|
||||||
{ id: 'place.4035845612564380', place_name: 'Berlin Township, New Jersey, United States' },
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin, Connecticut, United States',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin, New Jersey, United States',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin Township, New Jersey, United States',
|
||||||
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -128,11 +140,23 @@ describe('Location Service', () => {
|
|||||||
}
|
}
|
||||||
const result = await query({ query: queryLocations, variables })
|
const result = await query({ query: queryLocations, variables })
|
||||||
expect(result.data.queryLocations).toEqual([
|
expect(result.data.queryLocations).toEqual([
|
||||||
{ id: 'place.14094307404564380', place_name: 'Berlin, Deutschland' },
|
{ id: expect.stringMatching(/^place\.[0-9]+$/), place_name: 'Berlin, Deutschland' },
|
||||||
{ id: 'place.15095411613564380', place_name: 'Berlin, Maryland, Vereinigte Staaten' },
|
{
|
||||||
{ id: 'place.16922023226564380', place_name: 'Berlin, New Jersey, Vereinigte Staaten' },
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
{ id: 'place.10735893248465990', place_name: 'Berlin Heights, Ohio, Vereinigte Staaten' },
|
place_name: 'Berlin, Maryland, Vereinigte Staaten',
|
||||||
{ id: 'place.1165756679564380', place_name: 'Berlin, Massachusetts, Vereinigte Staaten' },
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin, New Jersey, Vereinigte Staaten',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin Heights, Ohio, Vereinigte Staaten',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: expect.stringMatching(/^place\.[0-9]+$/),
|
||||||
|
place_name: 'Berlin, Massachusetts, Vereinigte Staaten',
|
||||||
|
},
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -93,7 +93,7 @@ describe('muteUser', () => {
|
|||||||
muteAction = (variables) => {
|
muteAction = (variables) => {
|
||||||
const { mutate } = createTestClient(server)
|
const { mutate } = createTestClient(server)
|
||||||
const muteUserMutation = gql`
|
const muteUserMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
muteUser(id: $id) {
|
muteUser(id: $id) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
@ -310,7 +310,7 @@ describe('unmuteUser', () => {
|
|||||||
unmuteAction = (variables) => {
|
unmuteAction = (variables) => {
|
||||||
const { mutate } = createTestClient(server)
|
const { mutate } = createTestClient(server)
|
||||||
const unmuteUserMutation = gql`
|
const unmuteUserMutation = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
unmuteUser(id: $id) {
|
unmuteUser(id: $id) {
|
||||||
id
|
id
|
||||||
name
|
name
|
||||||
|
|||||||
@ -32,7 +32,7 @@ afterAll(async () => {
|
|||||||
describe('count post teaser views', () => {
|
describe('count post teaser views', () => {
|
||||||
let aUser, bUser
|
let aUser, bUser
|
||||||
const markTeaserAsViewed = gql`
|
const markTeaserAsViewed = gql`
|
||||||
mutation($id: ID!) {
|
mutation ($id: ID!) {
|
||||||
markTeaserAsViewed(id: $id) {
|
markTeaserAsViewed(id: $id) {
|
||||||
id
|
id
|
||||||
viewedTeaserCount
|
viewedTeaserCount
|
||||||
|
|||||||
@ -4,12 +4,16 @@ type EmailAddress {
|
|||||||
createdAt: String
|
createdAt: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Query {
|
||||||
|
VerifyNonce(email: String!, nonce: String!): Boolean!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
Signup(email: String!): EmailAddress
|
Signup(email: String!, inviteCode: String = null): EmailAddress
|
||||||
SignupByInvitation(email: String!, token: String!): EmailAddress
|
|
||||||
SignupVerification(
|
SignupVerification(
|
||||||
nonce: String!
|
nonce: String!
|
||||||
email: String!
|
email: String!
|
||||||
|
inviteCode: String = null
|
||||||
name: String!
|
name: String!
|
||||||
password: String!
|
password: String!
|
||||||
slug: String
|
slug: String
|
||||||
|
|||||||
@ -14,4 +14,5 @@ type Mutation {
|
|||||||
type Query {
|
type Query {
|
||||||
MyInviteCodes: [InviteCode]
|
MyInviteCodes: [InviteCode]
|
||||||
isValidInviteCode(code: ID!): Boolean
|
isValidInviteCode(code: ID!): Boolean
|
||||||
|
getInviteCode: InviteCode
|
||||||
}
|
}
|
||||||
|
|||||||
@ -910,6 +910,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.9.2":
|
||||||
|
version "7.12.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e"
|
||||||
|
integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
"@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
||||||
version "7.8.6"
|
version "7.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
||||||
@ -956,32 +963,32 @@
|
|||||||
exec-sh "^0.3.2"
|
exec-sh "^0.3.2"
|
||||||
minimist "^1.2.0"
|
minimist "^1.2.0"
|
||||||
|
|
||||||
"@graphql-toolkit/common@0.9.12":
|
"@graphql-toolkit/common@0.10.4":
|
||||||
version "0.9.12"
|
version "0.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-toolkit/common/-/common-0.9.12.tgz#a3bf91d00130100cfa0a72be718b16cc1011ab45"
|
resolved "https://registry.yarnpkg.com/@graphql-toolkit/common/-/common-0.10.4.tgz#7785f2a3f14559d0778859c49f4442078c196695"
|
||||||
integrity sha512-NjBVxeM1GB0bldiBm4UQoxKtbu/hjRfcqDEt1JPia+uXgFde3qTxtF5IZkworRFNCp+8KPSVrwcreQHSfFW63w==
|
integrity sha512-HQ3HaxCqX+UE8y/0h7LMDBBGSIKJxY/gaQesaksvE2Y+N4NpSWdiW6HpOcgXfC2HGf9yM0hEdsERzzL8z3mbHQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
aggregate-error "3.0.1"
|
aggregate-error "3.0.1"
|
||||||
camel-case "4.1.1"
|
camel-case "4.1.1"
|
||||||
graphql-tools-fork "9.0.1"
|
graphql-tools "5.0.0"
|
||||||
lodash "4.17.15"
|
lodash "4.17.15"
|
||||||
|
|
||||||
"@graphql-toolkit/file-loading@0.9.12":
|
"@graphql-toolkit/file-loading@0.10.4":
|
||||||
version "0.9.12"
|
version "0.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-toolkit/file-loading/-/file-loading-0.9.12.tgz#f7d5d7e042df59c4e6c8fe3417c67dd1cddf9043"
|
resolved "https://registry.yarnpkg.com/@graphql-toolkit/file-loading/-/file-loading-0.10.4.tgz#50e8933e44b17853544c1fe63350df93f33a5e80"
|
||||||
integrity sha512-xEmKkbWI5FXQARTN3dbPzshwcmduOJUvx1QGsfcuryVDdQJZynUEaPF7IxYaFpvQF7vwxGIRdff/jC/a5ypDrA==
|
integrity sha512-oUmy/sO3BJfax85pVKI7FZ6TWrViNuWXoJkRM293YV9bKGuYU9TgqZoHyM+oEqWO5ruXCL/nCdw3cIBau+rSNA==
|
||||||
dependencies:
|
dependencies:
|
||||||
globby "11.0.0"
|
globby "11.0.0"
|
||||||
unixify "1.0.0"
|
unixify "1.0.0"
|
||||||
|
|
||||||
"@graphql-toolkit/schema-merging@0.9.12":
|
"@graphql-toolkit/schema-merging@0.10.4":
|
||||||
version "0.9.12"
|
version "0.10.4"
|
||||||
resolved "https://registry.yarnpkg.com/@graphql-toolkit/schema-merging/-/schema-merging-0.9.12.tgz#f058a7d256a4ed0e61e9874f90ae03326b81668e"
|
resolved "https://registry.yarnpkg.com/@graphql-toolkit/schema-merging/-/schema-merging-0.10.4.tgz#2428590a531a33e9fe03be27cce9030f1c4c044b"
|
||||||
integrity sha512-ciqxLeMw7KPbJcq/xgnPbGyJGRO6bO1zQcdWCUSssyw8VDRHj5PFqEOAzT88eZQkEtg3qLN/wQEypeyFyNTHzw==
|
integrity sha512-naL6reYBuILLMrkMfKz0lOLL0kl6gGYnaaywnO/Dgp9F4NeAxDdAs5CV6Fy9NO5OzePFP58Dnc4sh2RyYrrFJg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@graphql-toolkit/common" "0.9.12"
|
"@graphql-toolkit/common" "0.10.4"
|
||||||
deepmerge "4.2.2"
|
deepmerge "4.2.2"
|
||||||
graphql-tools-fork "9.0.1"
|
graphql-tools "5.0.0"
|
||||||
tslib "1.11.1"
|
tslib "1.11.1"
|
||||||
|
|
||||||
"@hapi/address@2.x.x":
|
"@hapi/address@2.x.x":
|
||||||
@ -2036,7 +2043,7 @@ apollo-link-context@~1.0.20:
|
|||||||
apollo-link "^1.2.14"
|
apollo-link "^1.2.14"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
apollo-link-http-common@^0.2.15, apollo-link-http-common@^0.2.16:
|
apollo-link-http-common@^0.2.14, apollo-link-http-common@^0.2.16:
|
||||||
version "0.2.16"
|
version "0.2.16"
|
||||||
resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz#756749dafc732792c8ca0923f9a40564b7c59ecc"
|
resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz#756749dafc732792c8ca0923f9a40564b7c59ecc"
|
||||||
integrity sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==
|
integrity sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg==
|
||||||
@ -2054,7 +2061,7 @@ apollo-link-http@~1.5.17:
|
|||||||
apollo-link-http-common "^0.2.16"
|
apollo-link-http-common "^0.2.16"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
apollo-link@^1.0.0, apollo-link@^1.2.13, apollo-link@^1.2.14:
|
apollo-link@^1.0.0, apollo-link@^1.2.12, apollo-link@^1.2.14:
|
||||||
version "1.2.14"
|
version "1.2.14"
|
||||||
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9"
|
resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9"
|
||||||
integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==
|
integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg==
|
||||||
@ -2211,6 +2218,16 @@ apollo-tracing@^0.12.0:
|
|||||||
apollo-server-env "^2.4.5"
|
apollo-server-env "^2.4.5"
|
||||||
apollo-server-plugin-base "^0.10.2"
|
apollo-server-plugin-base "^0.10.2"
|
||||||
|
|
||||||
|
apollo-upload-client@^13.0.0:
|
||||||
|
version "13.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-13.0.0.tgz#146d1ddd85d711fcac8ca97a72d3ca6787f2b71b"
|
||||||
|
integrity sha512-lJ9/bk1BH1lD15WhWRha2J3+LrXrPIX5LP5EwiOUHv8PCORp4EUrcujrA3rI5hZeZygrTX8bshcuMdpqpSrvtA==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.9.2"
|
||||||
|
apollo-link "^1.2.12"
|
||||||
|
apollo-link-http-common "^0.2.14"
|
||||||
|
extract-files "^8.0.0"
|
||||||
|
|
||||||
apollo-utilities@1.3.3, apollo-utilities@^1.3.3:
|
apollo-utilities@1.3.3, apollo-utilities@^1.3.3:
|
||||||
version "1.3.3"
|
version "1.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.3.tgz#f1854715a7be80cd810bc3ac95df085815c0787c"
|
resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.3.tgz#f1854715a7be80cd810bc3ac95df085815c0787c"
|
||||||
@ -3392,10 +3409,10 @@ data-urls@^1.1.0:
|
|||||||
whatwg-mimetype "^2.2.0"
|
whatwg-mimetype "^2.2.0"
|
||||||
whatwg-url "^7.0.0"
|
whatwg-url "^7.0.0"
|
||||||
|
|
||||||
date-fns@2.11.1:
|
date-fns@2.22.1:
|
||||||
version "2.11.1"
|
version "2.22.1"
|
||||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.11.1.tgz#197b8be1bbf5c5e6fe8bea817f0fe111820e7a12"
|
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.22.1.tgz#1e5af959831ebb1d82992bf67b765052d8f0efc4"
|
||||||
integrity sha512-3RdUoinZ43URd2MJcquzBbDQo+J87cSzB8NkXdZiN5ia1UNyep0oCyitfiL88+R7clGTeq/RniXAc16gWyAu1w==
|
integrity sha512-yUFPQjrxEmIsMqlHhAhmxkuH769baF21Kk+nZwZGyrMoyLA+LugaQtC0+Tqf9CBUUULWwUJt6Q5ySI3LJDDCGg==
|
||||||
|
|
||||||
dateformat@^3.0.3:
|
dateformat@^3.0.3:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
@ -4299,10 +4316,10 @@ extglob@^2.0.4:
|
|||||||
snapdragon "^0.8.1"
|
snapdragon "^0.8.1"
|
||||||
to-regex "^3.0.1"
|
to-regex "^3.0.1"
|
||||||
|
|
||||||
extract-files@^7.0.0:
|
extract-files@^8.0.0:
|
||||||
version "7.0.0"
|
version "8.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-7.0.0.tgz#3dc7853320ff7876ec62d6e98f2f4e6f3e6282f6"
|
resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-8.1.0.tgz#46a0690d0fe77411a2e3804852adeaa65cd59288"
|
||||||
integrity sha512-3AUlT7TD+DbQXNe3t70QrgJU6Wgcp7rk1Zm0vqWz8OYnw4vxihgG0TgZ2SIGrVqScc4WfOu7B4a0BezGJ0YqvQ==
|
integrity sha512-PTGtfthZK79WUMk+avLmwx3NGdU8+iVFXC2NMGxKsn0MnihOG2lvumj+AZo8CTwTrwjXDgZ5tztbRlEdRjBonQ==
|
||||||
|
|
||||||
extsprintf@1.3.0:
|
extsprintf@1.3.0:
|
||||||
version "1.3.0"
|
version "1.3.0"
|
||||||
@ -4849,19 +4866,19 @@ graphql-tag@^2.9.2, graphql-tag@~2.10.3:
|
|||||||
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03"
|
resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.3.tgz#ea1baba5eb8fc6339e4c4cf049dabe522b0edf03"
|
||||||
integrity sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==
|
integrity sha512-4FOv3ZKfA4WdOKJeHdz6B3F/vxBLSgmBcGeAFPf4n1F64ltJUvOOerNj0rsJxONQGdhUMynQIvd6LzB+1J5oKA==
|
||||||
|
|
||||||
graphql-tools-fork@9.0.1:
|
graphql-tools@5.0.0:
|
||||||
version "9.0.1"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/graphql-tools-fork/-/graphql-tools-fork-9.0.1.tgz#fc8df40c108bdba3268999dea355cc614c765038"
|
resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-5.0.0.tgz#67281c834a0e29f458adba8018f424816fa627e9"
|
||||||
integrity sha512-kM6mUNVekgnWKtVqLGQ9HvQqQ3zZVPZRg1esltBoohsbUMaChl+9QkjBjoMxnZPnbTGOOGGagopNBQALIBysNg==
|
integrity sha512-5zn3vtn//382b7G3Wzz3d5q/sh+f7tVrnxeuhTMTJ7pWJijNqLxH7VEzv8VwXCq19zAzHYEosFHfXiK7qzvk7w==
|
||||||
dependencies:
|
dependencies:
|
||||||
apollo-link "^1.2.13"
|
apollo-link "^1.2.14"
|
||||||
apollo-link-http-common "^0.2.15"
|
apollo-upload-client "^13.0.0"
|
||||||
deprecated-decorator "^0.1.6"
|
deprecated-decorator "^0.1.6"
|
||||||
extract-files "^7.0.0"
|
|
||||||
form-data "^3.0.0"
|
form-data "^3.0.0"
|
||||||
iterall "^1.3.0"
|
iterall "^1.3.0"
|
||||||
node-fetch "^2.6.0"
|
node-fetch "^2.6.0"
|
||||||
uuid "^7.0.2"
|
tslib "^1.11.1"
|
||||||
|
uuid "^7.0.3"
|
||||||
|
|
||||||
graphql-tools@^4.0.0, graphql-tools@^4.0.4, graphql-tools@^4.0.5:
|
graphql-tools@^4.0.0, graphql-tools@^4.0.4, graphql-tools@^4.0.5:
|
||||||
version "4.0.8"
|
version "4.0.8"
|
||||||
@ -6606,13 +6623,13 @@ merge-descriptors@1.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
|
||||||
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
|
||||||
|
|
||||||
merge-graphql-schemas@^1.7.7:
|
merge-graphql-schemas@^1.7.8:
|
||||||
version "1.7.7"
|
version "1.7.8"
|
||||||
resolved "https://registry.yarnpkg.com/merge-graphql-schemas/-/merge-graphql-schemas-1.7.7.tgz#673898f97c384d88fd66e49359b2f604e5864441"
|
resolved "https://registry.yarnpkg.com/merge-graphql-schemas/-/merge-graphql-schemas-1.7.8.tgz#11a0a672a38a61d988c09ffdebe1bd4f8418de48"
|
||||||
integrity sha512-3mjFHXpsF+bnsnSy/FlZiWCcNo7AULuG8kEBE+YVX8qbzNrEPJVhSg7ANr5LjQZbGtSlTz7fa3Wqw0bfbQ5R5Q==
|
integrity sha512-C3EJ1i86OjmbcCT524wVPRl17M5VZzgyh9kIGYAlYnAILX+7xfh8cCbMKfehh9n4opZg6CtcPogCiVZ6PB2NyQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@graphql-toolkit/file-loading" "0.9.12"
|
"@graphql-toolkit/file-loading" "0.10.4"
|
||||||
"@graphql-toolkit/schema-merging" "0.9.12"
|
"@graphql-toolkit/schema-merging" "0.10.4"
|
||||||
tslib "1.11.1"
|
tslib "1.11.1"
|
||||||
|
|
||||||
merge-stream@^2.0.0:
|
merge-stream@^2.0.0:
|
||||||
@ -6942,10 +6959,10 @@ ms@^2.1.1:
|
|||||||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||||
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
|
||||||
|
|
||||||
mustache@^4.0.1:
|
mustache@^4.2.0:
|
||||||
version "4.0.1"
|
version "4.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.0.1.tgz#d99beb031701ad433338e7ea65e0489416c854a2"
|
resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64"
|
||||||
integrity sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA==
|
integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==
|
||||||
|
|
||||||
mute-stream@0.0.8:
|
mute-stream@0.0.8:
|
||||||
version "0.0.8"
|
version "0.0.8"
|
||||||
@ -7816,10 +7833,10 @@ prettier-linter-helpers@^1.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
fast-diff "^1.1.2"
|
fast-diff "^1.1.2"
|
||||||
|
|
||||||
prettier@~2.2.0:
|
prettier@~2.3.2:
|
||||||
version "2.2.0"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.2.0.tgz#8a03c7777883b29b37fb2c4348c66a78e980418b"
|
resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.3.2.tgz#ef280a05ec253712e486233db5c6f23441e7342d"
|
||||||
integrity sha512-yYerpkvseM4iKD/BXLYUkQV5aKt4tQPqaGW6EsZjzyu0r7sVZZNPJW4Y8MyKmicp6t42XUPcBVA+H6sB3gqndw==
|
integrity sha512-lnJzDfJ66zkMy58OL5/NY5zp70S7Nz6KqcKkXYzn2tMVrNxvbqaBpg7H3qHaLxCJ5lNMsGuM8+ohS7cZrthdLQ==
|
||||||
|
|
||||||
pretty-format@^25.3.0:
|
pretty-format@^25.3.0:
|
||||||
version "25.3.0"
|
version "25.3.0"
|
||||||
@ -9406,15 +9423,15 @@ ts-invariant@^0.4.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
tslib@1.11.1, tslib@^1.9.0:
|
tslib@1.11.1:
|
||||||
version "1.11.1"
|
version "1.11.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
|
||||||
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
|
||||||
|
|
||||||
tslib@^1.10.0, tslib@^1.9.3:
|
tslib@^1.10.0, tslib@^1.11.1, tslib@^1.9.0, tslib@^1.9.3:
|
||||||
version "1.13.0"
|
version "1.14.1"
|
||||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043"
|
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
|
||||||
integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==
|
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
|
||||||
|
|
||||||
tunnel-agent@^0.6.0:
|
tunnel-agent@^0.6.0:
|
||||||
version "0.6.0"
|
version "0.6.0"
|
||||||
@ -9689,7 +9706,7 @@ uuid@^3.1.0, uuid@^3.3.2:
|
|||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
|
||||||
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
|
||||||
|
|
||||||
uuid@^7.0.2:
|
uuid@^7.0.3:
|
||||||
version "7.0.3"
|
version "7.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
|
||||||
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
|
||||||
@ -9997,9 +10014,9 @@ yallist@^4.0.0:
|
|||||||
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
|
||||||
|
|
||||||
yargs-parser@^18.1.1:
|
yargs-parser@^18.1.1:
|
||||||
version "18.1.1"
|
version "18.1.3"
|
||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.1.tgz#bf7407b915427fc760fcbbccc6c82b4f0ffcbd37"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
|
||||||
integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==
|
integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
camelcase "^5.0.0"
|
camelcase "^5.0.0"
|
||||||
decamelize "^1.2.0"
|
decamelize "^1.2.0"
|
||||||
|
|||||||
@ -13,16 +13,13 @@ $ docker-compose up
|
|||||||
|
|
||||||
## Setup without docker
|
## Setup without docker
|
||||||
|
|
||||||
First, you have to tell cypress how to connect to your local neo4j database
|
To start the services that are required for cypress testing manually. You basically need the whole setup to run:
|
||||||
among other things. You can copy our template configuration and change the new
|
|
||||||
file according to your needs.
|
|
||||||
|
|
||||||
To start the services that are required for cypress testing, run:
|
- backend
|
||||||
|
- webapp
|
||||||
|
- neo4j
|
||||||
|
|
||||||
```bash
|
Navigate to the corresponding folders and start the services.
|
||||||
# in the top level folder Ocelot-Social/
|
|
||||||
$ yarn cypress:setup
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install cypress
|
## Install cypress
|
||||||
|
|
||||||
@ -35,21 +32,11 @@ without docker, you would have to install cypress and its dependencies first:
|
|||||||
$ yarn install
|
$ yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run cypress
|
|
||||||
|
|
||||||
After verifying that there are no errors with the servers starting, open another tab in your terminal and run the following command:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ yarn cypress:run
|
|
||||||
```
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### Open Interactive Test Console
|
### Open Interactive Test Console
|
||||||
|
|
||||||
If you are like me, you might want to see some visual output. The interactive cypress environment also helps at debugging your tests, you can even time travel between individual steps and see the exact state of the app.
|
The interactive cypress test console allows to run tests and have visual feedback on that. The interactive cypress environment also helps at debugging the tests, you can even time travel between individual steps and see the exact state of the app.
|
||||||
|
|
||||||
To use this feature, instead of `yarn cypress:run` you would run the following command:
|
To use this feature run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ yarn cypress:open
|
$ yarn cypress:open
|
||||||
@ -57,7 +44,19 @@ $ yarn cypress:open
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Run cypress
|
||||||
|
|
||||||
|
To run cypress without the user interface:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn cypress:run
|
||||||
|
```
|
||||||
|
|
||||||
|
This is used to run cypress in CI or in console
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## Write some Tests
|
## Write some Tests
|
||||||
|
|
||||||
Check out the Cypress documentation for further information on how to write tests:
|
Check out the Cypress documentation for further information on how to write tests:
|
||||||
[https://docs.cypress.io/guides/getting-started/writing-your-first-test.html\#Write-a-simple-test](https://docs.cypress.io/guides/getting-started/writing-your-first-test.html#Write-a-simple-test)
|
[Write-a-simple-test](https://docs.cypress.io/guides/getting-started/writing-your-first-test.html#Write-a-simple-test)
|
||||||
|
|||||||
@ -1,2 +0,0 @@
|
|||||||
// please change also version in file "webapp/constants/terms-and-conditions-version.js"
|
|
||||||
export const VERSION = '0.0.4'
|
|
||||||
@ -1,8 +1,10 @@
|
|||||||
{
|
{
|
||||||
"projectId": "qa7fe2",
|
"projectId": "qa7fe2",
|
||||||
"ignoreTestFiles": "*.js",
|
"ignoreTestFiles": "*.js",
|
||||||
|
"chromeWebSecurity": false,
|
||||||
"baseUrl": "http://localhost:3000",
|
"baseUrl": "http://localhost:3000",
|
||||||
"env": {
|
"retries": {
|
||||||
"RETRIES": 2
|
"runMode": 2,
|
||||||
|
"openMode": 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
43
cypress/integration/Admin.PinPost.feature
Normal file
43
cypress/integration/Admin.PinPost.feature
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
Feature: Admin pins a post
|
||||||
|
As an admin
|
||||||
|
I want to pin a post so that it always appears at the top
|
||||||
|
In order to make sure all network users read it
|
||||||
|
e.g. notify people about security incidents, maintenance downtimes
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" are in the database:
|
||||||
|
| slug | email | password | id | name | role | termsAndConditionsAgreedVersion |
|
||||||
|
| user | user@example.org | abcd | user | User-Chad | user | 0.0.4 |
|
||||||
|
| admin | admin@example.org | 1234 | admin | Admin-Man | admin | 0.0.4 |
|
||||||
|
Given the following "posts" are in the database:
|
||||||
|
| id | title | pinned | createdAt |
|
||||||
|
| p1 | Some other post | | 2020-01-21 |
|
||||||
|
| p2 | Houston we have a problem | x | 2020-01-20 |
|
||||||
|
| p3 | Yet another post | | 2020-01-19 |
|
||||||
|
|
||||||
|
Scenario: Pinned post always appears on the top of the newsfeed
|
||||||
|
When I am logged in as "user"
|
||||||
|
And I navigate to page "/"
|
||||||
|
Then the first post on the newsfeed has the title:
|
||||||
|
"""
|
||||||
|
Houston we have a problem
|
||||||
|
"""
|
||||||
|
And the post with title "Houston we have a problem" has a ribbon for pinned posts
|
||||||
|
|
||||||
|
Scenario: Ordinary users cannot pin a post
|
||||||
|
When I am logged in as "user"
|
||||||
|
And I navigate to page "/"
|
||||||
|
And I open the content menu of post "Yet another post"
|
||||||
|
Then there is no button to pin a post
|
||||||
|
|
||||||
|
Scenario: Admins are allowed to pin a post
|
||||||
|
When I am logged in as "admin"
|
||||||
|
And I navigate to page "/"
|
||||||
|
And I open the content menu of post "Yet another post"
|
||||||
|
And I click on "pin post"
|
||||||
|
Then I see a toaster with "Post pinned successfully"
|
||||||
|
And the first post on the newsfeed has the title:
|
||||||
|
"""
|
||||||
|
Yet another post
|
||||||
|
"""
|
||||||
|
And the post with title "Yet another post" has a ribbon for pinned posts
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("I open the content menu of post {string}", (title)=> {
|
||||||
|
cy.contains('.post-teaser', title)
|
||||||
|
.find('.content-menu .base-button')
|
||||||
|
.click()
|
||||||
|
})
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then("the post with title {string} has a ribbon for pinned posts", (title) => {
|
||||||
|
cy.get(".post-teaser").contains(title)
|
||||||
|
.parent()
|
||||||
|
.parent()
|
||||||
|
.find(".ribbon.--pinned")
|
||||||
|
.should("contain", "Announcement")
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then("there is no button to pin a post", () => {
|
||||||
|
cy.get("a.ds-menu-item-link")
|
||||||
|
.should('contain', "Report Post") // sanity check
|
||||||
|
.should('not.contain', "Pin post")
|
||||||
|
})
|
||||||
31
cypress/integration/Admin.TagOverview.feature
Normal file
31
cypress/integration/Admin.TagOverview.feature
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
Feature: Admin tag overview
|
||||||
|
As a database administrator
|
||||||
|
I would like to see a overview of all tags and their usage
|
||||||
|
In order to be able to decide which tags are popular or not
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" are in the database:
|
||||||
|
| slug | email | password | id | name | role | termsAndConditionsAgreedVersion |
|
||||||
|
| admin | admin@example.org | 1234 | admin | Admin-Man | admin | 0.0.4 |
|
||||||
|
| u1 | u1@example.org | 1234 | u1 | User1 | user | 0.0.4 |
|
||||||
|
| u2 | u2@example.org | 1234 | u2 | User2 | user | 0.0.4 |
|
||||||
|
| u3 | u3@example.org | 1234 | u3 | User3 | user | 0.0.4 |
|
||||||
|
And the following "tags" are in the database:
|
||||||
|
| id |
|
||||||
|
| Ecology |
|
||||||
|
| Nature |
|
||||||
|
| Democracy |
|
||||||
|
And the following "posts" are in the database:
|
||||||
|
| id | title | authorId | tagIds |
|
||||||
|
| p1 | P1 from U1 | u1 | Nature, Democracy |
|
||||||
|
| p2 | P2 from U2 | u2 | Ecology, Democracy |
|
||||||
|
| p3 | P3 from U3 | u3 | Nature, Democracy |
|
||||||
|
And I am logged in as "admin"
|
||||||
|
|
||||||
|
Scenario: See an overview of tags
|
||||||
|
When I navigate to page "/admin/hashtags"
|
||||||
|
Then I can see the following table:
|
||||||
|
| No. | Hashtags | Users | Posts |
|
||||||
|
| 1 | #Democracy | 3 | 3 |
|
||||||
|
| 2 | #Nature | 2 | 2 |
|
||||||
|
| 3 | #Ecology | 1 | 1 |
|
||||||
@ -4,7 +4,7 @@ Feature: Internationalization
|
|||||||
In order to be able to understand the interface
|
In order to be able to understand the interface
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given I am on the "login" page
|
Given I navigate to page "/login"
|
||||||
|
|
||||||
Scenario Outline: I select "<language>" in the language menu and see "<buttonLabel>"
|
Scenario Outline: I select "<language>" in the language menu and see "<buttonLabel>"
|
||||||
When I select "<language>" in the language menu
|
When I select "<language>" in the language menu
|
||||||
@ -18,6 +18,6 @@ Feature: Internationalization
|
|||||||
| English | Login |
|
| English | Login |
|
||||||
|
|
||||||
Scenario: Keep preferred language after refresh
|
Scenario: Keep preferred language after refresh
|
||||||
Given I previously switched the language to "Français"
|
When I select "Français" in the language menu
|
||||||
And I refresh the page
|
And I refresh the page
|
||||||
Then the whole user interface appears in "Français"
|
Then the whole user interface appears in "Français"
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then("I see a button with the label {string}", label => {
|
||||||
|
cy.contains("button", label);
|
||||||
|
});
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("I select {string} in the language menu", language => {
|
||||||
|
cy.get(".locale-menu")
|
||||||
|
.click();
|
||||||
|
cy.contains(".locale-menu-popover a", language)
|
||||||
|
.click();
|
||||||
|
});
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
import locales from '../../../webapp/locales'
|
||||||
|
|
||||||
|
Then("the whole user interface appears in {string}", language => {
|
||||||
|
const { code } = locales.find((entry) => entry.name === language);
|
||||||
|
cy.get(`html[lang=${code}]`);
|
||||||
|
cy.getCookie("locale").should("have.property", "value", code);
|
||||||
|
});
|
||||||
40
cypress/integration/Moderation.HidePost.feature
Normal file
40
cypress/integration/Moderation.HidePost.feature
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Feature: Hide Posts
|
||||||
|
As a moderator
|
||||||
|
I would like to be able to hide posts from the public
|
||||||
|
to enforce our network's code of conduct and/or legal regulations
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" are in the database:
|
||||||
|
| slug | email | password | id | name | role | termsAndConditionsAgreedVersion |
|
||||||
|
| user | user@example.org | abcd | user | User-Chad | user | 0.0.4 |
|
||||||
|
| moderator | moderator@example.org | 1234 | moderator | Mod-Man | moderator | 0.0.4 |
|
||||||
|
Given the following "posts" are in the database:
|
||||||
|
| id | title | deleted | disabled |
|
||||||
|
| p1 | This post should be visible | | |
|
||||||
|
| p2 | This post is disabled | | x |
|
||||||
|
| p3 | This post is deleted | x | |
|
||||||
|
|
||||||
|
Scenario: Disabled posts don't show up on the newsfeed as user
|
||||||
|
When I am logged in as "user"
|
||||||
|
And I navigate to page "/"
|
||||||
|
Then I should see only 1 posts on the newsfeed
|
||||||
|
And the first post on the newsfeed has the title:
|
||||||
|
"""
|
||||||
|
This post should be visible
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Disabled posts show up on the newsfeed as moderator
|
||||||
|
When I am logged in as "moderator"
|
||||||
|
And I navigate to page "/"
|
||||||
|
Then I should see only 2 posts on the newsfeed
|
||||||
|
And the first post on the newsfeed has the title:
|
||||||
|
"""
|
||||||
|
This post is disabled
|
||||||
|
"""
|
||||||
|
|
||||||
|
Scenario: Visiting a disabled post's page should return 404
|
||||||
|
Given I am logged in as "user"
|
||||||
|
Then the page "/post/this-post-is-disabled" returns a 404 error with a message:
|
||||||
|
"""
|
||||||
|
This post could not be found
|
||||||
|
"""
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then("I should see only {int} posts on the newsfeed", posts => {
|
||||||
|
cy.get(".post-teaser")
|
||||||
|
.should("have.length", posts);
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then("the page {string} returns a 404 error with a message:", (route, message) => {
|
||||||
|
cy.request({
|
||||||
|
url: route,
|
||||||
|
failOnStatusCode: false
|
||||||
|
})
|
||||||
|
.its("status")
|
||||||
|
.should("eq", 404);
|
||||||
|
cy.visit(route, {
|
||||||
|
failOnStatusCode: false
|
||||||
|
});
|
||||||
|
cy.get(".error-message").should("contain", message);
|
||||||
|
});
|
||||||
@ -8,51 +8,47 @@ Feature: Report and Moderate
|
|||||||
So I can look into it and decide what to do
|
So I can look into it and decide what to do
|
||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given we have the following user accounts:
|
Given the following "users" are in the database:
|
||||||
| id | name |
|
| slug | email | password | id | name | role | termsAndConditionsAgreedVersion |
|
||||||
| u67 | David Irving |
|
| user | user@example.org | abcd | user | User-Chad | user | 0.0.4 |
|
||||||
| annoying-user | I'm gonna mute Moderators and Admins HA HA HA |
|
| moderator | moderator@example.org | 1234 | moderator | Mod-Man | moderator | 0.0.4 |
|
||||||
|
| annoying | annoying@example.org | 1234 | annoying-user | I'm gonna mute Moderators and Admins HA HA HA | user | 0.0.4 |
|
||||||
Given we have the following posts in our database:
|
And the following "posts" are in the database:
|
||||||
| authorId | id | title | content |
|
| authorId | id | title | content |
|
||||||
| u67 | p1 | The Truth about the Holocaust | It never existed! |
|
| annoying-user | p1 | The Truth about the Holocaust | It never existed! |
|
||||||
| annoying-user | p2 | Fake news | This content is demonstratably infactual in some way |
|
| annoying-user | p2 | Fake news | This content is demonstratably infactual in some way |
|
||||||
|
|
||||||
Scenario Outline: Report a post from various pages
|
Scenario Outline: Report a post from various pages
|
||||||
Given I am logged in with a "user" role
|
When I am logged in as "user"
|
||||||
When I see David Irving's post on the <Page>
|
And I navigate to page "<Page>"
|
||||||
And I click on "Report Post" from the content menu of the post
|
And I click on "Report Post" from the content menu of the post
|
||||||
And I confirm the reporting dialog because it is a criminal act under German law:
|
And I confirm the reporting dialog because it is a criminal act under German law:
|
||||||
"""
|
"""
|
||||||
Do you really want to report the contribution "The Truth about the Holocaust"?
|
Do you really want to report the contribution "The Truth about the Holocaust"?
|
||||||
"""
|
"""
|
||||||
Then I see a success message:
|
Then I see a toaster with "Thanks for reporting!"
|
||||||
"""
|
|
||||||
Thanks for reporting!
|
|
||||||
"""
|
|
||||||
Examples:
|
Examples:
|
||||||
| Page |
|
| Page |
|
||||||
| landing page |
|
| / |
|
||||||
| post page |
|
| /post/p1 |
|
||||||
|
|
||||||
Scenario: Report user
|
Scenario: Report user
|
||||||
Given I am logged in with a "user" role
|
Given I am logged in as "user"
|
||||||
And I see David Irving's post on the post page
|
And I navigate to page "/post/the-truth-about-the-holocaust"
|
||||||
When I click on the author
|
When I click on the author
|
||||||
And I click on "Report User" from the content menu in the user info box
|
And I click on "Report User" from the content menu in the user info box
|
||||||
And I confirm the reporting dialog because he is a holocaust denier:
|
And I confirm the reporting dialog because he is a holocaust denier:
|
||||||
"""
|
"""
|
||||||
Do you really want to report the user "David Irving"?
|
Do you really want to report the user "I'm gonna mute Moderators and …"?
|
||||||
"""
|
|
||||||
Then I see a success message:
|
|
||||||
"""
|
|
||||||
Thanks for reporting!
|
|
||||||
"""
|
"""
|
||||||
|
Then I see a toaster with "Thanks for reporting!"
|
||||||
|
|
||||||
Scenario: Review reported content
|
Scenario: Review reported content
|
||||||
Given somebody reported the following posts:
|
Given somebody reported the following posts:
|
||||||
| submitterEmail | resourceId | reasonCategory | reasonDescription |
|
| submitterEmail | resourceId | reasonCategory | reasonDescription |
|
||||||
| p1.submitter@example.org | p1 | discrimination_etc | Offensive content |
|
| p1.submitter@example.org | p1 | discrimination_etc | Offensive content |
|
||||||
And I am logged in with a "moderator" role
|
And I am logged in as "moderator"
|
||||||
|
And I navigate to page "/"
|
||||||
When I click on the avatar menu in the top right corner
|
When I click on the avatar menu in the top right corner
|
||||||
And I click on "Moderation"
|
And I click on "Moderation"
|
||||||
Then I see all the reported posts including the one from above
|
Then I see all the reported posts including the one from above
|
||||||
@ -62,7 +58,8 @@ Feature: Report and Moderate
|
|||||||
Given somebody reported the following posts:
|
Given somebody reported the following posts:
|
||||||
| submitterEmail | resourceId | reasonCategory | reasonDescription |
|
| submitterEmail | resourceId | reasonCategory | reasonDescription |
|
||||||
| p2.submitter@example.org | p2 | other | Offensive content |
|
| p2.submitter@example.org | p2 | other | Offensive content |
|
||||||
And I am logged in with a "moderator" role
|
And I am logged in as "moderator"
|
||||||
|
And I navigate to page "/"
|
||||||
And there is an annoying user who has muted me
|
And there is an annoying user who has muted me
|
||||||
When I click on the avatar menu in the top right corner
|
When I click on the avatar menu in the top right corner
|
||||||
And I click on "Moderation"
|
And I click on "Moderation"
|
||||||
@ -70,6 +67,7 @@ Feature: Report and Moderate
|
|||||||
And I can visit the post page
|
And I can visit the post page
|
||||||
|
|
||||||
Scenario: Normal user can't see the moderation page
|
Scenario: Normal user can't see the moderation page
|
||||||
Given I am logged in with a "user" role
|
Given I am logged in as "user"
|
||||||
|
And I navigate to page "/"
|
||||||
When I click on the avatar menu in the top right corner
|
When I click on the avatar menu in the top right corner
|
||||||
Then I can't see the moderation menu item
|
Then I can't see the moderation menu item
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then(`I can't see the moderation menu item`, () => {
|
||||||
|
cy.get('.avatar-menu-popover')
|
||||||
|
.find('a[href="/settings"]', 'Settings')
|
||||||
|
.should('exist') // OK, the dropdown is actually open
|
||||||
|
|
||||||
|
cy.get('.avatar-menu-popover')
|
||||||
|
.find('a[href="/moderation"]', 'Moderation')
|
||||||
|
.should('not.exist')
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then('I can visit the post page', () => {
|
||||||
|
cy.contains('Fake news').click()
|
||||||
|
cy.location('pathname').should('contain', '/post')
|
||||||
|
.get('.base-card .title').should('contain', 'Fake news')
|
||||||
|
})
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When('I click on "Report Post" from the content menu of the post', () => {
|
||||||
|
cy.contains('.base-card', 'The Truth about the Holocaust')
|
||||||
|
.find('.content-menu .base-button')
|
||||||
|
.click({force: true})
|
||||||
|
|
||||||
|
cy.get('.popover .ds-menu-item-link')
|
||||||
|
.contains('Report Post')
|
||||||
|
.click()
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When('I click on the author', () => {
|
||||||
|
cy.get('.user-teaser')
|
||||||
|
.click()
|
||||||
|
.url().should('include', '/profile/')
|
||||||
|
})
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("I click on the avatar menu in the top right corner", () => {
|
||||||
|
cy.get(".avatar-menu").click();
|
||||||
|
});
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When(/^I confirm the reporting dialog .*:$/, message => {
|
||||||
|
cy.contains(message) // wait for element to become visible
|
||||||
|
cy.get('.ds-modal')
|
||||||
|
.within(() => {
|
||||||
|
cy.get('.ds-radio-option-label')
|
||||||
|
.first()
|
||||||
|
.click({
|
||||||
|
force: true
|
||||||
|
})
|
||||||
|
cy.get('button')
|
||||||
|
.contains('Report')
|
||||||
|
.click()
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then('I see all the reported posts including from the user who muted me', () => {
|
||||||
|
cy.get('table tbody').within(() => {
|
||||||
|
cy.contains('tr', 'Fake news')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then('I see all the reported posts including the one from above', () => {
|
||||||
|
cy.get('table tbody').within(() => {
|
||||||
|
cy.contains('tr', 'The Truth about the Holocaust')
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { Then } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
Then('each list item links to the post page', () => {
|
||||||
|
cy.contains('The Truth about the Holocaust').click();
|
||||||
|
cy.location('pathname').should('contain', '/post')
|
||||||
|
})
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
import { Given } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
import { gql } from '../../../backend/src/helpers/jest'
|
||||||
|
|
||||||
|
Given('somebody reported the following posts:', table => {
|
||||||
|
table.hashes().forEach(({ submitterEmail, resourceId, reasonCategory, reasonDescription }) => {
|
||||||
|
const submitter = {
|
||||||
|
email: submitterEmail,
|
||||||
|
password: '1234'
|
||||||
|
}
|
||||||
|
cy.factory()
|
||||||
|
.build('user', {}, submitter)
|
||||||
|
.authenticateAs(submitter)
|
||||||
|
.mutate(gql`mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) {
|
||||||
|
fileReport(resourceId: $resourceId, reasonCategory: $reasonCategory, reasonDescription: $reasonDescription) {
|
||||||
|
reportId
|
||||||
|
}
|
||||||
|
}`, {
|
||||||
|
resourceId,
|
||||||
|
reasonCategory,
|
||||||
|
reasonDescription
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
Given("there is an annoying user who has muted me", () => {
|
||||||
|
cy.neode()
|
||||||
|
.first("User", {
|
||||||
|
role: 'moderator'
|
||||||
|
})
|
||||||
|
.then(mutedUser => {
|
||||||
|
cy.neode()
|
||||||
|
.first("User", {
|
||||||
|
id: 'user'
|
||||||
|
})
|
||||||
|
.relateTo(mutedUser, "muted");
|
||||||
|
});
|
||||||
|
});
|
||||||
29
cypress/integration/Notification.Mention.feature
Normal file
29
cypress/integration/Notification.Mention.feature
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Feature: Notification for a mention
|
||||||
|
As a user
|
||||||
|
I want to be notified if somebody mentions me in a post or comment
|
||||||
|
In order join conversations about or related to me
|
||||||
|
|
||||||
|
Background:
|
||||||
|
Given the following "users" are in the database:
|
||||||
|
| slug | email | password | id | name | termsAndConditionsAgreedVersion |
|
||||||
|
| wolle-aus-hamburg | wolle@example.org | 1234 | wolle | Wolle aus Hamburg | 0.0.4 |
|
||||||
|
| matt-rider | matt@example.org | 4321 | matt | Matt Rider | 0.0.4 |
|
||||||
|
|
||||||
|
Scenario: Mention another user, re-login as this user and see notifications
|
||||||
|
Given I am logged in as "wolle-aus-hamburg"
|
||||||
|
And I navigate to page "/"
|
||||||
|
And I navigate to page "/post/create"
|
||||||
|
And I start to write a new post with the title "Hey Matt" beginning with:
|
||||||
|
"""
|
||||||
|
Big shout to our fellow contributor
|
||||||
|
"""
|
||||||
|
And mention "@matt-rider" in the text
|
||||||
|
And I click on "save button"
|
||||||
|
And I am logged in as "matt-rider"
|
||||||
|
And I navigate to page "/"
|
||||||
|
And see 1 unread notifications in the top menu
|
||||||
|
And open the notification menu and click on the first item
|
||||||
|
And I wait for 750 milliseconds
|
||||||
|
Then I am on page "/post/.*/hey-matt"
|
||||||
|
And the unread counter is removed
|
||||||
|
And the notification menu button links to the all notifications page
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("I start to write a new post with the title {string} beginning with:", (title, intro) => {
|
||||||
|
cy.get('input[name="title"]')
|
||||||
|
.type(title);
|
||||||
|
cy.get(".ProseMirror")
|
||||||
|
.type(intro);
|
||||||
|
});
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("mention {string} in the text", mention => {
|
||||||
|
cy.get(".ProseMirror")
|
||||||
|
.type(" @");
|
||||||
|
cy.get(".suggestion-list__item")
|
||||||
|
.contains(mention)
|
||||||
|
.click();
|
||||||
|
});
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { When } from "cypress-cucumber-preprocessor/steps";
|
||||||
|
|
||||||
|
When("open the notification menu and click on the first item", () => {
|
||||||
|
cy.get(".notifications-menu")
|
||||||
|
.invoke('show')
|
||||||
|
.click(); // "invoke('show')" because of the delay for show the menu
|
||||||
|
cy.get(".notification .link")
|
||||||
|
.first()
|
||||||
|
.click({force: true});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user