Compare commits

...

328 Commits

Author SHA1 Message Date
0e4e72429d
refactor(webapp): vue 2.7.16 (#9160) 2026-02-04 10:32:18 +00:00
a78c25a258
refactor(backend): test roles (#9157) 2026-02-03 16:00:37 +00:00
753a300c3f
refactor(backend): middleware before/after (#9128) 2026-02-03 14:20:19 +01:00
dependabot[bot]
b28cdada6d
build(deps): bump node from 25.4.0-alpine to 25.5.0-alpine in /webapp (#9147)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 19:36:31 +00:00
dependabot[bot]
8a04b09fd7
build(deps): bump node from 25.4.0-alpine to 25.5.0-alpine in /backend (#9148)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 19:10:07 +00:00
dependabot[bot]
b26a06f0ef
build(deps): bump actions/cache from 5.0.2 to 5.0.3 (#9149)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 18:50:29 +00:00
dependabot[bot]
e5231acd4f
build(deps): bump docker/login-action from 3.6.0 to 3.7.0 (#9150)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 18:24:21 +00:00
dependabot[bot]
8e8bab6f9d
build(deps): bump @aws-sdk/client-s3 and @aws-sdk/lib-storage in /backend (#9151)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 18:04:36 +00:00
dependabot[bot]
71228260e5
build(deps-dev): bump @types/node from 25.0.10 to 25.1.0 in /backend (#9152)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-31 17:41:59 +00:00
d96cb32f11
refactor(backend): properly model group-membership (#9124) 2026-01-30 04:56:03 +01:00
bea7c275e8
fix(webapp): allow internal path for custom button (#9129) 2026-01-29 18:51:53 +01:00
07ff0a6b5e
feat(backend): db script disable notifications (#9131) 2026-01-28 22:14:35 +01:00
6fc3c03860 feat(backend): group pins (#9034)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2026-01-28 16:53:29 +01:00
524c4caf5e
refactor(backend): lint graphql (#8473) 2026-01-28 14:42:53 +01:00
8136ec1aba
fix(backend): fix bug in notifications settings for currentUser (#9130) 2026-01-28 00:36:51 +01:00
0ee476cfff
fix(backend): fix email url encoding (#9127) 2026-01-27 22:39:40 +00:00
Wolfgang Huß
b39d1c737b
refactor(other): consolidate Node.js versions and fix e2e workflow (#9126)
Co-authored-by: mahula <lenzmath@posteo.de>
2026-01-27 18:23:26 +01:00
dependabot[bot]
dcff378727
build(deps): bump cheerio from 1.1.2 to 1.2.0 in /backend (#9141)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 17:12:12 +00:00
dependabot[bot]
6c34da94f4
build(deps-dev): bump @types/node from 25.0.9 to 25.0.10 in /backend (#9142)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 15:26:12 +00:00
dependabot[bot]
f5f6ceb2c5
build(deps): bump @aws-sdk/client-s3 and @aws-sdk/lib-storage in /backend (#9144)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 15:04:44 +00:00
dependabot[bot]
e109ac29b7
build(deps): bump peter-evans/repository-dispatch from 09094272a794c6105029af051e3831908c649b6c to cf70392543065ca62813db6712a06df1c4f4ae9f (#9145)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 14:17:06 +00:00
dependabot[bot]
05aeb1c20d
build(deps): bump the metascraper group in /backend with 12 updates (#9136)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-25 14:47:47 +01:00
dependabot[bot]
003ec2bda0
build(deps-dev): bump sass-embedded from 1.97.2 to 1.97.3 (#9135)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 22:24:46 +00:00
dependabot[bot]
208a6dca01
build(deps): bump preview-email from 3.1.0 to 3.1.1 in /backend (#9138)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 20:30:49 +00:00
af497deb77
fix(webapp): allow running frontend tests locally (#9125) 2026-01-24 20:09:36 +00:00
dependabot[bot]
ba481547f1
build(deps): bump node from 25.3.0-alpine to 25.4.0-alpine in /webapp (#9133)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 19:41:50 +00:00
dependabot[bot]
9f4c105335
build(deps-dev): bump @cucumber/cucumber from 12.5.0 to 12.6.0 in the cypress group (#9134)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 18:38:52 +00:00
dependabot[bot]
8012d56dc8
build(deps): bump lodash from 4.17.21 to 4.17.23 in /backend (#9140)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 15:02:14 +00:00
dependabot[bot]
9d994a7554
build(deps-dev): bump prettier from 3.8.0 to 3.8.1 in /webapp (#9139)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 14:41:41 +00:00
dependabot[bot]
322d2aeb97
build(deps-dev): bump prettier from 3.8.0 to 3.8.1 in /backend (#9143)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 14:15:01 +00:00
dependabot[bot]
afee1033af
build(deps): bump actions/checkout from 6.0.1 to 6.0.2 (#9146)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 13:48:54 +00:00
dependabot[bot]
d358fdf6b4
build(deps): bump node from 25.3.0-alpine to 25.4.0-alpine in /backend (#9132)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-24 11:55:14 +00:00
150b318aab
feat(backend): admin creation command for production (#9057)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2026-01-19 18:36:20 +00:00
f0f9b7faec
fix(backend): fix permissions for GroupInviteCodes (#9121)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2026-01-19 18:14:17 +00:00
b22974031c
fix(backend): fix group-myRole field query (#9102)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2026-01-19 17:48:25 +00:00
mahula
0ca45dd06e
refactor(e2e): optimize step definitions loading with filepart pairing (#9122) 2026-01-19 15:00:40 +00:00
6a42d12fda
fix(webapp): fix cta-join-group, can crash when group is not defined (#9103) 2026-01-19 12:05:09 +00:00
b7604e9af5
fix(backend): fix active categories when inproperly configured (#9123)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2026-01-19 11:29:26 +00:00
3d00ae4e25
fix(webapp): fix local webapp tests (#9104)
Co-authored-by: mahula <lenzmath@posteo.de>
2026-01-18 11:55:40 +00:00
dependabot[bot]
fa71b0e189
build(deps-dev): bump the cypress group across 1 directory with 3 updates (#9058)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-18 12:29:03 +01:00
dependabot[bot]
855a049f90
build(deps): bump node from 25.2.1-alpine to 25.3.0-alpine in /webapp (#9105)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-18 00:12:19 +00:00
dependabot[bot]
679e4876bc
build(deps): bump actions/setup-node from 6.1.0 to 6.2.0 (#9107)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 23:45:25 +00:00
dependabot[bot]
e84a81bd2f
build(deps): bump node from 25.2.1-alpine to 25.3.0-alpine in /backend (#9106)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 23:25:20 +00:00
dependabot[bot]
34e547553e
build(deps): bump actions/cache from 5.0.1 to 5.0.2 (#9108)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-18 00:04:22 +01:00
dependabot[bot]
5aa298b3a2
build(deps-dev): bump eslint-plugin-n from 17.23.1 to 17.23.2 in /backend (#9110)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 22:29:23 +00:00
dependabot[bot]
626372a741
build(deps-dev): bump @types/lodash from 4.17.21 to 4.17.23 in /backend (#9111)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 22:04:58 +00:00
dependabot[bot]
a0ac5157a1
build(deps): bump @aws-sdk/lib-storage from 3.958.0 to 3.967.0 in /backend (#9113)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 21:43:30 +00:00
dependabot[bot]
083d81be89
build(deps-dev): bump eslint-plugin-prettier from 5.5.4 to 5.5.5 in /backend (#9114)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 21:22:21 +00:00
dependabot[bot]
721fd75288
build(deps-dev): bump @eslint-community/eslint-plugin-eslint-comments from 4.5.0 to 4.6.0 in /backend (#9120)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 20:57:08 +00:00
dependabot[bot]
7e6d79f1dc
build(deps-dev): bump prettier from 3.7.4 to 3.8.0 in /webapp (#9117)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 20:35:57 +00:00
dependabot[bot]
bc7e750e83
build(deps): bump ioredis from 5.9.1 to 5.9.2 in /backend (#9119)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 20:07:42 +00:00
dependabot[bot]
fbe98aa2b4
build(deps): bump @aws-sdk/client-s3 from 3.967.0 to 3.971.0 in /backend (#9118)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 19:46:46 +00:00
dependabot[bot]
7ae516cf85
build(deps-dev): bump eslint-plugin-prettier from 5.5.4 to 5.5.5 in /webapp (#9115)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 19:22:19 +00:00
dependabot[bot]
02bf7f0ab8
build(deps-dev): bump prettier from 3.7.4 to 3.8.0 in /backend (#9116)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 19:54:52 +01:00
dependabot[bot]
cc8ab95eaf
build(deps-dev): bump @types/node from 25.0.7 to 25.0.9 in /backend (#9112)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-17 17:59:37 +01:00
dependabot[bot]
017bfbc820
build(deps): bump the metascraper group in /backend with 12 updates (#9063)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 10:59:59 +00:00
dependabot[bot]
25eeb8d485
build(deps-dev): bump eslint-plugin-jest from 29.12.0 to 29.12.1 in /backend (#9090)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 10:32:48 +01:00
dependabot[bot]
eca7f5096e
build(deps): bump ioredis from 5.8.2 to 5.9.1 in /backend (#9095)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-14 09:02:33 +00:00
dependabot[bot]
9f581f4773
build(deps): bump @aws-sdk/client-s3 from 3.958.0 to 3.966.0 in /backend (#9100)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:43:05 +00:00
dependabot[bot]
b767e02263
build(deps): bump @aws-sdk/lib-storage from 3.933.0 to 3.958.0 in /backend (#9093)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 11:20:48 +01:00
dependabot[bot]
b01d5e5a27
build(deps-dev): bump @types/node from 25.0.3 to 25.0.5 in /backend (#9096)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 10:54:01 +01:00
dependabot[bot]
06a79225f3
build(deps): bump peter-evans/repository-dispatch from 46fabd2783425293d3f24bc1080da28d046e2dd3 to 09094272a794c6105029af051e3831908c649b6c (#9089)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-13 09:34:38 +01:00
dependabot[bot]
eaa9b34d58
build(deps): bump vue-advanced-chat from 2.0.11 to 2.1.2 in /webapp (#9084)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 15:54:02 +00:00
dependabot[bot]
c4fcd558e3
build(deps): bump nginx from 1.29.3-alpine to 1.29.4-alpine in /webapp (#9070)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 15:09:57 +00:00
dependabot[bot]
0fef81464f
build(deps): bump @aws-sdk/client-s3 from 3.933.0 to 3.958.0 in /backend (#9086)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 12:52:30 +00:00
dependabot[bot]
9c3d3e2fcd
build(deps-dev): bump @types/node from 24.10.1 to 25.0.3 in /backend (#9078)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 10:40:44 +00:00
dependabot[bot]
cbb57622f7
build(deps-dev): bump eslint-plugin-jest from 29.1.0 to 29.11.0 in /backend (#9087)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-01-01 11:19:47 +01:00
dependabot[bot]
861275aeda
build(deps): bump express from 5.1.0 to 5.2.1 in /backend (#9065)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 12:08:44 +01:00
dependabot[bot]
c0c396653f
build(deps-dev): bump @types/lodash from 4.17.20 to 4.17.21 in /backend (#9051)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 10:42:27 +00:00
dependabot[bot]
5642e0db2c
build(deps-dev): bump ts-jest from 29.4.5 to 29.4.6 in /backend (#9069)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 10:20:32 +00:00
dependabot[bot]
49f7118468
build(deps): bump validator from 13.15.23 to 13.15.26 in /webapp (#9083)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 09:37:50 +00:00
dependabot[bot]
9a0c97e6ce
build(deps): bump validator from 13.15.23 to 13.15.26 in /backend (#9080)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 09:10:01 +00:00
dependabot[bot]
1dd7fc3d75
build(deps): bump nodemailer from 7.0.10 to 7.0.12 in /backend (#9088)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 08:48:33 +00:00
dependabot[bot]
0e2d90c634
build(deps): bump actions/upload-artifact from 5.0.0 to 6.0.0 (#9072)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-27 09:26:57 +01:00
dependabot[bot]
56338422a2
build(deps): bump peter-evans/repository-dispatch from d2c43ab06ec1cddd2c2a0aae659681b8465ce87a to 46fabd2783425293d3f24bc1080da28d046e2dd3 (#9060)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 15:30:08 +00:00
dependabot[bot]
817ac7226e
build(deps-dev): bump prettier from 3.6.2 to 3.7.4 in /webapp (#9059)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 14:05:00 +00:00
dependabot[bot]
eb81c0b7e4
build(deps): bump docker/metadata-action from 5.9.0 to 5.10.0 (#9049)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 13:38:02 +00:00
dependabot[bot]
34aa894068
build(deps): bump actions/setup-node from 6.0.0 to 6.1.0 (#9061)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 14:17:19 +01:00
dependabot[bot]
d751e7090f
build(deps): bump gaurav-nelson/github-action-markdown-link-check from 1.0.16 to 1.0.17 (#8329)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 10:15:30 +00:00
dependabot[bot]
a9949e1147
build(deps): bump actions/cache from 4.3.0 to 5.0.1 (#9071)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-13 09:48:11 +00:00
dependabot[bot]
c78f8deee9
build(deps): bump actions/checkout from 5.0.0 to 6.0.1 (#9062)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 10:04:02 +00:00
dependabot[bot]
2cabe0f4d2
build(deps-dev): bump prettier from 3.6.2 to 3.7.4 in /backend (#9067)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 10:09:34 +01:00
dependabot[bot]
0ac7bf908c
build(deps): bump mime-types from 3.0.1 to 3.0.2 in /backend (#9044)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-22 14:54:04 +01:00
dependabot[bot]
0368676b26
build(deps): bump cross-env from 10.0.0 to 10.1.0 in /backend (#8943)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 16:52:07 +00:00
dependabot[bot]
cd7931b77b
build(deps): bump validator from 13.15.20 to 13.15.23 in /webapp (#9029)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 16:09:36 +00:00
dependabot[bot]
801131e351
build(deps-dev): bump nodemon from 3.1.10 to 3.1.11 in /backend (#9028)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 15:20:23 +01:00
dependabot[bot]
30d30a2f2d
build(deps): bump node from 25.1.0-alpine to 25.2.0-alpine in /backend (#9024)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 14:57:02 +01:00
dependabot[bot]
9dfd5e31ef
build(deps): bump node from 25.1.0-alpine to 25.2.0-alpine in /webapp (#9023)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-20 11:13:03 +00:00
dependabot[bot]
c86d816e56
build(deps): bump docker/metadata-action from 5.8.0 to 5.9.0 (#9014)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-19 17:51:36 +00:00
dependabot[bot]
de89810eae
build(deps-dev): bump cypress from 15.5.0 to 15.6.0 in the cypress group (#9016)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-19 17:13:53 +00:00
dependabot[bot]
9df61a752a
build(deps): bump bcryptjs from 3.0.2 to 3.0.3 in /backend (#9019)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-19 17:04:20 +01:00
dependabot[bot]
2410fa8527
build(deps): bump @aws-sdk/lib-storage from 3.917.0 to 3.922.0 in /backend (#9022)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 18:38:28 +00:00
dependabot[bot]
b5b7b5d78c
build(deps): bump peter-evans/repository-dispatch from 2c856c63feddee6147cab2f38801935b6a59a765 to d2c43ab06ec1cddd2c2a0aae659681b8465ce87a (#9025)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 18:16:35 +00:00
dependabot[bot]
00d8fd960d
build(deps): bump amannn/action-semantic-pull-request from e49f57ce06c1747542fce2243c7a98682384bc0e to 069817c298f23fab00a8f29a2e556a5eac0f6390 (#9026)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 18:56:58 +01:00
dependabot[bot]
caf95664b8
build(deps-dev): bump eslint-plugin-jest from 29.0.1 to 29.1.0 in /backend (#9027)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 03:50:46 +00:00
dependabot[bot]
66f35ca51d
build(deps): bump @aws-sdk/client-s3 from 3.922.0 to 3.932.0 in /backend (#9030)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 03:52:03 +01:00
dependabot[bot]
c6878f19f9
build(deps-dev): bump @types/node from 24.9.2 to 24.10.1 in /backend (#9031)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 01:57:53 +00:00
dependabot[bot]
421bfe6755
build(deps): bump validator from 13.15.20 to 13.15.23 in /backend (#9033)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 02:36:41 +01:00
592a8af42c
chore(release): v3.13.1 (#9003) 2025-11-01 15:37:40 +00:00
dependabot[bot]
a48510f349
build(deps): bump nginx from 1.29.2-alpine to 1.29.3-alpine in /webapp (#9005)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 15:11:44 +00:00
dependabot[bot]
a03e5d888b
build(deps): bump minimatch from 10.0.3 to 10.1.1 in /backend (#9009)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 14:31:33 +00:00
dependabot[bot]
7f7c56cec2
build(deps-dev): bump @types/node from 24.9.1 to 24.9.2 in /backend (#9010)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 14:05:19 +00:00
dependabot[bot]
77d8a5092b
build(deps): bump @aws-sdk/client-s3 from 3.917.0 to 3.922.0 in /backend (#9012)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 13:44:25 +00:00
60bda5a949
fix(webapp): fix map (#9004) 2025-11-01 13:09:44 +00:00
dependabot[bot]
ff2b6465db
build(deps): bump validator from 13.15.15 to 13.15.20 in /backend (#9007)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 09:25:26 +00:00
dependabot[bot]
72b4af395f
build(deps): bump validator from 13.15.15 to 13.15.20 in /webapp (#9011)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-01 10:04:11 +01:00
2130aa0d68
fix(webapp): always link compiled styleguide (#8998) 2025-10-31 16:36:51 +01:00
dependabot[bot]
1c7f2f27b5
build(deps-dev): bump the babel group with 2 updates (#8982)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-31 00:54:02 +00:00
dependabot[bot]
e2ef636cf8
build(deps): bump node from 24.10.0-alpine to 25.0.0-alpine in /backend (#8972)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-31 00:24:33 +00:00
3f69b70820
fix(workflow): cleanup when running e2e jobs aswell (#9001) 2025-10-31 00:02:39 +00:00
dependabot[bot]
e6244c848c
build(deps): bump node from 20.12.1-alpine to 25.0.0-alpine in /webapp (#8974)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-31 00:21:54 +01:00
0280ac7201
fix(workflow): clean up space on workflow runner machines (#9000) 2025-10-30 23:53:11 +01:00
09327ddc01
fix(docker): fix on build copy command failing when branding (#8996) 2025-10-30 21:16:14 +00:00
49e6f0b7e8
fix(backend): fix node25 compatibility (#8995) 2025-10-30 20:47:39 +00:00
c9b5c02862
fix(webapp): update nuxt to v2.15.8 (#8997) 2025-10-30 20:34:48 +01:00
52459b23f1
chore(release): v3.13.0 (#8991) 2025-10-28 12:06:32 +01:00
dependabot[bot]
737f548f38
build(deps-dev): bump cypress from 15.4.0 to 15.5.0 in the cypress group (#8983)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-26 00:47:36 +00:00
dependabot[bot]
1a29167f08
build(deps): bump actions/upload-artifact from 4.6.2 to 5.0.0 (#8981)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-26 02:20:47 +02:00
dependabot[bot]
06bd4f1ea4
build(deps): bump the metascraper group in /backend with 12 updates (#8985)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-26 00:59:20 +02:00
dependabot[bot]
bbb2a189db
build(deps): bump @aws-sdk/lib-storage from 3.908.0 to 3.913.0 in /backend (#8986)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-25 19:23:48 +00:00
dependabot[bot]
a5f720dba1
build(deps-dev): bump @types/node from 24.8.1 to 24.9.1 in /backend (#8987)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-25 19:01:48 +00:00
dependabot[bot]
3b93d255a6
build(deps): bump nodemailer from 7.0.9 to 7.0.10 in /backend (#8988)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-25 18:41:14 +00:00
dependabot[bot]
5b84b93a16
build(deps): bump @aws-sdk/client-s3 from 3.913.0 to 3.917.0 in /backend (#8989)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-25 18:19:49 +00:00
dependabot[bot]
1622c31010
build(deps): bump ioredis from 5.8.1 to 5.8.2 in /backend (#8990)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-25 19:59:53 +02:00
dependabot[bot]
09536ed0e8
build(deps): bump actions/setup-node from 5.0.0 to 6.0.0 (#8973)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-19 13:37:41 +02:00
dependabot[bot]
005e2569a1
build(deps-dev): bump @types/node from 24.7.2 to 24.8.1 in /backend (#8976)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-18 15:28:48 +02:00
dependabot[bot]
ed328e70d8
build(deps): bump @aws-sdk/client-s3 from 3.908.0 to 3.913.0 in /backend (#8978)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-18 14:56:48 +02:00
dependabot[bot]
d96f145e2b
build(deps): bump @aws-sdk/lib-storage from 3.896.0 to 3.905.0 in /backend (#8970)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 16:40:02 +00:00
dependabot[bot]
f3d36bb779
build(deps): bump ioredis from 5.8.0 to 5.8.1 in /backend (#8969)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 16:19:48 +00:00
dependabot[bot]
5e1d9e280a
build(deps): bump @aws-sdk/client-s3 from 3.901.0 to 3.908.0 in /backend (#8968)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 15:37:22 +02:00
dependabot[bot]
5bb8508df9
build(deps-dev): bump @types/node from 24.6.2 to 24.7.1 in /backend (#8967)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-12 12:36:59 +02:00
dependabot[bot]
1b0ef1f81b
build(deps): bump nodemailer from 7.0.6 to 7.0.9 in /backend (#8964)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 23:02:08 +00:00
dependabot[bot]
0042914231
build(deps-dev): bump ts-jest from 29.4.4 to 29.4.5 in /backend (#8963)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 22:41:30 +00:00
dependabot[bot]
3f216d84d7
build(deps-dev): bump eslint-plugin-jsonc from 2.20.1 to 2.21.0 in /backend (#8962)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 22:20:44 +00:00
dependabot[bot]
88ec6fdef0
build(deps-dev): bump the cypress group with 2 updates (#8961)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 15:20:31 +00:00
dependabot[bot]
d22479cfa2
build(deps): bump nginx from 1.29.1-alpine to 1.29.2-alpine in /webapp (#8960)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 12:45:14 +00:00
dependabot[bot]
ad5108996f
build(deps): bump node from 24.9.0-alpine to 24.10.0-alpine in /backend (#8959)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-11 02:19:54 +00:00
Wolfgang Huß
558e964c83
feat(webapp): add reason and call to action on post view page if commenting is disabled (#8958)
Co-authored-by: Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>
2025-10-10 19:21:07 +02:00
dependabot[bot]
2a7d2f10ed
build(deps-dev): bump dotenv from 17.2.2 to 17.2.3 (#8939)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-07 23:44:45 +00:00
b5895afe3e
refactor(e2e): remove packages not used on root folder (#8954) 2025-10-07 21:16:22 +02:00
1964ff3eb1
refactor(workflow): lint pr title scope e2e (#8955) 2025-10-07 16:40:04 +00:00
9fc2379090
fix(webapp): fix line-height in styleguide when generated (#8952)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-10-07 09:41:09 +00:00
1e19bd1be7
refactor(workflow): pr titles - allow scope styleguide (#8953)
Co-authored-by: mahula <lenzmath@posteo.de>
2025-10-07 08:13:53 +00:00
dependabot[bot]
98af683277
build(deps): bump peter-evans/repository-dispatch from de78ac1a711fc6f29e77338f843065faf5335227 to 66739071c2122a05106fc2a2c306fdaf33bb9cda (#8936)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 19:52:48 +00:00
dependabot[bot]
b40015d408
build(deps): bump @aws-sdk/client-s3 from 3.896.0 to 3.901.0 in /backend (#8942)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 19:19:41 +00:00
dependabot[bot]
1512167197
build(deps): bump cross-env from 10.0.0 to 10.1.0 in /webapp (#8944)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 09:09:36 +00:00
dependabot[bot]
263f35d2e0
build(deps): bump docker/login-action from 3.5.0 to 3.6.0 (#8937)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 10:30:57 +02:00
dependabot[bot]
05ab27e868
build(deps): bump amannn/action-semantic-pull-request from e7d011b07ef37e089bea6539210f6a0d360d8af9 to e49f57ce06c1747542fce2243c7a98682384bc0e (#8938)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 08:01:56 +00:00
dependabot[bot]
f49022e94d
build(deps-dev): bump @testing-library/jest-dom from 6.8.0 to 6.9.1 in /webapp (#8945)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 07:41:47 +00:00
dependabot[bot]
719457b896
build(deps-dev): bump jest from 30.1.3 to 30.2.0 in /backend (#8946)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 09:13:20 +02:00
dependabot[bot]
6d4fd54c30
build(deps-dev): bump @types/node from 24.5.2 to 24.6.2 in /backend (#8951)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 06:51:42 +00:00
dependabot[bot]
54aeca0375
build(deps): bump tslog from 4.9.3 to 4.10.2 in /backend (#8949)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-04 08:31:00 +02:00
ffffd9e15f
feat(other): deploy styleguide scripts and description (#8935) 2025-10-02 17:57:40 +02:00
b06b29b858
feat(webapp): bind local styleguide & fix maintenance page (#8933) 2025-09-30 14:54:43 +02:00
ace5e9a89e
refactor(other): include styleguide repo including all commits (#8932) 2025-09-29 19:29:41 +02:00
c6fe18f1f7
Merge branch 'master' into include-styleguide-repo 2025-09-28 19:33:28 +02:00
f9af98da97
included whole styleguide repository including all commits instead of a submodule 2025-09-28 19:28:22 +02:00
d9ed8a42b5
remove styleguide
remove .gitmodule
2025-09-28 18:58:59 +02:00
52da131ee4
Merge pull request #2 from Ocelot-Social-Community/remove-gitattributes
fix(other): remove gitattributes
2025-09-28 18:56:12 +02:00
6d9529a021
remove git attributes 2025-09-28 18:51:53 +02:00
32eca68520
Merge pull request #1 from Ocelot-Social-Community/node24-support
Node24 support
2025-09-27 14:46:06 +02:00
e4717e0d89
fix(backend): fix potential leak in updateOnlineStatus (#8923)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-09-27 14:37:12 +02:00
33ca59343a
docs(docu): describe current and desired architecture of services (#8850)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-09-27 12:09:47 +00:00
dependabot[bot]
1044231e4e
build(deps): bump node from 24.8.0-alpine to 24.9.0-alpine in /backend (#8926)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-27 10:10:03 +00:00
dependabot[bot]
2bf39f5ad0
build(deps): bump actions/cache from 4.2.4 to 4.3.0 (#8927)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-27 09:52:41 +00:00
dependabot[bot]
68030dc5ec
build(deps): bump @aws-sdk/lib-storage from 3.888.0 to 3.896.0 in /backend (#8929)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-27 11:30:46 +02:00
dependabot[bot]
ced52e30db
build(deps): bump ioredis from 5.7.0 to 5.8.0 in /backend (#8931)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-27 10:46:45 +02:00
dependabot[bot]
35c3dd3bbc
build(deps-dev): bump the cypress group across 1 directory with 2 updates (#8925)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 20:23:30 +00:00
dependabot[bot]
768d80f2a1
build(deps): bump peter-evans/repository-dispatch from 7279ea08e172078316f128ed1118df40d2904f0f to de78ac1a711fc6f29e77338f843065faf5335227 (#8909)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 19:01:29 +00:00
dependabot[bot]
f7dc901c2a
build(deps): bump metascraper from 5.49.2 to 5.49.4 in /backend in the metascraper group (#8911)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 15:18:44 +00:00
dependabot[bot]
fadc37a49c
build(deps-dev): bump ts-jest from 29.4.1 to 29.4.4 in /backend (#8912)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 17:00:40 +02:00
21c253b690
remove docs folder 2025-09-26 15:36:41 +02:00
08c8cfe42c
fix build 2025-09-26 15:31:56 +02:00
59dd435138
update node-sass 2025-09-26 15:01:10 +02:00
dependabot[bot]
158e1ee4e0
build(deps-dev): bump @types/node from 24.4.0 to 24.5.2 in /backend (#8913)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 12:54:08 +02:00
dependabot[bot]
2350e594d4
build(deps): bump graphql-middleware from 4.0.2 to 4.0.3 in /backend (#8915)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 20:38:57 +00:00
30560bff69
fix(webapp): fix user avatar & post image urls (#8921)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-09-25 22:19:27 +02:00
dependabot[bot]
4fc71fc495
build(deps): bump @sentry/node from 5.15.4 to 5.30.0 in /backend (#8916)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 11:12:44 +00:00
dependabot[bot]
3ca00c83c0
build(deps-dev): bump eslint-plugin-n from 17.21.3 to 17.23.1 in /backend (#8918)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 10:52:37 +00:00
dependabot[bot]
9f91ff1124
build(deps): bump @aws-sdk/client-s3 from 3.888.0 to 3.893.0 in /backend (#8919)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 10:00:42 +00:00
Moriz Wahl
a15351aa42
feat(webapp): dynamic static pages (#8920)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-09-25 11:16:07 +02:00
Moriz Wahl
9a4f7326c1
fix(webapp): no distance to me on own profile (#8907)
Co-authored-by: Wolfgang Huß <wolle.huss@pjannto.com>
2025-09-22 23:59:18 +02:00
Wolfgang Huß
2ed92c0a78
feat(webapp): post page group link improvement (#8883) 2025-09-20 10:15:04 +00:00
de65a380ab
fix(backend): fix naming of PRIVATEKEY (#8905) 2025-09-20 11:47:29 +02:00
45d2283138
refactor(backend): externalize all remaining queries in spec files (#8902)
* refactor: externalize all remaining queries in spec files

* User with different queries due to permissions

* fix notification:groups

* fix hashtagsmiddleware

* fix blockedUsers

* fix softDeleteMiddleware

* fix shouts.spec

* fix userInteractions spec

* fix mutedUsers spec

* seocialMedia spec

* fix notificationMiddleware.spec

* fix user.spce & fix undefined activeCategories

* fix notifications.spec

* fix userInteractions.spec

* fix blockedUsers & mutedUsers spec

* remove unused comment

* fix locations spec

* fix orderByMiddleware & spec

* fix lint

* fix shout spec
2025-09-18 17:43:15 +02:00
Robert Schäfer
bdb3c204aa
Merge pull request #194 from Human-Connection/update-version
Update version to 0.5.22
2019-12-09 14:56:04 +01:00
Alina Beck
8be9662987
Update version to 0.5.22 2019-12-09 15:49:33 +03:00
Robert Schäfer
7ef8340500
Merge pull request #190 from Human-Connection/load-svgs-with-babel
Use babel-loader to transpile svgs
2019-12-06 12:33:07 +01:00
Alina Beck
5d6391f505 use babel loader to transpile svgs
IE and Edge had trouble loading the Human Connection network
because object spread operators were left in the minified code
using babel-loader for svgs (as suggested in the vue-svg-loader docs)
hopefully solves this issue
2019-12-05 13:07:45 +03:00
Robert Schäfer
d46fc1570c
Merge pull request #176 from hwilson2563/master
add a xxx-small box to space
2019-10-08 01:48:11 +02:00
Hilary Matusiak
c752e25221 add a xxx-small box to space 2019-10-04 11:46:06 -04:00
Alina Beck
808b3c5a95
Merge pull request #155 from Human-Connection/update_version
Update version to 0.5.21
2019-09-09 18:04:32 +01:00
roschaefer
b05583a8b0 Implement a custom deployment condition via bash 2019-09-09 16:16:29 +02:00
roschaefer
90f747a2a9 Don't deploy if you're not on master
Thank you:
https://github.com/travis-ci/travis-ci/issues/5419#issuecomment-222815942
2019-09-09 15:59:52 +02:00
roschaefer
3d85451aff Update version to 0.5.21 2019-09-09 15:59:52 +02:00
Robert Schäfer
8dafc50286
Merge pull request #154 from Human-Connection/deploy_github_pages_through_travis
Deploy Github pages through Travis CI
2019-09-09 13:02:11 +02:00
roschaefer
0b6360bc0b Deploy Github pages through Travis CI
This solves two problems
* It automatically deploys the documentation
* It deploys the documentation only if the npm package is deployed (ie.
if there is a git tag) and keeps both in sync
2019-09-09 12:59:11 +02:00
Robert Schäfer
2ad4602de3
Merge pull request #153 from Human-Connection/update_all_dependencies
Update all dependencies except `sass-loader`
2019-09-09 12:28:02 +02:00
roschaefer
80eb430460 Update all dependencies except sass-loader
We want to make another release and had a look which dependabot PR was
green. So I just updated the green dependencies manually and had a look
if anything breaks.

Again it would be great to have a build server and a reliable test suite.
2019-09-09 12:22:32 +02:00
Robert Schäfer
aa772ca19f
Merge pull request #149 from Human-Connection/dependabot/npm_and_yarn/sass-loader-8.0.0
Bump sass-loader from 7.2.0 to 8.0.0
2019-09-09 12:20:18 +02:00
Alina Beck
94c0ed83d9 use renamed sass-loader prependData option 2019-09-09 11:16:14 +01:00
Robert Schäfer
ac960d8ecd
Merge pull request #137 from Human-Connection/dependabot/npm_and_yarn/babel-jest-24.9.0
Bump babel-jest from 24.8.0 to 24.9.0
2019-09-09 12:04:12 +02:00
Robert Schäfer
0889e137d9
Merge pull request #144 from Human-Connection/dependabot/npm_and_yarn/vue/cli-plugin-unit-jest-3.11.0
Bump @vue/cli-plugin-unit-jest from 3.10.0 to 3.11.0
2019-09-09 12:03:31 +02:00
Robert Schäfer
66c049544e
Merge pull request #152 from Human-Connection/fix-grid-component
Fix grid component
2019-09-09 11:59:05 +02:00
Alina Beck
dc4ce74b21 add missing )) 2019-09-04 17:45:20 +01:00
dependabot-preview[bot]
e0d3717ca0
Bump sass-loader from 7.2.0 to 8.0.0
Bumps [sass-loader](https://github.com/webpack-contrib/sass-loader) from 7.2.0 to 8.0.0.
- [Release notes](https://github.com/webpack-contrib/sass-loader/releases)
- [Changelog](https://github.com/webpack-contrib/sass-loader/blob/master/CHANGELOG.md)
- [Commits](https://github.com/webpack-contrib/sass-loader/compare/v7.2.0...v8.0.0)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-30 04:20:00 +00:00
Grzegorz Leoniec
73195606fb Bump version 2019-08-25 12:48:11 +02:00
Grzegorz Leoniec
e6fe9ba4b7 Commit phantom changes 2019-08-25 12:47:49 +02:00
Grzegorz Leoniec
3c65a2426c Updated docs 2019-08-25 12:44:49 +02:00
Grzegorz Leoniec
c9d969afc6 Fixed font issues 2019-08-25 12:20:16 +02:00
dependabot-preview[bot]
573890102f
Bump @vue/cli-plugin-unit-jest from 3.10.0 to 3.11.0
Bumps [@vue/cli-plugin-unit-jest](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-unit-jest) from 3.10.0 to 3.11.0.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v3.11.0/packages/@vue/cli-plugin-unit-jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-22 04:23:15 +00:00
Robert Schäfer
64389852c4
Merge pull request #140 from Human-Connection/deploy_github_page
Deploy GitHub page
2019-08-21 12:00:15 +02:00
roschaefer
2536542aa9 Rebuild the docs/
Update the deployed docs at https://styleguide.human-connection.org/ via
Github pages.
2019-08-21 00:32:28 +02:00
roschaefer
bcad113682 Add missing http-server to dev dependencies
We cannot assume it to be installed globally.
2019-08-21 00:16:23 +02:00
dependabot-preview[bot]
208bfc1550
Bump babel-jest from 24.8.0 to 24.9.0
Bumps [babel-jest](https://github.com/facebook/jest/tree/HEAD/packages/babel-jest) from 24.8.0 to 24.9.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v24.9.0/packages/babel-jest)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-19 04:28:01 +00:00
Alina Beck
be4d526505 bump version to 0.5.19 2019-08-15 17:12:48 +01:00
Alina Beck
9e49afaac3
Merge pull request #131 from Human-Connection/add-grid-layout
Add grid component
2019-08-15 17:08:24 +01:00
roschaefer
79cbcf2901 Update to 0.5.18
dependency updates
2019-08-15 17:29:48 +02:00
roschaefer
f83a13c7f2 Update transitive dependencies
Should fix our security vulnerabilities
2019-08-15 17:29:04 +02:00
Robert Schäfer
f87d8dacfd
Merge pull request #122 from Human-Connection/dependabot/npm_and_yarn/remarkable-1.7.4
[Security] Bump remarkable from 1.7.1 to 1.7.4
2019-08-15 17:18:59 +02:00
dependabot-preview[bot]
461a762205
[Security] Bump remarkable from 1.7.1 to 1.7.4
Bumps [remarkable](https://github.com/jonschlinkert/remarkable) from 1.7.1 to 1.7.4. **This update includes security fixes.**
- [Release notes](https://github.com/jonschlinkert/remarkable/releases)
- [Changelog](https://github.com/jonschlinkert/remarkable/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jonschlinkert/remarkable/compare/1.7.1...v1.7.4)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>
2019-08-15 15:11:41 +00:00
Robert Schäfer
9d5cb51ed5
Merge pull request #134 from Human-Connection/massive_update
Update all packages with `ncu -u`
2019-08-15 17:10:10 +02:00
roschaefer
e6352cc6fb Update all packages with ncu -u
And fix another bug: require(...).default returns the string content
of the file.
2019-08-15 16:07:16 +02:00
Robert Schäfer
57fe905a96
Merge pull request #133 from Human-Connection/fix_build_errors
Fix build errors
2019-08-15 15:51:39 +02:00
roschaefer
79a8fc5f5f Fix javascript runtime errors
I could see a warning about duplicate keys on the `Chip` page, also
there seems to be a missing check if a param is null (I used a default).
2019-08-15 15:46:29 +02:00
roschaefer
3d41d811d2 Update the minimum of package to get a build
I was not able to build the styleguide on my machine. This commit allows
me to get at least to http://127.0.0.1:8080/ to see some components.

Here are my specs:
```sh
robert@e480 ~> node --version
v12.7.0
robert@e480 ~> yarn --version
1.17.3
robert@e480 ~> cat /proc/version
Linux version 5.2.6-arch1-1-ARCH (builduser@heftig-3961) (gcc version 9.1.0 (GCC)) #1 SMP PREEMPT Sun Aug 4 14:58:49 UTC 2019
```
2019-08-15 15:43:46 +02:00
Alina Beck
df7b736f5d add validator function for grid gap 2019-08-14 15:29:48 +01:00
Alina Beck
3f4a8a543e pass numbers as numbers 2019-08-14 15:22:08 +01:00
Alina Beck
3fa3d6f5c4 use design tokens for grid gap 2019-08-14 12:28:17 +01:00
Alina Beck
4edb8cd541 add grid item to grid docs 2019-08-13 19:08:27 +01:00
Alina Beck
f51890a54f adjust default values, add docs 2019-08-13 18:58:28 +01:00
Alina Beck
b730b91e2c set placeholder height to 100% 2019-08-13 17:54:17 +01:00
Alina Beck
2ccaeb6166 add grid item component 2019-08-13 17:34:35 +01:00
Alina Beck
3a93d9abb3 add grid component 2019-08-13 16:23:44 +01:00
6f1d94610e
Merge pull request #84 from Human-Connection/2019/kw19/fix_form_user_input_event
2019/kw19/fix_form_user_input_event
2019-05-07 20:04:25 +02:00
d70d331e6f
implemented input-valid ad input events to enable notification of every userinput, field match example, fixed console output handling, added ulfgebhardt to the authors, version bump to 0.5.17 2019-05-07 19:59:08 +02:00
396e08127a
Merge pull request #83 from Human-Connection/2019/kw19/fix_form_user_input_event
2019/kw19/fix_form_user_input_event
2019-05-07 14:31:57 +02:00
059072982f
bump version from 0.5.15 to 0.5.16 2019-05-07 13:36:27 +02:00
8da1d99e33
Merge pull request #82 from Human-Connection/2019/kw19/windows_lineendings
2019/kw19/windows_lineendings
2019-05-07 13:21:07 +02:00
bbbf051026
fixed user input event to be fired correctly, added reset event 2019-05-07 13:20:50 +02:00
5e9e55f00e
gitattributes lf 2019-05-06 16:50:22 +02:00
Grzegorz Leoniec
3f7a00b8b9
Use online instead of is-online on avatar 2019-03-15 17:48:16 +01:00
Grzegorz Leoniec
e76fed663c
Fixed some avatar styling 2019-03-15 13:36:35 +01:00
Grzegorz Leoniec
c8002a2b9a
Fixed avatar styling in conjunction with the size values 2019-03-15 13:29:30 +01:00
Grzegorz Leoniec
b3bde1aa2a
Added fixed size to avatar component 2019-03-15 11:24:37 +01:00
Grzegorz Leoniec
9b8ac8f1b3
Fixed avatar and added online status 2019-03-14 21:03:28 +01:00
Grzegorz Leoniec
baf3134f9d
Push 0.5.15 2019-03-12 15:25:32 +01:00
Grzegorz Leoniec
19a6badd55
Upgrade docs 2019-03-12 15:25:07 +01:00
Grzegorz Leoniec
bc3e3875e8
Update browser list 2019-03-12 15:24:53 +01:00
Grzegorz Leoniec
fe536b626f
0.5.14 2019-03-12 10:47:33 +01:00
Grzegorz Leoniec
4e43be4ff6
Merge branch 'master' of github.com:Human-Connection/Nitro-Styleguide 2019-03-12 10:47:02 +01:00
Grzegorz Leoniec
da22c5869a
Try to fix build issue 2019-03-12 10:46:49 +01:00
Grzegorz Leoniec
e77754cb78
Fixed npm badge 2019-03-11 20:48:12 +01:00
Grzegorz Leoniec
319f6a90f7
Try to fix some issues 0.5.12 2019-03-11 20:44:51 +01:00
Grzegorz Leoniec
562fddb1b4
Fixed docs 2019-03-11 11:21:36 +01:00
Grzegorz Leoniec
e7c25e15ed
Updated docs 2019-03-11 11:13:42 +01:00
Grzegorz Leoniec
ce20d5b58e
Update README.md 2019-03-09 08:59:51 +01:00
Grzegorz Leoniec
de26ce29e6
Added npm badge 2019-03-09 08:57:11 +01:00
Grzegorz Leoniec
fd83399e70
Merge pull request #34 from Human-Connection/remove_portal
Remove vue-portal
2019-03-09 08:48:59 +01:00
Grzegorz Leoniec
a100c09ac5
Updated to 0.5.11 2019-03-09 08:47:20 +01:00
Robert Schäfer
f6fbe058f6 Remove occurences of vue-portal
I don't quite get the reason for having `vue-portal`. Maybe to ensure,
the modal get's rendered only once? If yes, then I would suggest to give
the user the responsibility to render only one modal at a time.
2019-03-09 01:41:33 +01:00
Robert Schäfer
02f34f0fcf Run git clean -dfx
deletes ignored but indexed files
2019-03-09 01:39:26 +01:00
Grzegorz Leoniec
ce69fe5cb7
Style fixes 2019-03-07 11:02:29 +01:00
Grzegorz Leoniec
b941bb24d8
Fixed HC Documentation link to point to nitro 2019-03-07 10:41:30 +01:00
Grzegorz Leoniec
58cb1ee096
Try to fix build 0.5.10 2019-03-07 10:35:59 +01:00
Grzegorz Leoniec
c64d6fbb83
Update to 0.5.9 2019-03-07 10:16:22 +01:00
Grzegorz Leoniec
702cd07171
Remove dist folder 2019-03-07 10:15:45 +01:00
Grzegorz Leoniec
ba27f81977
Fixing build and styleguide CNAME 2019-03-07 10:15:22 +01:00
Grzegorz Leoniec
900dad94ae
Updated docs 2019-03-07 10:15:00 +01:00
Grzegorz Leoniec
a98e22dba1
Bumped version to 0.5.8 2019-03-07 09:46:47 +01:00
Grzegorz Leoniec
e9eaafd067
Extend build by also using yarn build beside build:lib 2019-03-07 09:46:33 +01:00
Grzegorz Leoniec
21dd977bec
Fixed some linting issues 2019-03-07 09:46:08 +01:00
Grzegorz Leoniec
23cbd42d3f
Bumped version to 0.5.7 2019-03-06 20:13:19 +01:00
Grzegorz Leoniec
201de94230
Improved select input 2019-03-06 20:13:01 +01:00
Grzegorz Leoniec
273c0ca1a6
Bump version to 0.5.6 2019-03-06 19:05:37 +01:00
Grzegorz Leoniec
4f87a3feec
Bumped version to 0.5.5 2019-03-04 16:43:58 +01:00
Grzegorz Leoniec
dca1ea6c42
Fixed select behavior 2019-03-04 14:24:27 +01:00
Grzegorz Leoniec
01ca0a6d8b
Fixed search input behavior 2019-03-04 11:25:19 +01:00
Grzegorz Leoniec
4a574ee430
Added vue and vue-portal as peed dependencies 0.5.4 2019-03-01 10:35:05 +01:00
Grzegorz Leoniec
83da96f8c8
Bumped version to 0.5.3 2019-03-01 10:11:41 +01:00
Grzegorz Leoniec
b7371582dc
Fixed issue with select filtering 2019-03-01 10:10:56 +01:00
Grzegorz Leoniec
fbc3ee5658
Added .npmignore 2019-02-26 11:08:28 +01:00
Grzegorz Leoniec
4edea10b18
ignore icons _all icons in npm package 2019-02-26 10:47:44 +01:00
Grzegorz Leoniec
0bbaba189f
Missed to bumb version 2019-02-25 11:32:26 +01:00
Grzegorz Leoniec
a0189658a8
Making the package public 2019-02-25 11:31:40 +01:00
Grzegorz Leoniec
f4f16962d9
Merge branch 'master' of github.com:Human-Connection/Nitro-Styleguide 2019-02-25 11:28:23 +01:00
Grzegorz Leoniec
095b1539ad
Added deyplo config for npm 2019-02-25 11:27:38 +01:00
Grzegorz Leoniec
6a7256f0c1
Merge pull request #14 from Human-Connection/dependabot/npm_and_yarn/vue-and-vue-template-compiler-2.6.7
Bump vue and vue-template-compiler
2019-02-25 10:49:56 +01:00
dependabot[bot]
1a82e6edda
Bump vue and vue-template-compiler
Bumps [vue](https://github.com/vuejs/vue) and [vue-template-compiler](https://github.com/vuejs/vue). These dependencies needed to be updated together.

Updates `vue` from 2.6.6 to 2.6.7
- [Release notes](https://github.com/vuejs/vue/releases)
- [Commits](https://github.com/vuejs/vue/compare/v2.6.6...v2.6.7)

Updates `vue-template-compiler` from 2.6.6 to 2.6.7
- [Release notes](https://github.com/vuejs/vue/releases)
- [Commits](https://github.com/vuejs/vue/compare/v2.6.6...v2.6.7)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-25 09:46:00 +00:00
Grzegorz Leoniec
ffe1ceabf1
Fixing build 2019-02-25 10:43:47 +01:00
Grzegorz Leoniec
81bcdc87e3
Disable testing for now 2019-02-25 10:35:57 +01:00
Grzegorz Leoniec
85114b78d6
Added missing Icon 2019-02-25 09:58:29 +01:00
Grzegorz Leoniec
105ab19424
Merge branch 'master' of github.com:Human-Connection/Nitro-Styleguide 2019-02-25 09:55:04 +01:00
Grzegorz Leoniec
7a628d515d
Added editor icons 2019-02-25 09:54:54 +01:00
Grzegorz Leoniec
71258216f2
Update README.md 2019-02-25 08:41:19 +01:00
Grzegorz Leoniec
9c35a70e07
Update README.md 2019-02-25 08:23:56 +01:00
Grzegorz Leoniec
2a8fef8cb6
Try to fix build 2019-02-24 21:00:34 +01:00
Grzegorz Leoniec
3a879a954f
Try to fix build 2019-02-24 20:55:45 +01:00
Grzegorz Leoniec
aea2bb079e
Force yarn on before_install 2019-02-24 20:38:08 +01:00
Grzegorz Leoniec
bdeb23a428
Build as lib 2019-02-24 20:34:05 +01:00
Grzegorz Leoniec
58b3034fc8
Removing yarn service as its invalid 2019-02-24 20:31:50 +01:00
Grzegorz Leoniec
d68357c74d
Merge branch 'master' of github.com:Human-Connection/Nitro-Styleguide 2019-02-24 20:26:45 +01:00
Grzegorz Leoniec
c05e80420f
Fixing travis 2019-02-24 20:26:42 +01:00
Grzegorz Leoniec
2021531ab4
Merge pull request #6 from Human-Connection/dependabot/npm_and_yarn/@babel/standalone-7.3.3
Bump @babel/standalone from 7.3.2 to 7.3.3
2019-02-24 20:25:14 +01:00
Grzegorz Leoniec
fd15352390
Renamed travis.yml 2019-02-24 20:21:16 +01:00
Grzegorz Leoniec
86c8c30bd4
added travis build 2019-02-24 20:19:35 +01:00
dependabot[bot]
0d7fe46cc4
Bump @babel/standalone from 7.3.2 to 7.3.3
Bumps [@babel/standalone](https://github.com/babel/babel-standalone) from 7.3.2 to 7.3.3.
- [Release notes](https://github.com/babel/babel-standalone/releases)
- [Commits](https://github.com/babel/babel-standalone/commits)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-24 19:17:13 +00:00
Grzegorz Leoniec
970613a416
Merge pull request #13 from Human-Connection/dependabot/npm_and_yarn/codemirror-5.44.0
Bump codemirror from 5.43.0 to 5.44.0
2019-02-24 20:14:26 +01:00
Grzegorz Leoniec
2e7acaa68b
Merge pull request #11 from Human-Connection/dependabot/npm_and_yarn/@vue/cli-plugin-unit-jest-3.4.1
Bump @vue/cli-plugin-unit-jest from 3.4.0 to 3.4.1
2019-02-24 20:14:03 +01:00
Grzegorz Leoniec
cc456da586
Merge pull request #16 from Human-Connection/search-input
Search input
2019-02-24 20:11:56 +01:00
Grzegorz Leoniec
47291993d2
Merge pull request #15 from Human-Connection/changes-for-editor
Improve input.js for editor use
2019-02-24 20:14:32 +01:00
Grzegorz Leoniec
58f40fbed8
Improve imput.js for editor use 2019-02-24 20:04:38 +01:00
Grzegorz Leoniec
3975ccfd71
Merge remote-tracking branch 'origin/master' into search-input 2019-02-24 17:22:08 +01:00
Grzegorz Leoniec
05dce9254c
added changes to use select as search input 2019-02-24 17:21:00 +01:00
dependabot[bot]
85c994e94c
Bump codemirror from 5.43.0 to 5.44.0
Bumps [codemirror](https://github.com/codemirror/CodeMirror) from 5.43.0 to 5.44.0.
- [Release notes](https://github.com/codemirror/CodeMirror/releases)
- [Changelog](https://github.com/codemirror/CodeMirror/blob/master/CHANGELOG.md)
- [Commits](https://github.com/codemirror/CodeMirror/compare/5.43.0...5.44.0)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-22 04:44:32 +00:00
dependabot[bot]
ea16195296
Bump @vue/cli-plugin-unit-jest from 3.4.0 to 3.4.1
Bumps [@vue/cli-plugin-unit-jest](https://github.com/vuejs/vue-cli/tree/HEAD/packages/@vue/cli-plugin-unit-jest) from 3.4.0 to 3.4.1.
- [Release notes](https://github.com/vuejs/vue-cli/releases)
- [Changelog](https://github.com/vuejs/vue-cli/blob/dev/CHANGELOG.md)
- [Commits](https://github.com/vuejs/vue-cli/commits/v3.4.1/packages/@vue/cli-plugin-unit-jest)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-21 21:46:27 +00:00
dependabot[bot]
01d0d4819f
Merge pull request #12 from Human-Connection/dependabot/npm_and_yarn/theo-8.1.2 2019-02-21 21:42:59 +00:00
dependabot[bot]
3089f99312
Bump theo from 8.1.1 to 8.1.2
Bumps [theo](https://github.com/salesforce-ux/theo) from 8.1.1 to 8.1.2.
- [Release notes](https://github.com/salesforce-ux/theo/releases)
- [Changelog](https://github.com/salesforce-ux/theo/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce-ux/theo/compare/v8.1.1...v8.1.2)

Signed-off-by: dependabot[bot] <support@dependabot.com>
2019-02-21 05:16:42 +00:00
Grzegorz Leoniec
c141674c65
Merge pull request #2 from Human-Connection/fix-missing-files
[WIP] Fix missing files
2019-02-16 10:33:36 +01:00
Grzegorz Leoniec
1a84af06ff
Reactivate source maps in production 2019-02-16 10:31:22 +01:00
Grzegorz Leoniec
da25934f4c
Remove generated files again 2019-02-16 10:28:54 +01:00
Grzegorz Leoniec
e767ee5870
Fixed build 2019-02-15 21:52:54 +01:00
Grzegorz Leoniec
746c48a35f
Try to fix build issues 2019-02-15 21:36:15 +01:00
Grzegorz Leoniec
8d759834e1
Stop fixing issues 2019-02-15 21:23:32 +01:00
Grzegorz Leoniec
f97029dc07
Do not automatically fix prettiere issues 2019-02-15 20:49:21 +01:00
Grzegorz Leoniec
2212cf70f5
Downgrade prettier 2019-02-15 20:40:03 +01:00
Grzegorz Leoniec
659c8b5106
Fixed missing files 2019-02-15 20:38:21 +01:00
Grzegorz Leoniec
3cdd06b252
Moved Styleguide to its own repo 2019-02-15 19:04:41 +01:00
1335 changed files with 54319 additions and 18233 deletions

View File

@ -12,6 +12,7 @@ docker: &docker
webapp: &webapp
- '.github/workflows/test-webapp.yml'
- 'webapp/**/*'
- 'styleguide/**/*'
- 'package.json'
docs-check: &docs-check

View File

@ -11,7 +11,7 @@ jobs:
documentation: ${{ steps.changes.outputs.documentation }}
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for markdown file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
@ -28,13 +28,13 @@ jobs:
if: needs.files-changed.outputs.markdown == 'true'
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Remove uncheckable documentation files
run: rm -rf ./CHANGELOG.md # workaround until https://github.com/gaurav-nelson/github-action-markdown-link-check/pull/183 has been done
- name: Check Markdown Links
uses: gaurav-nelson/github-action-markdown-link-check@1b916f2cf6c36510a6059943104e3c42ce6c16bc # 1.0.15
uses: gaurav-nelson/github-action-markdown-link-check@3c3b66f1f7d0900e37b71eca45b63ea9eedfce31 # 1.0.15
with:
use-quiet-mode: 'yes'
use-verbose-mode: 'no'
@ -51,10 +51,10 @@ jobs:
if: needs.files-changed.outputs.documentation == 'true'
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node 20
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v4.0.3
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version: '20'

View File

@ -22,7 +22,7 @@ jobs:
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Cleanup
run: |

View File

@ -13,7 +13,7 @@ jobs:
documentation: ${{ steps.changes.outputs.documentation }}
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
@ -27,10 +27,10 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node 20
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v4.0.3
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version: 20

View File

@ -24,27 +24,27 @@ jobs:
file: backend/Dockerfile
target: production
- name: webapp-base
context: webapp
context: .
file: webapp/Dockerfile
target: base
- name: webapp-build
context: webapp
context: .
file: webapp/Dockerfile
target: build
- name: webapp
context: webapp
context: .
file: webapp/Dockerfile
target: production
- name: maintenance-base
context: webapp
context: .
file: webapp/Dockerfile.maintenance
target: base
- name: maintenance-build
context: webapp
context: .
file: webapp/Dockerfile.maintenance
target: build
- name: maintenance
context: webapp
context: .
file: webapp/Dockerfile.maintenance
target: production
runs-on: ubuntu-latest
@ -59,16 +59,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Log in to the Container registry
uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f
uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |

View File

@ -14,9 +14,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
with:
fetch-depth: 0 # Fetch full History for changelog
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: '.nvmrc'
- name: Setup env
run: |
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
@ -54,9 +58,13 @@ jobs:
needs: [github_tag]
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
with:
fetch-depth: 0 # Fetch full History for changelog
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: '.nvmrc'
- name: Setup env
run: |
echo "VERSION=$(node -p -e "require('./package.json').version")" >> $GITHUB_ENV
@ -64,7 +72,7 @@ jobs:
echo "BUILD_COMMIT=${GITHUB_SHA}" >> $GITHUB_ENV
- run: echo "BUILD_VERSION=${VERSION}-${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
#- name: Repository Dispatch
# uses: peter-evans/repository-dispatch@7279ea08e172078316f128ed1118df40d2904f0f # v3.0.0
# uses: peter-evans/repository-dispatch@cf70392543065ca62813db6712a06df1c4f4ae9f # v3.0.0
# with:
# token: ${{ github.token }}
# event-type: trigger-ocelot-build-success
@ -72,7 +80,7 @@ jobs:
# client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
- name: Repository Dispatch stage.ocelot.social
uses: peter-evans/repository-dispatch@7279ea08e172078316f128ed1118df40d2904f0f # v3.0.0
uses: peter-evans/repository-dispatch@cf70392543065ca62813db6712a06df1c4f4ae9f # v3.0.0
with:
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
event-type: trigger-ocelot-build-success
@ -80,7 +88,7 @@ jobs:
client-payload: '{"ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "GITHUB_RUN_NUMBER": "${{ env.GITHUB_RUN_NUMBER }}", "VERSION": "${VERSION}", "BUILD_DATE": "${BUILD_DATE}", "BUILD_COMMIT": "${BUILD_COMMIT}", "BUILD_VERSION": "${BUILD_VERSION}"}'
- name: Repository Dispatch stage.yunite.me
uses: peter-evans/repository-dispatch@7279ea08e172078316f128ed1118df40d2904f0f # v3.0.0
uses: peter-evans/repository-dispatch@cf70392543065ca62813db6712a06df1c4f4ae9f # v3.0.0
with:
token: ${{ secrets.OCELOT_PUBLISH_EVENT_PAT }} # this token is required to access the other repository
event-type: trigger-ocelot-build-success

View File

@ -11,7 +11,7 @@ jobs:
backend: ${{ steps.changes.outputs.backend }}
docker: ${{ steps.changes.outputs.docker }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for backend file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Neo4J | Build 'community' image
run: |
@ -37,7 +37,7 @@ jobs:
- name: Cache docker images
id: cache-neo4j
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/neo4j.tar
key: ${{ github.run_id }}-backend-neo4j-cache
@ -49,7 +49,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: backend | Build 'test' image
run: |
@ -58,7 +58,7 @@ jobs:
- name: Cache docker images
id: cache-backend
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/backend.tar
key: ${{ github.run_id }}-backend-cache
@ -70,7 +70,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'backend/.nvmrc'
- name: backend | Lint
run: cd backend && yarn && yarn run lint
@ -84,17 +89,17 @@ jobs:
checks: write
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Restore Neo4J cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/neo4j.tar
key: ${{ github.run_id }}-backend-neo4j-cache
fail-on-cache-miss: true
- name: Restore Backend cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/backend.tar
key: ${{ github.run_id }}-backend-cache

View File

@ -8,7 +8,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Copy backend env file
run: |
@ -31,7 +31,7 @@ jobs:
docker compose -f docker-compose.yml -f docker-compose.test.yml down
- name: Cache docker images
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/tmp/backend.tar
@ -46,7 +46,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Copy backend env file
run: |
@ -59,7 +59,7 @@ jobs:
docker save "ghcr.io/ocelot-social-community/ocelot-social/webapp:test" > /tmp/webapp.tar
- name: Cache docker image
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-e2e-webapp-cache
@ -68,13 +68,16 @@ jobs:
name: Fullstack | prepare cypress
runs-on: ubuntu-latest
steps:
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v4.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.4.0
with:
node-version-file: 'backend/.tool-versions'
node-version-file: 'backend/.nvmrc'
cache: 'yarn'
- name: Copy env files
@ -84,7 +87,8 @@ jobs:
- name: Install cypress requirements
run: |
wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
sudo wget --no-verbose -O /opt/cucumber-json-formatter "https://github.com/cucumber/json-formatter/releases/download/v19.0.0/cucumber-json-formatter-linux-386"
sudo chmod +x /opt/cucumber-json-formatter
cd backend
yarn install
yarn build
@ -93,7 +97,7 @@ jobs:
- name: Cache docker image
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/opt/cucumber-json-formatter
@ -113,17 +117,20 @@ jobs:
# run copies of the current job in parallel
job: [1, 2, 3, 4, 5, 6, 7, 8]
steps:
- name: Delete huge unnecessary tools folder
run: rm -rf /opt/hostedtoolcache
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Setup Node.js
uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v4.4.0
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.4.0
with:
node-version-file: 'backend/.tool-versions'
node-version-file: 'backend/.nvmrc'
cache: 'yarn'
- name: Restore cypress cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/opt/cucumber-json-formatter
@ -133,7 +140,7 @@ jobs:
restore-keys: ${{ github.run_id }}-e2e-cypress
- name: Restore backend environment cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: |
/tmp/backend.tar
@ -144,7 +151,7 @@ jobs:
key: ${{ github.run_id }}-e2e-backend-environment-cache
- name: Restore webapp cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-e2e-webapp-cache
@ -175,7 +182,7 @@ jobs:
- name: Full stack tests | if tests failed, upload report
id: e2e-report
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
with:
name: ocelot-e2e-test-report-pr${{ needs.docker_preparation.outputs.pr-number }}
path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report
@ -187,7 +194,7 @@ jobs:
continue-on-error: true
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.2.2
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.2.2
- name: Full stack tests | cleanup cache
run: |

View File

@ -11,7 +11,7 @@ jobs:
docker: ${{ steps.changes.outputs.docker }}
webapp: ${{ steps.changes.outputs.webapp }}
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Check for frontend file changes
uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2
@ -28,7 +28,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'webapp/.nvmrc'
- name: Check translation files
run: |
@ -42,15 +47,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Webapp | Build 'test' image
run: |
docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/
docker build --target test -f webapp/Dockerfile -t "ocelotsocialnetwork/webapp:test" .
docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar
- name: Cache docker image
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-webapp-cache
@ -62,7 +67,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Setup Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v4.0.3
with:
node-version-file: 'webapp/.nvmrc'
- name: webapp | Lint
run: cd webapp && yarn && yarn run lint
@ -76,10 +86,10 @@ jobs:
checks: write
steps:
- name: Checkout code
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v4.1.7
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v4.1.7
- name: Restore webapp cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.0.2
uses: actions/cache@cdf6c1fa76f9f475f3d7449005a359c84ca0f306 # v4.0.2
with:
path: /tmp/webapp.tar
key: ${{ github.run_id }}-webapp-cache

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ github.actor != 'dependabot[bot]' }}
steps:
- uses: amannn/action-semantic-pull-request@e7d011b07ef37e089bea6539210f6a0d360d8af9 # v5.5.3
- uses: amannn/action-semantic-pull-request@069817c298f23fab00a8f29a2e556a5eac0f6390 # v5.5.3
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
@ -32,6 +32,7 @@ jobs:
webapp
maintenance
database
e2e
docu
docker
release

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "styleguide"]
path = styleguide
url = https://github.com/Human-Connection/Nitro-Styleguide.git
[submodule "deployment/configurations/stage.ocelot.social"]
path = deployment/configurations/stage.ocelot.social
url = git@github.com:Ocelot-Social-Community/stage.ocelot.social.git

2
.nvmrc
View File

@ -1 +1 @@
v24.2.0
v25.3.0

View File

@ -1 +0,0 @@
nodejs 20.12.1

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ SMTP_PASSWORD=
SMTP_SECURE="false" # true for 465, false for other ports
SMTP_DKIM_DOMAINNAME=
SMTP_DKIM_KEYSELECTOR=
SMTP_DKIM_PRIVATKEY=
SMTP_DKIM_PRIVATEKEY=
# E-Mail settings for our 'docker compose up mailserver'
# SMTP_HOST=localhost
# SMTP_PORT=1025
@ -48,3 +48,4 @@ IMAGOR_SECRET=mysecret
CATEGORIES_ACTIVE=false
MAX_PINNED_POSTS=1
MAX_GROUP_PINNED_POSTS=1

View File

@ -19,7 +19,7 @@ SMTP_PASSWORD=
SMTP_SECURE="false" # true for 465, false for other ports
SMTP_DKIM_DOMAINNAME=
SMTP_DKIM_KEYSELECTOR=
SMTP_DKIM_PRIVATKEY=
SMTP_DKIM_PRIVATEKEY=
JWT_SECRET="b/&&7b78BF&fv/Vd"
JWT_EXPIRES="2y"
@ -40,3 +40,4 @@ IMAGOR_SECRET=mysecret
CATEGORIES_ACTIVE=false
MAX_PINNED_POSTS=1
MAX_GROUP_PINNED_POSTS=1

View File

@ -14,7 +14,6 @@ module.exports = {
'plugin:import/recommended',
'plugin:import/typescript',
'plugin:promise/recommended',
'plugin:security/recommended-legacy',
'plugin:@eslint-community/eslint-comments/recommended',
'prettier',
],
@ -175,6 +174,10 @@ module.exports = {
'@eslint-community/eslint-comments/require-description': 'off',
},
overrides: [
{
files: ['*.js', '*.cjs', '*.ts', '*.tsx'],
extends: ['plugin:security/recommended-legacy'],
},
// only for ts files
{
files: ['*.ts', '*.tsx'],
@ -228,5 +231,33 @@ module.exports = {
files: ['*.json', '*.json5', '*.jsonc'],
parser: 'jsonc-eslint-parser',
},
{
files: ['*.graphql', '*.gql'],
parser: '@graphql-eslint/eslint-plugin',
plugins: ['@graphql-eslint'],
extends: ['plugin:@graphql-eslint/schema-recommended'],
rules: {
'@graphql-eslint/description-style': ['error', { style: 'inline' }],
'@graphql-eslint/require-description': 'off',
'@graphql-eslint/naming-convention': 'off',
'@graphql-eslint/strict-id-in-types': 'off',
'@graphql-eslint/no-typename-prefix': 'off',
// incompatible: `depends on a GraphQL validation rule "XXX" but it's not available in the "graphql" version you are using. Skipping…`
'@graphql-eslint/known-directives': 'off',
'@graphql-eslint/known-argument-names': 'off',
'@graphql-eslint/known-type-names': 'off',
'@graphql-eslint/lone-schema-definition': 'off',
'@graphql-eslint/provided-required-arguments': 'off',
'@graphql-eslint/unique-directive-names': 'off',
'@graphql-eslint/unique-directive-names-per-location': 'off',
'@graphql-eslint/unique-field-definition-names': 'off',
'@graphql-eslint/unique-operation-types': 'off',
'@graphql-eslint/unique-type-names': 'off',
},
parserOptions: {
schema: './src/graphql/types/**/*.gql',
assumeValid: true,
},
},
],
}

View File

@ -1 +1 @@
v24.2.0
v25.3.0

View File

@ -1 +0,0 @@
nodejs 24.2.0

View File

@ -1,4 +1,4 @@
FROM node:24.8.0-alpine AS base
FROM node:25.5.0-alpine AS base
LABEL org.label-schema.name="ocelot.social:backend"
LABEL org.label-schema.description="Backend of the Social Network Software ocelot.social"
LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md"

View File

@ -19,18 +19,16 @@ Wait a little until your backend is up and running at [http://localhost:4000/](h
## Installation without Docker
For the local installation you need a recent version of
[Node](https://nodejs.org/en/) (&gt;= `v16.19.0`). We are using
`v24.2.0` and therefore we recommend to use the same version
([see](https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4082)
some known problems with more recent node versions). You can use the
[Node](https://nodejs.org/en/). We are using
`v25.3.0` and therefore we recommend to use the same version. You can use the
[node version manager](https://github.com/nvm-sh/nvm) `nvm` to switch
between different local Node versions:
```sh
# install Node
# install Node using '.nvmrc' file
$ cd backend
$ nvm install v24.2.0
$ nvm use v24.2.0
$ nvm install
$ nvm use
```
Install node dependencies with [yarn](https://yarnpkg.com/en/):

View File

@ -1,6 +1,6 @@
{
"name": "ocelot-social-backend",
"version": "3.12.2",
"version": "3.13.1",
"description": "GraphQL Backend for ocelot.social",
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
"author": "ocelot.social Community",
@ -12,7 +12,7 @@
"build": "tsc && tsc-alias && ./scripts/build.copy.files.sh",
"dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql",
"dev:debug": "nodemon --exec node --inspect=0.0.0.0:9229 build/src/index.js -e js,ts,gql",
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc .",
"lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc,.graphql,.gql .",
"test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles",
"db:reset": "ts-node --require tsconfig-paths/register src/db/reset.ts",
"db:reset:withmigrations": "ts-node --require tsconfig-paths/register src/db/reset-with-migrations.ts",
@ -23,26 +23,29 @@
"db:data:categories": "ts-node --require tsconfig-paths/register src/db/categories.ts",
"db:migrate": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --store ./src/db/migrate/store.ts",
"db:migrate:create": "migrate --compiler 'ts:./src/db/compiler.ts' --migrations-dir ./src/db/migrations --template-file ./src/db/migrate/template.ts --date-format 'yyyymmddHHmmss' create",
"db:func:disable:notifications": "ts-node --require tsconfig-paths/register src/db/disable-notifications.ts",
"prod:migrate": "migrate --migrations-dir ./build/src/db/migrations --store ./build/src/db/migrate/store.js",
"prod:db:data:branding": "node build/src/db/data-branding.js",
"prod:db:data:categories": "node build/src/db/categories.js"
"prod:db:data:categories": "node build/src/db/categories.js",
"prod:db:data:admin": "node build/src/db/admin.js",
"prod:db:func:disable:notifications": "node build/src/db/disable-notifications.js"
},
"dependencies": {
"@aws-sdk/client-s3": "^3.888.0",
"@aws-sdk/lib-storage": "^3.888.0",
"@sentry/node": "^5.15.4",
"@aws-sdk/client-s3": "^3.980.0",
"@aws-sdk/lib-storage": "^3.980.0",
"@sentry/node": "^5.30.0",
"@types/mime-types": "^3.0.1",
"apollo-server": "~2.14.2",
"apollo-server-express": "^2.14.2",
"bcryptjs": "~3.0.2",
"bcryptjs": "~3.0.3",
"body-parser": "^1.20.3",
"cheerio": "~1.1.2",
"cross-env": "~10.0.0",
"cheerio": "~1.2.0",
"cross-env": "~10.1.0",
"dotenv": "~17.0.1",
"email-templates": "^12.0.3",
"express": "^5.1.0",
"express": "^4.22.1",
"graphql": "^14.6.0",
"graphql-middleware": "~4.0.2",
"graphql-middleware": "~6.1.35",
"graphql-middleware-sentry": "^3.2.1",
"graphql-redis-subscriptions": "^2.7.0",
"graphql-shield": "~7.2.2",
@ -50,55 +53,56 @@
"graphql-tag": "~2.10.3",
"graphql-upload": "^13.0.0",
"helmet": "~8.1.0",
"ioredis": "^5.7.0",
"ioredis": "^5.9.2",
"jsonwebtoken": "~8.5.1",
"languagedetect": "^2.0.0",
"linkify-html": "^4.3.2",
"linkifyjs": "^4.3.2",
"lodash": "~4.17.21",
"lodash": "~4.17.23",
"merge-graphql-schemas": "^1.7.8",
"metascraper": "^5.49.2",
"metascraper-author": "^5.49.2",
"metascraper-date": "^5.49.2",
"metascraper-description": "^5.49.2",
"metascraper-image": "^5.49.2",
"metascraper-lang": "^5.49.2",
"metascraper": "^5.49.19",
"metascraper-author": "^5.49.19",
"metascraper-date": "^5.49.19",
"metascraper-description": "^5.49.19",
"metascraper-image": "^5.49.19",
"metascraper-lang": "^5.49.19",
"metascraper-lang-detector": "^4.10.2",
"metascraper-logo": "^5.49.2",
"metascraper-publisher": "^5.49.2",
"metascraper-logo": "^5.49.19",
"metascraper-publisher": "^5.49.19",
"metascraper-soundcloud": "^5.34.4",
"metascraper-title": "^5.49.2",
"metascraper-url": "^5.49.2",
"metascraper-video": "^5.49.2",
"metascraper-youtube": "^5.49.2",
"metascraper-title": "^5.49.19",
"metascraper-url": "^5.49.19",
"metascraper-video": "^5.49.19",
"metascraper-youtube": "^5.49.20",
"migrate": "^2.1.0",
"mime-types": "^3.0.1",
"minimatch": "^10.0.3",
"mime-types": "^3.0.2",
"minimatch": "^10.1.1",
"mustache": "^4.2.0",
"neo4j-driver": "^4.4.11",
"neo4j-graphql-js": "^2.11.5",
"neo4j-graphql-js": "2.11.5",
"neode": "^0.4.9",
"node-fetch": "^2.7.0",
"nodemailer": "^7.0.6",
"nodemailer": "^7.0.12",
"nodemailer-html-to-text": "^3.2.0",
"preview-email": "^3.1.0",
"preview-email": "^3.1.1",
"pug": "^3.0.3",
"sanitize-html": "~2.17.0",
"slugify": "^1.6.6",
"trunc-html": "~1.1.2",
"tslog": "^4.9.3",
"tslog": "^4.10.2",
"uuid": "~9.0.1",
"validator": "^13.15.15",
"validator": "^13.15.26",
"xregexp": "^5.1.2"
},
"devDependencies": {
"@eslint-community/eslint-plugin-eslint-comments": "^4.5.0",
"@eslint-community/eslint-plugin-eslint-comments": "^4.6.0",
"@faker-js/faker": "9.9.0",
"@graphql-eslint/eslint-plugin": "^3.20.1",
"@types/email-templates": "^10.0.4",
"@types/jest": "^30.0.0",
"@types/jsonwebtoken": "~8.5.1",
"@types/lodash": "^4.17.20",
"@types/node": "^24.4.0",
"@types/lodash": "^4.17.23",
"@types/node": "^25.1.0",
"@types/request": "^2.48.13",
"@types/slug": "^5.0.9",
"@types/uuid": "~9.0.1",
@ -110,19 +114,19 @@
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.0.1",
"eslint-plugin-jsonc": "^2.20.1",
"eslint-plugin-n": "^17.21.3",
"eslint-plugin-jest": "^29.12.1",
"eslint-plugin-jsonc": "^2.21.0",
"eslint-plugin-n": "^17.23.2",
"eslint-plugin-no-catch-all": "^1.1.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-security": "^3.0.1",
"jest": "^30.1.3",
"nodemon": "~3.1.10",
"prettier": "^3.6.2",
"jest": "^30.2.0",
"nodemon": "~3.1.11",
"prettier": "^3.8.1",
"require-json5": "^1.3.0",
"rosie": "^2.1.1",
"ts-jest": "^29.4.1",
"ts-jest": "^29.4.6",
"ts-node": "^10.9.2",
"tsc-alias": "^1.8.16",
"tsconfig-paths": "^4.2.0",
@ -133,7 +137,9 @@
"**/graphql-upload": "^11.0.0",
"**/strip-ansi": "6.0.1",
"**/string-width": "4.2.0",
"**/wrap-ansi": "7.0.0"
"**/wrap-ansi": "7.0.0",
"**/jwa": "^2.0.1",
"**/@types/express": "4.17.25"
},
"engines": {
"node": ">=20.12.1"

View File

@ -48,7 +48,7 @@ const SMTP_PASSWORD = env.SMTP_PASSWORD
const SMTP_DKIM_DOMAINNAME = env.SMTP_DKIM_DOMAINNAME
const SMTP_DKIM_KEYSELECTOR = env.SMTP_DKIM_KEYSELECTOR
// PEM format = https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html
const SMTP_DKIM_PRIVATKEY = env.SMTP_DKIM_PRIVATKEY?.replace(/\\n/g, '\n') // replace all "\n" in .env string by real line break
const SMTP_DKIM_PRIVATEKEY = env.SMTP_DKIM_PRIVATEKEY?.replace(/\\n/g, '\n') // replace all "\n" in .env string by real line break
const SMTP_MAX_CONNECTIONS = (env.SMTP_MAX_CONNECTIONS && parseInt(env.SMTP_MAX_CONNECTIONS)) || 5
const SMTP_MAX_MESSAGES = (env.SMTP_MAX_MESSAGES && parseInt(env.SMTP_MAX_MESSAGES)) || 100
@ -67,11 +67,11 @@ if (SMTP_USERNAME && SMTP_PASSWORD) {
pass: SMTP_PASSWORD,
}
}
if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATKEY) {
if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATEKEY) {
nodemailerTransportOptions.dkim = {
domainName: SMTP_DKIM_DOMAINNAME,
keySelector: SMTP_DKIM_KEYSELECTOR,
privateKey: SMTP_DKIM_PRIVATKEY,
privateKey: SMTP_DKIM_PRIVATEKEY,
}
}
@ -138,6 +138,9 @@ const options = {
MAX_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_PINNED_POSTS))
? 1
: Number(process.env.MAX_PINNED_POSTS),
MAX_GROUP_PINNED_POSTS: Number.isNaN(Number(process.env.MAX_GROUP_PINNED_POSTS))
? 1
: Number(process.env.MAX_GROUP_PINNED_POSTS),
}
const language = {

View File

@ -0,0 +1,61 @@
import databaseContext from '@context/database'
const run = async () => {
const args = process.argv.slice(2)
if (args.length !== 1) {
// eslint-disable-next-line no-console
console.error('Usage: yarn run db:func:disable-notifications <email>')
// eslint-disable-next-line n/no-process-exit
process.exit(1)
}
const email = args[0]
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(email)) {
// eslint-disable-next-line no-console
console.error('Error: Invalid email address format')
// eslint-disable-next-line n/no-process-exit
process.exit(1)
}
const { write } = databaseContext()
const result = (
await write({
query: `
MATCH (:EmailAddress {email: $email})-[:BELONGS_TO]->(user:User)
SET user.emailNotificationsFollowingUsers = false
SET user.emailNotificationsPostInGroup = false
SET user.emailNotificationsCommentOnObservedPost = false
SET user.emailNotificationsMention = false
SET user.emailNotificationsChatMessage = false
SET user.emailNotificationsGroupMemberJoined = false
SET user.emailNotificationsGroupMemberLeft = false
SET user.emailNotificationsGroupMemberRemoved = false
SET user.emailNotificationsGroupMemberRoleChanged = false
RETURN toString(count(user)) as count
`,
variables: {
email,
},
})
).records[0].get('count') as string
if (result !== '1') {
// eslint-disable-next-line no-console
console.error(`User with email address ${email} not found`)
// eslint-disable-next-line n/no-process-exit
process.exit(1)
}
// eslint-disable-next-line no-console
console.log(`Notifications for User with email address ${email} disabled`)
// eslint-disable-next-line n/no-process-exit
process.exit(0)
}
void (async function () {
await run()
})()

View File

@ -18,7 +18,7 @@ export default {
},
title: { type: 'string', disallow: [null], min: 3 },
slug: { type: 'string', allow: [null], unique: 'true' },
content: { type: 'string', disallow: [null], min: 3 },
content: { type: 'string', disallow: [null], required: true, min: 3 },
contentExcerpt: { type: 'string', allow: [null] },
deleted: { type: 'boolean', default: false },
disabled: { type: 'boolean', default: false },
@ -58,6 +58,7 @@ export default {
},
},
pinned: { type: 'boolean', default: null, valid: [null, true] },
groupPinned: { type: 'boolean', default: null, valid: [null, true] },
postType: { type: 'string', default: 'Article', valid: ['Article', 'Event'] },
observes: {
type: 'relationship',

View File

@ -1,36 +1,49 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable n/no-missing-require */
/* eslint-disable n/global-require */
// NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm
// module that is not browser-compatible. Node's `fs` module is server-side only
// eslint-disable-next-line @typescript-eslint/no-explicit-any
declare let Cypress: any | undefined
//
// We use static imports instead of dynamic require() to ensure compatibility
// with both Node.js and Webpack (used by Cypress cucumber preprocessor).
import Badge from './Badge'
import Category from './Category'
import Comment from './Comment'
import Donations from './Donations'
import EmailAddress from './EmailAddress'
import File from './File'
import Group from './Group'
import Image from './Image'
import InviteCode from './InviteCode'
import Location from './Location'
import Migration from './Migration'
import Post from './Post'
import Report from './Report'
import SocialMedia from './SocialMedia'
import Tag from './Tag'
import UnverifiedEmailAddress from './UnverifiedEmailAddress'
import User from './User'
import type Neode from 'neode'
// Type assertion needed because TypeScript infers literal types from the model
// objects (e.g., type: 'string' as literal), but Neode expects the broader
// SchemaObject type with PropertyTypes union. The Neode type definitions are
// incomplete/incorrect, so we use double assertion to bypass the check.
export default {
File: typeof Cypress !== 'undefined' ? require('./File') : require('./File').default,
Image: typeof Cypress !== 'undefined' ? require('./Image') : require('./Image').default,
Badge: typeof Cypress !== 'undefined' ? require('./Badge') : require('./Badge').default,
User: typeof Cypress !== 'undefined' ? require('./User') : require('./User').default,
Group: typeof Cypress !== 'undefined' ? require('./Group') : require('./Group').default,
EmailAddress:
typeof Cypress !== 'undefined' ? require('./EmailAddress') : require('./EmailAddress').default,
UnverifiedEmailAddress:
typeof Cypress !== 'undefined'
? require('./UnverifiedEmailAddress')
: require('./UnverifiedEmailAddress').default,
SocialMedia:
typeof Cypress !== 'undefined' ? require('./SocialMedia') : require('./SocialMedia').default,
Post: typeof Cypress !== 'undefined' ? require('./Post') : require('./Post').default,
Comment: typeof Cypress !== 'undefined' ? require('./Comment') : require('./Comment').default,
Category: typeof Cypress !== 'undefined' ? require('./Category') : require('./Category').default,
Tag: typeof Cypress !== 'undefined' ? require('./Tag') : require('./Tag').default,
Location: typeof Cypress !== 'undefined' ? require('./Location') : require('./Location').default,
Donations:
typeof Cypress !== 'undefined' ? require('./Donations') : require('./Donations').default,
Report: typeof Cypress !== 'undefined' ? require('./Report') : require('./Report').default,
Migration:
typeof Cypress !== 'undefined' ? require('./Migration') : require('./Migration').default,
InviteCode:
typeof Cypress !== 'undefined' ? require('./InviteCode') : require('./InviteCode').default,
}
Badge,
Category,
Comment,
Donations,
EmailAddress,
File,
Group,
Image,
InviteCode,
Location,
Migration,
Post,
Report,
SocialMedia,
Tag,
UnverifiedEmailAddress,
User,
} as unknown as Record<string, Neode.SchemaObject>

View File

@ -2,7 +2,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import path from 'node:path'
import Email from 'email-templates'
@ -94,8 +93,8 @@ export const sendNotificationMail = async (notification: any): Promise<OriginalM
: notification?.from?.title,
postUrl: new URL(
notification?.from?.__typename === 'Comment'
? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}`
: `/post/${notification?.from?.id}/${notification?.from?.slug}`,
? `/post/${encodeURIComponent(notification?.from?.post?.id)}/${encodeURIComponent(notification?.from?.post?.slug)}`
: `/post/${encodeURIComponent(notification?.from?.id)}/${encodeURIComponent(notification?.from?.slug)}`,
CONFIG.CLIENT_URI,
),
postAuthorName:
@ -106,7 +105,7 @@ export const sendNotificationMail = async (notification: any): Promise<OriginalM
notification?.from?.__typename === 'Comment'
? undefined
: new URL(
`profile/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
`profile/${encodeURIComponent(notification?.from?.author?.id)}/${encodeURIComponent(notification?.from?.author?.slug)}`,
CONFIG.CLIENT_URI,
),
commenterName:
@ -116,14 +115,14 @@ export const sendNotificationMail = async (notification: any): Promise<OriginalM
commenterUrl:
notification?.from?.__typename === 'Comment'
? new URL(
`/profile/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`,
`/profile/${encodeURIComponent(notification?.from?.author?.id)}/${encodeURIComponent(notification?.from?.author?.slug)}`,
CONFIG.CLIENT_URI,
)
: undefined,
commentUrl:
notification?.from?.__typename === 'Comment'
? new URL(
`/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`,
`/post/${encodeURIComponent(notification?.from?.post?.id)}/${encodeURIComponent(notification?.from?.post?.slug)}#commentId-${encodeURIComponent(notification?.from?.id)}`,
CONFIG.CLIENT_URI,
)
: undefined,
@ -132,7 +131,7 @@ export const sendNotificationMail = async (notification: any): Promise<OriginalM
groupUrl:
notification?.from?.__typename === 'Group'
? new URL(
`/groups/${notification?.from?.id}/${notification?.from?.slug}`,
`/groups/${encodeURIComponent(notification?.from?.id)}/${encodeURIComponent(notification?.from?.slug)}`,
CONFIG.CLIENT_URI,
)
: undefined,
@ -143,7 +142,7 @@ export const sendNotificationMail = async (notification: any): Promise<OriginalM
groupRelatedUserUrl:
notification?.from?.__typename === 'Group'
? new URL(
`/profile/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`,
`/profile/${encodeURIComponent(notification?.relatedUser?.id)}/${encodeURIComponent(notification?.relatedUser?.slug)}`,
CONFIG.CLIENT_URI,
)
: undefined,
@ -177,7 +176,10 @@ export const sendChatMessageMail = async (
locale: recipientUser.locale,
name: recipientUser.name,
chattingUser: senderUser.name,
chattingUserUrl: new URL(`/profile/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI),
chattingUserUrl: new URL(
`/profile/${encodeURIComponent(senderUser.id)}/${encodeURIComponent(senderUser.slug)}`,
CONFIG.CLIENT_URI,
),
chatUrl: new URL('/chat', CONFIG.CLIENT_URI),
},
})

View File

@ -3,10 +3,14 @@ import gql from 'graphql-tag'
export const ChangeGroupMemberRole = gql`
mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
id
name
slug
myRoleInGroup
user {
id
name
slug
}
membership {
role
}
}
}
`

View File

@ -8,6 +8,8 @@ export const CreateComment = gql`
author {
name
}
isPostObservedByMe
postObservingUsersCount
}
}
`

View File

@ -45,6 +45,7 @@ export const CreatePost = gql`
}
isObservedByMe
observingUsersCount
language
}
}
`

View File

@ -0,0 +1,14 @@
import gql from 'graphql-tag'
export const CreateSocialMedia = gql`
mutation ($url: String!) {
CreateSocialMedia(url: $url) {
id
url
url
ownedBy {
name
}
}
}
`

View File

@ -3,10 +3,14 @@ import gql from 'graphql-tag'
export const GroupMembers = gql`
query GroupMembers($id: ID!) {
GroupMembers(id: $id) {
id
name
slug
myRoleInGroup
user {
id
name
slug
}
membership {
role
}
}
}
`

View File

@ -3,10 +3,14 @@ import gql from 'graphql-tag'
export const JoinGroup = gql`
mutation ($groupId: ID!, $userId: ID!) {
JoinGroup(groupId: $groupId, userId: $userId) {
id
name
slug
myRoleInGroup
user {
id
name
slug
}
membership {
role
}
}
}
`

View File

@ -3,10 +3,14 @@ import gql from 'graphql-tag'
export const LeaveGroup = gql`
mutation ($groupId: ID!, $userId: ID!) {
LeaveGroup(groupId: $groupId, userId: $userId) {
id
name
slug
myRoleInGroup
user {
id
name
slug
}
membership {
role
}
}
}
`

View File

@ -6,10 +6,34 @@ export const Post = gql`
id
title
content
contentExcerpt
eventStart
pinned
createdAt
pinnedAt
isObservedByMe
observingUsersCount
clickedCount
emotionsCount
emotions {
emotion
User {
id
}
}
author {
id
name
}
shoutedBy {
id
}
tags {
id
}
comments {
content
}
}
}
`

View File

@ -3,10 +3,14 @@ import gql from 'graphql-tag'
export const RemoveUserFromGroup = gql`
mutation ($groupId: ID!, $userId: ID!) {
RemoveUserFromGroup(groupId: $groupId, userId: $userId) {
id
name
slug
myRoleInGroup
user {
id
name
slug
}
membership {
role
}
}
}
`

View File

@ -8,6 +8,8 @@ export const SignupVerification = gql`
$slug: String
$nonce: String!
$termsAndConditionsAgreedVersion: String!
$about: String
$locale: String
) {
SignupVerification(
email: $email
@ -16,9 +18,13 @@ export const SignupVerification = gql`
slug: $slug
nonce: $nonce
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
about: $about
locale: $locale
) {
id
slug
termsAndConditionsAgreedVersion
termsAndConditionsAgreedAt
}
}
`

View File

@ -1,10 +1,44 @@
import gql from 'graphql-tag'
export const UpdatePost = gql`
mutation ($id: ID!, $title: String!, $postContent: String!, $categoryIds: [ID]!) {
UpdatePost(id: $id, content: $postContent, title: $title, categoryIds: $categoryIds) {
mutation (
$id: ID!
$title: String!
$content: String!
$image: ImageInput
$categoryIds: [ID]
$postType: PostType
$eventInput: _EventInput
) {
UpdatePost(
id: $id
title: $title
content: $content
image: $image
categoryIds: $categoryIds
postType: $postType
eventInput: $eventInput
) {
id
title
content
author {
name
slug
}
createdAt
updatedAt
categories {
id
}
postType
eventStart
eventLocationName
eventVenue
eventLocation {
lng
lat
}
}
}
`

View File

@ -1,9 +1,38 @@
import gql from 'graphql-tag'
export const UpdateUser = gql`
mutation ($id: ID!, $name: String) {
UpdateUser(id: $id, name: $name) {
mutation (
$id: ID!
$name: String
$termsAndConditionsAgreedVersion: String
$locationName: String # empty string '' sets it to null
$emailNotificationSettings: [EmailNotificationSettingsInput]
) {
UpdateUser(
id: $id
name: $name
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
locationName: $locationName
emailNotificationSettings: $emailNotificationSettings
) {
id
name
termsAndConditionsAgreedVersion
termsAndConditionsAgreedAt
locationName
location {
name
nameDE
nameEN
nameRU
}
emailNotificationSettings {
type
settings {
name
value
}
}
}
}
`

View File

@ -1,9 +1,165 @@
import gql from 'graphql-tag'
export const User = gql`
query ($name: String) {
User(name: $name) {
email
query ($id: ID, $name: String, $email: String) {
User(id: $id, name: $name, email: $email) {
id
name
badgeTrophiesCount
badgeTrophies {
id
}
badgeVerification {
id
isDefault
}
badgeTrophiesSelected {
id
isDefault
}
followedBy {
id
}
followedByCurrentUser
following {
name
slug
about
avatar {
url
}
comments {
content
contentExcerpt
}
contributions {
title
slug
image {
url
}
content
contentExcerpt
}
}
isMuted
isBlocked
location {
distanceToMe
}
activeCategories
}
}
`
export const UserEmailNotificationSettings = gql`
query ($id: ID, $name: String, $email: String) {
User(id: $id, name: $name, email: $email) {
id
name
badgeTrophiesCount
badgeTrophies {
id
}
badgeVerification {
id
isDefault
}
badgeTrophiesSelected {
id
isDefault
}
followedBy {
id
}
followedByCurrentUser
following {
name
slug
about
avatar {
url
}
comments {
content
contentExcerpt
}
contributions {
title
slug
image {
url
}
content
contentExcerpt
}
}
isMuted
isBlocked
location {
distanceToMe
}
emailNotificationSettings {
type
settings {
name
value
}
}
activeCategories
}
}
`
export const UserEmail = gql`
query ($id: ID, $name: String, $email: String) {
User(id: $id, name: $name, email: $email) {
id
name
email
badgeTrophiesCount
badgeTrophies {
id
}
badgeVerification {
id
isDefault
}
badgeTrophiesSelected {
id
isDefault
}
followedBy {
id
}
followedByCurrentUser
following {
name
slug
about
avatar {
url
}
comments {
content
contentExcerpt
}
contributions {
title
slug
image {
url
}
content
contentExcerpt
}
}
isMuted
isBlocked
location {
distanceToMe
}
activeCategories
}
}
`

View File

@ -0,0 +1,7 @@
import gql from 'graphql-tag'
export const availableRoles = gql`
query {
availableRoles
}
`

View File

@ -3,6 +3,15 @@ import gql from 'graphql-tag'
export const currentUser = gql`
query currentUser {
currentUser {
id
slug
name
avatar {
url
}
email
role
activeCategories
following {
name
}

View File

@ -3,6 +3,7 @@ import gql from 'graphql-tag'
export const followUser = gql`
mutation ($id: ID!) {
followUser(id: $id) {
id
name
followedBy {
id

View File

@ -3,6 +3,7 @@ import gql from 'graphql-tag'
export const markAllAsRead = gql`
mutation {
markAllAsRead {
id
from {
__typename
... on Post {

View File

@ -3,14 +3,23 @@ import gql from 'graphql-tag'
export const notifications = gql`
query ($read: Boolean, $orderBy: NotificationOrdering) {
notifications(read: $read, orderBy: $orderBy) {
reason
relatedUser {
id
}
from {
__typename
... on Post {
id
content
}
... on Comment {
id
content
}
... on Group {
id
}
}
read
createdAt

View File

@ -0,0 +1,25 @@
import gql from 'graphql-tag'
export const pinGroupPost = gql`
mutation ($id: ID!) {
pinGroupPost(id: $id) {
id
title
content
author {
name
slug
}
pinnedBy {
id
name
role
}
createdAt
updatedAt
pinnedAt
pinned
groupPinned
}
}
`

View File

@ -11,6 +11,7 @@ export const profilePagePosts = gql`
id
title
content
groupPinned
}
}
`

View File

@ -5,6 +5,7 @@ export const searchPosts = gql`
searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) {
postCount
posts {
__typename
id
title
content

View File

@ -0,0 +1,25 @@
import gql from 'graphql-tag'
export const unpinGroupPost = gql`
mutation ($id: ID!) {
unpinGroupPost(id: $id) {
id
title
content
author {
name
slug
}
pinnedBy {
id
name
role
}
createdAt
updatedAt
pinned
pinnedAt
groupPinned
}
}
`

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { GraphQLUpload } from 'graphql-upload'
export default {

View File

@ -130,10 +130,13 @@ export const attachments = (config: S3Config) => {
const { upload } = fileInput
if (!upload) throw new UserInputError('Cannot find attachment for given resource')
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const uploadFile = await upload
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
const { name: fileName, ext } = path.parse(uploadFile.filename)
const uniqueFilename = `${uuid()}-${slug(fileName)}${ext}`
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
const url = await s3.uploadFile({
...uploadFile,
uniqueFilename,

View File

@ -1,14 +1,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import gql from 'graphql-tag'
import { TROPHY_BADGES_SELECTED_MAX } from '@constants/badges'
import Factory, { cleanDatabase } from '@db/factories'
import { revokeBadge } from '@graphql/queries/revokeBadge'
import { rewardTrophyBadge } from '@graphql/queries/rewardTrophyBadge'
import { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected'
import { setVerificationBadge } from '@graphql/queries/setVerificationBadge'
import { User } from '@graphql/queries/User'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -800,24 +799,6 @@ describe('Badges', () => {
describe('check test setup', () => {
it('user has one badge and has it selected', async () => {
authenticatedUser = await regularUser.toJson()
const userQuery = gql`
{
User(id: "regular-user-id") {
badgeTrophiesCount
badgeTrophies {
id
}
badgeVerification {
id
isDefault
}
badgeTrophiesSelected {
id
isDefault
}
}
}
`
const expected = {
data: {
User: [
@ -871,7 +852,9 @@ describe('Badges', () => {
},
errors: undefined,
}
await expect(query({ query: userQuery })).resolves.toMatchObject(expected)
await expect(
query({ query: User, variables: { id: 'regular-user-id' } }),
).resolves.toMatchObject(expected)
})
})

View File

@ -1,6 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import fs from 'node:fs'
import path from 'node:path'

View File

@ -2,59 +2,39 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getDriver, getNeode } from '@db/neo4j'
import { followUser } from '@graphql/queries/followUser'
import { unfollowUser } from '@graphql/queries/unfollowUser'
import createServer from '@src/server'
import { User } from '@graphql/queries/User'
import { createApolloTestSetup } from '@root/test/helpers'
import type { ApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const driver = getDriver()
const neode = getNeode()
let query
let mutate
let authenticatedUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
let user1
let user2
let variables
const userQuery = gql`
query ($id: ID) {
User(id: $id) {
followedBy {
id
}
followedByCurrentUser
}
}
`
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => ({
driver,
neode,
user: authenticatedUser,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}),
})
const testClient = createTestClient(server)
query = testClient.query
mutate = testClient.mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(async () => {
@ -129,7 +109,7 @@ describe('follow', () => {
mutation: followUser,
variables,
})
const relation = await neode.cypher(
const relation = await database.neode.cypher(
'MATCH (user:User {id: $id})-[relationship:FOLLOWS]->(followed:User) WHERE relationship.createdAt IS NOT NULL RETURN relationship',
{ id: 'u1' },
)
@ -152,7 +132,7 @@ describe('follow', () => {
}
await expect(
query({
query: userQuery,
query: User,
variables: { id: user1.id },
}),
).resolves.toMatchObject({

View File

@ -891,8 +891,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-closed-group',
myRoleInGroup: 'usual',
user: {
id: 'owner-of-closed-group',
},
membership: {
role: 'usual',
},
},
},
errors: undefined,
@ -914,8 +918,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'current-user',
myRoleInGroup: 'owner',
user: {
id: 'current-user',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -939,8 +947,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'current-user',
myRoleInGroup: 'pending',
user: {
id: 'current-user',
},
membership: {
role: 'pending',
},
},
},
errors: undefined,
@ -962,8 +974,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-closed-group',
myRoleInGroup: 'owner',
user: {
id: 'owner-of-closed-group',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -1001,8 +1017,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
user: {
id: 'owner-of-hidden-group',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -1208,16 +1228,28 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
]),
},
@ -1241,16 +1273,28 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
]),
},
@ -1274,16 +1318,28 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
]),
},
@ -1317,16 +1373,28 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'pending',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'pending',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
]),
},
@ -1350,16 +1418,28 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'pending',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'pending',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
]),
},
@ -1415,20 +1495,36 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
user: expect.objectContaining({
id: 'pending-user',
}),
membership: expect.objectContaining({
role: 'pending',
}),
}),
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'admin',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'admin',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
]),
},
@ -1452,20 +1548,36 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
user: expect.objectContaining({
id: 'pending-user',
}),
membership: expect.objectContaining({
role: 'pending',
}),
}),
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'admin',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'admin',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
]),
},
@ -1489,20 +1601,36 @@ describe('in mode', () => {
data: {
GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
user: expect.objectContaining({
id: 'pending-user',
}),
membership: expect.objectContaining({
role: 'pending',
}),
}),
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'usual',
user: expect.objectContaining({
id: 'current-user',
}),
membership: expect.objectContaining({
role: 'usual',
}),
}),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'admin',
user: expect.objectContaining({
id: 'owner-of-closed-group',
}),
membership: expect.objectContaining({
role: 'admin',
}),
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
user: expect.objectContaining({
id: 'owner-of-hidden-group',
}),
membership: expect.objectContaining({
role: 'owner',
}),
}),
]),
},
@ -1600,8 +1728,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'usual',
user: {
id: 'usual-member-user',
},
membership: {
role: 'usual',
},
},
},
errors: undefined,
@ -1638,8 +1770,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'admin-member-user',
myRoleInGroup: 'admin',
user: {
id: 'admin-member-user',
},
membership: {
role: 'admin',
},
},
},
errors: undefined,
@ -1673,8 +1809,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'second-owner-member-user',
myRoleInGroup: 'owner',
user: {
id: 'second-owner-member-user',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -1759,8 +1899,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'owner-member-user',
myRoleInGroup: 'owner',
user: {
id: 'owner-member-user',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -1869,8 +2013,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'admin-member-user',
myRoleInGroup: 'owner',
user: {
id: 'admin-member-user',
},
membership: {
role: 'owner',
},
},
},
errors: undefined,
@ -2047,8 +2195,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'admin',
user: {
id: 'usual-member-user',
},
membership: {
role: 'admin',
},
},
},
errors: undefined,
@ -2073,8 +2225,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'usual',
user: {
id: 'usual-member-user',
},
membership: {
role: 'usual',
},
},
},
errors: undefined,
@ -2234,8 +2390,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'pending-member-user',
myRoleInGroup: 'usual',
user: {
id: 'pending-member-user',
},
membership: {
role: 'usual',
},
},
},
errors: undefined,
@ -2260,8 +2420,12 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'pending-member-user',
myRoleInGroup: 'pending',
user: {
id: 'pending-member-user',
},
membership: {
role: 'pending',
},
},
},
errors: undefined,
@ -2413,7 +2577,7 @@ describe('in mode', () => {
},
})
return result.data?.GroupMembers
? !!result.data.GroupMembers.find((member) => member.id === userId)
? !!result.data.GroupMembers.find((member) => member.user.id === userId)
: null
}
@ -2440,8 +2604,10 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
LeaveGroup: {
id: 'pending-member-user',
myRoleInGroup: null,
user: {
id: 'pending-member-user',
},
membership: null,
},
},
errors: undefined,
@ -2467,8 +2633,10 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
LeaveGroup: {
id: 'usual-member-user',
myRoleInGroup: null,
user: {
id: 'usual-member-user',
},
membership: null,
},
},
errors: undefined,
@ -2494,8 +2662,10 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
LeaveGroup: {
id: 'admin-member-user',
myRoleInGroup: null,
user: {
id: 'admin-member-user',
},
membership: null,
},
},
errors: undefined,
@ -3021,8 +3191,10 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
RemoveUserFromGroup: expect.objectContaining({
id: 'usual-member-user',
myRoleInGroup: null,
user: expect.objectContaining({
id: 'usual-member-user',
}),
membership: null,
}),
},
errors: undefined,
@ -3093,8 +3265,10 @@ describe('in mode', () => {
).resolves.toMatchObject({
data: {
RemoveUserFromGroup: expect.objectContaining({
id: 'usual-member-user',
myRoleInGroup: null,
user: {
id: 'usual-member-user',
},
membership: null,
}),
},
errors: undefined,

View File

@ -24,9 +24,6 @@ export default {
Query: {
Group: async (_object, params, context: Context, _resolveInfo) => {
const { isMember, id, slug, first, offset } = params
let pagination = ''
const orderBy = 'ORDER BY group.createdAt DESC'
if (first !== undefined && offset !== undefined) pagination = `SKIP ${offset} LIMIT ${first}`
const matchParams = { id, slug }
removeUndefinedNullValuesFromObject(matchParams)
const session = context.driver.session()
@ -34,43 +31,22 @@ export default {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const groupMatchParamsCypher = convertObjectToCypherMapLiteral(matchParams, true)
let groupCypher
if (isMember === true) {
groupCypher = `
MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group${groupMatchParamsCypher})
WITH group, membership
WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])
RETURN group {.*, myRole: membership.role}
${orderBy}
${pagination}
const transactionResponse = await txc.run(
`
} else {
if (isMember === false) {
groupCypher = `
MATCH (group:Group${groupMatchParamsCypher})
WHERE (NOT (:User {id: $userId})-[:MEMBER_OF]->(group))
WITH group
WHERE group.groupType IN ['public', 'closed']
RETURN group {.*, myRole: NULL}
${orderBy}
${pagination}
`
} else {
groupCypher = `
MATCH (group:Group${groupMatchParamsCypher})
OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group)
WITH group, membership
WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])
RETURN group {.*, myRole: membership.role}
${orderBy}
${pagination}
`
}
}
const transactionResponse = await txc.run(groupCypher, {
userId: context.user.id,
})
MATCH (group:Group${convertObjectToCypherMapLiteral(matchParams, true)})
OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group)
WITH group, membership
${(isMember === true && "WHERE membership IS NOT NULL AND (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])") || ''}
${(isMember === false && "WHERE membership IS NULL AND (group.groupType IN ['public', 'closed'])") || ''}
${(isMember === undefined && "WHERE (group.groupType IN ['public', 'closed']) OR (group.groupType = 'hidden' AND membership.role IN ['usual', 'admin', 'owner'])") || ''}
RETURN group {.*, myRole: membership.role}
ORDER BY group.createdAt DESC
${first !== undefined && offset !== undefined ? `SKIP ${offset} LIMIT ${first}` : ''}
`,
{
userId: context.user.id,
},
)
return transactionResponse.records.map((record) => record.get('group'))
})
try {
@ -87,7 +63,7 @@ export default {
const readTxResultPromise = session.readTransaction(async (txc) => {
const groupMemberCypher = `
MATCH (user:User)-[membership:MEMBER_OF]->(:Group {id: $groupId})
RETURN user {.*, myRoleInGroup: membership.role}
RETURN user {.*}, membership {.*}
SKIP toInteger($offset) LIMIT toInteger($first)
`
const transactionResponse = await txc.run(groupMemberCypher, {
@ -95,7 +71,9 @@ export default {
first,
offset,
})
return transactionResponse.records.map((record) => record.get('user'))
return transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
})
try {
return await readTxResultPromise
@ -297,8 +275,8 @@ export default {
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const joinGroupCypher = `
MATCH (member:User {id: $userId}), (group:Group {id: $groupId})
MERGE (member)-[membership:MEMBER_OF]->(group)
MATCH (user:User {id: $userId}), (group:Group {id: $groupId})
MERGE (user)-[membership:MEMBER_OF]->(group)
ON CREATE SET
membership.createdAt = toString(datetime()),
membership.updatedAt = null,
@ -307,14 +285,15 @@ export default {
THEN 'usual'
ELSE 'pending'
END
RETURN member {.*, myRoleInGroup: membership.role}
RETURN user {.*}, membership {.*}
`
const transactionResponse = await transaction.run(joinGroupCypher, { groupId, userId })
const [member] = transactionResponse.records.map((record) => record.get('member'))
return member
return transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
})
try {
return await writeTxResultPromise
return (await writeTxResultPromise)[0]
} catch (error) {
throw new Error(error)
} finally {
@ -361,7 +340,7 @@ export default {
membership.updatedAt = toString(datetime()),
membership.role = $roleInGroup
${postRestrictionCypher}
RETURN member {.*, myRoleInGroup: membership.role}
RETURN member {.*} as user, membership {.*}
`
const transactionResponse = await transaction.run(joinGroupCypher, {
@ -369,7 +348,9 @@ export default {
userId,
roleInGroup,
})
const [member] = transactionResponse.records.map((record) => record.get('member'))
const [member] = transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
return member
})
try {
@ -460,6 +441,23 @@ export default {
},
},
Group: {
myRole: async (parent, _args, context: Context, _resolveInfo) => {
if (!parent.id) {
throw new Error('Can not identify selected Group!')
}
return (
await context.database.query({
query: `
MATCH (:User {id: $user.id})-[membership:MEMBER_OF]->(group:Group {id: $parent.id})
RETURN membership.role as role
`,
variables: {
user: context.user,
parent,
},
})
).records.map((r) => r.get('role'))[0]
},
inviteCodes: async (parent, _args, context: Context, _resolveInfo) => {
if (!parent.id) {
throw new Error('Can not identify selected Group!')
@ -478,6 +476,18 @@ export default {
})
).records.map((r) => r.get('inviteCodes'))
},
currentlyPinnedPostsCount: async (parent, _args, context: Context, _resolveInfo) => {
if (!parent.id) {
throw new Error('Can not identify selected Group!')
}
const result = await context.database.query({
query: `
MATCH (:User)-[pinned:GROUP_PINNED]->(pinnedPosts:Post)-[:IN]->(:Group {id: $group.id})
RETURN toString(count(pinnedPosts)) as count`,
variables: { group: parent },
})
return result.records[0].get('count')
},
...Resolver('Group', {
undefinedToNull: ['deleted', 'disabled', 'locationName', 'about'],
hasMany: {
@ -523,14 +533,16 @@ const removeUserFromGroupWriteTxResultPromise = async (session, groupId, userId)
WITH user, collect(p) AS posts
FOREACH (post IN posts |
MERGE (user)-[:CANNOT_SEE]->(post))
RETURN user {.*, myRoleInGroup: NULL}
RETURN user {.*}, NULL as membership
`
const transactionResponse = await transaction.run(removeUserFromGroupCypher, {
groupId,
userId,
})
const [user] = await transactionResponse.records.map((record) => record.get('user'))
const [user] = await transactionResponse.records.map((record) => {
return { user: record.get('user'), membership: record.get('membership') }
})
return user
})
}

View File

@ -84,9 +84,12 @@ export const images = (config: S3Config) => {
const uploadImageFile = async (uploadPromise: Promise<FileUpload> | undefined) => {
if (!uploadPromise) return undefined
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const upload = await uploadPromise
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-member-access
const { name, ext } = path.parse(upload.filename)
const uniqueFilename = `${uuid()}-${slug(name)}${ext}`
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
return await s3.uploadFile({ ...upload, uniqueFilename })
}

View File

@ -1089,16 +1089,24 @@ describe('redeemInviteCode', () => {
data: {
GroupMembers: expect.arrayContaining([
{
id: 'inviting-user',
myRoleInGroup: 'owner',
name: 'Inviting User',
slug: 'inviting-user',
user: {
id: 'inviting-user',
name: 'Inviting User',
slug: 'inviting-user',
},
membership: {
role: 'owner',
},
},
{
id: 'other-user',
myRoleInGroup: 'pending',
name: 'Other User',
slug: 'other-user',
user: {
id: 'other-user',
name: 'Other User',
slug: 'other-user',
},
membership: {
role: 'pending',
},
},
]),
},

View File

@ -1,37 +1,34 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import createServer from '@src/server'
import { UpdateUser } from '@graphql/queries/UpdateUser'
import { User } from '@graphql/queries/User'
import { createApolloTestSetup } from '@root/test/helpers'
import type { ApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
let query, mutate, authenticatedUser
const driver = getDriver()
const neode = getNeode()
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode,
user: authenticatedUser,
}
},
})
query = createTestClient(server).query
mutate = createTestClient(server).mutate
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
void server.stop()
void database.driver.close()
database.neode.close()
})
// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543
@ -43,17 +40,6 @@ describe('resolvers', () => {
describe('Location', () => {
describe('custom mutation, not handled by neo4j-graphql-js', () => {
let variables
const updateUserMutation = gql`
mutation ($id: ID!, $name: String) {
UpdateUser(id: $id, name: $name) {
name
location {
name: nameRU
nameEN
}
}
}
`
beforeEach(async () => {
variables = {
@ -78,12 +64,12 @@ describe('resolvers', () => {
})
it('returns `null` if location translation is not available', async () => {
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({
data: {
UpdateUser: {
name: 'John Doughnut',
location: {
name: null,
nameRU: null,
nameEN: 'Paris',
},
},
@ -95,15 +81,6 @@ describe('resolvers', () => {
})
})
const distanceToMeQuery = gql`
query ($id: ID!) {
User(id: $id) {
location {
distanceToMe
}
}
}
`
let user, myPlaceUser, otherPlaceUser, noCordsPlaceUser, noPlaceUser
describe('distanceToMe', () => {
@ -191,21 +168,19 @@ describe('distanceToMe', () => {
authenticatedUser = await user.toJson()
const targetUser = await user.toJson()
await expect(
query({ query: distanceToMeQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
location: {
distanceToMe: 0,
},
query({ query: User, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: {
User: [
expect.objectContaining({
location: {
distanceToMe: 0,
},
],
},
errors: undefined,
}),
)
}),
],
},
errors: undefined,
})
})
})
@ -214,21 +189,19 @@ describe('distanceToMe', () => {
authenticatedUser = await user.toJson()
const targetUser = await myPlaceUser.toJson()
await expect(
query({ query: distanceToMeQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
location: {
distanceToMe: 0,
},
query({ query: User, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: {
User: [
expect.objectContaining({
location: {
distanceToMe: 0,
},
],
},
errors: undefined,
}),
)
}),
],
},
errors: undefined,
})
})
})
@ -237,21 +210,19 @@ describe('distanceToMe', () => {
authenticatedUser = await user.toJson()
const targetUser = await otherPlaceUser.toJson()
await expect(
query({ query: distanceToMeQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
location: {
distanceToMe: 746,
},
query({ query: User, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: {
User: [
expect.objectContaining({
location: {
distanceToMe: 746,
},
],
},
errors: undefined,
}),
)
}),
],
},
errors: undefined,
})
})
})
@ -260,21 +231,19 @@ describe('distanceToMe', () => {
authenticatedUser = await user.toJson()
const targetUser = await noCordsPlaceUser.toJson()
await expect(
query({ query: distanceToMeQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
location: {
distanceToMe: null,
},
query({ query: User, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: {
User: [
expect.objectContaining({
location: {
distanceToMe: null,
},
],
},
errors: undefined,
}),
)
}),
],
},
errors: undefined,
})
})
})
@ -283,19 +252,17 @@ describe('distanceToMe', () => {
authenticatedUser = await user.toJson()
const targetUser = await noPlaceUser.toJson()
await expect(
query({ query: distanceToMeQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
location: null,
},
],
},
errors: undefined,
}),
)
query({ query: User, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: {
User: [
expect.objectContaining({
location: null,
}),
],
},
errors: undefined,
})
})
})
})

View File

@ -165,44 +165,46 @@ describe('given some notifications', () => {
describe('no filters', () => {
it('returns all notifications of current user', async () => {
const expected = [
{
from: {
__typename: 'Comment',
content: 'You have seen this comment mentioning already',
},
read: true,
createdAt: '2019-08-30T15:33:48.651Z',
},
{
from: {
__typename: 'Post',
content: 'Already seen post mention',
},
read: true,
createdAt: '2019-08-30T17:33:48.651Z',
},
{
from: {
__typename: 'Comment',
content: 'You have been mentioned in a comment',
},
read: false,
createdAt: '2019-08-30T19:33:48.651Z',
},
{
from: {
__typename: 'Post',
content: 'You have been mentioned in a post',
},
read: false,
createdAt: '2019-08-31T17:33:48.651Z',
},
]
await expect(query({ query: notifications, variables })).resolves.toMatchObject({
data: {
notifications: expect.arrayContaining(expected),
notifications: expect.arrayContaining([
expect.objectContaining({
from: {
__typename: 'Comment',
content: 'You have seen this comment mentioning already',
id: 'c1',
},
read: true,
createdAt: '2019-08-30T15:33:48.651Z',
}),
expect.objectContaining({
from: {
__typename: 'Post',
content: 'Already seen post mention',
id: 'p2',
},
read: true,
createdAt: '2019-08-30T17:33:48.651Z',
}),
expect.objectContaining({
from: {
__typename: 'Comment',
content: 'You have been mentioned in a comment',
id: 'c2',
},
read: false,
createdAt: '2019-08-30T19:33:48.651Z',
}),
expect.objectContaining({
from: {
__typename: 'Post',
content: 'You have been mentioned in a post',
id: 'p3',
},
read: false,
createdAt: '2019-08-31T17:33:48.651Z',
}),
]),
},
errors: undefined,
})
@ -211,33 +213,34 @@ describe('given some notifications', () => {
describe('filter for read: false', () => {
it('returns only unread notifications of current user', async () => {
const expected = expect.objectContaining({
data: {
notifications: expect.arrayContaining([
{
from: {
__typename: 'Comment',
content: 'You have been mentioned in a comment',
},
read: false,
createdAt: '2019-08-30T19:33:48.651Z',
},
{
from: {
__typename: 'Post',
content: 'You have been mentioned in a post',
},
read: false,
createdAt: '2019-08-31T17:33:48.651Z',
},
]),
},
})
const response = await query({
query: notifications,
variables: { ...variables, read: false },
})
await expect(response).toMatchObject(expected)
await expect(response).toMatchObject({
data: {
notifications: expect.arrayContaining([
expect.objectContaining({
from: {
__typename: 'Comment',
content: 'You have been mentioned in a comment',
id: 'c2',
},
read: false,
createdAt: '2019-08-30T19:33:48.651Z',
}),
expect.objectContaining({
from: {
__typename: 'Post',
content: 'You have been mentioned in a post',
id: 'p3',
},
read: false,
createdAt: '2019-08-31T17:33:48.651Z',
}),
]),
},
})
await expect(response.data?.notifications).toHaveLength(2) // double-check
})
@ -394,11 +397,13 @@ describe('given some notifications', () => {
{
createdAt: '2019-08-30T19:33:48.651Z',
from: { __typename: 'Comment', content: 'You have been mentioned in a comment' },
id: 'mentioned_in_comment/c2/you',
read: true,
},
{
createdAt: '2019-08-31T17:33:48.651Z',
from: { __typename: 'Post', content: 'You have been mentioned in a post' },
id: 'mentioned_in_post/p3/you',
read: true,
},
]),

View File

@ -0,0 +1,368 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import Factory, { cleanDatabase } from '@db/factories'
import { ChangeGroupMemberRole } from '@graphql/queries/ChangeGroupMemberRole'
import { CreateGroup } from '@graphql/queries/CreateGroup'
import { CreatePost } from '@graphql/queries/CreatePost'
import { pinGroupPost } from '@graphql/queries/pinGroupPost'
import { profilePagePosts } from '@graphql/queries/profilePagePosts'
import { unpinGroupPost } from '@graphql/queries/unpinGroupPost'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
const defaultConfig = {
CATEGORIES_ACTIVE: false,
}
let config: Partial<Context['config']>
let anyUser
let allGroupsUser
let publicUser
let publicAdminUser
let authenticatedUser: Context['user']
const context = () => ({ authenticatedUser, config })
let mutate: ApolloTestSetup['mutate']
let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
beforeAll(async () => {
await cleanDatabase()
const apolloSetup = createApolloTestSetup({ context })
mutate = apolloSetup.mutate
query = apolloSetup.query
database = apolloSetup.database
server = apolloSetup.server
})
afterAll(() => {
void server.stop()
void database.driver.close()
database.neode.close()
})
beforeEach(async () => {
config = { ...defaultConfig }
authenticatedUser = null
anyUser = await Factory.build('user', {
id: 'any-user',
name: 'Any User',
about: 'I am just an ordinary user and do not belong to any group.',
})
allGroupsUser = await Factory.build('user', {
id: 'all-groups-user',
name: 'All Groups User',
about: 'I am a member of all groups.',
})
publicUser = await Factory.build('user', {
id: 'public-user',
name: 'Public User',
about: 'I am the owner of the public group.',
})
publicAdminUser = await Factory.build('user', {
id: 'public-admin-user',
name: 'Public Admin User',
about: 'I am the admin of the public group.',
})
authenticatedUser = await publicUser.toJson()
await mutate({
mutation: CreateGroup,
variables: {
id: 'public-group',
name: 'The Public Group',
about: 'The public group!',
description: 'Anyone can see the posts of this group.',
groupType: 'public',
actionRadius: 'regional',
},
})
await mutate({
mutation: ChangeGroupMemberRole,
variables: {
groupId: 'public-group',
userId: 'all-groups-user',
roleInGroup: 'usual',
},
})
await mutate({
mutation: ChangeGroupMemberRole,
variables: {
groupId: 'public-group',
userId: 'public-admin-user',
roleInGroup: 'admin',
},
})
await mutate({
mutation: ChangeGroupMemberRole,
variables: {
groupId: 'closed-group',
userId: 'all-groups-user',
roleInGroup: 'usual',
},
})
authenticatedUser = await anyUser.toJson()
await mutate({
mutation: CreatePost,
variables: {
id: 'post-without-group',
title: 'A post without a group',
content: 'I am a user who does not belong to a group yet.',
},
})
authenticatedUser = await publicUser.toJson()
await mutate({
mutation: CreatePost,
variables: {
id: 'post-1-to-public-group',
title: 'Post 1 to a public group',
content: 'I am posting into a public group as a member of the group',
groupId: 'public-group',
},
})
await mutate({
mutation: CreatePost,
variables: {
id: 'post-2-to-public-group',
title: 'Post 1 to a public group',
content: 'I am posting into a public group as a member of the group',
groupId: 'public-group',
},
})
await mutate({
mutation: CreatePost,
variables: {
id: 'post-3-to-public-group',
title: 'Post 1 to a public group',
content: 'I am posting into a public group as a member of the group',
groupId: 'public-group',
},
})
})
afterEach(async () => {
await cleanDatabase()
})
describe('pin groupPosts', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinGroupPost: null },
})
})
})
describe('ordinary users', () => {
it('throws authorization error', async () => {
authenticatedUser = await anyUser.toJson()
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinGroupPost: null },
})
})
})
describe('group usual', () => {
it('throws authorization error', async () => {
authenticatedUser = await allGroupsUser.toJson()
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { pinGroupPost: null },
})
})
})
describe('group admin', () => {
it('resolves without error', async () => {
authenticatedUser = await publicAdminUser.toJson()
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: undefined,
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
})
})
})
describe('group owner', () => {
it('resolves without error', async () => {
authenticatedUser = await publicUser.toJson()
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: undefined,
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
})
})
})
describe('MAX_GROUP_PINNED_POSTS is 1', () => {
beforeEach(async () => {
config = { ...defaultConfig, MAX_GROUP_PINNED_POSTS: 1 }
authenticatedUser = await publicUser.toJson()
})
it('returns post-1-to-public-group as first, pinned post', async () => {
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await expect(
query({
query: profilePagePosts,
variables: {
filter: { group: { id: 'public-group' } },
orderBy: ['groupPinned_asc', 'sortDate_desc'],
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
profilePagePosts: [
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: null }),
],
},
})
})
it('no error thrown when pinned post was pinned again', async () => {
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } }),
).resolves.toMatchObject({
errors: undefined,
data: { pinGroupPost: { id: 'post-1-to-public-group', groupPinned: true } },
})
})
it('returns post-2-to-public-group as first, pinned post', async () => {
authenticatedUser = await publicUser.toJson()
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
await expect(
query({
query: profilePagePosts,
variables: {
filter: { group: { id: 'public-group' } },
orderBy: ['groupPinned_asc', 'sortDate_desc'],
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
profilePagePosts: [
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
],
},
})
})
it('returns post-3-to-public-group as first, pinned post, when multiple are pinned', async () => {
authenticatedUser = await publicUser.toJson()
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
await mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } })
await expect(
query({
query: profilePagePosts,
variables: {
filter: { group: { id: 'public-group' } },
orderBy: ['groupPinned_asc', 'sortDate_desc'],
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
profilePagePosts: [
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: null }),
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
],
},
})
})
})
describe('MAX_GROUP_PINNED_POSTS is 2', () => {
beforeEach(async () => {
config = { ...defaultConfig, MAX_GROUP_PINNED_POSTS: 2 }
authenticatedUser = await publicUser.toJson()
})
it('returns post-1-to-public-group as first, post-2-to-public-group as second pinned post', async () => {
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
await expect(
query({
query: profilePagePosts,
variables: {
filter: { group: { id: 'public-group' } },
orderBy: ['groupPinned_asc', 'sortDate_desc'],
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
profilePagePosts: [
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: null }),
],
},
})
})
it('throws an error when three posts are pinned', async () => {
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } }),
).resolves.toMatchObject({
errors: [{ message: 'Reached maxed pinned posts already. Unpin a post first.' }],
data: {
pinGroupPost: null,
},
})
})
it('throws no error when first unpinned before a third post is pinned', async () => {
await mutate({ mutation: pinGroupPost, variables: { id: 'post-1-to-public-group' } })
await mutate({ mutation: pinGroupPost, variables: { id: 'post-2-to-public-group' } })
await mutate({ mutation: unpinGroupPost, variables: { id: 'post-1-to-public-group' } })
await expect(
mutate({ mutation: pinGroupPost, variables: { id: 'post-3-to-public-group' } }),
).resolves.toMatchObject({
errors: undefined,
})
await expect(
query({
query: profilePagePosts,
variables: {
filter: { group: { id: 'public-group' } },
orderBy: ['groupPinned_asc', 'sortDate_desc'],
},
}),
).resolves.toMatchObject({
errors: undefined,
data: {
profilePagePosts: [
expect.objectContaining({ id: 'post-3-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-2-to-public-group', groupPinned: true }),
expect.objectContaining({ id: 'post-1-to-public-group', groupPinned: null }),
],
},
})
})
})
})

View File

@ -1,10 +1,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { CreateComment } from '@graphql/queries/CreateComment'
import { CreatePost } from '@graphql/queries/CreatePost'
import { Post } from '@graphql/queries/Post'
import { toggleObservePost } from '@graphql/queries/toggleObservePost'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
@ -20,25 +20,6 @@ let query: ApolloTestSetup['query']
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const createCommentMutation = gql`
mutation ($id: ID, $postId: ID!, $content: String!) {
CreateComment(id: $id, postId: $postId, content: $content) {
id
isPostObservedByMe
postObservingUsersCount
}
}
`
const postQuery = gql`
query Post($id: ID) {
Post(id: $id) {
isObservedByMe
observingUsersCount
}
}
`
beforeAll(async () => {
await cleanDatabase()
const apolloSetup = createApolloTestSetup({ context })
@ -101,7 +82,7 @@ describe('observing posts', () => {
it('has another user NOT observing the post BEFORE commenting it', async () => {
await expect(
query({
query: postQuery,
query: Post,
variables: { id: 'p2' },
}),
).resolves.toMatchObject({
@ -120,7 +101,7 @@ describe('observing posts', () => {
it('has another user observing the post AFTER commenting it', async () => {
await expect(
mutate({
mutation: createCommentMutation,
mutation: CreateComment,
variables: {
postId: 'p2',
content: 'After commenting the post, I should observe the post automatically',
@ -137,7 +118,7 @@ describe('observing posts', () => {
await expect(
query({
query: postQuery,
query: Post,
variables: { id: 'p2' },
}),
).resolves.toMatchObject({
@ -185,7 +166,7 @@ describe('observing posts', () => {
it('does NOT alter the observation state', async () => {
await expect(
mutate({
mutation: createCommentMutation,
mutation: CreateComment,
variables: {
postId: 'p2',
content:
@ -203,7 +184,7 @@ describe('observing posts', () => {
await expect(
query({
query: postQuery,
query: Post,
variables: { id: 'p2' },
}),
).resolves.toMatchObject({

View File

@ -2,8 +2,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import Image from '@db/models/Image'
import { AddPostEmotions } from '@graphql/queries/AddPostEmotions'
@ -18,6 +16,7 @@ import { pushPost } from '@graphql/queries/pushPost'
import { RemovePostEmotions } from '@graphql/queries/RemovePostEmotions'
import { unpinPost } from '@graphql/queries/unpinPost'
import { unpushPost } from '@graphql/queries/unpushPost'
import { UpdatePost } from '@graphql/queries/UpdatePost'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -133,18 +132,14 @@ describe('Post', () => {
describe('no filter', () => {
it('returns all posts', async () => {
const postQueryNoFilters = gql`
query Post($filter: _PostFilter) {
Post(filter: $filter) {
id
}
}
`
const expected = [{ id: 'happy-post' }, { id: 'cry-post' }, { id: 'post-by-followed-user' }]
variables = { filter: {} }
await expect(query({ query: postQueryNoFilters, variables })).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: expect.arrayContaining(expected),
Post: expect.arrayContaining([
expect.objectContaining({ id: 'happy-post' }),
expect.objectContaining({ id: 'cry-post' }),
expect.objectContaining({ id: 'post-by-followed-user' }),
]),
},
})
})
@ -178,17 +173,6 @@ describe('Post', () => {
}) */
describe('by emotions', () => {
const postQueryFilteredByEmotions = gql`
query Post($filter: _PostFilter) {
Post(filter: $filter) {
id
emotions {
emotion
}
}
}
`
it('filters by single emotion', async () => {
const expected = {
data: {
@ -202,30 +186,25 @@ describe('Post', () => {
}
await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
await expect(
query({ query: postQueryFilteredByEmotions, variables }),
).resolves.toMatchObject(expected)
await expect(query({ query: Post, variables })).resolves.toMatchObject(expected)
})
it('filters by multiple emotions', async () => {
const expected = [
{
id: 'happy-post',
emotions: [{ emotion: 'happy' }],
},
{
id: 'cry-post',
emotions: [{ emotion: 'cry' }],
},
]
await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
await user.relateTo(cryPost, 'emoted', { emotion: 'cry' })
variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy', 'cry'] } } }
await expect(
query({ query: postQueryFilteredByEmotions, variables }),
).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: expect.arrayContaining(expected),
Post: expect.arrayContaining([
expect.objectContaining({
id: 'happy-post',
emotions: [expect.objectContaining({ emotion: 'happy' })],
}),
expect.objectContaining({
id: 'cry-post',
emotions: [expect.objectContaining({ emotion: 'cry' })],
}),
]),
},
errors: undefined,
})
@ -233,22 +212,9 @@ describe('Post', () => {
})
it('by followed-by', async () => {
const postQueryFilteredByUsersFollowed = gql`
query Post($filter: _PostFilter) {
Post(filter: $filter) {
id
author {
name
}
}
}
`
await user.relateTo(followedUser, 'following')
variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
await expect(
query({ query: postQueryFilteredByUsersFollowed, variables }),
).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: {
Post: [
{
@ -655,48 +621,6 @@ describe('CreatePost', () => {
describe('UpdatePost', () => {
let author, newlyCreatedPost
const updatePostMutation = gql`
mutation (
$id: ID!
$title: String!
$content: String!
$image: ImageInput
$categoryIds: [ID]
$postType: PostType
$eventInput: _EventInput
) {
UpdatePost(
id: $id
title: $title
content: $content
image: $image
categoryIds: $categoryIds
postType: $postType
eventInput: $eventInput
) {
id
title
content
author {
name
slug
}
createdAt
updatedAt
categories {
id
}
postType
eventStart
eventLocationName
eventVenue
eventLocation {
lng
lat
}
}
}
`
beforeEach(async () => {
author = await Factory.build('user', { slug: 'the-author' })
authenticatedUser = await author.toJson()
@ -719,7 +643,7 @@ describe('UpdatePost', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject({
errors: [{ message: 'Not Authorized!' }],
data: { UpdatePost: null },
})
@ -732,7 +656,7 @@ describe('UpdatePost', () => {
})
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: updatePostMutation, variables })
const { errors } = await mutate({ mutation: UpdatePost, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@ -747,9 +671,7 @@ describe('UpdatePost', () => {
data: { UpdatePost: { id: newlyCreatedPost.id, content: 'New content' } },
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
it('updates a post, but maintains non-updated attributes', async () => {
@ -763,18 +685,16 @@ describe('UpdatePost', () => {
},
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
it('updates the updatedAt attribute', async () => {
const {
data: { UpdatePost },
} = (await mutate({ mutation: updatePostMutation, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
expect(UpdatePost.updatedAt).toBeTruthy()
expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
data: { UpdatePost: UpdatePostData },
} = (await mutate({ mutation: UpdatePost, variables })) as any // eslint-disable-line @typescript-eslint/no-explicit-any
expect(UpdatePostData.updatedAt).toBeTruthy()
expect(Date.parse(UpdatePostData.updatedAt)).toEqual(expect.any(Number))
expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePostData.updatedAt)
})
describe('no new category ids provided for update', () => {
@ -788,9 +708,7 @@ describe('UpdatePost', () => {
},
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
})
@ -809,9 +727,7 @@ describe('UpdatePost', () => {
},
errors: undefined,
}
await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdatePost, variables })).resolves.toMatchObject(expected)
})
})
@ -820,7 +736,7 @@ describe('UpdatePost', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: { ...variables, postType: 'Event' },
}),
).resolves.toMatchObject({
@ -837,7 +753,7 @@ describe('UpdatePost', () => {
it('throws an error', async () => {
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
@ -861,7 +777,7 @@ describe('UpdatePost', () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
@ -885,7 +801,7 @@ describe('UpdatePost', () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
@ -910,7 +826,7 @@ describe('UpdatePost', () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
@ -936,7 +852,7 @@ describe('UpdatePost', () => {
const now = new Date()
await expect(
mutate({
mutation: updatePostMutation,
mutation: UpdatePost,
variables: {
...variables,
postType: 'Event',
@ -976,7 +892,7 @@ describe('UpdatePost', () => {
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: updatePostMutation, variables })
await mutate({ mutation: UpdatePost, variables })
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeTruthy()
@ -989,7 +905,7 @@ describe('UpdatePost', () => {
})
it('deletes the image', async () => {
await expect(database.neode.all('Image')).resolves.toHaveLength(6)
await mutate({ mutation: updatePostMutation, variables })
await mutate({ mutation: UpdatePost, variables })
await expect(database.neode.all('Image')).resolves.toHaveLength(5)
})
})
@ -1002,7 +918,7 @@ describe('UpdatePost', () => {
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
await mutate({ mutation: updatePostMutation, variables })
await mutate({ mutation: UpdatePost, variables })
await expect(
database.neode.first<typeof Image>('Image', { sensitive: true }, undefined),
).resolves.toBeFalsy()
@ -2131,25 +2047,6 @@ describe('DeletePost', () => {
describe('emotions', () => {
let author, postToEmote
const PostsEmotionsCountQuery = gql`
query ($id: ID!) {
Post(id: $id) {
emotionsCount
}
}
`
const PostsEmotionsQuery = gql`
query ($id: ID!) {
Post(id: $id) {
emotions {
emotion
User {
id
}
}
}
}
`
beforeEach(async () => {
author = await database.neode.create('User', { id: 'u257' })
@ -2226,8 +2123,8 @@ describe('emotions', () => {
await mutate({ mutation: AddPostEmotions, variables })
await mutate({ mutation: AddPostEmotions, variables })
await expect(
query({ query: PostsEmotionsCountQuery, variables: postsEmotionsQueryVariables }),
).resolves.toEqual(expect.objectContaining(expected))
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expected)
})
it('allows a user to add more than one emotion', async () => {
@ -2247,8 +2144,8 @@ describe('emotions', () => {
variables = { ...variables, data: { emotion: 'surprised' } }
await mutate({ mutation: AddPostEmotions, variables })
await expect(
query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }),
).resolves.toEqual(expect.objectContaining(expected))
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expected)
})
})
@ -2351,8 +2248,8 @@ describe('emotions', () => {
variables: removePostEmotionsVariables,
})
await expect(
query({ query: PostsEmotionsQuery, variables: postsEmotionsQueryVariables }),
).resolves.toEqual(expect.objectContaining(expectedResponse))
query({ query: Post, variables: postsEmotionsQueryVariables }),
).resolves.toMatchObject(expectedResponse)
})
})
})

View File

@ -29,6 +29,20 @@ const maintainPinnedPosts = (params) => {
return params
}
const maintainGroupPinnedPosts = (params) => {
// only show GroupPinnedPosts when Groups is selected
if (!params.filter?.group) {
return params
}
const pinnedPostFilter = { groupPinned: true, group: params.filter.group }
if (isEmpty(params.filter)) {
params.filter = { OR: [pinnedPostFilter, {}] }
} else {
params.filter = { OR: [pinnedPostFilter, { ...params.filter }] }
}
return params
}
const filterEventDates = (params) => {
if (params.filter?.eventStart_gte) {
const date = params.filter.eventStart_gte
@ -52,6 +66,7 @@ export default {
params = await filterPostsOfMyGroups(params, context)
params = await filterInvisiblePosts(params, context)
params = await filterForMutedUsers(params, context)
params = await maintainGroupPinnedPosts(params)
return neo4jgraphql(object, params, context, resolveInfo)
},
PostsEmotionsCountByEmotion: async (_object, params, context, _resolveInfo) => {
@ -154,7 +169,7 @@ export default {
)`
}
const categoriesCypher =
config.CATEGORIES_ACTIVE && categoryIds
config.CATEGORIES_ACTIVE && categoryIds && categoryIds.length > 0
? `WITH post
UNWIND $categoryIds AS categoryId
MATCH (category:Category {id: categoryId})
@ -453,6 +468,68 @@ export default {
}
return unpinnedPost
},
pinGroupPost: async (_parent, params, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('Missing authenticated user.')
}
const { config } = context
if (config.MAX_GROUP_PINNED_POSTS === 0) {
throw new Error('Pinned posts are not allowed!')
}
// If MAX_GROUP_PINNED_POSTS === 1 -> Delete old pin
if (config.MAX_GROUP_PINNED_POSTS === 1) {
await context.database.write({
query: `
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
MATCH (:User)-[pinned:GROUP_PINNED]->(oldPinnedPost:Post)-[:IN]->(:Group {id: group.id})
REMOVE oldPinnedPost.groupPinned
DELETE pinned`,
variables: { user: context.user, params },
})
// If MAX_GROUP_PINNED_POSTS !== 1 -> Check if max is reached
} else {
const result = await context.database.query({
query: `
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
MATCH (:User)-[pinned:GROUP_PINNED]->(pinnedPosts:Post)-[:IN]->(:Group {id: group.id})
RETURN toString(count(pinnedPosts)) as count`,
variables: { user: context.user, params },
})
if (result.records[0].get('count') >= config.MAX_GROUP_PINNED_POSTS) {
throw new Error('Reached maxed pinned posts already. Unpin a post first.')
}
}
// Set new pin
const result = await context.database.write({
query: `
MATCH (user:User {id: $user.id})
MATCH (post:Post {id: $params.id})-[:IN]->(group:Group)
MERGE (user)-[pinned:GROUP_PINNED {createdAt: toString(datetime())}]->(post)
SET post.groupPinned = true
RETURN post {.*, pinnedAt: pinned.createdAt}`,
variables: { user: context.user, params },
})
// Return post
return result.records[0].get('post')
},
unpinGroupPost: async (_parent, params, context, _resolveInfo) => {
const result = await context.database.write({
query: `
MATCH (post:Post {id: $postId})
OPTIONAL MATCH (:User)-[pinned:GROUP_PINNED]->(post)
DELETE pinned
REMOVE post.groupPinned
RETURN post {.*}`,
variables: { postId: params.id },
})
// Return post
return result.records[0].get('post')
},
markTeaserAsViewed: async (_parent, params, context, _resolveInfo) => {
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
@ -550,6 +627,7 @@ export default {
'language',
'pinnedAt',
'pinned',
'groupPinned',
'eventVenue',
'eventLocation',
'eventLocationName',
@ -589,6 +667,21 @@ export default {
'MATCH (this)<-[obs:OBSERVES]-(related:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(related) >= 1',
},
}),
// As long as we rely on the filter capabilities of the neo4jgraphql library,
// we cannot filter on a relation or their properties.
// Hence we need to save the value to the group node in the database.
/* groupPinned: async (parent, _params, context, _resolveInfo) => {
return (
(
await context.database.query({
query: `
MATCH (:User)-[pinned:GROUP_PINNED]->(:Post {id: $parent.id})
RETURN pinned`,
variables: { parent },
})
).records.length === 1
)
}, */
relatedContributions: async (parent, _params, context, _resolveInfo) => {
if (typeof parent.relatedContributions !== 'undefined') return parent.relatedContributions
const { id } = parent

View File

@ -1,12 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import EmailAddress from '@db/models/EmailAddress'
import User from '@db/models/User'
import { Signup } from '@graphql/queries/Signup'
import { SignupVerification } from '@graphql/queries/SignupVerification'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -158,31 +157,6 @@ describe('Signup', () => {
})
describe('SignupVerification', () => {
const mutation = gql`
mutation (
$name: String!
$password: String!
$email: String!
$nonce: String!
$about: String
$termsAndConditionsAgreedVersion: String!
$locale: String
) {
SignupVerification(
name: $name
password: $password
email: $email
nonce: $nonce
about: $about
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
locale: $locale
) {
id
termsAndConditionsAgreedVersion
termsAndConditionsAgreedAt
}
}
`
describe('given valid password and email', () => {
beforeEach(() => {
variables = {
@ -219,7 +193,9 @@ describe('SignupVerification', () => {
})
it('rejects', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
errors: [{ message: 'Invalid email or nonce' }],
})
})
@ -237,7 +213,9 @@ describe('SignupVerification', () => {
describe('sending a valid nonce', () => {
it('creates a user account', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
data: {
SignupVerification: expect.objectContaining({
id: expect.any(String),
@ -247,7 +225,7 @@ describe('SignupVerification', () => {
})
it('sets `verifiedAt` attribute of EmailAddress', async () => {
await mutate({ mutation, variables })
await mutate({ mutation: SignupVerification, variables })
const email = await database.neode.first(
'EmailAddress',
{ email: 'john@example.org' },
@ -265,14 +243,14 @@ describe('SignupVerification', () => {
MATCH(email:EmailAddress)-[:BELONGS_TO]->(u:User {name: $name})
RETURN email
`
await mutate({ mutation, variables })
await mutate({ mutation: SignupVerification, variables })
const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' })
expect(emails).toHaveLength(1)
})
it('sets `about` attribute of User', async () => {
variables = { ...variables, about: 'Find this description in the user profile' }
await mutate({ mutation, variables })
await mutate({ mutation: SignupVerification, variables })
const user = await database.neode.first<typeof User>(
'User',
{ name: 'John Doe' },
@ -285,7 +263,9 @@ describe('SignupVerification', () => {
it('allowing the about field to be an empty string', async () => {
variables = { ...variables, about: '' }
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
data: {
SignupVerification: expect.objectContaining({
id: expect.any(String),
@ -299,13 +279,15 @@ describe('SignupVerification', () => {
MATCH(email:EmailAddress)<-[:PRIMARY_EMAIL]-(u:User {name: $name})
RETURN email
`
await mutate({ mutation, variables })
await mutate({ mutation: SignupVerification, variables })
const { records: emails } = await database.neode.cypher(cypher, { name: 'John Doe' })
expect(emails).toHaveLength(1)
})
it('updates termsAndConditionsAgreedVersion', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
data: {
SignupVerification: expect.objectContaining({
termsAndConditionsAgreedVersion: '0.1.0',
@ -315,7 +297,9 @@ describe('SignupVerification', () => {
})
it('updates termsAndConditionsAgreedAt', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
data: {
SignupVerification: expect.objectContaining({
termsAndConditionsAgreedAt: expect.any(String),
@ -326,7 +310,9 @@ describe('SignupVerification', () => {
it('rejects if version of terms and conditions is missing', async () => {
variables = { ...variables, termsAndConditionsAgreedVersion: null }
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
errors: [
{
message:
@ -338,7 +324,9 @@ describe('SignupVerification', () => {
it('rejects if version of terms and conditions has wrong format', async () => {
variables = { ...variables, termsAndConditionsAgreedVersion: 'invalid version format' }
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
errors: [{ message: 'Invalid version format!' }],
})
})
@ -350,7 +338,9 @@ describe('SignupVerification', () => {
})
it('rejects', async () => {
await expect(mutate({ mutation, variables })).resolves.toMatchObject({
await expect(
mutate({ mutation: SignupVerification, variables }),
).resolves.toMatchObject({
errors: [{ message: 'Invalid email or nonce' }],
})
})

View File

@ -0,0 +1,103 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ApolloServerTestClient, createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '@db/factories'
import { getDriver, getNeode } from '@db/neo4j'
import { availableRoles } from '@graphql/queries/availableRoles'
import createServer from '@src/server'
const instance = getNeode()
const driver = getDriver()
describe('availableRoles', () => {
let authenticatedUser
let query: ApolloServerTestClient['query']
beforeAll(async () => {
await cleanDatabase()
const { server } = createServer({
context: () => {
return {
driver,
neode: instance,
user: authenticatedUser,
}
},
})
query = createTestClient(server).query
})
afterAll(async () => {
await cleanDatabase()
await driver.close()
})
afterEach(async () => {
await cleanDatabase()
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
authenticatedUser = null
const { data, errors } = await query({ query: availableRoles })
expect(data).toEqual(null)
expect(errors).toEqual([expect.objectContaining({ message: 'Not Authorized!' })])
})
})
describe('authenticated', () => {
describe('as user', () => {
beforeEach(async () => {
const user = await Factory.build(
'user',
{ id: 'user-id', role: 'user' },
{ email: 'user@example.org', password: '1234' },
)
authenticatedUser = await user.toJson()
})
it('throws authorization error', async () => {
const { data, errors } = await query({ query: availableRoles })
expect(data).toEqual(null)
expect(errors).toEqual([expect.objectContaining({ message: 'Not Authorized!' })])
})
})
describe('as moderator', () => {
beforeEach(async () => {
const moderator = await Factory.build(
'user',
{ id: 'moderator-id', role: 'moderator' },
{ email: 'moderator@example.org', password: '1234' },
)
authenticatedUser = await moderator.toJson()
})
it('throws authorization error', async () => {
const { data, errors } = await query({ query: availableRoles })
expect(data).toEqual(null)
expect(errors).toEqual([expect.objectContaining({ message: 'Not Authorized!' })])
})
})
describe('as admin', () => {
beforeEach(async () => {
const admin = await Factory.build(
'user',
{ id: 'admin-id', role: 'admin' },
{ email: 'admin@example.org', password: '1234' },
)
authenticatedUser = await admin.toJson()
})
it('returns available roles', async () => {
const { data, errors } = await query({ query: availableRoles })
expect(errors).toBeUndefined()
expect(data?.availableRoles).toEqual(['admin', 'moderator', 'user'])
})
})
})
})

View File

@ -2,10 +2,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { searchPosts } from '@graphql/queries/searchPosts'
import { searchResults } from '@graphql/queries/searchResults'
import createServer from '@src/server'
@ -34,19 +34,6 @@ afterAll(async () => {
await driver.close()
neode.close()
})
const searchPostQuery = gql`
query ($query: String!, $firstPosts: Int, $postsOffset: Int) {
searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) {
postCount
posts {
__typename
id
title
content
}
}
}
`
describe('resolvers/searches', () => {
let variables
@ -605,7 +592,7 @@ und hinter tausend Stäben keine Welt.`,
describe('query with limit 1', () => {
it('has a count greater than 1', async () => {
variables = { query: 'beitrag', firstPosts: 1, postsOffset: 0 }
await expect(query({ query: searchPostQuery, variables })).resolves.toMatchObject({
await expect(query({ query: searchPosts, variables })).resolves.toMatchObject({
data: {
searchPosts: {
postCount: 2,

View File

@ -3,10 +3,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { Post } from '@graphql/queries/Post'
import { shout } from '@graphql/queries/shout'
import { unshout } from '@graphql/queries/unshout'
import createServer from '@src/server'
@ -14,16 +14,6 @@ import createServer from '@src/server'
let mutate, query, authenticatedUser, variables
const instance = getNeode()
const driver = getDriver()
const queryPost = gql`
query ($id: ID!) {
Post(id: $id) {
id
shoutedBy {
id
}
}
}
`
describe('shout and unshout posts', () => {
let currentUser, postAuthor
@ -38,6 +28,9 @@ describe('shout and unshout posts', () => {
driver,
neode: instance,
user: authenticatedUser,
cypherParams: {
currentUserId: authenticatedUser ? authenticatedUser.id : null,
},
}
},
})
@ -122,7 +115,7 @@ describe('shout and unshout posts', () => {
await expect(mutate({ mutation: shout, variables })).resolves.toMatchObject({
data: { shout: true },
})
await expect(query({ query: queryPost, variables })).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: { Post: [{ id: 'another-user-post-id', shoutedBy: [{ id: 'current-user-id' }] }] },
errors: undefined,
})
@ -149,7 +142,7 @@ describe('shout and unshout posts', () => {
await expect(mutate({ mutation: shout, variables })).resolves.toMatchObject({
data: { shout: false },
})
await expect(query({ query: queryPost, variables })).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: { Post: [{ id: 'current-user-post-id', shoutedBy: [] }] },
errors: undefined,
})
@ -191,7 +184,7 @@ describe('shout and unshout posts', () => {
await expect(mutate({ mutation: unshout, variables })).resolves.toMatchObject({
data: { unshout: true },
})
await expect(query({ query: queryPost, variables })).resolves.toMatchObject({
await expect(query({ query: Post, variables })).resolves.toMatchObject({
data: { Post: [{ id: 'posted-by-another-user', shoutedBy: [] }] },
errors: undefined,
})

View File

@ -4,10 +4,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { getDriver } from '@db/neo4j'
import { CreateSocialMedia } from '@graphql/queries/CreateSocialMedia'
import { DeleteSocialMedia } from '@graphql/queries/DeleteSocialMedia'
import { UpdateSocialMedia } from '@graphql/queries/UpdateSocialMedia'
import createServer from '@src/server'
@ -84,24 +84,16 @@ describe('SocialMedia', () => {
})
describe('create social media', () => {
let mutation, variables
let variables
beforeEach(() => {
mutation = gql`
mutation ($url: String!) {
CreateSocialMedia(url: $url) {
id
url
}
}
`
variables = { url }
})
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const user = null
const result = await socialMediaAction(user, mutation, variables)
const result = await socialMediaAction(user, CreateSocialMedia, variables)
expect(result.errors[0]).toHaveProperty('message', 'Not Authorized!')
})
@ -115,21 +107,19 @@ describe('SocialMedia', () => {
})
it('creates social media with the given url', async () => {
await expect(socialMediaAction(user, mutation, variables)).resolves.toEqual(
expect.objectContaining({
data: {
CreateSocialMedia: {
id: expect.any(String),
url,
},
await expect(socialMediaAction(user, CreateSocialMedia, variables)).resolves.toMatchObject({
data: {
CreateSocialMedia: {
id: expect.any(String),
url,
},
}),
)
},
})
})
it('rejects an empty string as url', async () => {
variables = { url: '' }
const result = await socialMediaAction(user, mutation, variables)
const result = await socialMediaAction(user, CreateSocialMedia, variables)
expect(result.errors[0].message).toEqual(
expect.stringContaining('"url" is not allowed to be empty'),
@ -138,7 +128,7 @@ describe('SocialMedia', () => {
it('rejects invalid urls', async () => {
variables = { url: 'not-a-url' }
const result = await socialMediaAction(user, mutation, variables)
const result = await socialMediaAction(user, CreateSocialMedia, variables)
expect(result.errors[0].message).toEqual(
expect.stringContaining('"url" must be a valid uri'),
@ -147,28 +137,13 @@ describe('SocialMedia', () => {
})
describe('ownedBy', () => {
beforeEach(() => {
mutation = gql`
mutation ($url: String!) {
CreateSocialMedia(url: $url) {
url
ownedBy {
name
}
}
}
`
})
it('resolves', async () => {
const user = someUser
await expect(socialMediaAction(user, mutation, variables)).resolves.toEqual(
expect.objectContaining({
data: {
CreateSocialMedia: { url, ownedBy: { name: 'Kalle Blomqvist' } },
},
}),
)
await expect(socialMediaAction(user, CreateSocialMedia, variables)).resolves.toMatchObject({
data: {
CreateSocialMedia: { url, ownedBy: { name: 'Kalle Blomqvist' } },
},
})
})
})
})

View File

@ -6,12 +6,12 @@
/* eslint-disable promise/prefer-await-to-callbacks */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable jest/unbound-method */
import gql from 'graphql-tag'
import { verify } from 'jsonwebtoken'
import { categories } from '@constants/categories'
import Factory, { cleanDatabase } from '@db/factories'
import { changePassword } from '@graphql/queries/changePassword'
import { currentUser } from '@graphql/queries/currentUser'
import { login } from '@graphql/queries/login'
import { saveCategorySettings } from '@graphql/queries/saveCategorySettings'
import { decode } from '@jwt/decode'
@ -86,24 +86,8 @@ afterEach(async () => {
})
describe('currentUser', () => {
const currentUserQuery = gql`
{
currentUser {
id
slug
name
avatar {
url
}
email
role
activeCategories
}
}
`
const respondsWith = async (expected) => {
await expect(query({ query: currentUserQuery, variables })).resolves.toMatchObject(expected)
await expect(query({ query: currentUser, variables })).resolves.toMatchObject(expected)
}
describe('unauthenticated', () => {
@ -211,7 +195,7 @@ describe('currentUser', () => {
})
it('returns only the saved active categories', async () => {
const result = await query({ query: currentUserQuery, variables })
const result = await query({ query: currentUser, variables })
expect(result.data?.currentUser.activeCategories).toHaveLength(4)
expect(result.data?.currentUser.activeCategories).toContain('cat1')
expect(result.data?.currentUser.activeCategories).toContain('cat3')

View File

@ -1,12 +1,9 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { AuthenticationError } from 'apollo-server'
import bcrypt from 'bcryptjs'
import { neo4jgraphql } from 'neo4j-graphql-js'
import { getNeode } from '@db/neo4j'
import { encode } from '@jwt/encode'
@ -18,8 +15,21 @@ const neode = getNeode()
export default {
Query: {
currentUser: async (object, params, context, resolveInfo) =>
neo4jgraphql(object, { id: context.user.id }, context, resolveInfo),
currentUser: async (_object, _params, context: Context, _resolveInfo) => {
if (!context.user) {
throw new Error('You must be logged in')
}
const [user] = (
await context.database.query({
query: `
MATCH (user:User {id: $user.id})-[:PRIMARY_EMAIL]->(e:EmailAddress)
RETURN user {.*, email: e.email}
`,
variables: { user: context.user },
})
).records.map((record) => record.get('user'))
return user
},
},
Mutation: {
login: async (_, { email, password }, context: Context) => {

View File

@ -3,8 +3,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import gql from 'graphql-tag'
import { categories } from '@constants/categories'
import pubsubContext from '@context/pubsub'
import Factory, { cleanDatabase } from '@db/factories'
@ -15,6 +13,8 @@ import { saveCategorySettings } from '@graphql/queries/saveCategorySettings'
import { setTrophyBadgeSelected } from '@graphql/queries/setTrophyBadgeSelected'
import { switchUserRole } from '@graphql/queries/switchUserRole'
import { updateOnlineStatus } from '@graphql/queries/updateOnlineStatus'
import { UpdateUser } from '@graphql/queries/UpdateUser'
import { UserEmailNotificationSettings, User as userQuery } from '@graphql/queries/User'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -63,21 +63,12 @@ afterEach(async () => {
describe('User', () => {
describe('query by email address', () => {
let userQuery
beforeEach(async () => {
const user = await Factory.build('user', {
id: 'user',
role: 'user',
})
authenticatedUser = await user.toJson()
userQuery = gql`
query ($email: String) {
User(email: $email) {
name
}
}
`
variables = {
email: 'any-email-address@example.org',
}
@ -131,35 +122,7 @@ describe('User', () => {
})
describe('UpdateUser', () => {
let updateUserMutation
beforeEach(async () => {
updateUserMutation = gql`
mutation (
$id: ID!
$name: String
$termsAndConditionsAgreedVersion: String
$locationName: String # empty string '' sets it to null
) {
UpdateUser(
id: $id
name: $name
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
locationName: $locationName
) {
id
name
termsAndConditionsAgreedVersion
termsAndConditionsAgreedAt
locationName
location {
name
nameDE
nameEN
}
}
}
`
variables = {
id: 'u47',
name: 'John Doughnut',
@ -196,8 +159,10 @@ describe('UpdateUser', () => {
})
it('is not allowed to change other user accounts', async () => {
const { errors } = await mutate({ mutation: updateUserMutation, variables })
expect(errors?.[0]).toHaveProperty('message', 'Not Authorized!')
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({
data: { UpdateUser: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -216,9 +181,7 @@ describe('UpdateUser', () => {
},
errors: undefined,
}
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected)
})
describe('given a new agreed version of terms and conditions', () => {
@ -236,9 +199,7 @@ describe('UpdateUser', () => {
errors: undefined,
}
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected)
})
})
@ -257,9 +218,7 @@ describe('UpdateUser', () => {
errors: undefined,
}
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject(
expected,
)
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject(expected)
})
})
@ -268,7 +227,7 @@ describe('UpdateUser', () => {
...variables,
termsAndConditionsAgreedVersion: 'invalid version format',
}
const { errors } = await mutate({ mutation: updateUserMutation, variables })
const { errors } = await mutate({ mutation: UpdateUser, variables })
expect(errors?.[0]).toHaveProperty('message', 'Invalid version format!')
})
@ -276,7 +235,7 @@ describe('UpdateUser', () => {
describe('change location to "Hamburg, New Jersey, United States"', () => {
it('has updated location to "Hamburg, New Jersey, United States"', async () => {
variables = { ...variables, locationName: 'Hamburg, New Jersey, United States' }
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({
data: {
UpdateUser: {
locationName: 'Hamburg, New Jersey, United States',
@ -295,7 +254,7 @@ describe('UpdateUser', () => {
describe('change location to unset location', () => {
it('has updated location to unset location', async () => {
variables = { ...variables, locationName: '' }
await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({
await expect(mutate({ mutation: UpdateUser, variables })).resolves.toMatchObject({
data: {
UpdateUser: {
locationName: null,
@ -548,15 +507,10 @@ describe('switch user role', () => {
id: 'user',
role: 'admin',
}
await expect(mutate({ mutation: switchUserRole, variables })).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
await expect(mutate({ mutation: switchUserRole, variables })).resolves.toMatchObject({
data: { switchUserRole: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -598,33 +552,6 @@ describe('switch user role', () => {
})
let anotherUser
const emailNotificationSettingsQuery = gql`
query ($id: ID!) {
User(id: $id) {
emailNotificationSettings {
type
settings {
name
value
}
}
}
}
`
const emailNotificationSettingsMutation = gql`
mutation ($id: ID!, $emailNotificationSettings: [EmailNotificationSettingsInput]!) {
UpdateUser(id: $id, emailNotificationSettings: $emailNotificationSettings) {
emailNotificationSettings {
type
settings {
name
value
}
}
}
}
`
describe('emailNotificationSettings', () => {
beforeEach(async () => {
@ -644,16 +571,11 @@ describe('emailNotificationSettings', () => {
authenticatedUser = await anotherUser.toJson()
const targetUser = await user.toJson()
await expect(
query({ query: emailNotificationSettingsQuery, variables: { id: targetUser.id } }),
).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
query({ query: UserEmailNotificationSettings, variables: { id: targetUser.id } }),
).resolves.toMatchObject({
data: { User: [null] },
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -662,112 +584,13 @@ describe('emailNotificationSettings', () => {
authenticatedUser = await user.toJson()
await expect(
query({
query: emailNotificationSettingsQuery,
query: UserEmailNotificationSettings,
variables: { id: authenticatedUser?.id },
}),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
emailNotificationSettings: [
{
type: 'post',
settings: [
{
name: 'commentOnObservedPost',
value: true,
},
{
name: 'mention',
value: true,
},
{
name: 'followingUsers',
value: true,
},
{
name: 'postInGroup',
value: true,
},
],
},
{
type: 'chat',
settings: [
{
name: 'chatMessage',
value: true,
},
],
},
{
type: 'group',
settings: [
{
name: 'groupMemberJoined',
value: true,
},
{
name: 'groupMemberLeft',
value: true,
},
{
name: 'groupMemberRemoved',
value: true,
},
{
name: 'groupMemberRoleChanged',
value: true,
},
],
},
],
},
],
},
}),
)
})
})
})
describe('mutate the field', () => {
const emailNotificationSettings = [{ name: 'mention', value: false }]
describe('as another user', () => {
it('throws an error', async () => {
authenticatedUser = await anotherUser.toJson()
const targetUser = await user.toJson()
await expect(
mutate({
mutation: emailNotificationSettingsMutation,
variables: { id: targetUser.id, emailNotificationSettings },
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
})
})
describe('as self', () => {
it('updates the emailNotificationSettings', async () => {
authenticatedUser = (await user.toJson()) as DecodedUser
await expect(
mutate({
mutation: emailNotificationSettingsMutation,
variables: { id: authenticatedUser.id, emailNotificationSettings },
}),
).resolves.toEqual(
expect.objectContaining({
data: {
UpdateUser: {
).resolves.toMatchObject({
data: {
User: [
{
emailNotificationSettings: [
{
type: 'post',
@ -778,7 +601,7 @@ describe('emailNotificationSettings', () => {
},
{
name: 'mention',
value: false,
value: true,
},
{
name: 'followingUsers',
@ -822,9 +645,99 @@ describe('emailNotificationSettings', () => {
},
],
},
},
],
},
})
})
})
})
describe('mutate the field', () => {
const emailNotificationSettings = [{ name: 'mention', value: false }]
describe('as another user', () => {
it('throws an error', async () => {
authenticatedUser = await anotherUser.toJson()
const targetUser = await user.toJson()
await expect(
mutate({
mutation: UpdateUser,
variables: { id: targetUser.id, emailNotificationSettings },
}),
)
).resolves.toMatchObject({
data: { UpdateUser: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})
describe('as self', () => {
it('updates the emailNotificationSettings', async () => {
authenticatedUser = (await user.toJson()) as DecodedUser
await expect(
mutate({
mutation: UpdateUser,
variables: { id: authenticatedUser.id, emailNotificationSettings },
}),
).resolves.toMatchObject({
data: {
UpdateUser: {
emailNotificationSettings: [
{
type: 'post',
settings: [
{
name: 'commentOnObservedPost',
value: true,
},
{
name: 'mention',
value: false,
},
{
name: 'followingUsers',
value: true,
},
{
name: 'postInGroup',
value: true,
},
],
},
{
type: 'chat',
settings: [
{
name: 'chatMessage',
value: true,
},
],
},
{
type: 'group',
settings: [
{
name: 'groupMemberJoined',
value: true,
},
{
name: 'groupMemberLeft',
value: true,
},
{
name: 'groupMemberRemoved',
value: true,
},
{
name: 'groupMemberRoleChanged',
value: true,
},
],
},
],
},
},
})
})
})
})
@ -860,15 +773,10 @@ describe('save category settings', () => {
})
it('throws an error', async () => {
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toMatchObject({
data: { saveCategorySettings: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -877,14 +785,6 @@ describe('save category settings', () => {
authenticatedUser = await user.toJson()
})
const userQuery = gql`
query ($id: ID) {
User(id: $id) {
activeCategories
}
}
`
describe('no categories saved', () => {
it('returns true for active categories mutation', async () => {
await expect(mutate({ mutation: saveCategorySettings, variables })).resolves.toEqual(
@ -902,17 +802,15 @@ describe('save category settings', () => {
it('returns the active categories when user is queried', async () => {
await expect(
query({ query: userQuery, variables: { id: authenticatedUser?.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']),
},
],
},
}),
)
).resolves.toMatchObject({
data: {
User: [
{
activeCategories: expect.arrayContaining(['cat1', 'cat3', 'cat5']),
},
],
},
})
})
})
})
@ -944,23 +842,21 @@ describe('save category settings', () => {
it('returns the new active categories when user is queried', async () => {
await expect(
query({ query: userQuery, variables: { id: authenticatedUser?.id } }),
).resolves.toEqual(
expect.objectContaining({
data: {
User: [
{
activeCategories: expect.arrayContaining([
'cat10',
'cat11',
'cat12',
'cat8',
'cat9',
]),
},
],
},
}),
)
).resolves.toMatchObject({
data: {
User: [
{
activeCategories: expect.arrayContaining([
'cat10',
'cat11',
'cat12',
'cat8',
'cat9',
]),
},
],
},
})
})
})
})
@ -984,15 +880,10 @@ describe('updateOnlineStatus', () => {
})
it('throws an error', async () => {
await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
await expect(mutate({ mutation: updateOnlineStatus, variables })).resolves.toMatchObject({
data: null,
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -1122,15 +1013,10 @@ describe('setTrophyBadgeSelected', () => {
mutation: setTrophyBadgeSelected,
variables: { slot: 0, badgeId: 'trophy_bear' },
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
).resolves.toMatchObject({
data: { setTrophyBadgeSelected: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})
@ -1500,15 +1386,10 @@ describe('resetTrophyBadgesSelected', () => {
})
it('throws an error', async () => {
await expect(mutate({ mutation: resetTrophyBadgesSelected })).resolves.toEqual(
expect.objectContaining({
errors: [
expect.objectContaining({
message: 'Not Authorized!',
}),
],
}),
)
await expect(mutate({ mutation: resetTrophyBadgesSelected })).resolves.toMatchObject({
data: { resetTrophyBadgesSelected: null },
errors: [{ message: 'Not Authorized!' }],
})
})
})

View File

@ -1,4 +1,3 @@
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-unsafe-call */
@ -353,14 +352,11 @@ export default {
session.close()
}
},
updateOnlineStatus: async (_object, args, context, _resolveInfo) => {
updateOnlineStatus: async (_object, args, context: Context, _resolveInfo) => {
const { status } = args
const {
user: { id },
} = context
const CYPHER_AWAY = `
MATCH (user:User {id: $id})
MATCH (user:User {id: $user.id})
WITH user,
CASE user.lastOnlineStatus
WHEN 'away' THEN user.awaySince
@ -370,16 +366,14 @@ export default {
SET user.lastOnlineStatus = $status
`
const CYPHER_ONLINE = `
MATCH (user:User {id: $id})
MATCH (user:User {id: $user.id})
SET user.awaySince = null
SET user.lastOnlineStatus = $status
`
// Last Online Time is saved as `lastActiveAt`
const session = context.driver.session()
await session.writeTransaction((transaction) => {
// return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status })
return transaction.run(status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE, { id, status })
await context.database.write({
query: status === 'away' ? CYPHER_AWAY : CYPHER_ONLINE,
variables: { user: context.user, status },
})
return true
@ -463,6 +457,18 @@ export default {
},
},
User: {
activeCategories: async (parent, _args, context: Context, _resolveInfo) => {
return (
await context.database.query({
query: `
MATCH (category:Category)
WHERE NOT ((:User{id: $user.id})-[:NOT_INTERESTED_IN]->(category))
RETURN collect(category.id) as categories
`,
variables: { user: parent },
})
).records.map((record) => record.get('categories'))[0]
},
inviteCodes: async (_parent, _args, context: Context, _resolveInfo) => {
return (
await context.database.query({
@ -476,7 +482,7 @@ export default {
})
).records.map((record) => record.get('inviteCodes'))
},
emailNotificationSettings: async (parent, _params, _context, _resolveInfo) => {
emailNotificationSettings: (parent, _params, _context, _resolveInfo) => {
return [
{
type: 'post',

View File

@ -2,12 +2,12 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
import gql from 'graphql-tag'
import { cleanDatabase } from '@db/factories'
import { blockedUsers } from '@graphql/queries/blockedUsers'
import { blockUser } from '@graphql/queries/blockUser'
import { Post } from '@graphql/queries/Post'
import { unblockUser } from '@graphql/queries/unblockUser'
import { User } from '@graphql/queries/User'
import { createApolloTestSetup } from '@root/test/helpers'
import type { ApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -155,56 +155,37 @@ describe('blockUser', () => {
it('unfollows the user when blocking', async () => {
await currentUser.relateTo(blockedUser, 'following')
const queryUser = gql`
query {
User(id: "u2") {
id
isBlocked
followedByCurrentUser
}
}
`
await expect(query({ query: queryUser })).resolves.toMatchObject({
await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({
data: { User: [{ id: 'u2', isBlocked: false, followedByCurrentUser: true }] },
})
await mutate({ mutation: blockUser, variables: { id: 'u2' } })
await expect(query({ query: queryUser })).resolves.toMatchObject({
await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({
data: { User: [{ id: 'u2', isBlocked: true, followedByCurrentUser: false }] },
})
})
describe('given both the current user and the to-be-blocked user write a post', () => {
let postQuery
beforeEach(async () => {
const post1 = await database.neode.create('Post', {
id: 'p12',
title: 'A post written by the current user',
content: 'content',
})
const post2 = await database.neode.create('Post', {
id: 'p23',
title: 'A post written by the blocked user',
content: 'content',
})
await Promise.all([
post1.relateTo(currentUser, 'author'),
post2.relateTo(blockedUser, 'author'),
])
postQuery = gql`
query {
Post(orderBy: createdAt_asc) {
id
title
author {
id
name
}
}
}
`
})
const bothPostsAreInTheNewsfeed = async () => {
await expect(query({ query: postQuery })).resolves.toMatchObject({
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: [
{
@ -238,7 +219,9 @@ describe('blockUser', () => {
// TODO: clarify proper behaviour
it("the blocked user's post still shows up in the newsfeed of the current user", async () => {
await expect(query({ query: postQuery })).resolves.toMatchObject({
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: [
{
@ -276,19 +259,21 @@ describe('blockUser', () => {
})
it("the current user's post will show up in the newsfeed of the blocked user", async () => {
await expect(query({ query: postQuery })).resolves.toMatchObject({
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: expect.arrayContaining([
{
expect.objectContaining({
id: 'p23',
title: 'A post written by the blocked user',
author: { name: 'Blocked User', id: 'u2' },
},
{
}),
expect.objectContaining({
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
},
}),
]),
},
})

View File

@ -1,10 +1,9 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import gql from 'graphql-tag'
import Factory, { cleanDatabase } from '@db/factories'
import { queryLocations } from '@graphql/queries/queryLocations'
import { UpdateUser } from '@graphql/queries/UpdateUser'
import type { ApolloTestSetup } from '@root/test/helpers'
import { createApolloTestSetup } from '@root/test/helpers'
import type { Context } from '@src/context'
@ -19,13 +18,6 @@ let query: any // eslint-disable-line @typescript-eslint/no-explicit-any
let database: ApolloTestSetup['database']
let server: ApolloTestSetup['server']
const updateUserMutation = gql`
mutation ($id: ID!, $name: String!, $locationName: String) {
UpdateUser(id: $id, name: $name, locationName: $locationName) {
locationName
}
}
`
const newlyCreatedNodesWithLocales = [
{
city: {
@ -203,7 +195,7 @@ describe('userMiddleware', () => {
name: 'Updating user',
locationName: 'Welzheim, Baden-Württemberg, Germany',
}
await mutate({ mutation: updateUserMutation, variables })
await mutate({ mutation: UpdateUser, variables })
const locations = await database.neode.cypher(
`MATCH (city:Location)-[:IS_IN]->(district:Location)-[:IS_IN]->(state:Location)-[:IS_IN]->(country:Location) return city {.*}, state {.*}, country {.*}`,
{},

View File

@ -3,13 +3,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { createTestClient } from 'apollo-server-testing'
import gql from 'graphql-tag'
import { cleanDatabase } from '@db/factories'
import { getNeode, getDriver } from '@db/neo4j'
import { mutedUsers } from '@graphql/queries/mutedUsers'
import { muteUser } from '@graphql/queries/muteUser'
import { Post } from '@graphql/queries/Post'
import { unmuteUser } from '@graphql/queries/unmuteUser'
import { User } from '@graphql/queries/User'
import createServer from '@src/server'
const driver = getDriver()
@ -152,85 +153,68 @@ describe('muteUser', () => {
it('unfollows the user', async () => {
await currentUser.relateTo(mutedUser, 'following')
const queryUser = gql`
query {
User(id: "u2") {
id
isMuted
followedByCurrentUser
}
}
`
const { query } = createTestClient(server)
await expect(query({ query: queryUser })).resolves.toEqual(
expect.objectContaining({
data: { User: [{ id: 'u2', isMuted: false, followedByCurrentUser: true }] },
}),
)
await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({
data: {
User: expect.arrayContaining([
expect.objectContaining({ id: 'u2', isMuted: false, followedByCurrentUser: true }),
]),
},
})
await muteAction({ id: 'u2' })
await expect(query({ query: queryUser })).resolves.toEqual(
expect.objectContaining({
data: { User: [{ id: 'u2', isMuted: true, followedByCurrentUser: false }] },
}),
)
await expect(query({ query: User, variables: { id: 'u2' } })).resolves.toMatchObject({
data: {
User: expect.arrayContaining([
expect.objectContaining({ id: 'u2', isMuted: true, followedByCurrentUser: false }),
]),
},
})
})
describe('given both the current user and the to-be-muted user write a post', () => {
let postQuery
beforeEach(async () => {
const post1 = await neode.create('Post', {
id: 'p12',
title: 'A post written by the current user',
content: 'content',
})
const post2 = await neode.create('Post', {
id: 'p23',
title: 'A post written by the muted user',
content: 'content',
})
await Promise.all([
post1.relateTo(currentUser, 'author'),
post2.relateTo(mutedUser, 'author'),
])
postQuery = gql`
query {
Post(orderBy: createdAt_asc) {
id
title
author {
id
name
}
}
}
`
})
const bothPostsAreInTheNewsfeed = async () => {
const { query } = createTestClient(server)
await expect(query({ query: postQuery })).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
id: 'p12',
title: 'A post written by the current user',
author: {
name: 'Current User',
id: 'u1',
},
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: expect.arrayContaining([
expect.objectContaining({
id: 'p12',
title: 'A post written by the current user',
author: {
name: 'Current User',
id: 'u1',
},
{
id: 'p23',
title: 'A post written by the muted user',
author: {
name: 'Muted User',
id: 'u2',
},
}),
expect.objectContaining({
id: 'p23',
title: 'A post written by the muted user',
author: {
name: 'Muted User',
id: 'u2',
},
],
},
}),
)
}),
]),
},
})
}
describe('from the perspective of the current user', () => {
@ -243,19 +227,19 @@ describe('muteUser', () => {
it("the muted user's post won't show up in the newsfeed of the current user", async () => {
const { query } = createTestClient(server)
await expect(query({ query: postQuery })).resolves.toEqual(
expect.objectContaining({
data: {
Post: [
{
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
},
],
},
}),
)
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: [
expect.objectContaining({
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
}),
],
},
})
})
})
})
@ -273,24 +257,24 @@ describe('muteUser', () => {
it("the current user's post will show up in the newsfeed of the muted user", async () => {
const { query } = createTestClient(server)
await expect(query({ query: postQuery })).resolves.toEqual(
expect.objectContaining({
data: {
Post: expect.arrayContaining([
{
id: 'p23',
title: 'A post written by the muted user',
author: { name: 'Muted User', id: 'u2' },
},
{
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
},
]),
},
}),
)
await expect(
query({ query: Post, variables: { orderBy: 'createdAt_asc' } }),
).resolves.toMatchObject({
data: {
Post: expect.arrayContaining([
expect.objectContaining({
id: 'p23',
title: 'A post written by the muted user',
author: { name: 'Muted User', id: 'u2' },
}),
expect.objectContaining({
id: 'p12',
title: 'A post written by the current user',
author: { name: 'Current User', id: 'u1' },
}),
]),
},
})
})
})
})

View File

@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
import { makeAugmentedSchema } from 'neo4j-graphql-js'
import typeDefs from '@graphql/types/index'

View File

@ -0,0 +1,14 @@
# directive @MutationMeta on FIELD_DEFINITION
# directive @isAuthenticated on FIELD_DEFINITION
# directive @hasRole on FIELD_DEFINITION
# directive @hasScope on FIELD_DEFINITION
# directive @additionalLabels on FIELD_DEFINITION
directive @cypher(statement: String) on FIELD_DEFINITION
directive @relation(
name: String
from: String
to: String
direction: String
) on FIELD_DEFINITION | OBJECT
directive @neo4j_ignore on FIELD_DEFINITION

View File

@ -2,4 +2,4 @@ enum EmailNotificationSettingsType {
post
chat
group
}
}

View File

@ -4,4 +4,4 @@ enum Emotion {
happy
angry
funny
}
}

View File

@ -1,4 +1,4 @@
enum ShoutTypeEnum {
Post
Comment
}
}

View File

@ -2,4 +2,4 @@ enum Visibility {
public
friends
private
}
}

View File

@ -50,12 +50,16 @@ type Comment {
isPostObservedByMe: Boolean!
@cypher(
statement: "MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(u:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(u) >= 1"
)
postObservingUsersCount: Int!
@cypher(statement: "MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.disabled = true AND NOT u.deleted = true RETURN COUNT(DISTINCT u)")
)
postObservingUsersCount: Int!
@cypher(
statement: "MATCH (this)-[:COMMENTS]->(:Post)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.disabled = true AND NOT u.deleted = true RETURN COUNT(DISTINCT u)"
)
shoutedByCurrentUser: Boolean!
@cypher(statement: "MATCH (this) RETURN EXISTS((this)<-[:SHOUTED]-(:User {id: $cypherParams.currentUserId}))")
@cypher(
statement: "MATCH (this) RETURN EXISTS((this)<-[:SHOUTED]-(:User {id: $cypherParams.currentUserId}))"
)
shoutedCount: Int!
@cypher(
@ -77,16 +81,7 @@ type Query {
}
type Mutation {
CreateComment(
id: ID
postId: ID!
content: String!
contentExcerpt: String
): Comment
UpdateComment(
id: ID!
content: String!
contentExcerpt: String
): Comment
CreateComment(id: ID, postId: ID!, content: String!, contentExcerpt: String): Comment
UpdateComment(id: ID!, content: String!, contentExcerpt: String): Comment
DeleteComment(id: ID!): Comment
}

View File

@ -13,4 +13,4 @@ type Query {
type Mutation {
UpdateDonations(showDonations: Boolean, goal: Int, progress: Int): Donations
}
}

View File

@ -9,11 +9,7 @@ type Query {
}
type Mutation {
Signup(
email: String!
locale: String!
inviteCode: String = null
): EmailAddress
Signup(email: String!, locale: String!, inviteCode: String = null): EmailAddress
SignupVerification(
nonce: String!
email: String!
@ -27,8 +23,5 @@ type Mutation {
locationName: String = null
): User
AddEmailAddress(email: String!): EmailAddress
VerifyEmailAddress(
nonce: String!
email: String!
): EmailAddress
VerifyEmailAddress(nonce: String!, email: String!): EmailAddress
}

View File

@ -5,7 +5,7 @@ type FILED {
submitter: User
}
# this list equals the strings of an array in file "webapp/constants/modals.js"
"this list equals the strings of an array in file `webapp/constants/modals.js`"
enum ReasonCategory {
other
discrimination_etc
@ -26,5 +26,9 @@ type FiledReport {
}
type Mutation {
fileReport(resourceId: ID!, reasonCategory: ReasonCategory!, reasonDescription: String!): FiledReport
}
fileReport(
resourceId: ID!
reasonCategory: ReasonCategory!
reasonDescription: String!
): FiledReport
}

View File

@ -1,16 +1,16 @@
type File {
url: ID!,
name: String,
#size: Int,
type: String,
#audio: Boolean,
#duration: Float,
#preview: String,
#progress: Int,
url: ID!
name: String
type: String
# size: Int
# audio: Boolean
# duration: Float
# preview: String
# progress: Int
}
input FileInput {
upload: Upload,
name: String,
type: String,
upload: Upload
name: String
type: String
}

View File

@ -1,19 +1,19 @@
enum _GroupOrdering {
id_asc
id_desc
name_asc
name_desc
slug_asc
slug_desc
locationName_asc
locationName_desc
about_asc
about_desc
createdAt_asc
createdAt_desc
updatedAt_asc
updatedAt_desc
}
# enum _GroupOrdering {
# id_asc
# id_desc
# name_asc
# name_desc
# slug_asc
# slug_desc
# locationName_asc
# locationName_desc
# about_asc
# about_desc
# createdAt_asc
# createdAt_desc
# updatedAt_asc
# updatedAt_desc
# }
type Group {
id: ID!
@ -38,18 +38,27 @@ type Group {
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
membersCount: Int! @cypher(statement: "MATCH (this)<-[:MEMBER_OF]-(r:User) RETURN COUNT(DISTINCT r)")
membersCount: Int!
@cypher(statement: "MATCH (this)<-[:MEMBER_OF]-(r:User) RETURN COUNT(DISTINCT r)")
myRole: GroupMemberRole # if 'null' then the current user is no member
posts: [Post] @relation(name: "IN", direction: "IN")
isMutedByMe: Boolean! @cypher(statement: "MATCH (this) RETURN EXISTS( (this)<-[:MUTED]-(:User {id: $cypherParams.currentUserId}) )")
isMutedByMe: Boolean!
@cypher(
statement: "MATCH (this) RETURN EXISTS( (this)<-[:MUTED]-(:User {id: $cypherParams.currentUserId}) )"
)
"inviteCodes to this group the current user has generated"
inviteCodes: [InviteCode]! @neo4j_ignore
currentlyPinnedPostsCount: Int! @neo4j_ignore
}
type GroupMember {
user: User
membership: MEMBER_OF
}
input _GroupFilter {
AND: [_GroupFilter!]
@ -74,20 +83,16 @@ type Query {
slug: String
first: Int
offset: Int
# orderBy: [_GroupOrdering] # not implemented yet
# filter: _GroupFilter # not implemented yet
): [Group]
# orderBy: [_GroupOrdering] # not implemented yet
# filter: _GroupFilter # not implemented yet
GroupMembers(
id: ID!
first: Int
offset: Int
# orderBy: [_UserOrdering] # not implemented yet
# filter: _UserFilter # not implemented yet
): [User]
GroupMembers(id: ID!, first: Int, offset: Int): [GroupMember]
# orderBy: [_UserOrdering] # not implemented yet
# filter: _UserFilter # not implemented yet
GroupCount(isMember: Boolean): Int
# AvailableGroupTypes: [GroupType]!
# AvailableGroupActionRadii: [GroupActionRadius]!
@ -105,7 +110,9 @@ type Mutation {
groupType: GroupType!
actionRadius: GroupActionRadius!
categoryIds: [ID]
# avatar: ImageInput # a group can not be created with an avatar
locationName: String # empty string '' sets it to null
): Group
@ -115,7 +122,9 @@ type Mutation {
slug: String
about: String
description: String
# groupType: GroupType # is not possible at the moment and has to be discussed. may be in the stronger direction: public → closed → hidden
actionRadius: GroupActionRadius
categoryIds: [ID]
avatar: ImageInput # test this as result
@ -124,27 +133,14 @@ type Mutation {
# DeleteGroup(id: ID!): Group
JoinGroup(
groupId: ID!
userId: ID!
): User
JoinGroup(groupId: ID!, userId: ID!): GroupMember
LeaveGroup(
groupId: ID!
userId: ID!
): User
LeaveGroup(groupId: ID!, userId: ID!): GroupMember
ChangeGroupMemberRole(
groupId: ID!
userId: ID!
roleInGroup: GroupMemberRole!
): User
ChangeGroupMemberRole(groupId: ID!, userId: ID!, roleInGroup: GroupMemberRole!): GroupMember
RemoveUserFromGroup(
groupId: ID!
userId: ID!
): User
RemoveUserFromGroup(groupId: ID!, userId: ID!): GroupMember
muteGroup(groupId: ID!): Group
unmuteGroup(groupId: ID!): Group
unmuteGroup(groupId: ID!): Group
}

View File

@ -1,21 +1,23 @@
type Image {
url: ID!,
url: ID!
transform(width: Int, height: Int): String
# urlW34: String,
# urlW160: String,
# urlW320: String,
# urlW640: String,
# urlW1024: String,
alt: String,
sensitive: Boolean,
aspectRatio: Float,
type: String,
alt: String
sensitive: Boolean
aspectRatio: Float
type: String
}
input ImageInput {
alt: String,
upload: Upload,
sensitive: Boolean,
aspectRatio: Float,
type: String,
alt: String
upload: Upload
sensitive: Boolean
aspectRatio: Float
type: String
}

View File

@ -19,7 +19,11 @@ type Query {
type Mutation {
generatePersonalInviteCode(expiresAt: String = null, comment: String = null): InviteCode!
generateGroupInviteCode(groupId: ID!, expiresAt: String = null, comment: String = null): InviteCode!
generateGroupInviteCode(
groupId: ID!
expiresAt: String = null
comment: String = null
): InviteCode!
invalidateInviteCode(code: String!): InviteCode
redeemInviteCode(code: String!): Boolean!
}

View File

@ -18,6 +18,7 @@ type Location {
}
# This is not smart - we need one location for everything - use the same type everywhere!
type LocationMapBox {
id: ID!
place_name: String!

View File

@ -19,8 +19,11 @@ type Message {
senderId: String! @cypher(statement: "MATCH (this)<-[:CREATED]-(user:User) RETURN user.id")
username: String! @cypher(statement: "MATCH (this)<-[:CREATED]-(user:User) RETURN user.name")
avatar: String @cypher(statement: "MATCH (this)<-[:CREATED]-(:User)-[:AVATAR_IMAGE]->(image:Image) RETURN image.url")
date: String! @cypher(statement: "RETURN this.createdAt")
avatar: String
@cypher(
statement: "MATCH (this)<-[:CREATED]-(:User)-[:AVATAR_IMAGE]->(image:Image) RETURN image.url"
)
date: String! @cypher(statement: "RETURN this.createdAt")
saved: Boolean
distributed: Boolean
@ -29,22 +32,13 @@ type Message {
}
type Mutation {
CreateMessage(
roomId: ID!
content: String
files: [FileInput]
): Message
CreateMessage(roomId: ID!, content: String, files: [FileInput]): Message
MarkMessagesAsSeen(messageIds: [String!]): Boolean
}
type Query {
Message(
roomId: ID!,
first: Int
offset: Int
orderBy: [_MessageOrdering]
): [Message]
Message(roomId: ID!, first: Int, offset: Int, orderBy: [_MessageOrdering]): [Message]
}
type Subscription {

View File

@ -33,7 +33,7 @@ enum NotificationReason {
type Query {
notifications(read: Boolean, orderBy: NotificationOrdering, first: Int, offset: Int): [NOTIFIED]
}
type Mutation {
markAsRead(id: ID!): NOTIFIED
markAllAsRead: [NOTIFIED]

View File

@ -1,3 +1,7 @@
input _CategoryFilter {
AND: [_CategoryFilter!]
OR: [_CategoryFilter!]
}
input _PostFilter {
AND: [_PostFilter!]
OR: [_PostFilter!]
@ -49,6 +53,7 @@ input _PostFilter {
language_in: [String!]
language_not_in: [String!]
pinned: Boolean # required for `maintainPinnedPost`
groupPinned: Boolean # required for `maintainGroupPinnedPost`
tags: _TagFilter
tags_not: _TagFilter
tags_in: [_TagFilter!]
@ -111,9 +116,10 @@ enum _PostOrdering {
pinned_desc
eventStart_asc
eventStart_desc
groupPinned_asc
groupPinned_desc
}
type Post {
id: ID!
activityId: String
@ -128,14 +134,16 @@ type Post {
deleted: Boolean
disabled: Boolean
pinned: Boolean
groupPinned: Boolean
createdAt: String
updatedAt: String
sortDate: String
language: String
pinnedAt: String @cypher(
pinnedAt: String
@cypher(
statement: "MATCH (this)<-[pinned:PINNED]-(:User) WHERE NOT this.deleted = true AND NOT this.disabled = true RETURN pinned.createdAt"
)
pinnedBy: User @relation(name:"PINNED", direction: "IN")
pinnedBy: User @relation(name: "PINNED", direction: "IN")
relatedContributions: [Post]!
@cypher(
statement: """
@ -160,7 +168,7 @@ type Post {
statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)"
)
# Has the currently logged in user shouted that post?
"Has the currently logged in user shouted that post?"
shoutedByCurrentUser: Boolean!
@cypher(
statement: "MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1"
@ -173,15 +181,14 @@ type Post {
@cypher(
statement: "MATCH (this)<-[:VIEWED_TEASER]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1"
)
emotions: [EMOTED]
emotionsCount: Int!
@cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)")
group: Group @relation(name: "IN", direction: "OUT")
postType: [PostType]
@cypher(statement: "RETURN [l IN labels(this) WHERE NOT l = 'Post']")
postType: [PostType] @cypher(statement: "RETURN [l IN labels(this) WHERE NOT l = 'Post']")
eventLocationName: String
eventLocation: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
@ -193,9 +200,11 @@ type Post {
isObservedByMe: Boolean!
@cypher(
statement: "MATCH (this)<-[obs:OBSERVES]-(u:User {id: $cypherParams.currentUserId}) WHERE obs.active = true RETURN COUNT(u) >= 1"
)
)
observingUsersCount: Int!
@cypher(statement: "MATCH (this)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.deleted = true AND NOT u.disabled = true RETURN COUNT(DISTINCT u)")
@cypher(
statement: "MATCH (this)<-[obs:OBSERVES]-(u:User) WHERE obs.active = true AND NOT u.deleted = true AND NOT u.disabled = true RETURN COUNT(DISTINCT u)"
)
}
input _PostInput {
@ -216,7 +225,7 @@ type Mutation {
title: String!
slug: String
content: String!
image: ImageInput,
image: ImageInput
visibility: Visibility
language: String
categoryIds: [ID]
@ -231,7 +240,7 @@ type Mutation {
slug: String
content: String!
contentExcerpt: String
image: ImageInput,
image: ImageInput
visibility: Visibility
language: String
categoryIds: [ID]
@ -241,15 +250,19 @@ type Mutation {
DeletePost(id: ID!): Post
AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
pinPost(id: ID!): Post
unpinPost(id: ID!): Post
pinGroupPost(id: ID!): Post
unpinGroupPost(id: ID!): Post
markTeaserAsViewed(id: ID!): Post
pushPost(id: ID!): Post!
unpushPost(id: ID!): Post!
# Shout the given Type and ID
"Shout the given Type and ID"
shout(id: ID!, type: ShoutTypeEnum!): Boolean!
# Unshout the given Type and ID
"Unshout the given Type and ID"
unshout(id: ID!, type: ShoutTypeEnum!): Boolean!
toggleObservePost(id: ID!, value: Boolean!): Post!

View File

@ -17,7 +17,13 @@ enum ReportRule {
}
type Query {
reports(orderBy: ReportOrdering, first: Int, offset: Int, reviewed: Boolean, closed: Boolean): [Report]
reports(
orderBy: ReportOrdering
first: Int
offset: Int
reviewed: Boolean
closed: Boolean
): [Report]
}
enum ReportOrdering {

View File

@ -6,6 +6,7 @@
# }
# TODO change this to last message date
enum _RoomOrdering {
lastMessageAt_desc
createdAt_desc
@ -19,41 +20,48 @@ type Room {
users: [User]! @relation(name: "CHATS_IN", direction: "IN")
roomId: String! @cypher(statement: "RETURN this.id")
roomName: String! @cypher(statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.name")
avatar: String @cypher(statement: """
MATCH (this)<-[:CHATS_IN]-(user:User)
WHERE NOT user.id = $cypherParams.currentUserId
OPTIONAL MATCH (user)-[:AVATAR_IMAGE]->(image:Image)
RETURN image.url
""")
roomName: String!
@cypher(
statement: "MATCH (this)<-[:CHATS_IN]-(user:User) WHERE NOT user.id = $cypherParams.currentUserId RETURN user.name"
)
avatar: String
@cypher(
statement: """
MATCH (this)<-[:CHATS_IN]-(user:User)
WHERE NOT user.id = $cypherParams.currentUserId
OPTIONAL MATCH (user)-[:AVATAR_IMAGE]->(image:Image)
RETURN image.url
"""
)
lastMessageAt: String
lastMessage: Message @cypher(statement: """
MATCH (this)<-[:INSIDE]-(message:Message)
WITH message ORDER BY message.indexId DESC LIMIT 1
RETURN message
""")
lastMessage: Message
@cypher(
statement: """
MATCH (this)<-[:INSIDE]-(message:Message)
WITH message ORDER BY message.indexId DESC LIMIT 1
RETURN message
"""
)
unreadCount: Int @cypher(statement: """
MATCH (this)<-[:INSIDE]-(message:Message)<-[:CREATED]-(user:User)
WHERE NOT user.id = $cypherParams.currentUserId
AND NOT message.seen
RETURN count(message)
""")
unreadCount: Int
@cypher(
statement: """
MATCH (this)<-[:INSIDE]-(message:Message)<-[:CREATED]-(user:User)
WHERE NOT user.id = $cypherParams.currentUserId
AND NOT message.seen
RETURN count(message)
"""
)
}
type Mutation {
CreateRoom(
userId: ID!
): Room
CreateRoom(userId: ID!): Room
}
type Query {
Room(
id: ID
orderBy: [_RoomOrdering]
): [Room]
Room(id: ID, orderBy: [_RoomOrdering]): [Room]
UnreadRooms: Int
}

View File

@ -25,4 +25,3 @@ type Statistics {
usersVerified: Int!
reports: Int!
}

View File

@ -19,7 +19,8 @@ type Tag {
id: ID!
taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN")
taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)")
taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
taggedCountUnique: Int!
@cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
deleted: Boolean
disabled: Boolean
}
@ -34,11 +35,5 @@ enum _TagOrdering {
}
type Query {
Tag(
id: ID
first: Int
offset: Int
orderBy: [_TagOrdering]
filter: _TagFilter
): [Tag]
Tag(id: ID, first: Int, offset: Int, orderBy: [_TagOrdering], filter: _TagFilter): [Tag]
}

View File

@ -38,7 +38,8 @@ type User {
id: ID!
actorId: String
name: String
email: String! @cypher(statement: "MATCH (this)-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e.email")
email: String!
@cypher(statement: "MATCH (this)-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e.email")
slug: String!
avatar: Image @relation(name: "AVATAR_IMAGE", direction: "OUT")
deleted: Boolean
@ -64,65 +65,78 @@ type User {
emailNotificationSettings: [EmailNotificationSettings]! @neo4j_ignore
locale: String
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)")
friendsCount: Int!
@cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)")
following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)")
followingCount: Int!
@cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)")
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
followedByCount: Int!
@cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
# Is the currently logged in user following that user?
followedByCurrentUser: Boolean! @cypher(
statement: """
MATCH (this)<-[:FOLLOWS]-(u:User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1
"""
)
"Is the currently logged in user following that user?"
followedByCurrentUser: Boolean!
@cypher(
statement: "MATCH (this)<-[:FOLLOWS]-(u:User { id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1"
)
isBlocked: Boolean! @cypher(
statement: """
MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
blocked: Boolean! @cypher(
statement: """
MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
isBlocked: Boolean!
@cypher(
statement: """
MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
blocked: Boolean!
@cypher(
statement: """
MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
isMuted: Boolean!
@cypher(
statement: """
MATCH (this)<-[:MUTED]-(user:User { id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
isMuted: Boolean! @cypher(
statement: """
MATCH (this)<-[:MUTED]-(user:User { id: $cypherParams.currentUserId})
RETURN COUNT(user) >= 1
"""
)
# contributions: [WrittenPost]!
# contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
# @cypher(
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
# )
contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
contributionsCount: Int! @cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
contributionsCount: Int!
@cypher(
statement: """
MATCH (this)-[:WROTE]->(r:Post)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)
"""
)
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
commentedCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(:Comment)-[:COMMENTS]->(p:Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))")
commentedCount: Int!
@cypher(
statement: "MATCH (this)-[:WROTE]->(:Comment)-[:COMMENTS]->(p:Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))"
)
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
shoutedCount: Int!
@cypher(
statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)"
)
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
# Badges
badgeVerification: Badge! @neo4j_ignore
badgeTrophies: [Badge]! @relation(name: "REWARDED", direction: "IN")
badgeTrophiesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
@ -133,22 +147,14 @@ type User {
"personal inviteCodes the user has generated"
inviteCodes: [InviteCode]! @neo4j_ignore
# inviteCodes: [InviteCode]! @relation(name: "GENERATED", direction: "OUT")
redeemedInviteCode: InviteCode @relation(name: "REDEEMED", direction: "OUT")
emotions: [EMOTED]
activeCategories: [String] @cypher(
statement: """
MATCH (category:Category)
WHERE NOT ((this)-[:NOT_INTERESTED_IN]->(category))
RETURN collect(category.id)
"""
)
myRoleInGroup: GroupMemberRole
activeCategories: [String] @neo4j_ignore
}
input _UserFilter {
AND: [_UserFilter!]
OR: [_UserFilter!]
@ -203,7 +209,7 @@ type Query {
filter: _UserFilter
): [User]
availableRoles: [UserRole]!
availableRoles: [UserRole]!
mutedUsers: [User]
blockedUsers: [User]
currentUser: User!
@ -215,7 +221,7 @@ enum Deletable {
}
type Mutation {
UpdateUser (
UpdateUser(
id: ID!
name: String
email: String
@ -245,14 +251,14 @@ type Mutation {
switchUserRole(role: UserRole!, id: ID!): User
saveCategorySettings(activeCategories: [String]): Boolean
updateOnlineStatus(status: OnlineStatus!): Boolean!
requestPasswordReset(email: String!, locale: String!): Boolean!
resetPassword(email: String!, nonce: String!, newPassword: String!): Boolean!
changePassword(oldPassword: String!, newPassword: String!): String!
# Get a JWT Token for the given Email and password
"Get a JWT Token for the given Email and password"
login(email: String!, password: String!): String!
setTrophyBadgeSelected(slot: Int!, badgeId: ID): User

View File

@ -4,7 +4,5 @@ type UserData {
}
type Query {
userData(
id: ID
): UserData
userData(id: ID): UserData
}

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