From d57c02c48e2ab857eac297f227f82dc4a5610a3b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 4 Oct 2021 13:03:04 +0200
Subject: [PATCH 001/374] Refine backend README.md
---
backend/README.md | 19 +++++++++++++++++--
1 file changed, 17 insertions(+), 2 deletions(-)
diff --git a/backend/README.md b/backend/README.md
index 6d837856c2..3601dd2fa0 100644
--- a/backend/README.md
+++ b/backend/README.md
@@ -7,8 +7,9 @@ Run the following command to install everything through docker.
The installation takes a bit longer on the first pass or on rebuild ...
```bash
+# in main folder
$ docker-compose up
-
+# or
# rebuild the containers for a cleanup
$ docker-compose up --build
```
@@ -28,6 +29,7 @@ between different local node versions.
Install node dependencies with [yarn](https://yarnpkg.com/en/):
```bash
+# in main folder
$ cd backend
$ yarn install
```
@@ -45,12 +47,14 @@ a [local Neo4J](http://localhost:7474) instance is up and running.
Start the backend for development with:
```bash
+# in backend/
$ yarn run dev
```
or start the backend in production environment with:
```bash
+# in backend/
$ yarn run start
```
@@ -72,6 +76,7 @@ backend is running:
{% tab title="Docker" %}
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate init
```
@@ -79,7 +84,7 @@ $ docker-compose exec backend yarn run db:migrate init
{% tab title="Without Docker" %}
```bash
-# in folder backend/
+# in folder backend/ while database is running
# make sure your database is running on http://localhost:7474/browser/
yarn run db:migrate init
```
@@ -98,12 +103,14 @@ need to seed your database:
In another terminal run:
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:seed
```
To reset the database run:
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:reset
# you could also wipe out your neo4j database and delete all volumes with:
$ docker-compose down -v
@@ -117,12 +124,14 @@ $ docker-compose exec backend yarn run db:migrate init
Run:
```bash
+# in backend/ while database is running
$ yarn run db:seed
```
To reset the database run:
```bash
+# in backend/ while database is running
$ yarn run db:reset
```
@@ -140,6 +149,7 @@ you have to migrate your data e.g. because your data modeling has changed.
Generate a data migration file:
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate:create your_data_migration
# Edit the file in ./src/db/migrations/
```
@@ -147,6 +157,7 @@ $ docker-compose exec backend yarn run db:migrate:create your_data_migration
To run the migration:
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run db:migrate up
```
@@ -156,6 +167,7 @@ $ docker-compose exec backend yarn run db:migrate up
Generate a data migration file:
```bash
+# in backend/
$ yarn run db:migrate:create your_data_migration
# Edit the file in ./src/db/migrations/
```
@@ -163,6 +175,7 @@ $ yarn run db:migrate:create your_data_migration
To run the migration:
```bash
+# in backend/ while database is running
$ yarn run db:migrate up
```
@@ -180,6 +193,7 @@ database after each test, running the tests will wipe out all your data!
Run the unit tests:
```bash
+# in main folder while docker-compose is running
$ docker-compose exec backend yarn run test
```
@@ -190,6 +204,7 @@ $ docker-compose exec backend yarn run test
Run the unit tests:
```bash
+# in backend/ while database is running
$ yarn run test
```
From bf9dd205ca6a790c12550182f68ec43e99bf8076 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 2 Aug 2022 08:26:14 +0200
Subject: [PATCH 002/374] Implement GQL for groups
---
backend/src/db/migrate/store.js | 3 +-
backend/src/models/Groups.js | 133 ++++++++++
backend/src/models/index.js | 1 +
.../schema/types/enum/GroupActionRadius.gql | 6 +
backend/src/schema/types/enum/GroupType.gql | 5 +
backend/src/schema/types/type/Group.gql | 249 ++++++++++++++++++
backend/src/schema/types/type/User.gql | 52 ++--
7 files changed, 422 insertions(+), 27 deletions(-)
create mode 100644 backend/src/models/Groups.js
create mode 100644 backend/src/schema/types/enum/GroupActionRadius.gql
create mode 100644 backend/src/schema/types/enum/GroupType.gql
create mode 100644 backend/src/schema/types/type/Group.gql
diff --git a/backend/src/db/migrate/store.js b/backend/src/db/migrate/store.js
index 377caf0b0e..7a8be0b945 100644
--- a/backend/src/db/migrate/store.js
+++ b/backend/src/db/migrate/store.js
@@ -62,8 +62,9 @@ class Store {
await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
return Promise.all(
[
- 'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
+ 'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["Group"],["name", "slug", "description"])', // Wolle: check for 'name', 'slug', 'description'
+ 'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])',
].map((statement) => txc.run(statement)),
)
diff --git a/backend/src/models/Groups.js b/backend/src/models/Groups.js
new file mode 100644
index 0000000000..aa6f5767e0
--- /dev/null
+++ b/backend/src/models/Groups.js
@@ -0,0 +1,133 @@
+import { v4 as uuid } from 'uuid'
+
+export default {
+ id: { type: 'string', primary: true, default: uuid }, // TODO: should be type: 'uuid' but simplified for our tests
+ name: { type: 'string', disallow: [null], min: 3 },
+ slug: { type: 'string', unique: 'true', regex: /^[a-z0-9_-]+$/, lowercase: true },
+ avatar: {
+ type: 'relationship',
+ relationship: 'AVATAR_IMAGE',
+ target: 'Image',
+ direction: 'out',
+ },
+ deleted: { type: 'boolean', default: false },
+ disabled: { type: 'boolean', default: false },
+ wasSeeded: 'boolean', // Wolle: used or needed?
+ locationName: { type: 'string', allow: [null] },
+ about: { type: 'string', allow: [null, ''] }, // Wolle: null?
+ description: { type: 'string', allow: [null, ''] }, // Wolle: null? HTML with Tiptap, similar to post content
+ // Wolle: followedBy: {
+ // type: 'relationship',
+ // relationship: 'FOLLOWS',
+ // target: 'User',
+ // direction: 'in',
+ // properties: {
+ // createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
+ // },
+ // },
+ // Wolle: correct this way?
+ members: { type: 'relationship',
+ relationship: 'MEMBERS',
+ target: 'User',
+ direction: 'out'
+ },
+ // Wolle: needed? lastActiveAt: { type: 'string', isoDate: true },
+ createdAt: {
+ type: 'string',
+ isoDate: true,
+ default: () => new Date().toISOString()
+ },
+ updatedAt: {
+ type: 'string',
+ isoDate: true,
+ required: true,
+ default: () => new Date().toISOString(),
+ },
+ // Wolle: emoted: {
+ // type: 'relationships',
+ // relationship: 'EMOTED',
+ // target: 'Post',
+ // direction: 'out',
+ // properties: {
+ // emotion: {
+ // type: 'string',
+ // valid: ['happy', 'cry', 'surprised', 'angry', 'funny'],
+ // invalid: [null],
+ // },
+ // },
+ // eager: true,
+ // cascade: true,
+ // },
+ // Wolle: blocked: {
+ // type: 'relationship',
+ // relationship: 'BLOCKED',
+ // target: 'User',
+ // direction: 'out',
+ // properties: {
+ // createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
+ // },
+ // },
+ // Wolle: muted: {
+ // type: 'relationship',
+ // relationship: 'MUTED',
+ // target: 'User',
+ // direction: 'out',
+ // properties: {
+ // createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
+ // },
+ // },
+ // Wolle: notifications: {
+ // type: 'relationship',
+ // relationship: 'NOTIFIED',
+ // target: 'User',
+ // direction: 'in',
+ // },
+ // Wolle inviteCodes: {
+ // type: 'relationship',
+ // relationship: 'GENERATED',
+ // target: 'InviteCode',
+ // direction: 'out',
+ // },
+ // Wolle: redeemedInviteCode: {
+ // type: 'relationship',
+ // relationship: 'REDEEMED',
+ // target: 'InviteCode',
+ // direction: 'out',
+ // },
+ // Wolle: shouted: {
+ // type: 'relationship',
+ // relationship: 'SHOUTED',
+ // target: 'Post',
+ // direction: 'out',
+ // properties: {
+ // createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
+ // },
+ // },
+ isIn: {
+ type: 'relationship',
+ relationship: 'IS_IN',
+ target: 'Location',
+ direction: 'out',
+ },
+ // Wolle: pinned: {
+ // type: 'relationship',
+ // relationship: 'PINNED',
+ // target: 'Post',
+ // direction: 'out',
+ // properties: {
+ // createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
+ // },
+ // },
+ // Wolle: showShoutsPublicly: {
+ // type: 'boolean',
+ // default: false,
+ // },
+ // Wolle: sendNotificationEmails: {
+ // type: 'boolean',
+ // default: true,
+ // },
+ // Wolle: locale: {
+ // type: 'string',
+ // allow: [null],
+ // },
+}
diff --git a/backend/src/models/index.js b/backend/src/models/index.js
index 8d6a021ab0..d476e5f9b1 100644
--- a/backend/src/models/index.js
+++ b/backend/src/models/index.js
@@ -4,6 +4,7 @@ export default {
Image: require('./Image.js').default,
Badge: require('./Badge.js').default,
User: require('./User.js').default,
+ Group: require('./Group.js').default,
EmailAddress: require('./EmailAddress.js').default,
UnverifiedEmailAddress: require('./UnverifiedEmailAddress.js').default,
SocialMedia: require('./SocialMedia.js').default,
diff --git a/backend/src/schema/types/enum/GroupActionRadius.gql b/backend/src/schema/types/enum/GroupActionRadius.gql
new file mode 100644
index 0000000000..afc4211337
--- /dev/null
+++ b/backend/src/schema/types/enum/GroupActionRadius.gql
@@ -0,0 +1,6 @@
+enum GroupActionRadius {
+ regional
+ national
+ continental
+ international
+}
diff --git a/backend/src/schema/types/enum/GroupType.gql b/backend/src/schema/types/enum/GroupType.gql
new file mode 100644
index 0000000000..2cf2984748
--- /dev/null
+++ b/backend/src/schema/types/enum/GroupType.gql
@@ -0,0 +1,5 @@
+enum GroupType {
+ public
+ closed
+ hidden
+}
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
new file mode 100644
index 0000000000..655a251d6e
--- /dev/null
+++ b/backend/src/schema/types/type/Group.gql
@@ -0,0 +1,249 @@
+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
+ # Wolle: needed? locale_asc
+ # locale_desc
+}
+
+type Group {
+ id: ID!
+ name: String # title
+ slug: String!
+
+ createdAt: String
+ updatedAt: String
+ deleted: Boolean
+ disabled: Boolean
+
+ avatar: Image @relation(name: "AVATAR_IMAGE", direction: "OUT")
+
+ location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
+ locationName: String
+ about: String # goal
+ description: String
+ groupType: GroupType
+ actionRadius: GroupActionRadius
+
+ categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
+
+ # Wolle: needed?
+ socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
+
+ # Wolle: showShoutsPublicly: Boolean
+ # Wolle: sendNotificationEmails: Boolean
+ # Wolle: needed? locale: String
+ members: [User]! @relation(name: "MEMBERS", direction: "OUT")
+ membersCount: Int!
+ @cypher(statement: "MATCH (this)-[:MEMBERS]->(r:User) RETURN COUNT(DISTINCT r)")
+
+ # Wolle: followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
+ # Wolle: followedByCount: Int!
+ # @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
+
+ # Wolle: inviteCodes: [InviteCode] @relation(name: "GENERATED", direction: "OUT")
+ # Wolle: redeemedInviteCode: InviteCode @relation(name: "REDEEMED", direction: "OUT")
+
+ # Is the currently logged in user following that user?
+ # Wolle: followedByCurrentUser: Boolean!
+ # @cypher(
+ # statement: """
+ # MATCH (this)<-[:FOLLOWS]-(u:User { id: $cypherParams.currentUserId})
+ # RETURN COUNT(u) >= 1
+ # """
+ # )
+
+ # Wolle: isBlocked: Boolean!
+ # @cypher(
+ # statement: """
+ # MATCH (this)<-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
+ # RETURN COUNT(user) >= 1
+ # """
+ # )
+ # Wolle: blocked: Boolean!
+ # @cypher(
+ # statement: """
+ # MATCH (this)-[:BLOCKED]-(user:User {id: $cypherParams.currentUserId})
+ # RETURN COUNT(user) >= 1
+ # """
+ # )
+
+ # Wolle: 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"
+ # )
+ # Wolle: needed?
+ # 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)
+ # """
+ # )
+
+ # Wolle: 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))"
+ # )
+
+ # Wolle: 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)"
+ # )
+
+ # Wolle: badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
+ # badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
+
+ # Wolle: emotions: [EMOTED]
+}
+
+
+input _GroupFilter {
+ AND: [_GroupFilter!]
+ OR: [_GroupFilter!]
+ name_contains: String
+ about_contains: String
+ description_contains: String
+ slug_contains: String
+ id: ID
+ id_not: ID
+ id_in: [ID!]
+ id_not_in: [ID!]
+ # Wolle:
+ # friends: _GroupFilter
+ # friends_not: _GroupFilter
+ # friends_in: [_GroupFilter!]
+ # friends_not_in: [_GroupFilter!]
+ # friends_some: _GroupFilter
+ # friends_none: _GroupFilter
+ # friends_single: _GroupFilter
+ # friends_every: _GroupFilter
+ # following: _GroupFilter
+ # following_not: _GroupFilter
+ # following_in: [_GroupFilter!]
+ # following_not_in: [_GroupFilter!]
+ # following_some: _GroupFilter
+ # following_none: _GroupFilter
+ # following_single: _GroupFilter
+ # following_every: _GroupFilter
+ # followedBy: _GroupFilter
+ # followedBy_not: _GroupFilter
+ # followedBy_in: [_GroupFilter!]
+ # followedBy_not_in: [_GroupFilter!]
+ # followedBy_some: _GroupFilter
+ # followedBy_none: _GroupFilter
+ # followedBy_single: _GroupFilter
+ # followedBy_every: _GroupFilter
+ # role_in: [UserGroup!]
+}
+
+type Query {
+ Group(
+ id: ID
+ email: String # admins need to search for a user sometimes
+ name: String
+ slug: String
+ locationName: String
+ about: String
+ description: String
+ createdAt: String
+ updatedAt: String
+ first: Int
+ offset: Int
+ orderBy: [_GroupOrdering]
+ filter: _GroupFilter
+ ): [Group]
+
+ availableGroupTypes: [GroupType]!
+
+ # Wolle:
+ # availableRoles: [UserGroup]!
+ # mutedUsers: [User]
+ # blockedUsers: [User]
+ # isLoggedIn: Boolean!
+ # currentUser: User
+ # findUsers(query: String!,limit: Int = 10, filter: _GroupFilter): [User]!
+ # @cypher(
+ # statement: """
+ # CALL db.index.fulltext.queryNodes('user_fulltext_search', $query)
+ # YIELD node as post, score
+ # MATCH (user)
+ # WHERE score >= 0.2
+ # AND NOT user.deleted = true AND NOT user.disabled = true
+ # RETURN user
+ # LIMIT $limit
+ # """
+ # )
+}
+
+# Wolle: enum Deletable {
+# Post
+# Comment
+# }
+
+type Mutation {
+ CreateGroup (
+ id: ID!
+ name: String
+ email: String
+ slug: String
+ avatar: ImageInput
+ locationName: String
+ about: String
+ description: String
+ # Wolle: add group settings
+ # Wolle:
+ # showShoutsPublicly: Boolean
+ # sendNotificationEmails: Boolean
+ # locale: String
+ ): Group
+
+ UpdateUser (
+ id: ID!
+ name: String
+ email: String
+ slug: String
+ avatar: ImageInput
+ locationName: String
+ about: String
+ description: String
+ # Wolle:
+ # showShoutsPublicly: Boolean
+ # sendNotificationEmails: Boolean
+ # locale: String
+ ): Group
+
+ DeleteGroup(id: ID!): Group
+
+ # Wolle:
+ # muteUser(id: ID!): User
+ # unmuteUser(id: ID!): User
+ # blockUser(id: ID!): User
+ # unblockUser(id: ID!): User
+
+ # Wolle: switchUserRole(role: UserGroup!, id: ID!): User
+}
diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql
index 772dedf6b3..aca08df0eb 100644
--- a/backend/src/schema/types/type/User.gql
+++ b/backend/src/schema/types/type/User.gql
@@ -156,19 +156,19 @@ input _UserFilter {
type Query {
User(
- id: ID
- email: String # admins need to search for a user sometimes
- name: String
- slug: String
- role: UserGroup
- locationName: String
- about: String
- createdAt: String
- updatedAt: String
- first: Int
- offset: Int
- orderBy: [_UserOrdering]
- filter: _UserFilter
+ id: ID
+ email: String # admins need to search for a user sometimes
+ name: String
+ slug: String
+ role: UserGroup
+ locationName: String
+ about: String
+ createdAt: String
+ updatedAt: String
+ first: Int
+ offset: Int
+ orderBy: [_UserOrdering]
+ filter: _UserFilter
): [User]
availableRoles: [UserGroup]!
@@ -197,19 +197,19 @@ enum Deletable {
type Mutation {
UpdateUser (
- id: ID!
- name: String
- email: String
- slug: String
- avatar: ImageInput
- locationName: String
- about: String
- termsAndConditionsAgreedVersion: String
- termsAndConditionsAgreedAt: String
- allowEmbedIframes: Boolean
- showShoutsPublicly: Boolean
- sendNotificationEmails: Boolean
- locale: String
+ id: ID!
+ name: String
+ email: String
+ slug: String
+ avatar: ImageInput
+ locationName: String
+ about: String
+ termsAndConditionsAgreedVersion: String
+ termsAndConditionsAgreedAt: String
+ allowEmbedIframes: Boolean
+ showShoutsPublicly: Boolean
+ sendNotificationEmails: Boolean
+ locale: String
): User
DeleteUser(id: ID!, resource: [Deletable]): User
From 52bffa426b798378a2eef7ba00c773b92361c182 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 2 Aug 2022 09:10:02 +0200
Subject: [PATCH 003/374] Fix linting
---
backend/src/models/Groups.js | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/backend/src/models/Groups.js b/backend/src/models/Groups.js
index aa6f5767e0..b26f7779cb 100644
--- a/backend/src/models/Groups.js
+++ b/backend/src/models/Groups.js
@@ -26,16 +26,12 @@ export default {
// },
// },
// Wolle: correct this way?
- members: { type: 'relationship',
- relationship: 'MEMBERS',
- target: 'User',
- direction: 'out'
- },
+ members: { type: 'relationship', relationship: 'MEMBERS', target: 'User', direction: 'out' },
// Wolle: needed? lastActiveAt: { type: 'string', isoDate: true },
createdAt: {
type: 'string',
isoDate: true,
- default: () => new Date().toISOString()
+ default: () => new Date().toISOString(),
},
updatedAt: {
type: 'string',
From 9632d0f8524a9868a984a54409cc2e68bd6a7d0a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 2 Aug 2022 09:18:30 +0200
Subject: [PATCH 004/374] Fix file name from 'Groups.js' to singular 'Group.js'
---
backend/src/models/{Groups.js => Group.js} | 0
1 file changed, 0 insertions(+), 0 deletions(-)
rename backend/src/models/{Groups.js => Group.js} (100%)
diff --git a/backend/src/models/Groups.js b/backend/src/models/Group.js
similarity index 100%
rename from backend/src/models/Groups.js
rename to backend/src/models/Group.js
From f565e5fb6a042286c4f5eeed484011b0f24ca58f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 2 Aug 2022 16:20:11 +0200
Subject: [PATCH 005/374] Implement 'CreateGroup' with tests
---
backend/.env.template | 2 +
backend/src/config/index.js | 1 +
backend/src/db/graphql/mutations.ts | 29 +
backend/src/db/migrate/store.js | 2 +-
backend/src/middleware/excerptMiddleware.js | 5 +
.../src/middleware/permissionsMiddleware.js | 1 +
backend/src/middleware/sluggifyMiddleware.js | 4 +
backend/src/middleware/slugify/uniqueSlug.js | 1 +
backend/src/models/Group.js | 11 +-
backend/src/schema/resolvers/groups.js | 224 +++++++
backend/src/schema/resolvers/groups.spec.js | 610 ++++++++++++++++++
backend/src/schema/types/type/Group.gql | 19 +-
webapp/.env.template | 1 +
webapp/config/index.js | 1 +
14 files changed, 899 insertions(+), 12 deletions(-)
create mode 100644 backend/src/db/graphql/mutations.ts
create mode 100644 backend/src/schema/resolvers/groups.js
create mode 100644 backend/src/schema/resolvers/groups.spec.js
diff --git a/backend/.env.template b/backend/.env.template
index 5858a5d1e3..dd46846a99 100644
--- a/backend/.env.template
+++ b/backend/.env.template
@@ -28,3 +28,5 @@ AWS_BUCKET=
EMAIL_DEFAULT_SENDER="devops@ocelot.social"
EMAIL_SUPPORT="devops@ocelot.social"
+
+CATEGORIES_ACTIVE=false
diff --git a/backend/src/config/index.js b/backend/src/config/index.js
index 6ad8c578b8..7df780cfc2 100644
--- a/backend/src/config/index.js
+++ b/backend/src/config/index.js
@@ -86,6 +86,7 @@ const options = {
ORGANIZATION_URL: emails.ORGANIZATION_LINK,
PUBLIC_REGISTRATION: env.PUBLIC_REGISTRATION === 'true' || false,
INVITE_REGISTRATION: env.INVITE_REGISTRATION !== 'false', // default = true
+ CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false,
}
// Check if all required configs are present
diff --git a/backend/src/db/graphql/mutations.ts b/backend/src/db/graphql/mutations.ts
new file mode 100644
index 0000000000..a29cfa1123
--- /dev/null
+++ b/backend/src/db/graphql/mutations.ts
@@ -0,0 +1,29 @@
+import gql from 'graphql-tag'
+
+export const createGroupMutation = gql`
+ mutation (
+ $id: ID,
+ $name: String!,
+ $slug: String,
+ $about: String,
+ $categoryIds: [ID]
+ ) {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ categoryIds: $categoryIds
+ ) {
+ id
+ name
+ slug
+ about
+ disabled
+ deleted
+ owner {
+ name
+ }
+ }
+ }
+`
diff --git a/backend/src/db/migrate/store.js b/backend/src/db/migrate/store.js
index 7a8be0b945..78960be6b8 100644
--- a/backend/src/db/migrate/store.js
+++ b/backend/src/db/migrate/store.js
@@ -59,7 +59,7 @@ class Store {
const session = driver.session()
await createDefaultAdminUser(session)
const writeTxResultPromise = session.writeTransaction(async (txc) => {
- await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices
+ await txc.run('CALL apoc.schema.assert({},{},true)') // drop all indices and contraints
return Promise.all(
[
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
diff --git a/backend/src/middleware/excerptMiddleware.js b/backend/src/middleware/excerptMiddleware.js
index 40a6a6ae40..cfaf7f1b05 100644
--- a/backend/src/middleware/excerptMiddleware.js
+++ b/backend/src/middleware/excerptMiddleware.js
@@ -2,6 +2,11 @@ import trunc from 'trunc-html'
export default {
Mutation: {
+ CreateGroup: async (resolve, root, args, context, info) => {
+ args.descriptionExcerpt = trunc(args.description, 120).html
+ const result = await resolve(root, args, context, info)
+ return result
+ },
CreatePost: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html
const result = await resolve(root, args, context, info)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index b10389f501..7e23cfe0ff 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -140,6 +140,7 @@ export default shield(
Signup: or(publicRegistration, inviteRegistration, isAdmin),
SignupVerification: allow,
UpdateUser: onlyYourself,
+ CreateGroup: isAuthenticated,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
diff --git a/backend/src/middleware/sluggifyMiddleware.js b/backend/src/middleware/sluggifyMiddleware.js
index 165235be9d..25c7c21a49 100644
--- a/backend/src/middleware/sluggifyMiddleware.js
+++ b/backend/src/middleware/sluggifyMiddleware.js
@@ -26,6 +26,10 @@ export default {
args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User')))
return resolve(root, args, context, info)
},
+ CreateGroup: async (resolve, root, args, context, info) => {
+ args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Group')))
+ return resolve(root, args, context, info)
+ },
CreatePost: async (resolve, root, args, context, info) => {
args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post')))
return resolve(root, args, context, info)
diff --git a/backend/src/middleware/slugify/uniqueSlug.js b/backend/src/middleware/slugify/uniqueSlug.js
index 7cfb89c193..41d58ece3d 100644
--- a/backend/src/middleware/slugify/uniqueSlug.js
+++ b/backend/src/middleware/slugify/uniqueSlug.js
@@ -1,4 +1,5 @@
import slugify from 'slug'
+
export default async function uniqueSlug(string, isUnique) {
const slug = slugify(string || 'anonymous', {
lower: true,
diff --git a/backend/src/models/Group.js b/backend/src/models/Group.js
index b26f7779cb..651c2983e8 100644
--- a/backend/src/models/Group.js
+++ b/backend/src/models/Group.js
@@ -15,7 +15,8 @@ export default {
wasSeeded: 'boolean', // Wolle: used or needed?
locationName: { type: 'string', allow: [null] },
about: { type: 'string', allow: [null, ''] }, // Wolle: null?
- description: { type: 'string', allow: [null, ''] }, // Wolle: null? HTML with Tiptap, similar to post content
+ description: { type: 'string', allow: [null, ''] }, // Wolle: null? HTML with Tiptap, similar to post content, wie bei Posts "content: { type: 'string', disallow: [null], min: 3 },"?
+ descriptionExcerpt: { type: 'string', allow: [null] },
// Wolle: followedBy: {
// type: 'relationship',
// relationship: 'FOLLOWS',
@@ -25,8 +26,14 @@ export default {
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
// },
// },
+ owner: {
+ type: 'relationship',
+ relationship: 'OWNS',
+ target: 'User',
+ direction: 'in',
+ },
// Wolle: correct this way?
- members: { type: 'relationship', relationship: 'MEMBERS', target: 'User', direction: 'out' },
+ // members: { type: 'relationship', relationship: 'MEMBERS', target: 'User', direction: 'out' },
// Wolle: needed? lastActiveAt: { type: 'string', isoDate: true },
createdAt: {
type: 'string',
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
new file mode 100644
index 0000000000..0ed5d1356b
--- /dev/null
+++ b/backend/src/schema/resolvers/groups.js
@@ -0,0 +1,224 @@
+import { v4 as uuid } from 'uuid'
+// Wolle: import { neo4jgraphql } from 'neo4j-graphql-js'
+// Wolle: import { isEmpty } from 'lodash'
+import { UserInputError } from 'apollo-server'
+import CONFIG from '../../config'
+// Wolle: import { mergeImage, deleteImage } from './images/images'
+import Resolver from './helpers/Resolver'
+// Wolle: import { filterForMutedUsers } from './helpers/filterForMutedUsers'
+
+// Wolle: const maintainPinnedPosts = (params) => {
+// const pinnedPostFilter = { pinned: true }
+// if (isEmpty(params.filter)) {
+// params.filter = { OR: [pinnedPostFilter, {}] }
+// } else {
+// params.filter = { OR: [pinnedPostFilter, { ...params.filter }] }
+// }
+// return params
+// }
+
+export default {
+ // Wolle: Query: {
+ // Post: async (object, params, context, resolveInfo) => {
+ // params = await filterForMutedUsers(params, context)
+ // params = await maintainPinnedPosts(params)
+ // return neo4jgraphql(object, params, context, resolveInfo)
+ // },
+ // findPosts: async (object, params, context, resolveInfo) => {
+ // params = await filterForMutedUsers(params, context)
+ // return neo4jgraphql(object, params, context, resolveInfo)
+ // },
+ // profilePagePosts: async (object, params, context, resolveInfo) => {
+ // params = await filterForMutedUsers(params, context)
+ // return neo4jgraphql(object, params, context, resolveInfo)
+ // },
+ // PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => {
+ // const { postId, data } = params
+ // const session = context.driver.session()
+ // const readTxResultPromise = session.readTransaction(async (transaction) => {
+ // const emotionsCountTransactionResponse = await transaction.run(
+ // `
+ // MATCH (post:Post {id: $postId})<-[emoted:EMOTED {emotion: $data.emotion}]-()
+ // RETURN COUNT(DISTINCT emoted) as emotionsCount
+ // `,
+ // { postId, data },
+ // )
+ // return emotionsCountTransactionResponse.records.map(
+ // (record) => record.get('emotionsCount').low,
+ // )
+ // })
+ // try {
+ // const [emotionsCount] = await readTxResultPromise
+ // return emotionsCount
+ // } finally {
+ // session.close()
+ // }
+ // },
+ // PostsEmotionsByCurrentUser: async (object, params, context, resolveInfo) => {
+ // const { postId } = params
+ // const session = context.driver.session()
+ // const readTxResultPromise = session.readTransaction(async (transaction) => {
+ // const emotionsTransactionResponse = await transaction.run(
+ // `
+ // MATCH (user:User {id: $userId})-[emoted:EMOTED]->(post:Post {id: $postId})
+ // RETURN collect(emoted.emotion) as emotion
+ // `,
+ // { userId: context.user.id, postId },
+ // )
+ // return emotionsTransactionResponse.records.map((record) => record.get('emotion'))
+ // })
+ // try {
+ // const [emotions] = await readTxResultPromise
+ // return emotions
+ // } finally {
+ // session.close()
+ // }
+ // },
+ // },
+ Mutation: {
+ CreateGroup: async (_parent, params, context, _resolveInfo) => {
+ const { categoryIds } = params
+ delete params.categoryIds
+ params.id = params.id || uuid()
+ const session = context.driver.session()
+ const writeTxResultPromise = session.writeTransaction(async (transaction) => {
+ const categoriesCypher =
+ CONFIG.CATEGORIES_ACTIVE && categoryIds
+ ? `WITH group
+ UNWIND $categoryIds AS categoryId
+ MATCH (category:Category {id: categoryId})
+ MERGE (group)-[:CATEGORIZED]->(category)`
+ : ''
+ const ownercreateGroupTransactionResponse = await transaction.run(
+ `
+ CREATE (group:Group)
+ SET group += $params
+ SET group.createdAt = toString(datetime())
+ SET group.updatedAt = toString(datetime())
+ WITH group
+ MATCH (owner:User {id: $userId})
+ MERGE (group)<-[:OWNS]-(owner)
+ MERGE (group)<-[:ADMINISTERS]-(owner)
+ ${categoriesCypher}
+ RETURN group {.*}
+ `,
+ { userId: context.user.id, categoryIds, params },
+ )
+ const [group] = ownercreateGroupTransactionResponse.records.map((record) =>
+ record.get('group'),
+ )
+ return group
+ })
+ try {
+ const group = await writeTxResultPromise
+ return group
+ } catch (e) {
+ if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
+ throw new UserInputError('Group with this slug already exists!')
+ throw new Error(e)
+ } finally {
+ session.close()
+ }
+ },
+ // UpdatePost: async (_parent, params, context, _resolveInfo) => {
+ // const { categoryIds } = params
+ // const { image: imageInput } = params
+ // delete params.categoryIds
+ // delete params.image
+ // const session = context.driver.session()
+ // let updatePostCypher = `
+ // MATCH (post:Post {id: $params.id})
+ // SET post += $params
+ // SET post.updatedAt = toString(datetime())
+ // WITH post
+ // `
+
+ // if (categoryIds && categoryIds.length) {
+ // const cypherDeletePreviousRelations = `
+ // MATCH (post:Post { id: $params.id })-[previousRelations:CATEGORIZED]->(category:Category)
+ // DELETE previousRelations
+ // RETURN post, category
+ // `
+
+ // await session.writeTransaction((transaction) => {
+ // return transaction.run(cypherDeletePreviousRelations, { params })
+ // })
+
+ // updatePostCypher += `
+ // UNWIND $categoryIds AS categoryId
+ // MATCH (category:Category {id: categoryId})
+ // MERGE (post)-[:CATEGORIZED]->(category)
+ // WITH post
+ // `
+ // }
+
+ // updatePostCypher += `RETURN post {.*}`
+ // const updatePostVariables = { categoryIds, params }
+ // try {
+ // const writeTxResultPromise = session.writeTransaction(async (transaction) => {
+ // const updatePostTransactionResponse = await transaction.run(
+ // updatePostCypher,
+ // updatePostVariables,
+ // )
+ // const [post] = updatePostTransactionResponse.records.map((record) => record.get('post'))
+ // await mergeImage(post, 'HERO_IMAGE', imageInput, { transaction })
+ // return post
+ // })
+ // const post = await writeTxResultPromise
+ // return post
+ // } finally {
+ // session.close()
+ // }
+ // },
+
+ // DeletePost: async (object, args, context, resolveInfo) => {
+ // const session = context.driver.session()
+ // const writeTxResultPromise = session.writeTransaction(async (transaction) => {
+ // const deletePostTransactionResponse = await transaction.run(
+ // `
+ // MATCH (post:Post {id: $postId})
+ // OPTIONAL MATCH (post)<-[:COMMENTS]-(comment:Comment)
+ // SET post.deleted = TRUE
+ // SET post.content = 'UNAVAILABLE'
+ // SET post.contentExcerpt = 'UNAVAILABLE'
+ // SET post.title = 'UNAVAILABLE'
+ // SET comment.deleted = TRUE
+ // RETURN post {.*}
+ // `,
+ // { postId: args.id },
+ // )
+ // const [post] = deletePostTransactionResponse.records.map((record) => record.get('post'))
+ // await deleteImage(post, 'HERO_IMAGE', { transaction })
+ // return post
+ // })
+ // try {
+ // const post = await writeTxResultPromise
+ // return post
+ // } finally {
+ // session.close()
+ // }
+ },
+ Group: {
+ ...Resolver('Group', {
+ // Wolle: undefinedToNull: ['activityId', 'objectId', 'language', 'pinnedAt', 'pinned'],
+ hasMany: {
+ // Wolle: tags: '-[:TAGGED]->(related:Tag)',
+ categories: '-[:CATEGORIZED]->(related:Category)',
+ },
+ hasOne: {
+ owner: '<-[:OWNS]-(related:User)',
+ // Wolle: image: '-[:HERO_IMAGE]->(related:Image)',
+ },
+ // Wolle: count: {
+ // contributionsCount:
+ // '-[:WROTE]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
+ // },
+ // Wolle: boolean: {
+ // shoutedByCurrentUser:
+ // 'MATCH(this)<-[:SHOUTED]-(related:User {id: $cypherParams.currentUserId}) RETURN COUNT(related) >= 1',
+ // viewedTeaserByCurrentUser:
+ // 'MATCH (this)<-[:VIEWED_TEASER]-(u:User {id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1',
+ // },
+ }),
+ },
+}
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
new file mode 100644
index 0000000000..aba7181595
--- /dev/null
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -0,0 +1,610 @@
+import { createTestClient } from 'apollo-server-testing'
+import Factory, { cleanDatabase } from '../../db/factories'
+import { createGroupMutation } from '../../db/graphql/mutations'
+import { getNeode, getDriver } from '../../db/neo4j'
+import createServer from '../../server'
+
+const driver = getDriver()
+const neode = getNeode()
+
+// Wolle: let query
+let mutate
+let authenticatedUser
+let user
+
+const categoryIds = ['cat9', 'cat4', 'cat15']
+let variables = {}
+
+beforeAll(async () => {
+ await cleanDatabase()
+
+ const { server } = createServer({
+ context: () => {
+ return {
+ driver,
+ neode,
+ user: authenticatedUser,
+ }
+ },
+ })
+ // Wolle: query = createTestClient(server).query
+ mutate = createTestClient(server).mutate
+})
+
+afterAll(async () => {
+ await cleanDatabase()
+})
+
+beforeEach(async () => {
+ variables = {}
+ user = await Factory.build(
+ 'user',
+ {
+ id: 'current-user',
+ name: 'TestUser',
+ },
+ {
+ email: 'test@example.org',
+ password: '1234',
+ },
+ )
+ await Promise.all([
+ neode.create('Category', {
+ id: 'cat9',
+ name: 'Democracy & Politics',
+ icon: 'university',
+ }),
+ neode.create('Category', {
+ id: 'cat4',
+ name: 'Environment & Nature',
+ icon: 'tree',
+ }),
+ neode.create('Category', {
+ id: 'cat15',
+ name: 'Consumption & Sustainability',
+ icon: 'shopping-cart',
+ }),
+ neode.create('Category', {
+ id: 'cat27',
+ name: 'Animal Protection',
+ icon: 'paw',
+ }),
+ ])
+ authenticatedUser = null
+})
+
+// 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
+afterEach(async () => {
+ await cleanDatabase()
+})
+
+// describe('Group', () => {
+// describe('can be filtered', () => {
+// let followedUser, happyPost, cryPost
+// beforeEach(async () => {
+// ;[followedUser] = await Promise.all([
+// Factory.build(
+// 'user',
+// {
+// id: 'followed-by-me',
+// name: 'Followed User',
+// },
+// {
+// email: 'followed@example.org',
+// password: '1234',
+// },
+// ),
+// ])
+// ;[happyPost, cryPost] = await Promise.all([
+// Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
+// Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
+// Factory.build(
+// 'post',
+// {
+// id: 'post-by-followed-user',
+// },
+// {
+// categoryIds: ['cat9'],
+// author: followedUser,
+// },
+// ),
+// ])
+// })
+
+// 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({
+// data: {
+// Post: expect.arrayContaining(expected),
+// },
+// })
+// })
+// })
+
+// /* it('by categories', async () => {
+// const postQueryFilteredByCategories = gql`
+// query Post($filter: _PostFilter) {
+// Post(filter: $filter) {
+// id
+// categories {
+// id
+// }
+// }
+// }
+// `
+// const expected = {
+// data: {
+// Post: [
+// {
+// id: 'post-by-followed-user',
+// categories: [{ id: 'cat9' }],
+// },
+// ],
+// },
+// }
+// variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
+// await expect(
+// query({ query: postQueryFilteredByCategories, variables }),
+// ).resolves.toMatchObject(expected)
+// }) */
+
+// 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: {
+// Post: [
+// {
+// id: 'happy-post',
+// emotions: [{ emotion: 'happy' }],
+// },
+// ],
+// },
+// }
+// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
+// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
+// await expect(
+// query({ query: postQueryFilteredByEmotions, 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({
+// data: {
+// Post: expect.arrayContaining(expected),
+// },
+// errors: undefined,
+// })
+// })
+// })
+
+// it('by followed-by', async () => {
+// const postQueryFilteredByUsersFollowed = gql`
+// query Post($filter: _PostFilter) {
+// Post(filter: $filter) {
+// id
+// author {
+// id
+// name
+// }
+// }
+// }
+// `
+
+// await user.relateTo(followedUser, 'following')
+// variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
+// await expect(
+// query({ query: postQueryFilteredByUsersFollowed, variables }),
+// ).resolves.toMatchObject({
+// data: {
+// Post: [
+// {
+// id: 'post-by-followed-user',
+// author: { id: 'followed-by-me', name: 'Followed User' },
+// },
+// ],
+// },
+// errors: undefined,
+// })
+// })
+// })
+// })
+
+describe('CreateGroup', () => {
+ beforeEach(() => {
+ variables = {
+ ...variables,
+ id: 'g589',
+ name: 'The Best Group',
+ slug: 'the-best-group',
+ about: 'We will change the world!',
+ categoryIds,
+ }
+ })
+
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('creates a group', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ // Wolle: id: 'g589',
+ name: 'The Best Group',
+ slug: 'the-best-group',
+ about: 'We will change the world!',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+
+ it('assigns the authenticated user as owner', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ owner: {
+ name: 'TestUser',
+ },
+ },
+ },
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+
+ it('`disabled` and `deleted` default to `false`', async () => {
+ const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+ })
+})
+
+// describe('UpdatePost', () => {
+// let author, newlyCreatedPost
+// const updatePostMutation = gql`
+// mutation ($id: ID!, $title: String!, $content: String!, $image: ImageInput) {
+// UpdatePost(id: $id, title: $title, content: $content, image: $image) {
+// id
+// title
+// content
+// author {
+// name
+// slug
+// }
+// createdAt
+// updatedAt
+// }
+// }
+// `
+// beforeEach(async () => {
+// author = await Factory.build('user', { slug: 'the-author' })
+// newlyCreatedPost = await Factory.build(
+// 'post',
+// {
+// id: 'p9876',
+// title: 'Old title',
+// content: 'Old content',
+// },
+// {
+// author,
+// categoryIds,
+// },
+// )
+
+// variables = {
+// id: 'p9876',
+// title: 'New title',
+// content: 'New content',
+// }
+// })
+
+// describe('unauthenticated', () => {
+// it('throws authorization error', async () => {
+// authenticatedUser = null
+// expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject({
+// errors: [{ message: 'Not Authorised!' }],
+// data: { UpdatePost: null },
+// })
+// })
+// })
+
+// describe('authenticated but not the author', () => {
+// beforeEach(async () => {
+// authenticatedUser = await user.toJson()
+// })
+
+// it('throws authorization error', async () => {
+// const { errors } = await mutate({ mutation: updatePostMutation, variables })
+// expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+// })
+// })
+
+// describe('authenticated as author', () => {
+// beforeEach(async () => {
+// authenticatedUser = await author.toJson()
+// })
+
+// it('updates a post', async () => {
+// const expected = {
+// data: { UpdatePost: { id: 'p9876', content: 'New content' } },
+// errors: undefined,
+// }
+// await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+
+// it('updates a post, but maintains non-updated attributes', async () => {
+// const expected = {
+// data: {
+// UpdatePost: { id: 'p9876', content: 'New content', createdAt: expect.any(String) },
+// },
+// errors: undefined,
+// }
+// await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+
+// it('updates the updatedAt attribute', async () => {
+// newlyCreatedPost = await newlyCreatedPost.toJson()
+// const {
+// data: { UpdatePost },
+// } = await mutate({ mutation: updatePostMutation, variables })
+// expect(newlyCreatedPost.updatedAt).toBeTruthy()
+// expect(Date.parse(newlyCreatedPost.updatedAt)).toEqual(expect.any(Number))
+// expect(UpdatePost.updatedAt).toBeTruthy()
+// expect(Date.parse(UpdatePost.updatedAt)).toEqual(expect.any(Number))
+// expect(newlyCreatedPost.updatedAt).not.toEqual(UpdatePost.updatedAt)
+// })
+
+// /* describe('no new category ids provided for update', () => {
+// it('resolves and keeps current categories', async () => {
+// const expected = {
+// data: {
+// UpdatePost: {
+// id: 'p9876',
+// categories: expect.arrayContaining([{ id: 'cat9' }, { id: 'cat4' }, { id: 'cat15' }]),
+// },
+// },
+// errors: undefined,
+// }
+// await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+// }) */
+
+// /* describe('given category ids', () => {
+// beforeEach(() => {
+// variables = { ...variables, categoryIds: ['cat27'] }
+// })
+
+// it('updates categories of a post', async () => {
+// const expected = {
+// data: {
+// UpdatePost: {
+// id: 'p9876',
+// categories: expect.arrayContaining([{ id: 'cat27' }]),
+// },
+// },
+// errors: undefined,
+// }
+// await expect(mutate({ mutation: updatePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+// }) */
+
+// describe('params.image', () => {
+// describe('is object', () => {
+// beforeEach(() => {
+// variables = { ...variables, image: { sensitive: true } }
+// })
+// it('updates the image', async () => {
+// await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy()
+// await mutate({ mutation: updatePostMutation, variables })
+// await expect(neode.first('Image', { sensitive: true })).resolves.toBeTruthy()
+// })
+// })
+
+// describe('is null', () => {
+// beforeEach(() => {
+// variables = { ...variables, image: null }
+// })
+// it('deletes the image', async () => {
+// await expect(neode.all('Image')).resolves.toHaveLength(6)
+// await mutate({ mutation: updatePostMutation, variables })
+// await expect(neode.all('Image')).resolves.toHaveLength(5)
+// })
+// })
+
+// describe('is undefined', () => {
+// beforeEach(() => {
+// delete variables.image
+// })
+// it('keeps the image unchanged', async () => {
+// await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy()
+// await mutate({ mutation: updatePostMutation, variables })
+// await expect(neode.first('Image', { sensitive: true })).resolves.toBeFalsy()
+// })
+// })
+// })
+// })
+// })
+
+// describe('DeletePost', () => {
+// let author
+// const deletePostMutation = gql`
+// mutation ($id: ID!) {
+// DeletePost(id: $id) {
+// id
+// deleted
+// content
+// contentExcerpt
+// image {
+// url
+// }
+// comments {
+// deleted
+// content
+// contentExcerpt
+// }
+// }
+// }
+// `
+
+// beforeEach(async () => {
+// author = await Factory.build('user')
+// await Factory.build(
+// 'post',
+// {
+// id: 'p4711',
+// title: 'I will be deleted',
+// content: 'To be deleted',
+// },
+// {
+// image: Factory.build('image', {
+// url: 'path/to/some/image',
+// }),
+// author,
+// categoryIds,
+// },
+// )
+// variables = { ...variables, id: 'p4711' }
+// })
+
+// describe('unauthenticated', () => {
+// it('throws authorization error', async () => {
+// const { errors } = await mutate({ mutation: deletePostMutation, variables })
+// expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+// })
+// })
+
+// describe('authenticated but not the author', () => {
+// beforeEach(async () => {
+// authenticatedUser = await user.toJson()
+// })
+
+// it('throws authorization error', async () => {
+// const { errors } = await mutate({ mutation: deletePostMutation, variables })
+// expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+// })
+// })
+
+// describe('authenticated as author', () => {
+// beforeEach(async () => {
+// authenticatedUser = await author.toJson()
+// })
+
+// it('marks the post as deleted and blacks out attributes', async () => {
+// const expected = {
+// data: {
+// DeletePost: {
+// id: 'p4711',
+// deleted: true,
+// content: 'UNAVAILABLE',
+// contentExcerpt: 'UNAVAILABLE',
+// image: null,
+// comments: [],
+// },
+// },
+// }
+// await expect(mutate({ mutation: deletePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+
+// describe('if there are comments on the post', () => {
+// beforeEach(async () => {
+// await Factory.build(
+// 'comment',
+// {
+// content: 'to be deleted comment content',
+// contentExcerpt: 'to be deleted comment content',
+// },
+// {
+// postId: 'p4711',
+// },
+// )
+// })
+
+// it('marks the comments as deleted', async () => {
+// const expected = {
+// data: {
+// DeletePost: {
+// id: 'p4711',
+// deleted: true,
+// content: 'UNAVAILABLE',
+// contentExcerpt: 'UNAVAILABLE',
+// image: null,
+// comments: [
+// {
+// deleted: true,
+// // Should we black out the comment content in the database, too?
+// content: 'UNAVAILABLE',
+// contentExcerpt: 'UNAVAILABLE',
+// },
+// ],
+// },
+// },
+// }
+// await expect(mutate({ mutation: deletePostMutation, variables })).resolves.toMatchObject(
+// expected,
+// )
+// })
+// })
+// })
+// })
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 655a251d6e..2dbe3c8c6b 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -21,7 +21,7 @@ type Group {
id: ID!
name: String # title
slug: String!
-
+
createdAt: String
updatedAt: String
deleted: Boolean
@@ -41,12 +41,14 @@ type Group {
# Wolle: needed?
socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
+ owner: User @relation(name: "OWNS", direction: "IN")
+
# Wolle: showShoutsPublicly: Boolean
# Wolle: sendNotificationEmails: Boolean
# Wolle: needed? locale: String
- members: [User]! @relation(name: "MEMBERS", direction: "OUT")
- membersCount: Int!
- @cypher(statement: "MATCH (this)-[:MEMBERS]->(r:User) RETURN COUNT(DISTINCT r)")
+ # members: [User]! @relation(name: "MEMBERS", direction: "OUT")
+ # membersCount: Int!
+ # @cypher(statement: "MATCH (this)-[:MEMBERS]->(r:User) RETURN COUNT(DISTINCT r)")
# Wolle: followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
# Wolle: followedByCount: Int!
@@ -207,14 +209,14 @@ type Query {
type Mutation {
CreateGroup (
- id: ID!
- name: String
- email: String
+ id: ID
+ name: String!
slug: String
avatar: ImageInput
locationName: String
about: String
description: String
+ categoryIds: [ID]
# Wolle: add group settings
# Wolle:
# showShoutsPublicly: Boolean
@@ -222,10 +224,9 @@ type Mutation {
# locale: String
): Group
- UpdateUser (
+ UpdateGroup (
id: ID!
name: String
- email: String
slug: String
avatar: ImageInput
locationName: String
diff --git a/webapp/.env.template b/webapp/.env.template
index 7373255a91..9776fcea29 100644
--- a/webapp/.env.template
+++ b/webapp/.env.template
@@ -4,3 +4,4 @@ PUBLIC_REGISTRATION=false
INVITE_REGISTRATION=true
WEBSOCKETS_URI=ws://localhost:3000/api/graphql
GRAPHQL_URI=http://localhost:4000/
+CATEGORIES_ACTIVE=false
diff --git a/webapp/config/index.js b/webapp/config/index.js
index 00df85baca..db030e9298 100644
--- a/webapp/config/index.js
+++ b/webapp/config/index.js
@@ -33,6 +33,7 @@ const options = {
// Cookies
COOKIE_EXPIRE_TIME: process.env.COOKIE_EXPIRE_TIME || 730, // Two years by default
COOKIE_HTTPS_ONLY: process.env.COOKIE_HTTPS_ONLY || process.env.NODE_ENV === 'production', // ensure true in production if not set explicitly
+ CATEGORIES_ACTIVE: process.env.CATEGORIES_ACTIVE === 'true' || false,
}
const CONFIG = {
From 4f1646509b40a9ddc21b30dadd7056b10ffaa91e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 2 Aug 2022 16:25:46 +0200
Subject: [PATCH 006/374] Fix Group name slugification
---
backend/src/middleware/sluggifyMiddleware.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/backend/src/middleware/sluggifyMiddleware.js b/backend/src/middleware/sluggifyMiddleware.js
index 25c7c21a49..2a965c87f0 100644
--- a/backend/src/middleware/sluggifyMiddleware.js
+++ b/backend/src/middleware/sluggifyMiddleware.js
@@ -27,7 +27,7 @@ export default {
return resolve(root, args, context, info)
},
CreateGroup: async (resolve, root, args, context, info) => {
- args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Group')))
+ args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group')))
return resolve(root, args, context, info)
},
CreatePost: async (resolve, root, args, context, info) => {
From da2c7dc6845cd100d8b9d3e0a6b0ef4a7426cbbe Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 07:29:51 +0200
Subject: [PATCH 007/374] Add migration with fulltext indeces and unique keys
for groups
---
backend/src/db/migrate/store.js | 1 -
...text_indices_and_unique_keys_for_groups.js | 66 +++++++++++++++++++
2 files changed, 66 insertions(+), 1 deletion(-)
create mode 100644 backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.js
diff --git a/backend/src/db/migrate/store.js b/backend/src/db/migrate/store.js
index 78960be6b8..938ebef020 100644
--- a/backend/src/db/migrate/store.js
+++ b/backend/src/db/migrate/store.js
@@ -63,7 +63,6 @@ class Store {
return Promise.all(
[
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
- 'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["Group"],["name", "slug", "description"])', // Wolle: check for 'name', 'slug', 'description'
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
'CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])',
].map((statement) => txc.run(statement)),
diff --git a/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.js b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.js
new file mode 100644
index 0000000000..b87e5632a3
--- /dev/null
+++ b/backend/src/db/migrations/20220803060819-create_fulltext_indices_and_unique_keys_for_groups.js
@@ -0,0 +1,66 @@
+import { getDriver } from '../../db/neo4j'
+
+export const description = `
+ We introduced a new node label 'Group' and we need two primary keys 'id' and 'slug' for it.
+ Additional we like to have fulltext indices the keys 'name', 'slug', 'about', and 'description'.
+`
+
+export async function up(next) {
+ const driver = getDriver()
+ const session = driver.session()
+ const transaction = session.beginTransaction()
+
+ try {
+ // Implement your migration here.
+ await transaction.run(`
+ CREATE CONSTRAINT ON ( group:Group ) ASSERT group.id IS UNIQUE
+ `)
+ await transaction.run(`
+ CREATE CONSTRAINT ON ( group:Group ) ASSERT group.slug IS UNIQUE
+ `)
+ await transaction.run(`
+ CALL db.index.fulltext.createNodeIndex("group_fulltext_search",["Group"],["name", "slug", "about", "description"])
+ `)
+ await transaction.commit()
+ next()
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log(error)
+ await transaction.rollback()
+ // eslint-disable-next-line no-console
+ console.log('rolled back')
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+}
+
+export async function down(next) {
+ const driver = getDriver()
+ const session = driver.session()
+ const transaction = session.beginTransaction()
+
+ try {
+ // Implement your migration here.
+ await transaction.run(`
+ DROP CONSTRAINT ON ( group:Group ) ASSERT group.id IS UNIQUE
+ `)
+ await transaction.run(`
+ DROP CONSTRAINT ON ( group:Group ) ASSERT group.slug IS UNIQUE
+ `)
+ await transaction.run(`
+ CALL db.index.fulltext.drop("group_fulltext_search")
+ `)
+ await transaction.commit()
+ next()
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.log(error)
+ await transaction.rollback()
+ // eslint-disable-next-line no-console
+ console.log('rolled back')
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+}
From fc20143a6566df4e81dd437c833f3e606abe31dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 07:51:29 +0200
Subject: [PATCH 008/374] Test groups creation in slugifyMiddleware
---
backend/src/db/graphql/mutations.ts | 30 +++
.../src/middleware/slugifyMiddleware.spec.js | 246 ++++++++++++++----
backend/src/models/User.spec.js | 2 +-
backend/src/schema/resolvers/groups.spec.js | 4 +-
4 files changed, 222 insertions(+), 60 deletions(-)
diff --git a/backend/src/db/graphql/mutations.ts b/backend/src/db/graphql/mutations.ts
index a29cfa1123..5fc554ee2b 100644
--- a/backend/src/db/graphql/mutations.ts
+++ b/backend/src/db/graphql/mutations.ts
@@ -27,3 +27,33 @@ export const createGroupMutation = gql`
}
}
`
+
+export const createPostMutation = gql`
+ mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
+ CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
+ slug
+ }
+ }
+`
+
+export const signupVerificationMutation = gql`
+ mutation (
+ $password: String!
+ $email: String!
+ $name: String!
+ $slug: String
+ $nonce: String!
+ $termsAndConditionsAgreedVersion: String!
+ ) {
+ SignupVerification(
+ email: $email
+ password: $password
+ name: $name
+ slug: $slug
+ nonce: $nonce
+ termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
+ ) {
+ slug
+ }
+ }
+`
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 7c6f18ab19..fbf03c6755 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -1,8 +1,9 @@
-import Factory, { cleanDatabase } from '../db/factories'
-import { gql } from '../helpers/jest'
+import gql from 'graphql-tag'
import { getNeode, getDriver } from '../db/neo4j'
import createServer from '../server'
import { createTestClient } from 'apollo-server-testing'
+import Factory, { cleanDatabase } from '../db/factories'
+import { createPostMutation, createGroupMutation, signupVerificationMutation } from '../db/graphql/mutations'
let mutate
let authenticatedUser
@@ -57,15 +58,128 @@ afterEach(async () => {
})
describe('slugifyMiddleware', () => {
- describe('CreatePost', () => {
+ describe('CreateGroup', () => {
const categoryIds = ['cat9']
- const createPostMutation = gql`
- mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
- CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
- slug
- }
+
+ beforeEach(() => {
+ variables = {
+ ...variables,
+ name: 'The Best Group',
+ about: 'Some about',
+ categoryIds,
}
- `
+ })
+
+ describe('if slug not exists', () => {
+ it('generates a slug based on name', async () => {
+ await expect(
+ mutate({
+ mutation: createGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ slug: 'the-best-group',
+ },
+ },
+ })
+ })
+
+ it('generates a slug based on given slug', async () => {
+ await expect(
+ mutate({
+ mutation: createGroupMutation,
+ variables: {
+ ...variables,
+ slug: 'the-group',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ slug: 'the-group',
+ },
+ },
+ })
+ })
+ })
+
+ describe('if slug exists', () => {
+ beforeEach(async () => {
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ ...variables,
+ name: 'Pre-Existing Group',
+ slug: 'pre-existing-group',
+ about: 'As an about',
+ },
+ })
+ })
+
+ it('chooses another slug', async () => {
+ variables = {
+ ...variables,
+ name: 'Pre-Existing Group',
+ about: 'As an about',
+ }
+ await expect(
+ mutate({
+ mutation: createGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ slug: 'pre-existing-group-1',
+ },
+ },
+ })
+ })
+
+ describe('but if the client specifies a slug', () => {
+ it('rejects CreateGroup', async (done) => {
+ variables = {
+ ...variables,
+ name: 'Pre-Existing Group',
+ about: 'As an about',
+ slug: 'pre-existing-group',
+ }
+ try {
+ await expect(
+ mutate({ mutation: createGroupMutation, variables }),
+ ).resolves.toMatchObject({
+ errors: [
+ {
+ message: 'Group with this slug already exists!',
+ },
+ ],
+ })
+ done()
+ } catch (error) {
+ throw new Error(`
+ ${error}
+
+ Probably your database has no unique constraints!
+
+ To see all constraints go to http://localhost:7474/browser/ and
+ paste the following:
+ \`\`\`
+ CALL db.constraints();
+ \`\`\`
+
+ Learn how to setup the database here:
+ https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/backend/README.md#database-indices-and-constraints
+ `)
+ }
+ })
+ })
+ })
+ })
+
+ describe('CreatePost', () => {
+ const categoryIds = ['cat9']
beforeEach(() => {
variables = {
@@ -76,18 +190,38 @@ describe('slugifyMiddleware', () => {
}
})
- it('generates a slug based on title', async () => {
- await expect(
- mutate({
- mutation: createPostMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- CreatePost: {
- slug: 'i-am-a-brand-new-post',
+ describe('if slug not exists', () => {
+ it('generates a slug based on title', async () => {
+ await expect(
+ mutate({
+ mutation: createPostMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ CreatePost: {
+ slug: 'i-am-a-brand-new-post',
+ },
},
- },
+ })
+ })
+
+ it('generates a slug based on given slug', async () => {
+ await expect(
+ mutate({
+ mutation: createPostMutation,
+ variables: {
+ ...variables,
+ slug: 'the-post',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ CreatePost: {
+ slug: 'the-post',
+ },
+ },
+ })
})
})
@@ -160,7 +294,7 @@ describe('slugifyMiddleware', () => {
\`\`\`
Learn how to setup the database here:
- https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints
+ https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/backend/README.md#database-indices-and-constraints
`)
}
})
@@ -169,28 +303,6 @@ describe('slugifyMiddleware', () => {
})
describe('SignupVerification', () => {
- const mutation = gql`
- mutation (
- $password: String!
- $email: String!
- $name: String!
- $slug: String
- $nonce: String!
- $termsAndConditionsAgreedVersion: String!
- ) {
- SignupVerification(
- email: $email
- password: $password
- name: $name
- slug: $slug
- nonce: $nonce
- termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
- ) {
- slug
- }
- }
- `
-
beforeEach(() => {
variables = {
...variables,
@@ -211,21 +323,41 @@ describe('slugifyMiddleware', () => {
})
})
- it('generates a slug based on name', async () => {
- await expect(
- mutate({
- mutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- SignupVerification: {
- slug: 'i-am-a-user',
+ describe('if slug not exists', () => {
+ it('generates a slug based on name', async () => {
+ await expect(
+ mutate({
+ mutation: signupVerificationMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ SignupVerification: {
+ slug: 'i-am-a-user',
+ },
},
- },
+ })
})
- })
+ it('generates a slug based on given slug', async () => {
+ await expect(
+ mutate({
+ mutation: signupVerificationMutation,
+ variables: {
+ ...variables,
+ slug: 'the-user',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ SignupVerification: {
+ slug: 'the-user',
+ },
+ },
+ })
+ })
+ })
+
describe('if slug exists', () => {
beforeEach(async () => {
await Factory.build('user', {
@@ -237,7 +369,7 @@ describe('slugifyMiddleware', () => {
it('chooses another slug', async () => {
await expect(
mutate({
- mutation,
+ mutation: signupVerificationMutation,
variables,
}),
).resolves.toMatchObject({
@@ -260,7 +392,7 @@ describe('slugifyMiddleware', () => {
it('rejects SignupVerification (on FAIL Neo4j constraints may not defined in database)', async () => {
await expect(
mutate({
- mutation,
+ mutation: signupVerificationMutation,
variables,
}),
).resolves.toMatchObject({
diff --git a/backend/src/models/User.spec.js b/backend/src/models/User.spec.js
index 102acde6ab..c64d1fd374 100644
--- a/backend/src/models/User.spec.js
+++ b/backend/src/models/User.spec.js
@@ -55,7 +55,7 @@ describe('slug', () => {
\`\`\`
Learn how to setup the database here:
- https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints
+ https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/backend/README.md#database-indices-and-constraints
`)
}
})
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index aba7181595..8d8c7c3d8c 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -250,7 +250,7 @@ describe('CreateGroup', () => {
...variables,
id: 'g589',
name: 'The Best Group',
- slug: 'the-best-group',
+ slug: 'the-group',
about: 'We will change the world!',
categoryIds,
}
@@ -274,7 +274,7 @@ describe('CreateGroup', () => {
CreateGroup: {
// Wolle: id: 'g589',
name: 'The Best Group',
- slug: 'the-best-group',
+ slug: 'the-group',
about: 'We will change the world!',
},
},
From ea0223b6f2a9f19c74307d28612e0cd16f192628 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 07:59:00 +0200
Subject: [PATCH 009/374] Rename 'UserGroup' to 'UserRole'
---
backend/src/schema/types/type/Group.gql | 34 ++++++++++++-------------
1 file changed, 17 insertions(+), 17 deletions(-)
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 2dbe3c8c6b..310df9dbc3 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -160,7 +160,7 @@ input _GroupFilter {
# followedBy_none: _GroupFilter
# followedBy_single: _GroupFilter
# followedBy_every: _GroupFilter
- # role_in: [UserGroup!]
+ # role_in: [UserRole!]
}
type Query {
@@ -183,7 +183,7 @@ type Query {
availableGroupTypes: [GroupType]!
# Wolle:
- # availableRoles: [UserGroup]!
+ # availableRoles: [UserRole]!
# mutedUsers: [User]
# blockedUsers: [User]
# isLoggedIn: Boolean!
@@ -208,7 +208,7 @@ type Query {
# }
type Mutation {
- CreateGroup (
+ CreateGroup(
id: ID
name: String!
slug: String
@@ -217,14 +217,14 @@ type Mutation {
about: String
description: String
categoryIds: [ID]
- # Wolle: add group settings
- # Wolle:
- # showShoutsPublicly: Boolean
- # sendNotificationEmails: Boolean
- # locale: String
- ): Group
-
- UpdateGroup (
+ ): # Wolle: add group settings
+ # Wolle:
+ # showShoutsPublicly: Boolean
+ # sendNotificationEmails: Boolean
+ # locale: String
+ Group
+
+ UpdateGroup(
id: ID!
name: String
slug: String
@@ -232,11 +232,11 @@ type Mutation {
locationName: String
about: String
description: String
- # Wolle:
- # showShoutsPublicly: Boolean
- # sendNotificationEmails: Boolean
- # locale: String
- ): Group
+ ): # Wolle:
+ # showShoutsPublicly: Boolean
+ # sendNotificationEmails: Boolean
+ # locale: String
+ Group
DeleteGroup(id: ID!): Group
@@ -246,5 +246,5 @@ type Mutation {
# blockUser(id: ID!): User
# unblockUser(id: ID!): User
- # Wolle: switchUserRole(role: UserGroup!, id: ID!): User
+ # Wolle: switchUserRole(role: UserRole!, id: ID!): User
}
From 2c402ba25bd20e71e7997d712bd85423b6bc359f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 08:22:53 +0200
Subject: [PATCH 010/374] Fix linting
---
backend/src/middleware/slugifyMiddleware.spec.js | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index fbf03c6755..44701970d7 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -1,9 +1,12 @@
-import gql from 'graphql-tag'
import { getNeode, getDriver } from '../db/neo4j'
import createServer from '../server'
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../db/factories'
-import { createPostMutation, createGroupMutation, signupVerificationMutation } from '../db/graphql/mutations'
+import {
+ createPostMutation,
+ createGroupMutation,
+ signupVerificationMutation,
+} from '../db/graphql/mutations'
let mutate
let authenticatedUser
@@ -357,7 +360,7 @@ describe('slugifyMiddleware', () => {
})
})
})
-
+
describe('if slug exists', () => {
beforeEach(async () => {
await Factory.build('user', {
From cf31f3c1d6ed25cd516aaf8f810a7fb1350c324d Mon Sep 17 00:00:00 2001
From: ogerly
Date: Wed, 3 Aug 2022 10:26:08 +0200
Subject: [PATCH 011/374] preparation for creating a new group
---
webapp/components/GroupForm/GroupForm.vue | 272 ++++++++++++++++++
webapp/components/GroupTeaser/GroupTeaser.vue | 24 ++
webapp/locales/de.json | 3 +
webapp/locales/en.json | 3 +
webapp/pages/group/create.vue | 18 ++
webapp/pages/my-groups.vue | 16 ++
6 files changed, 336 insertions(+)
create mode 100644 webapp/components/GroupForm/GroupForm.vue
create mode 100644 webapp/components/GroupTeaser/GroupTeaser.vue
create mode 100644 webapp/pages/group/create.vue
create mode 100644 webapp/pages/my-groups.vue
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
new file mode 100644
index 0000000000..76d659b961
--- /dev/null
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -0,0 +1,272 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reset form
+
+
+ Save group
+
+
+
+
+
+
+
+ zurück
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/GroupTeaser/GroupTeaser.vue b/webapp/components/GroupTeaser/GroupTeaser.vue
new file mode 100644
index 0000000000..e9b3ba670c
--- /dev/null
+++ b/webapp/components/GroupTeaser/GroupTeaser.vue
@@ -0,0 +1,24 @@
+
+ Group Teaser
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 297daa511b..1628978839 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -366,6 +366,9 @@
"follow": "Folgen",
"following": "Folge Ich"
},
+ "group": {
+ "newGroup":"Erstelle eine neue Gruppe"
+ },
"hashtags-filter": {
"clearSearch": "Suche löschen",
"hashtag-search": "Suche nach #{hashtag}",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 8499b0290c..4043eefd85 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -366,6 +366,9 @@
"follow": "Follow",
"following": "Following"
},
+ "group": {
+ "newGroup":"Create a new Group"
+ },
"hashtags-filter": {
"clearSearch": "Clear search",
"hashtag-search": "Searching for #{hashtag}",
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
new file mode 100644
index 0000000000..6b52167f2a
--- /dev/null
+++ b/webapp/pages/group/create.vue
@@ -0,0 +1,18 @@
+
+
+
Neue Gruppe
+
+
+
+
+
+
+
diff --git a/webapp/pages/my-groups.vue b/webapp/pages/my-groups.vue
new file mode 100644
index 0000000000..386bb1e51b
--- /dev/null
+++ b/webapp/pages/my-groups.vue
@@ -0,0 +1,16 @@
+
+
+
+
From 45558f06dd4ef948ff785aa0a065677eac5f7829 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 11:49:46 +0200
Subject: [PATCH 012/374] Add comment about faking the 'gql' tag in the backend
---
backend/src/helpers/jest.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/backend/src/helpers/jest.js b/backend/src/helpers/jest.js
index 201d68c141..14317642b5 100644
--- a/backend/src/helpers/jest.js
+++ b/backend/src/helpers/jest.js
@@ -1,3 +1,6 @@
+// TODO: can be replaced with, which is no a fake:
+// import gql from 'graphql-tag'
+
//* This is a fake ES2015 template string, just to benefit of syntax
// highlighting of `gql` template strings in certain editors.
export function gql(strings) {
From 520598c89770044534a1c64f67be593a76b3b861 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 11:50:56 +0200
Subject: [PATCH 013/374] Implement 'description', 'groupType', and
'actionRadius' in 'CreateGroup'
---
backend/src/db/graphql/mutations.ts | 9 ++++
.../src/middleware/slugifyMiddleware.spec.js | 8 +++
backend/src/models/Group.js | 54 +++++++++++--------
backend/src/schema/resolvers/groups.spec.js | 4 +-
backend/src/schema/types/type/Group.gql | 23 ++++----
5 files changed, 64 insertions(+), 34 deletions(-)
diff --git a/backend/src/db/graphql/mutations.ts b/backend/src/db/graphql/mutations.ts
index 5fc554ee2b..c49856f2a1 100644
--- a/backend/src/db/graphql/mutations.ts
+++ b/backend/src/db/graphql/mutations.ts
@@ -6,6 +6,9 @@ export const createGroupMutation = gql`
$name: String!,
$slug: String,
$about: String,
+ $description: String!,
+ $groupType: GroupType!,
+ $actionRadius: GroupActionRadius!,
$categoryIds: [ID]
) {
CreateGroup(
@@ -13,12 +16,18 @@ export const createGroupMutation = gql`
name: $name
slug: $slug
about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
categoryIds: $categoryIds
) {
id
name
slug
about
+ description
+ groupType
+ actionRadius
disabled
deleted
owner {
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 44701970d7..af6ff25b0f 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -69,6 +69,9 @@ describe('slugifyMiddleware', () => {
...variables,
name: 'The Best Group',
about: 'Some about',
+ description: 'Some description',
+ groupType: 'closed',
+ actionRadius: 'national',
categoryIds,
}
})
@@ -83,7 +86,12 @@ describe('slugifyMiddleware', () => {
).resolves.toMatchObject({
data: {
CreateGroup: {
+ name: 'The Best Group',
slug: 'the-best-group',
+ about: 'Some about',
+ description: 'Some description',
+ groupType: 'closed',
+ actionRadius: 'national',
},
},
})
diff --git a/backend/src/models/Group.js b/backend/src/models/Group.js
index 651c2983e8..53b02fbec5 100644
--- a/backend/src/models/Group.js
+++ b/backend/src/models/Group.js
@@ -4,19 +4,44 @@ export default {
id: { type: 'string', primary: true, default: uuid }, // TODO: should be type: 'uuid' but simplified for our tests
name: { type: 'string', disallow: [null], min: 3 },
slug: { type: 'string', unique: 'true', regex: /^[a-z0-9_-]+$/, lowercase: true },
+
+ createdAt: {
+ type: 'string',
+ isoDate: true,
+ required: true,
+ default: () => new Date().toISOString(),
+ },
+ updatedAt: {
+ type: 'string',
+ isoDate: true,
+ required: true,
+ default: () => new Date().toISOString(),
+ },
+ deleted: { type: 'boolean', default: false },
+ disabled: { type: 'boolean', default: false },
+
avatar: {
type: 'relationship',
relationship: 'AVATAR_IMAGE',
target: 'Image',
direction: 'out',
},
- deleted: { type: 'boolean', default: false },
- disabled: { type: 'boolean', default: false },
- wasSeeded: 'boolean', // Wolle: used or needed?
- locationName: { type: 'string', allow: [null] },
- about: { type: 'string', allow: [null, ''] }, // Wolle: null?
- description: { type: 'string', allow: [null, ''] }, // Wolle: null? HTML with Tiptap, similar to post content, wie bei Posts "content: { type: 'string', disallow: [null], min: 3 },"?
+
+ about: { type: 'string', allow: [null, ''] },
+ description: { type: 'string', disallow: [null], min: 100 },
descriptionExcerpt: { type: 'string', allow: [null] },
+ groupType: { type: 'string', default: 'public' },
+ actionRadius: { type: 'string', default: 'regional' },
+
+ locationName: { type: 'string', allow: [null] },
+
+ wasSeeded: 'boolean', // Wolle: used or needed?
+ owner: {
+ type: 'relationship',
+ relationship: 'OWNS',
+ target: 'User',
+ direction: 'in',
+ },
// Wolle: followedBy: {
// type: 'relationship',
// relationship: 'FOLLOWS',
@@ -26,26 +51,9 @@ export default {
// createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
// },
// },
- owner: {
- type: 'relationship',
- relationship: 'OWNS',
- target: 'User',
- direction: 'in',
- },
// Wolle: correct this way?
// members: { type: 'relationship', relationship: 'MEMBERS', target: 'User', direction: 'out' },
// Wolle: needed? lastActiveAt: { type: 'string', isoDate: true },
- createdAt: {
- type: 'string',
- isoDate: true,
- default: () => new Date().toISOString(),
- },
- updatedAt: {
- type: 'string',
- isoDate: true,
- required: true,
- default: () => new Date().toISOString(),
- },
// Wolle: emoted: {
// type: 'relationships',
// relationship: 'EMOTED',
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 8d8c7c3d8c..4932c2c5e7 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -252,6 +252,9 @@ describe('CreateGroup', () => {
name: 'The Best Group',
slug: 'the-group',
about: 'We will change the world!',
+ description: 'Some description',
+ groupType: 'public',
+ actionRadius: 'regional',
categoryIds,
}
})
@@ -272,7 +275,6 @@ describe('CreateGroup', () => {
const expected = {
data: {
CreateGroup: {
- // Wolle: id: 'g589',
name: 'The Best Group',
slug: 'the-group',
about: 'We will change the world!',
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 310df9dbc3..ee71f3e1f1 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -19,27 +19,28 @@ enum _GroupOrdering {
type Group {
id: ID!
- name: String # title
+ name: String! # title
slug: String!
- createdAt: String
- updatedAt: String
+ createdAt: String!
+ updatedAt: String!
deleted: Boolean
disabled: Boolean
avatar: Image @relation(name: "AVATAR_IMAGE", direction: "OUT")
+ about: String # goal
+ description: String!
+ groupType: GroupType!
+ actionRadius: GroupActionRadius!
+
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
locationName: String
- about: String # goal
- description: String
- groupType: GroupType
- actionRadius: GroupActionRadius
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
# Wolle: needed?
- socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
+ # socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
owner: User @relation(name: "OWNS", direction: "IN")
@@ -213,10 +214,12 @@ type Mutation {
name: String!
slug: String
avatar: ImageInput
- locationName: String
about: String
- description: String
+ description: String!
+ groupType: GroupType!
+ actionRadius: GroupActionRadius!
categoryIds: [ID]
+ locationName: String
): # Wolle: add group settings
# Wolle:
# showShoutsPublicly: Boolean
From cc44ee8ebcf6157172d92d2267d1e4607e7df73b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 13:12:50 +0200
Subject: [PATCH 014/374] Refactor relations ':OWNS' and ':ADMINISTERS' to
':MEMBER_OF' with properties
---
backend/src/db/graphql/mutations.ts | 7 +-
backend/src/models/Group.js | 14 +-
backend/src/schema/resolvers/groups.js | 128 ++++++++----------
backend/src/schema/resolvers/groups.spec.js | 7 +-
.../src/schema/types/enum/GroupMemberRole.gql | 6 +
backend/src/schema/types/type/Group.gql | 15 +-
backend/src/schema/types/type/MEMBER_OF.gql | 5 +
7 files changed, 94 insertions(+), 88 deletions(-)
create mode 100644 backend/src/schema/types/enum/GroupMemberRole.gql
create mode 100644 backend/src/schema/types/type/MEMBER_OF.gql
diff --git a/backend/src/db/graphql/mutations.ts b/backend/src/db/graphql/mutations.ts
index c49856f2a1..4f07e0f1e7 100644
--- a/backend/src/db/graphql/mutations.ts
+++ b/backend/src/db/graphql/mutations.ts
@@ -30,9 +30,10 @@ export const createGroupMutation = gql`
actionRadius
disabled
deleted
- owner {
- name
- }
+ myRole
+ # Wolle: owner {
+ # name
+ # }
}
}
`
diff --git a/backend/src/models/Group.js b/backend/src/models/Group.js
index 53b02fbec5..0cec02bf8d 100644
--- a/backend/src/models/Group.js
+++ b/backend/src/models/Group.js
@@ -33,15 +33,17 @@ export default {
groupType: { type: 'string', default: 'public' },
actionRadius: { type: 'string', default: 'regional' },
+ myRole: { type: 'string', default: 'pending' },
+
locationName: { type: 'string', allow: [null] },
wasSeeded: 'boolean', // Wolle: used or needed?
- owner: {
- type: 'relationship',
- relationship: 'OWNS',
- target: 'User',
- direction: 'in',
- },
+ // Wolle: owner: {
+ // type: 'relationship',
+ // relationship: 'OWNS',
+ // target: 'User',
+ // direction: 'in',
+ // },
// Wolle: followedBy: {
// type: 'relationship',
// relationship: 'FOLLOWS',
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 0ed5d1356b..b202c60372 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -18,63 +18,47 @@ import Resolver from './helpers/Resolver'
// }
export default {
- // Wolle: Query: {
- // Post: async (object, params, context, resolveInfo) => {
- // params = await filterForMutedUsers(params, context)
- // params = await maintainPinnedPosts(params)
- // return neo4jgraphql(object, params, context, resolveInfo)
- // },
- // findPosts: async (object, params, context, resolveInfo) => {
- // params = await filterForMutedUsers(params, context)
- // return neo4jgraphql(object, params, context, resolveInfo)
- // },
- // profilePagePosts: async (object, params, context, resolveInfo) => {
- // params = await filterForMutedUsers(params, context)
- // return neo4jgraphql(object, params, context, resolveInfo)
- // },
- // PostsEmotionsCountByEmotion: async (object, params, context, resolveInfo) => {
- // const { postId, data } = params
- // const session = context.driver.session()
- // const readTxResultPromise = session.readTransaction(async (transaction) => {
- // const emotionsCountTransactionResponse = await transaction.run(
- // `
- // MATCH (post:Post {id: $postId})<-[emoted:EMOTED {emotion: $data.emotion}]-()
- // RETURN COUNT(DISTINCT emoted) as emotionsCount
- // `,
- // { postId, data },
- // )
- // return emotionsCountTransactionResponse.records.map(
- // (record) => record.get('emotionsCount').low,
- // )
- // })
- // try {
- // const [emotionsCount] = await readTxResultPromise
- // return emotionsCount
- // } finally {
- // session.close()
- // }
- // },
- // PostsEmotionsByCurrentUser: async (object, params, context, resolveInfo) => {
- // const { postId } = params
- // const session = context.driver.session()
- // const readTxResultPromise = session.readTransaction(async (transaction) => {
- // const emotionsTransactionResponse = await transaction.run(
- // `
- // MATCH (user:User {id: $userId})-[emoted:EMOTED]->(post:Post {id: $postId})
- // RETURN collect(emoted.emotion) as emotion
- // `,
- // { userId: context.user.id, postId },
- // )
- // return emotionsTransactionResponse.records.map((record) => record.get('emotion'))
- // })
- // try {
- // const [emotions] = await readTxResultPromise
- // return emotions
- // } finally {
- // session.close()
- // }
- // },
- // },
+ Query: {
+ // Wolle: Post: async (object, params, context, resolveInfo) => {
+ // params = await filterForMutedUsers(params, context)
+ // // params = await maintainPinnedPosts(params)
+ // return neo4jgraphql(object, params, context, resolveInfo)
+ // },
+ // Group: async (object, params, context, resolveInfo) => {
+ // // const { email } = params
+ // const session = context.driver.session()
+ // const readTxResultPromise = session.readTransaction(async (txc) => {
+ // const result = await txc.run(
+ // `
+ // MATCH (user:User {id: $userId})-[:MEMBER_OF]->(group:Group)
+ // RETURN properties(group) AS inviteCodes
+ // `,
+ // {
+ // userId: context.user.id,
+ // },
+ // )
+ // return result.records.map((record) => record.get('inviteCodes'))
+ // })
+ // if (email) {
+ // try {
+ // session = context.driver.session()
+ // const readTxResult = await session.readTransaction((txc) => {
+ // const result = txc.run(
+ // `
+ // MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email})
+ // RETURN user`,
+ // { args },
+ // )
+ // return result
+ // })
+ // return readTxResult.records.map((r) => r.get('user').properties)
+ // } finally {
+ // session.close()
+ // }
+ // }
+ // return neo4jgraphql(object, args, context, resolveInfo)
+ // },
+ },
Mutation: {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
@@ -84,12 +68,14 @@ export default {
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const categoriesCypher =
CONFIG.CATEGORIES_ACTIVE && categoryIds
- ? `WITH group
- UNWIND $categoryIds AS categoryId
- MATCH (category:Category {id: categoryId})
- MERGE (group)-[:CATEGORIZED]->(category)`
+ ? `
+ WITH group, membership
+ UNWIND $categoryIds AS categoryId
+ MATCH (category:Category {id: categoryId})
+ MERGE (group)-[:CATEGORIZED]->(category)
+ `
: ''
- const ownercreateGroupTransactionResponse = await transaction.run(
+ const ownerCreateGroupTransactionResponse = await transaction.run(
`
CREATE (group:Group)
SET group += $params
@@ -97,14 +83,16 @@ export default {
SET group.updatedAt = toString(datetime())
WITH group
MATCH (owner:User {id: $userId})
- MERGE (group)<-[:OWNS]-(owner)
- MERGE (group)<-[:ADMINISTERS]-(owner)
+ MERGE (owner)-[membership:MEMBER_OF]->(group)
+ SET membership.createdAt = toString(datetime())
+ SET membership.updatedAt = toString(datetime())
+ SET membership.role = 'owner'
${categoriesCypher}
- RETURN group {.*}
+ RETURN group {.*, myRole: membership.role}
`,
{ userId: context.user.id, categoryIds, params },
)
- const [group] = ownercreateGroupTransactionResponse.records.map((record) =>
+ const [group] = ownerCreateGroupTransactionResponse.records.map((record) =>
record.get('group'),
)
return group
@@ -205,10 +193,10 @@ export default {
// Wolle: tags: '-[:TAGGED]->(related:Tag)',
categories: '-[:CATEGORIZED]->(related:Category)',
},
- hasOne: {
- owner: '<-[:OWNS]-(related:User)',
- // Wolle: image: '-[:HERO_IMAGE]->(related:Image)',
- },
+ // hasOne: {
+ // owner: '<-[:OWNS]-(related:User)',
+ // // Wolle: image: '-[:HERO_IMAGE]->(related:Image)',
+ // },
// Wolle: count: {
// contributionsCount:
// '-[:WROTE]->(related:Post) WHERE NOT related.disabled = true AND NOT related.deleted = true',
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 4932c2c5e7..17fc4b1da7 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -292,9 +292,10 @@ describe('CreateGroup', () => {
data: {
CreateGroup: {
name: 'The Best Group',
- owner: {
- name: 'TestUser',
- },
+ myRole: 'owner',
+ // Wolle: owner: {
+ // name: 'TestUser',
+ // },
},
},
errors: undefined,
diff --git a/backend/src/schema/types/enum/GroupMemberRole.gql b/backend/src/schema/types/enum/GroupMemberRole.gql
new file mode 100644
index 0000000000..dacdd4b526
--- /dev/null
+++ b/backend/src/schema/types/enum/GroupMemberRole.gql
@@ -0,0 +1,6 @@
+enum GroupMemberRole {
+ pending
+ usual
+ admin
+ owner
+}
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index ee71f3e1f1..72ac9b57ae 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -39,10 +39,12 @@ type Group {
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
+ myRole: GroupMemberRole # if 'null' then the current user is no member
+
# Wolle: needed?
# socialMedia: [SocialMedia]! @relation(name: "OWNED_BY", direction: "IN")
- owner: User @relation(name: "OWNS", direction: "IN")
+ # Wolle: owner: User @relation(name: "OWNS", direction: "IN")
# Wolle: showShoutsPublicly: Boolean
# Wolle: sendNotificationEmails: Boolean
@@ -129,9 +131,12 @@ input _GroupFilter {
AND: [_GroupFilter!]
OR: [_GroupFilter!]
name_contains: String
+ slug_contains: String
about_contains: String
description_contains: String
- slug_contains: String
+ groupType_in: [GroupType!]
+ actionRadius_in: [GroupActionRadius!]
+ myRole_in: [GroupMemberRole!]
id: ID
id_not: ID
id_in: [ID!]
@@ -161,20 +166,18 @@ input _GroupFilter {
# followedBy_none: _GroupFilter
# followedBy_single: _GroupFilter
# followedBy_every: _GroupFilter
- # role_in: [UserRole!]
}
type Query {
Group(
id: ID
- email: String # admins need to search for a user sometimes
name: String
slug: String
+ createdAt: String
+ updatedAt: String
locationName: String
about: String
description: String
- createdAt: String
- updatedAt: String
first: Int
offset: Int
orderBy: [_GroupOrdering]
diff --git a/backend/src/schema/types/type/MEMBER_OF.gql b/backend/src/schema/types/type/MEMBER_OF.gql
new file mode 100644
index 0000000000..edda989f61
--- /dev/null
+++ b/backend/src/schema/types/type/MEMBER_OF.gql
@@ -0,0 +1,5 @@
+type MEMBER_OF {
+ createdAt: String!
+ updatedAt: String!
+ role: GroupMemberRole!
+}
From 94411648fd23feb74564f978c8e6988829de196f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 13:29:49 +0200
Subject: [PATCH 015/374] Implement 'Group' query, first step
---
.../src/middleware/permissionsMiddleware.js | 1 +
backend/src/schema/resolvers/groups.js | 64 ++++++++-----------
2 files changed, 28 insertions(+), 37 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 7e23cfe0ff..99dcfc0cde 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -114,6 +114,7 @@ export default shield(
reports: isModerator,
statistics: allow,
currentUser: allow,
+ Group: isAuthenticated,
Post: allow,
profilePagePosts: allow,
Comment: allow,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index b202c60372..9a55c9f4df 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -24,40 +24,30 @@ export default {
// // params = await maintainPinnedPosts(params)
// return neo4jgraphql(object, params, context, resolveInfo)
// },
- // Group: async (object, params, context, resolveInfo) => {
- // // const { email } = params
- // const session = context.driver.session()
- // const readTxResultPromise = session.readTransaction(async (txc) => {
- // const result = await txc.run(
- // `
- // MATCH (user:User {id: $userId})-[:MEMBER_OF]->(group:Group)
- // RETURN properties(group) AS inviteCodes
- // `,
- // {
- // userId: context.user.id,
- // },
- // )
- // return result.records.map((record) => record.get('inviteCodes'))
- // })
- // if (email) {
- // try {
- // session = context.driver.session()
- // const readTxResult = await session.readTransaction((txc) => {
- // const result = txc.run(
- // `
- // MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email})
- // RETURN user`,
- // { args },
- // )
- // return result
- // })
- // return readTxResult.records.map((r) => r.get('user').properties)
- // } finally {
- // session.close()
- // }
- // }
- // return neo4jgraphql(object, args, context, resolveInfo)
- // },
+ Group: async (_object, _params, context, _resolveInfo) => {
+ const session = context.driver.session()
+ const readTxResultPromise = session.readTransaction(async (txc) => {
+ const result = await txc.run(
+ `
+ MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(group:Group)
+ RETURN group {.*, myRole: membership.role}
+ `,
+ {
+ userId: context.user.id,
+ },
+ )
+ const group = result.records.map((record) => record.get('group'))
+ return group
+ })
+ try {
+ const group = await readTxResultPromise
+ return group
+ } catch (error) {
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+ },
},
Mutation: {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
@@ -100,10 +90,10 @@ export default {
try {
const group = await writeTxResultPromise
return group
- } catch (e) {
- if (e.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
+ } catch (error) {
+ if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
throw new UserInputError('Group with this slug already exists!')
- throw new Error(e)
+ throw new Error(error)
} finally {
session.close()
}
From 867b78dfa3983f92978ff7ec5284830a6fc34d5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 3 Aug 2022 15:16:23 +0200
Subject: [PATCH 016/374] Implement 'Group' query, second step
---
backend/src/db/graphql/authentications.ts | 29 ++
backend/src/db/graphql/groups.ts | 95 +++++
backend/src/db/graphql/mutations.ts | 69 ----
backend/src/db/graphql/posts.ts | 15 +
.../src/middleware/slugifyMiddleware.spec.js | 8 +-
backend/src/schema/resolvers/groups.spec.js | 325 +++++++++---------
backend/src/schema/types/type/Group.gql | 2 +-
7 files changed, 302 insertions(+), 241 deletions(-)
create mode 100644 backend/src/db/graphql/authentications.ts
create mode 100644 backend/src/db/graphql/groups.ts
delete mode 100644 backend/src/db/graphql/mutations.ts
create mode 100644 backend/src/db/graphql/posts.ts
diff --git a/backend/src/db/graphql/authentications.ts b/backend/src/db/graphql/authentications.ts
new file mode 100644
index 0000000000..f059706508
--- /dev/null
+++ b/backend/src/db/graphql/authentications.ts
@@ -0,0 +1,29 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const signupVerificationMutation = gql`
+ mutation (
+ $password: String!
+ $email: String!
+ $name: String!
+ $slug: String
+ $nonce: String!
+ $termsAndConditionsAgreedVersion: String!
+ ) {
+ SignupVerification(
+ email: $email
+ password: $password
+ name: $name
+ slug: $slug
+ nonce: $nonce
+ termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
+ ) {
+ slug
+ }
+ }
+`
+
+// ------ queries
+
+// fill queries in here
diff --git a/backend/src/db/graphql/groups.ts b/backend/src/db/graphql/groups.ts
new file mode 100644
index 0000000000..80b5996586
--- /dev/null
+++ b/backend/src/db/graphql/groups.ts
@@ -0,0 +1,95 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createGroupMutation = gql`
+ mutation (
+ $id: ID,
+ $name: String!,
+ $slug: String,
+ $about: String,
+ $description: String!,
+ $groupType: GroupType!,
+ $actionRadius: GroupActionRadius!,
+ $categoryIds: [ID]
+ ) {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ # Wolle: owner {
+ # name
+ # }
+ }
+ }
+`
+
+// ------ queries
+
+export const groupQuery = gql`
+ query (
+ $id: ID,
+ $name: String,
+ $slug: String,
+ $createdAt: String
+ $updatedAt: String
+ $about: String,
+ $description: String,
+ # $groupType: GroupType!,
+ # $actionRadius: GroupActionRadius!,
+ $categoryIds: [ID]
+ $locationName: String
+ $first: Int
+ $offset: Int
+ $orderBy: [_GroupOrdering]
+ $filter: _GroupFilter
+ ) {
+ Group(
+ id: $id
+ name: $name
+ slug: $slug
+ createdAt: $createdAt
+ updatedAt: $updatedAt
+ about: $about
+ description: $description
+ # groupType: $groupType
+ # actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ locationName: $locationName
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ # Wolle: owner {
+ # name
+ # }
+ }
+ }
+`
diff --git a/backend/src/db/graphql/mutations.ts b/backend/src/db/graphql/mutations.ts
deleted file mode 100644
index 4f07e0f1e7..0000000000
--- a/backend/src/db/graphql/mutations.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import gql from 'graphql-tag'
-
-export const createGroupMutation = gql`
- mutation (
- $id: ID,
- $name: String!,
- $slug: String,
- $about: String,
- $description: String!,
- $groupType: GroupType!,
- $actionRadius: GroupActionRadius!,
- $categoryIds: [ID]
- ) {
- CreateGroup(
- id: $id
- name: $name
- slug: $slug
- about: $about
- description: $description
- groupType: $groupType
- actionRadius: $actionRadius
- categoryIds: $categoryIds
- ) {
- id
- name
- slug
- about
- description
- groupType
- actionRadius
- disabled
- deleted
- myRole
- # Wolle: owner {
- # name
- # }
- }
- }
-`
-
-export const createPostMutation = gql`
- mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
- CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
- slug
- }
- }
-`
-
-export const signupVerificationMutation = gql`
- mutation (
- $password: String!
- $email: String!
- $name: String!
- $slug: String
- $nonce: String!
- $termsAndConditionsAgreedVersion: String!
- ) {
- SignupVerification(
- email: $email
- password: $password
- name: $name
- slug: $slug
- nonce: $nonce
- termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
- ) {
- slug
- }
- }
-`
diff --git a/backend/src/db/graphql/posts.ts b/backend/src/db/graphql/posts.ts
new file mode 100644
index 0000000000..3277af8207
--- /dev/null
+++ b/backend/src/db/graphql/posts.ts
@@ -0,0 +1,15 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createPostMutation = gql`
+ mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
+ CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
+ slug
+ }
+ }
+`
+
+// ------ queries
+
+// fill queries in here
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index af6ff25b0f..3c18e70b0b 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -2,11 +2,9 @@ import { getNeode, getDriver } from '../db/neo4j'
import createServer from '../server'
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../db/factories'
-import {
- createPostMutation,
- createGroupMutation,
- signupVerificationMutation,
-} from '../db/graphql/mutations'
+import { createGroupMutation } from '../db/graphql/groups'
+import { createPostMutation } from '../db/graphql/posts'
+import { signupVerificationMutation } from '../db/graphql/authentications'
let mutate
let authenticatedUser
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 17fc4b1da7..8860f87f26 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -1,6 +1,6 @@
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
-import { createGroupMutation } from '../../db/graphql/mutations'
+import { createGroupMutation } from '../../db/graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
@@ -78,171 +78,164 @@ afterEach(async () => {
await cleanDatabase()
})
-// describe('Group', () => {
-// describe('can be filtered', () => {
-// let followedUser, happyPost, cryPost
-// beforeEach(async () => {
-// ;[followedUser] = await Promise.all([
-// Factory.build(
-// 'user',
-// {
-// id: 'followed-by-me',
-// name: 'Followed User',
-// },
-// {
-// email: 'followed@example.org',
-// password: '1234',
-// },
-// ),
-// ])
-// ;[happyPost, cryPost] = await Promise.all([
-// Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
-// Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
-// Factory.build(
-// 'post',
-// {
-// id: 'post-by-followed-user',
-// },
-// {
-// categoryIds: ['cat9'],
-// author: followedUser,
-// },
-// ),
-// ])
-// })
-
-// 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({
-// data: {
-// Post: expect.arrayContaining(expected),
-// },
-// })
-// })
-// })
-
-// /* it('by categories', async () => {
-// const postQueryFilteredByCategories = gql`
-// query Post($filter: _PostFilter) {
-// Post(filter: $filter) {
-// id
-// categories {
-// id
-// }
-// }
-// }
-// `
-// const expected = {
-// data: {
-// Post: [
-// {
-// id: 'post-by-followed-user',
-// categories: [{ id: 'cat9' }],
-// },
-// ],
-// },
-// }
-// variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
-// await expect(
-// query({ query: postQueryFilteredByCategories, variables }),
-// ).resolves.toMatchObject(expected)
-// }) */
-
-// 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: {
-// Post: [
-// {
-// id: 'happy-post',
-// emotions: [{ emotion: 'happy' }],
-// },
-// ],
-// },
-// }
-// await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
-// variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
-// await expect(
-// query({ query: postQueryFilteredByEmotions, 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({
-// data: {
-// Post: expect.arrayContaining(expected),
-// },
-// errors: undefined,
-// })
-// })
-// })
-
-// it('by followed-by', async () => {
-// const postQueryFilteredByUsersFollowed = gql`
-// query Post($filter: _PostFilter) {
-// Post(filter: $filter) {
-// id
-// author {
-// id
-// name
-// }
-// }
-// }
-// `
-
-// await user.relateTo(followedUser, 'following')
-// variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
-// await expect(
-// query({ query: postQueryFilteredByUsersFollowed, variables }),
-// ).resolves.toMatchObject({
-// data: {
-// Post: [
-// {
-// id: 'post-by-followed-user',
-// author: { id: 'followed-by-me', name: 'Followed User' },
-// },
-// ],
-// },
-// errors: undefined,
-// })
-// })
-// })
-// })
+describe('Group', () => {
+ // describe('can be filtered', () => {
+ // let followedUser, happyPost, cryPost
+ // beforeEach(async () => {
+ // ;[followedUser] = await Promise.all([
+ // Factory.build(
+ // 'user',
+ // {
+ // id: 'followed-by-me',
+ // name: 'Followed User',
+ // },
+ // {
+ // email: 'followed@example.org',
+ // password: '1234',
+ // },
+ // ),
+ // ])
+ // ;[happyPost, cryPost] = await Promise.all([
+ // Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
+ // Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
+ // Factory.build(
+ // 'post',
+ // {
+ // id: 'post-by-followed-user',
+ // },
+ // {
+ // categoryIds: ['cat9'],
+ // author: followedUser,
+ // },
+ // ),
+ // ])
+ // })
+ // 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({
+ // data: {
+ // Post: expect.arrayContaining(expected),
+ // },
+ // })
+ // })
+ // })
+ // /* it('by categories', async () => {
+ // const postQueryFilteredByCategories = gql`
+ // query Post($filter: _PostFilter) {
+ // Post(filter: $filter) {
+ // id
+ // categories {
+ // id
+ // }
+ // }
+ // }
+ // `
+ // const expected = {
+ // data: {
+ // Post: [
+ // {
+ // id: 'post-by-followed-user',
+ // categories: [{ id: 'cat9' }],
+ // },
+ // ],
+ // },
+ // }
+ // variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
+ // await expect(
+ // query({ query: postQueryFilteredByCategories, variables }),
+ // ).resolves.toMatchObject(expected)
+ // }) */
+ // 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: {
+ // Post: [
+ // {
+ // id: 'happy-post',
+ // emotions: [{ emotion: 'happy' }],
+ // },
+ // ],
+ // },
+ // }
+ // await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
+ // variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
+ // await expect(
+ // query({ query: postQueryFilteredByEmotions, 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({
+ // data: {
+ // Post: expect.arrayContaining(expected),
+ // },
+ // errors: undefined,
+ // })
+ // })
+ // })
+ // it('by followed-by', async () => {
+ // const postQueryFilteredByUsersFollowed = gql`
+ // query Post($filter: _PostFilter) {
+ // Post(filter: $filter) {
+ // id
+ // author {
+ // id
+ // name
+ // }
+ // }
+ // }
+ // `
+ // await user.relateTo(followedUser, 'following')
+ // variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
+ // await expect(
+ // query({ query: postQueryFilteredByUsersFollowed, variables }),
+ // ).resolves.toMatchObject({
+ // data: {
+ // Post: [
+ // {
+ // id: 'post-by-followed-user',
+ // author: { id: 'followed-by-me', name: 'Followed User' },
+ // },
+ // ],
+ // },
+ // errors: undefined,
+ // })
+ // })
+ // })
+})
describe('CreateGroup', () => {
beforeEach(() => {
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 72ac9b57ae..cd15689ec7 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -175,9 +175,9 @@ type Query {
slug: String
createdAt: String
updatedAt: String
- locationName: String
about: String
description: String
+ locationName: String
first: Int
offset: Int
orderBy: [_GroupOrdering]
From fcca0f378d92726f5dc63e0fd0e6672edd210d72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 4 Aug 2022 09:24:04 +0200
Subject: [PATCH 017/374] Implement 'Group' query, next step
---
...{authentications.ts => authentications.js} | 0
.../src/db/graphql/{groups.ts => groups.js} | 10 +-
backend/src/db/graphql/{posts.ts => posts.js} | 0
backend/src/schema/resolvers/groups.js | 34 +-
backend/src/schema/resolvers/groups.spec.js | 432 +++++++++++-------
backend/src/schema/types/type/Group.gql | 1 +
6 files changed, 307 insertions(+), 170 deletions(-)
rename backend/src/db/graphql/{authentications.ts => authentications.js} (100%)
rename backend/src/db/graphql/{groups.ts => groups.js} (89%)
rename backend/src/db/graphql/{posts.ts => posts.js} (100%)
diff --git a/backend/src/db/graphql/authentications.ts b/backend/src/db/graphql/authentications.js
similarity index 100%
rename from backend/src/db/graphql/authentications.ts
rename to backend/src/db/graphql/authentications.js
diff --git a/backend/src/db/graphql/groups.ts b/backend/src/db/graphql/groups.js
similarity index 89%
rename from backend/src/db/graphql/groups.ts
rename to backend/src/db/graphql/groups.js
index 80b5996586..e8da8e90bd 100644
--- a/backend/src/db/graphql/groups.ts
+++ b/backend/src/db/graphql/groups.js
@@ -46,6 +46,7 @@ export const createGroupMutation = gql`
export const groupQuery = gql`
query (
+ $isMember: Boolean
$id: ID,
$name: String,
$slug: String,
@@ -55,7 +56,7 @@ export const groupQuery = gql`
$description: String,
# $groupType: GroupType!,
# $actionRadius: GroupActionRadius!,
- $categoryIds: [ID]
+ # $categoryIds: [ID]
$locationName: String
$first: Int
$offset: Int
@@ -63,6 +64,7 @@ export const groupQuery = gql`
$filter: _GroupFilter
) {
Group(
+ isMember: $isMember
id: $id
name: $name
slug: $slug
@@ -72,8 +74,12 @@ export const groupQuery = gql`
description: $description
# groupType: $groupType
# actionRadius: $actionRadius
- categoryIds: $categoryIds
+ # categoryIds: $categoryIds
locationName: $locationName
+ first: $first
+ offset: $offset
+ orderBy: $orderBy
+ filter: $filter
) {
id
name
diff --git a/backend/src/db/graphql/posts.ts b/backend/src/db/graphql/posts.js
similarity index 100%
rename from backend/src/db/graphql/posts.ts
rename to backend/src/db/graphql/posts.js
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 9a55c9f4df..be07fecc6e 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -24,18 +24,34 @@ export default {
// // params = await maintainPinnedPosts(params)
// return neo4jgraphql(object, params, context, resolveInfo)
// },
- Group: async (_object, _params, context, _resolveInfo) => {
+ Group: async (_object, params, context, _resolveInfo) => {
+ const { isMember } = params
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
- const result = await txc.run(
- `
- MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(group:Group)
+ let groupCypher
+ if (isMember === true) {
+ groupCypher = `
+ MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group)
RETURN group {.*, myRole: membership.role}
- `,
- {
- userId: context.user.id,
- },
- )
+ `
+ } else {
+ if (isMember === false) {
+ groupCypher = `
+ MATCH (group:Group)
+ WHERE NOT (:User {id: $userId})-[:MEMBER_OF]->(group)
+ RETURN group {.*, myRole: NULL}
+ `
+ } else {
+ groupCypher = `
+ MATCH (group:Group)
+ OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group)
+ RETURN group {.*, myRole: membership.role}
+ `
+ }
+ }
+ const result = await txc.run(groupCypher, {
+ userId: context.user.id,
+ })
const group = result.records.map((record) => record.get('group'))
return group
})
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 8860f87f26..58e5f37bef 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -1,13 +1,13 @@
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
-import { createGroupMutation } from '../../db/graphql/groups'
+import { createGroupMutation, groupQuery } from '../../db/graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
const driver = getDriver()
const neode = getNeode()
-// Wolle: let query
+let query
let mutate
let authenticatedUser
let user
@@ -27,7 +27,7 @@ beforeAll(async () => {
}
},
})
- // Wolle: query = createTestClient(server).query
+ query = createTestClient(server).query
mutate = createTestClient(server).mutate
})
@@ -79,162 +79,276 @@ afterEach(async () => {
})
describe('Group', () => {
- // describe('can be filtered', () => {
- // let followedUser, happyPost, cryPost
- // beforeEach(async () => {
- // ;[followedUser] = await Promise.all([
- // Factory.build(
- // 'user',
- // {
- // id: 'followed-by-me',
- // name: 'Followed User',
- // },
- // {
- // email: 'followed@example.org',
- // password: '1234',
- // },
- // ),
- // ])
- // ;[happyPost, cryPost] = await Promise.all([
- // Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
- // Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
- // Factory.build(
- // 'post',
- // {
- // id: 'post-by-followed-user',
- // },
- // {
- // categoryIds: ['cat9'],
- // author: followedUser,
- // },
- // ),
- // ])
- // })
- // 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({
- // data: {
- // Post: expect.arrayContaining(expected),
- // },
- // })
- // })
- // })
- // /* it('by categories', async () => {
- // const postQueryFilteredByCategories = gql`
- // query Post($filter: _PostFilter) {
- // Post(filter: $filter) {
- // id
- // categories {
- // id
- // }
- // }
- // }
- // `
- // const expected = {
- // data: {
- // Post: [
- // {
- // id: 'post-by-followed-user',
- // categories: [{ id: 'cat9' }],
- // },
- // ],
- // },
- // }
- // variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
- // await expect(
- // query({ query: postQueryFilteredByCategories, variables }),
- // ).resolves.toMatchObject(expected)
- // }) */
- // 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: {
- // Post: [
- // {
- // id: 'happy-post',
- // emotions: [{ emotion: 'happy' }],
- // },
- // ],
- // },
- // }
- // await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
- // variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
- // await expect(
- // query({ query: postQueryFilteredByEmotions, 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({
- // data: {
- // Post: expect.arrayContaining(expected),
- // },
- // errors: undefined,
- // })
- // })
- // })
- // it('by followed-by', async () => {
- // const postQueryFilteredByUsersFollowed = gql`
- // query Post($filter: _PostFilter) {
- // Post(filter: $filter) {
- // id
- // author {
- // id
- // name
- // }
- // }
- // }
- // `
- // await user.relateTo(followedUser, 'following')
- // variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
- // await expect(
- // query({ query: postQueryFilteredByUsersFollowed, variables }),
- // ).resolves.toMatchObject({
- // data: {
- // Post: [
- // {
- // id: 'post-by-followed-user',
- // author: { id: 'followed-by-me', name: 'Followed User' },
- // },
- // ],
- // },
- // errors: undefined,
- // })
- // })
- // })
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ let otherUser
+
+ beforeEach(async () => {
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await otherUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'others-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?',
+ groupType: 'closed',
+ actionRadius: 'international',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'my-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description',
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ })
+
+ describe('query can fetch', () => {
+ it('groups where user is member (or owner in this case)', async () => {
+ const expected = {
+ data: {
+ Group: [
+ {
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ },
+ ],
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject(expected)
+ })
+
+ it('groups where user is not(!) member', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject(expected)
+ })
+
+ it('all groups', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+ })
+ })
+
+ // Wolle: describe('can be filtered', () => {
+ // let followedUser, happyPost, cryPost
+ // beforeEach(async () => {
+ // ;[followedUser] = await Promise.all([
+ // Factory.build(
+ // 'user',
+ // {
+ // id: 'followed-by-me',
+ // name: 'Followed User',
+ // },
+ // {
+ // email: 'followed@example.org',
+ // password: '1234',
+ // },
+ // ),
+ // ])
+ // ;[happyPost, cryPost] = await Promise.all([
+ // Factory.build('post', { id: 'happy-post' }, { categoryIds: ['cat4'] }),
+ // Factory.build('post', { id: 'cry-post' }, { categoryIds: ['cat15'] }),
+ // Factory.build(
+ // 'post',
+ // {
+ // id: 'post-by-followed-user',
+ // },
+ // {
+ // categoryIds: ['cat9'],
+ // author: followedUser,
+ // },
+ // ),
+ // ])
+ // })
+ // 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({
+ // data: {
+ // Post: expect.arrayContaining(expected),
+ // },
+ // })
+ // })
+ // })
+ // /* it('by categories', async () => {
+ // const postQueryFilteredByCategories = gql`
+ // query Post($filter: _PostFilter) {
+ // Post(filter: $filter) {
+ // id
+ // categories {
+ // id
+ // }
+ // }
+ // }
+ // `
+ // const expected = {
+ // data: {
+ // Post: [
+ // {
+ // id: 'post-by-followed-user',
+ // categories: [{ id: 'cat9' }],
+ // },
+ // ],
+ // },
+ // }
+ // variables = { ...variables, filter: { categories_some: { id_in: ['cat9'] } } }
+ // await expect(
+ // query({ query: postQueryFilteredByCategories, variables }),
+ // ).resolves.toMatchObject(expected)
+ // }) */
+ // 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: {
+ // Post: [
+ // {
+ // id: 'happy-post',
+ // emotions: [{ emotion: 'happy' }],
+ // },
+ // ],
+ // },
+ // }
+ // await user.relateTo(happyPost, 'emoted', { emotion: 'happy' })
+ // variables = { ...variables, filter: { emotions_some: { emotion_in: ['happy'] } } }
+ // await expect(
+ // query({ query: postQueryFilteredByEmotions, 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({
+ // data: {
+ // Post: expect.arrayContaining(expected),
+ // },
+ // errors: undefined,
+ // })
+ // })
+ // })
+ // it('by followed-by', async () => {
+ // const postQueryFilteredByUsersFollowed = gql`
+ // query Post($filter: _PostFilter) {
+ // Post(filter: $filter) {
+ // id
+ // author {
+ // id
+ // name
+ // }
+ // }
+ // }
+ // `
+ // await user.relateTo(followedUser, 'following')
+ // variables = { filter: { author: { followedBy_some: { id: 'current-user' } } } }
+ // await expect(
+ // query({ query: postQueryFilteredByUsersFollowed, variables }),
+ // ).resolves.toMatchObject({
+ // data: {
+ // Post: [
+ // {
+ // id: 'post-by-followed-user',
+ // author: { id: 'followed-by-me', name: 'Followed User' },
+ // },
+ // ],
+ // },
+ // errors: undefined,
+ // })
+ // })
+ // })
+ })
})
describe('CreateGroup', () => {
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index cd15689ec7..b8e00f0eef 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -170,6 +170,7 @@ input _GroupFilter {
type Query {
Group(
+ isMember: Boolean # if 'undefined' or 'null' then all groups
id: ID
name: String
slug: String
From f1831661e58c1547485b16d49036e8ef5a14ac9b Mon Sep 17 00:00:00 2001
From: ogerly
Date: Sun, 7 Aug 2022 14:12:17 +0200
Subject: [PATCH 018/374] add my-groups page and setting link
---
webapp/components/GroupForm/GroupForm.vue | 208 +++++-------------
webapp/components/GroupTeaser/GroupTeaser.vue | 40 ++--
webapp/locales/de.json | 3 +
webapp/locales/en.json | 3 +
webapp/pages/group/create.vue | 9 +-
webapp/pages/settings.vue | 4 +
6 files changed, 88 insertions(+), 179 deletions(-)
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
index 76d659b961..e730a3c138 100644
--- a/webapp/components/GroupForm/GroupForm.vue
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -3,10 +3,7 @@
+
+
+
+ {{ formData.categoryIds.length }} / 3
+
+
Reset form
@@ -47,122 +58,47 @@
-
+
-
-
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
index b77096859d..9463083f1c 100644
--- a/webapp/pages/group/create.vue
+++ b/webapp/pages/group/create.vue
@@ -1,11 +1,13 @@
-
-
Neue Gruppe
-
-
-
+
+
+
+
+
+
+
diff --git a/webapp/components/GroupTeaser/GroupTeaser.vue b/webapp/components/GroupTeaser/GroupTeaser.vue
index a199106a55..22df641e97 100644
--- a/webapp/components/GroupTeaser/GroupTeaser.vue
+++ b/webapp/components/GroupTeaser/GroupTeaser.vue
@@ -1,7 +1,5 @@
- Group Teaser
-
@@ -22,3 +20,10 @@
+
+
diff --git a/webapp/pages/my-groups.vue b/webapp/pages/my-groups.vue
index 386bb1e51b..a861403ed1 100644
--- a/webapp/pages/my-groups.vue
+++ b/webapp/pages/my-groups.vue
@@ -2,15 +2,45 @@
From 7682aa7e45a289df8018eba72442ff197f5234ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 10 Aug 2022 11:34:51 +0200
Subject: [PATCH 028/374] Fix description length for slugify tests
---
backend/src/middleware/slugifyMiddleware.spec.js | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 3c18e70b0b..59fa72ba72 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -12,6 +12,8 @@ let variables
const driver = getDriver()
const neode = getNeode()
+const descriptionAddition100 =
+ ' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
beforeAll(async () => {
await cleanDatabase()
@@ -67,7 +69,7 @@ describe('slugifyMiddleware', () => {
...variables,
name: 'The Best Group',
about: 'Some about',
- description: 'Some description',
+ description: 'Some description' + descriptionAddition100,
groupType: 'closed',
actionRadius: 'national',
categoryIds,
@@ -87,7 +89,7 @@ describe('slugifyMiddleware', () => {
name: 'The Best Group',
slug: 'the-best-group',
about: 'Some about',
- description: 'Some description',
+ description: 'Some description' + descriptionAddition100,
groupType: 'closed',
actionRadius: 'national',
},
From 82401b1488dd6aee9282b6b9f810f480f25edf2e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 10 Aug 2022 12:52:20 +0200
Subject: [PATCH 029/374] Update backend/src/schema/resolvers/groups.js
Co-authored-by: Moriz Wahl
---
backend/src/schema/resolvers/groups.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index e6a8c3a181..f6d482421e 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -36,8 +36,7 @@ export default {
const result = await txc.run(groupCypher, {
userId: context.user.id,
})
- const group = result.records.map((record) => record.get('group'))
- return group
+ return result.records.map((record) => record.get('group'))
})
try {
const group = await readTxResultPromise
From f150ea3d7ce24286127d2ddf8fa08ca977c5ded7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 10 Aug 2022 12:52:31 +0200
Subject: [PATCH 030/374] Update backend/src/schema/resolvers/groups.js
Co-authored-by: Moriz Wahl
---
backend/src/schema/resolvers/groups.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index f6d482421e..dadbcd2a1e 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -39,8 +39,7 @@ export default {
return result.records.map((record) => record.get('group'))
})
try {
- const group = await readTxResultPromise
- return group
+ return await readTxResultPromise
} catch (error) {
throw new Error(error)
} finally {
From 5e741ead8d3f96d5d4eacf88a7e7e3f70a361fff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 10 Aug 2022 13:15:43 +0200
Subject: [PATCH 031/374] Overtake Moriz suggestions
---
backend/src/models/Group.js | 2 -
backend/src/schema/resolvers/groups.js | 6 +-
backend/src/schema/resolvers/groups.spec.js | 126 +++++++++---------
.../schema/types/enum/GroupActionRadius.gql | 3 +-
4 files changed, 71 insertions(+), 66 deletions(-)
diff --git a/backend/src/models/Group.js b/backend/src/models/Group.js
index 25149e9c39..a75ad518f9 100644
--- a/backend/src/models/Group.js
+++ b/backend/src/models/Group.js
@@ -37,8 +37,6 @@ export default {
locationName: { type: 'string', allow: [null] },
- wasSeeded: 'boolean', // Wolle: used or needed?
-
isIn: {
type: 'relationship',
relationship: 'IS_IN',
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index dadbcd2a1e..d1af985138 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -52,17 +52,17 @@ export default {
const { categoryIds } = params
delete params.categoryIds
if (!categoryIds || categoryIds.length < CATEGORIES_MIN) {
- throw new UserInputError('To Less Categories!')
+ throw new UserInputError('Too view categories!')
}
if (categoryIds && categoryIds.length > CATEGORIES_MAX) {
- throw new UserInputError('To Many Categories!')
+ throw new UserInputError('Too many categories!')
}
if (
params.description === undefined ||
params.description === null ||
removeHtmlTags(params.description).length < DESCRIPTION_WITHOUT_HTML_LENGTH_MIN
) {
- throw new UserInputError('To Short Description!')
+ throw new UserInputError('Description too short!')
}
params.id = params.id || uuid()
const session = context.driver.session()
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index bae530c612..5354f5ebeb 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -120,7 +120,7 @@ describe('Group', () => {
about: 'We will change nothing!',
description: 'We love it like it is!?' + descriptionAddition100,
groupType: 'closed',
- actionRadius: 'international',
+ actionRadius: 'global',
categoryIds,
},
})
@@ -139,62 +139,68 @@ describe('Group', () => {
})
})
- describe('can find', () => {
- it('all', async () => {
- const expected = {
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- }),
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
- }
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+ describe('query groups', () => {
+ describe('without any filters', () => {
+ it('finds all groups', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+ })
})
- it('where user is member (or owner in this case)', async () => {
- const expected = {
- data: {
- Group: [
- {
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- },
- ],
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject(expected)
+ describe('isMember = true', () => {
+ it('finds only groups where user is member', async () => {
+ const expected = {
+ data: {
+ Group: [
+ {
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ },
+ ],
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject(expected)
+ })
})
- it('where user is not(!) member', async () => {
- const expected = {
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject(expected)
+ describe('isMember = false', () => {
+ it('finds only groups where user is not(!) member', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -258,7 +264,7 @@ describe('CreateGroup', () => {
)
})
- it('"disabled" and "deleted" default to "false"', async () => {
+ it('has "disabled" and "deleted" default to "false"', async () => {
const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
expected,
@@ -268,7 +274,7 @@ describe('CreateGroup', () => {
describe('description', () => {
describe('length without HTML', () => {
describe('less then 100 chars', () => {
- it('throws error: "To Less Categories!"', async () => {
+ it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
mutation: createGroupMutation,
variables: {
@@ -278,7 +284,7 @@ describe('CreateGroup', () => {
'0123456789 ',
},
})
- expect(errors[0]).toHaveProperty('message', 'To Short Description!')
+ expect(errors[0]).toHaveProperty('message', 'Description too short!')
})
})
})
@@ -286,22 +292,22 @@ describe('CreateGroup', () => {
describe('categories', () => {
describe('not even one', () => {
- it('throws error: "To Less Categories!"', async () => {
+ it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
mutation: createGroupMutation,
variables: { ...variables, categoryIds: null },
})
- expect(errors[0]).toHaveProperty('message', 'To Less Categories!')
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
})
describe('four', () => {
- it('throws error: "To Many Categories!"', async () => {
+ it('throws error: "Too many categories!"', async () => {
const { errors } = await mutate({
mutation: createGroupMutation,
variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
})
- expect(errors[0]).toHaveProperty('message', 'To Many Categories!')
+ expect(errors[0]).toHaveProperty('message', 'Too many categories!')
})
})
})
diff --git a/backend/src/schema/types/enum/GroupActionRadius.gql b/backend/src/schema/types/enum/GroupActionRadius.gql
index afc4211337..221ed7f877 100644
--- a/backend/src/schema/types/enum/GroupActionRadius.gql
+++ b/backend/src/schema/types/enum/GroupActionRadius.gql
@@ -2,5 +2,6 @@ enum GroupActionRadius {
regional
national
continental
- international
+ global
+ interplanetary
}
From b0d28f8649bba912a11263f206d6d368b579b648 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 10 Aug 2022 13:19:42 +0200
Subject: [PATCH 032/374] Rename 'descriptionAddition100' to
'descriptionAdditional100'
---
backend/src/middleware/slugifyMiddleware.spec.js | 6 +++---
backend/src/schema/resolvers/groups.spec.js | 8 ++++----
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 59fa72ba72..9605aada90 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -12,7 +12,7 @@ let variables
const driver = getDriver()
const neode = getNeode()
-const descriptionAddition100 =
+const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
beforeAll(async () => {
@@ -69,7 +69,7 @@ describe('slugifyMiddleware', () => {
...variables,
name: 'The Best Group',
about: 'Some about',
- description: 'Some description' + descriptionAddition100,
+ description: 'Some description' + descriptionAdditional100,
groupType: 'closed',
actionRadius: 'national',
categoryIds,
@@ -89,7 +89,7 @@ describe('slugifyMiddleware', () => {
name: 'The Best Group',
slug: 'the-best-group',
about: 'Some about',
- description: 'Some description' + descriptionAddition100,
+ description: 'Some description' + descriptionAdditional100,
groupType: 'closed',
actionRadius: 'national',
},
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 5354f5ebeb..b3327d44a1 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -13,7 +13,7 @@ let authenticatedUser
let user
const categoryIds = ['cat9', 'cat4', 'cat15']
-const descriptionAddition100 =
+const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
let variables = {}
@@ -118,7 +118,7 @@ describe('Group', () => {
id: 'others-group',
name: 'Uninteresting Group',
about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAddition100,
+ description: 'We love it like it is!?' + descriptionAdditional100,
groupType: 'closed',
actionRadius: 'global',
categoryIds,
@@ -131,7 +131,7 @@ describe('Group', () => {
id: 'my-group',
name: 'The Best Group',
about: 'We will change the world!',
- description: 'Some description' + descriptionAddition100,
+ description: 'Some description' + descriptionAdditional100,
groupType: 'public',
actionRadius: 'regional',
categoryIds,
@@ -214,7 +214,7 @@ describe('CreateGroup', () => {
name: 'The Best Group',
slug: 'the-group',
about: 'We will change the world!',
- description: 'Some description' + descriptionAddition100,
+ description: 'Some description' + descriptionAdditional100,
groupType: 'public',
actionRadius: 'regional',
categoryIds,
From addfc39c4c3b1a6132b4e1b82061a01ed651fb48 Mon Sep 17 00:00:00 2001
From: ogerly
Date: Thu, 11 Aug 2022 11:10:52 +0200
Subject: [PATCH 033/374] add groupe site id und slug
---
webapp/components/GroupForm/GroupForm.vue | 60 +++++++--------
webapp/components/GroupList/GroupCard.vue | 50 ++++++++++++
webapp/components/GroupList/GroupList.vue | 73 +++++++++++++-----
webapp/components/GroupMember/GroupMember.vue | 67 ++++++++++++++++
webapp/components/GroupTeaser/GroupTeaser.vue | 6 +-
webapp/pages/group/_id.vue | 9 +++
webapp/pages/group/_id/_slug.vue | 6 ++
webapp/pages/group/create.vue | 7 +-
webapp/pages/my-groups.vue | 76 ++++++++++++-------
9 files changed, 270 insertions(+), 84 deletions(-)
create mode 100644 webapp/components/GroupList/GroupCard.vue
create mode 100644 webapp/components/GroupMember/GroupMember.vue
create mode 100644 webapp/pages/group/_id.vue
create mode 100644 webapp/pages/group/_id/_slug.vue
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
index 1bd4455d7d..703adc45c7 100644
--- a/webapp/components/GroupForm/GroupForm.vue
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -1,32 +1,31 @@
-
-
+
+
-
+
-
+
- {{name}}
- {{status}}
- {{description}}
+ {{ name }}
+ {{ status }}
+ {{ description }}
-
- Reset form
- Save group
-
-
+
+ Reset form
+ Save group
+
- zurück
-
+ zurück
+
@@ -44,19 +43,20 @@ export default {
name: '',
status: '',
description: '',
+ disable: false,
}
},
-
+
methods: {
- submit() {
- console.log('handleSubmit')
- },
- handleSubmit() {
- console.log('handleSubmit')
- },
- reset() {
- console.log('handleSubmit')
+ submit() {
+ console.log('handleSubmit')
+ },
+ handleSubmit() {
+ console.log('handleSubmit')
+ },
+ reset() {
+ console.log('handleSubmit')
+ },
},
- },
}
diff --git a/webapp/components/GroupList/GroupCard.vue b/webapp/components/GroupList/GroupCard.vue
new file mode 100644
index 0000000000..4ae9f807fd
--- /dev/null
+++ b/webapp/components/GroupList/GroupCard.vue
@@ -0,0 +1,50 @@
+
+
+
+ Group Card
+
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/GroupList/GroupList.vue b/webapp/components/GroupList/GroupList.vue
index bfaceed21a..69df8acb72 100644
--- a/webapp/components/GroupList/GroupList.vue
+++ b/webapp/components/GroupList/GroupList.vue
@@ -1,27 +1,58 @@
-
-
- Group List
-
-
- {{ scope.row.name }} loves {{ scope.row.loves }}
-
-
- delete
-
-
-
-
+
+
+ Group List
+
+
+
+
+
+ {{ scope.row.name }}
+
+
+ {{ scope.row.status }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/webapp/components/GroupMember/GroupMember.vue b/webapp/components/GroupMember/GroupMember.vue
new file mode 100644
index 0000000000..f83b00685e
--- /dev/null
+++ b/webapp/components/GroupMember/GroupMember.vue
@@ -0,0 +1,67 @@
+
+
+
Link zur Gruppe
+
+ Copy Link for Invite Member please!
+
+
+
+
+
+
Members
+
+
+
+
+
+ {{ scope.row.name }} loves {{ scope.row.loves }}
+
+
+ delete
+
+
+
+
+
diff --git a/webapp/components/GroupTeaser/GroupTeaser.vue b/webapp/components/GroupTeaser/GroupTeaser.vue
index 22df641e97..33a50dca74 100644
--- a/webapp/components/GroupTeaser/GroupTeaser.vue
+++ b/webapp/components/GroupTeaser/GroupTeaser.vue
@@ -21,9 +21,7 @@
-
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
new file mode 100644
index 0000000000..7ce25e827c
--- /dev/null
+++ b/webapp/pages/group/_id.vue
@@ -0,0 +1,9 @@
+
+ _id groupe page
+
{{$route.params}}
+
+ bearbeiten
+
+
+
+
\ No newline at end of file
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
new file mode 100644
index 0000000000..36f2ccb2be
--- /dev/null
+++ b/webapp/pages/group/_id/_slug.vue
@@ -0,0 +1,6 @@
+
+
+
+ Group Page
+
+
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
index 9463083f1c..e82fadd6ed 100644
--- a/webapp/pages/group/create.vue
+++ b/webapp/pages/group/create.vue
@@ -1,19 +1,24 @@
+
+
+
+
-
diff --git a/webapp/pages/my-groups.vue b/webapp/pages/my-groups.vue
index a861403ed1..81843f5d28 100644
--- a/webapp/pages/my-groups.vue
+++ b/webapp/pages/my-groups.vue
@@ -2,45 +2,65 @@
From 46d26a0ef8ab16b32662e53f763e25fc676b6037 Mon Sep 17 00:00:00 2001
From: ogerly
Date: Sun, 14 Aug 2022 11:45:33 +0200
Subject: [PATCH 034/374] add graphql groups, add createGroup mutation
---
webapp/components/GroupForm/GroupForm.vue | 41 +++++----
webapp/graphql/groups.js | 107 ++++++++++++++++++++++
webapp/pages/group/create.vue | 40 +++++++-
3 files changed, 171 insertions(+), 17 deletions(-)
create mode 100644 webapp/graphql/groups.js
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
index 703adc45c7..2cbc674647 100644
--- a/webapp/components/GroupForm/GroupForm.vue
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -2,25 +2,25 @@
-
+
-
+
- {{ name }}
- {{ status }}
- {{ description }}
+ {{ form.name }}
+ {{ form.status }}
+ {{ form.description }}
Reset form
- Save group
+ Save group
@@ -38,24 +38,33 @@ export default {
components: {
CategoriesSelect,
},
+ props:{
+ value: {
+ type: Object,
+ default: () => ({}),
+ required: true,
+ }
+ },
data() {
return {
- name: '',
- status: '',
- description: '',
- disable: false,
+ form: {
+ name: '',
+ status: '',
+ description: '',
+ disable: false,
+ }
+
}
},
methods: {
submit() {
- console.log('handleSubmit')
- },
- handleSubmit() {
- console.log('handleSubmit')
+ console.log('submit', this.form)
+ this.$emit('createGroup', this.form)
+
},
reset() {
- console.log('handleSubmit')
+ console.log('reset')
},
},
}
diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js
new file mode 100644
index 0000000000..c41f06e4d9
--- /dev/null
+++ b/webapp/graphql/groups.js
@@ -0,0 +1,107 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createGroupMutation = gql`
+ mutation (
+ $id: ID
+ $name: String!
+ $slug: String
+ $about: String
+ $description: String!
+ $groupType: GroupType!
+ $actionRadius: GroupActionRadius!
+ $categoryIds: [ID]
+ ) {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ # Wolle: owner {
+ # name
+ # }
+ }
+ }
+`
+
+// ------ queries
+
+export const groupQuery = gql`
+ query (
+ $isMember: Boolean
+ $id: ID
+ $name: String
+ $slug: String
+ $createdAt: String
+ $updatedAt: String
+ $about: String
+ $description: String
+ # $groupType: GroupType!,
+ # $actionRadius: GroupActionRadius!,
+ # $categoryIds: [ID]
+ $locationName: String
+ $first: Int
+ $offset: Int
+ $orderBy: [_GroupOrdering]
+ $filter: _GroupFilter
+ ) {
+ Group(
+ isMember: $isMember
+ id: $id
+ name: $name
+ slug: $slug
+ createdAt: $createdAt
+ updatedAt: $updatedAt
+ about: $about
+ description: $description
+ # groupType: $groupType
+ # actionRadius: $actionRadius
+ # categoryIds: $categoryIds
+ locationName: $locationName
+ first: $first
+ offset: $offset
+ orderBy: $orderBy
+ filter: $filter
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ # Wolle: owner {
+ # name
+ # }
+ }
+ }
+`
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
index e82fadd6ed..c76be5360f 100644
--- a/webapp/pages/group/create.vue
+++ b/webapp/pages/group/create.vue
@@ -2,7 +2,7 @@
-
+
@@ -14,11 +14,49 @@
From c2a769f325c98665f97ebb1366a7e85ddec70f51 Mon Sep 17 00:00:00 2001
From: ogerly
Date: Sun, 14 Aug 2022 12:49:15 +0200
Subject: [PATCH 035/374] add CategoriesSelect in GroupForm.vue
---
webapp/components/GroupForm/GroupForm.vue | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
index 2cbc674647..8e29bf300a 100644
--- a/webapp/components/GroupForm/GroupForm.vue
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -14,9 +14,13 @@
+
{{ form.name }}
{{ form.status }}
{{ form.description }}
+ {{ form.categoryIds }}
Reset form
@@ -39,19 +43,18 @@ export default {
CategoriesSelect,
},
props:{
- value: {
- type: Object,
- default: () => ({}),
- required: true,
- }
+ model: { type: String, required: true },
+ value: { type: String, default: '' },
},
data() {
return {
+ categoriesActive: this.$env.CATEGORIES_ACTIVE,
form: {
name: '',
status: '',
description: '',
disable: false,
+ categoryIds: [],
}
}
From dd876c52fab328eabccb1379923f2dbf6ba9891d Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Mon, 15 Aug 2022 10:52:31 +0200
Subject: [PATCH 036/374] increas max-old-space-size for jest, handle some
asyncs, test validation for caregories only if categories are active
---
backend/package.json | 2 +-
backend/src/middleware/excerptMiddleware.js | 15 ++++--------
.../src/middleware/slugifyMiddleware.spec.js | 24 +++++++++----------
backend/src/schema/resolvers/groups.js | 6 ++---
4 files changed, 21 insertions(+), 26 deletions(-)
diff --git a/backend/package.json b/backend/package.json
index 9651cbb95c..9aa7f539fb 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -15,7 +15,7 @@
"dev": "nodemon --exec babel-node src/ -e js,gql",
"dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,gql",
"lint": "eslint src --config .eslintrc.js",
- "test": "cross-env NODE_ENV=test jest --forceExit --detectOpenHandles --runInBand --coverage",
+ "test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --forceExit --detectOpenHandles --runInBand --coverage",
"db:clean": "babel-node src/db/clean.js",
"db:reset": "yarn run db:clean",
"db:seed": "babel-node src/db/seed.js",
diff --git a/backend/src/middleware/excerptMiddleware.js b/backend/src/middleware/excerptMiddleware.js
index cfaf7f1b05..ca061609a0 100644
--- a/backend/src/middleware/excerptMiddleware.js
+++ b/backend/src/middleware/excerptMiddleware.js
@@ -4,28 +4,23 @@ export default {
Mutation: {
CreateGroup: async (resolve, root, args, context, info) => {
args.descriptionExcerpt = trunc(args.description, 120).html
- const result = await resolve(root, args, context, info)
- return result
+ return resolve(root, args, context, info)
},
CreatePost: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html
- const result = await resolve(root, args, context, info)
- return result
+ return resolve(root, args, context, info)
},
UpdatePost: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 120).html
- const result = await resolve(root, args, context, info)
- return result
+ return resolve(root, args, context, info)
},
CreateComment: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 180).html
- const result = await resolve(root, args, context, info)
- return result
+ return resolve(root, args, context, info)
},
UpdateComment: async (resolve, root, args, context, info) => {
args.contentExcerpt = trunc(args.content, 180).html
- const result = await resolve(root, args, context, info)
- return result
+ return resolve(root, args, context, info)
},
},
}
diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js
index 9605aada90..3fea526eef 100644
--- a/backend/src/middleware/slugifyMiddleware.spec.js
+++ b/backend/src/middleware/slugifyMiddleware.spec.js
@@ -6,7 +6,6 @@ import { createGroupMutation } from '../db/graphql/groups'
import { createPostMutation } from '../db/graphql/posts'
import { signupVerificationMutation } from '../db/graphql/authentications'
-let mutate
let authenticatedUser
let variables
@@ -15,19 +14,20 @@ const neode = getNeode()
const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
+const { server } = createServer({
+ context: () => {
+ return {
+ driver,
+ neode,
+ user: authenticatedUser,
+ }
+ },
+})
+
+const { mutate } = createTestClient(server)
+
beforeAll(async () => {
await cleanDatabase()
-
- const { server } = createServer({
- context: () => {
- return {
- driver,
- neode,
- user: authenticatedUser,
- }
- },
- })
- mutate = createTestClient(server).mutate
})
afterAll(async () => {
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index d1af985138..5737f5505f 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -51,10 +51,10 @@ export default {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
const { categoryIds } = params
delete params.categoryIds
- if (!categoryIds || categoryIds.length < CATEGORIES_MIN) {
+ if (CONFIG.CATEGORIES_ACTIVE && (!categoryIds || categoryIds.length < CATEGORIES_MIN)) {
throw new UserInputError('Too view categories!')
}
- if (categoryIds && categoryIds.length > CATEGORIES_MAX) {
+ if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length > CATEGORIES_MAX) {
throw new UserInputError('Too many categories!')
}
if (
@@ -94,7 +94,7 @@ export default {
`,
{ userId: context.user.id, categoryIds, params },
)
- const [group] = ownerCreateGroupTransactionResponse.records.map((record) =>
+ const [group] = await ownerCreateGroupTransactionResponse.records.map((record) =>
record.get('group'),
)
return group
From beacad4a17b33ececa908b6fe655626f1487f949 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Mon, 15 Aug 2022 11:07:45 +0200
Subject: [PATCH 037/374] set CONFIG in specs
---
backend/src/schema/resolvers/groups.spec.js | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index b3327d44a1..707558a063 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -3,6 +3,7 @@ import Factory, { cleanDatabase } from '../../db/factories'
import { createGroupMutation, groupQuery } from '../../db/graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
+import CONFIG from '../../config'
const driver = getDriver()
const neode = getNeode()
@@ -291,6 +292,10 @@ describe('CreateGroup', () => {
})
describe('categories', () => {
+ beforeEach(() => {
+ CONFIG.CATEGORIES_ACTIVE = true
+ })
+
describe('not even one', () => {
it('throws error: "Too view categories!"', async () => {
const { errors } = await mutate({
From 916dfbb46ed08cb7e786b9b16b723ca78c0ccf7a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 15 Aug 2022 14:00:36 +0200
Subject: [PATCH 038/374] Move or use GQL mutations in seeding to or from
separate files
---
backend/src/db/graphql/comments.js | 15 +++++++++++++++
backend/src/db/graphql/posts.js | 5 +++--
backend/src/db/seed.js | 18 +++---------------
3 files changed, 21 insertions(+), 17 deletions(-)
create mode 100644 backend/src/db/graphql/comments.js
diff --git a/backend/src/db/graphql/comments.js b/backend/src/db/graphql/comments.js
new file mode 100644
index 0000000000..b408c5e95f
--- /dev/null
+++ b/backend/src/db/graphql/comments.js
@@ -0,0 +1,15 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createCommentMutation = gql`
+ mutation ($id: ID, $postId: ID!, $content: String!) {
+ CreateComment(id: $id, postId: $postId, content: $content) {
+ id
+ }
+ }
+`
+
+// ------ queries
+
+// fill queries in here
diff --git a/backend/src/db/graphql/posts.js b/backend/src/db/graphql/posts.js
index 3277af8207..237446d41b 100644
--- a/backend/src/db/graphql/posts.js
+++ b/backend/src/db/graphql/posts.js
@@ -3,8 +3,9 @@ import gql from 'graphql-tag'
// ------ mutations
export const createPostMutation = gql`
- mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
- CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
+ mutation ($id: ID, $title: String!, $slug: String, $content: String!, $categoryIds: [ID]!) {
+ CreatePost(id: $id, title: $title, slug: $slug, content: $content, categoryIds: $categoryIds) {
+ id
slug
}
}
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 46c5870e0e..0a0926f06e 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -5,7 +5,9 @@ import createServer from '../server'
import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
-import { gql } from '../helpers/jest'
+// import { createGroupMutation } from './graphql/groups'
+import { createPostMutation } from './graphql/posts'
+import { createCommentMutation } from './graphql/comments'
if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) {
throw new Error(`You cannot seed the database in a non-staging and real production environment!`)
@@ -558,13 +560,6 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
'See #NaturphilosophieYoga , it can really help you!'
const hashtagAndMention1 =
'The new physics of #QuantenFlussTheorie can explain #QuantumGravity ! @peter-lustig got that already. ;-)'
- const createPostMutation = gql`
- mutation ($id: ID, $title: String!, $content: String!, $categoryIds: [ID]) {
- CreatePost(id: $id, title: $title, content: $content, categoryIds: $categoryIds) {
- id
- }
- }
- `
await Promise.all([
mutate({
@@ -615,13 +610,6 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
'I heard @jenny-rostock has practiced it for 3 years now.'
const mentionInComment2 =
'Did @peter-lustig tell you?'
- const createCommentMutation = gql`
- mutation ($id: ID, $postId: ID!, $content: String!) {
- CreateComment(id: $id, postId: $postId, content: $content) {
- id
- }
- }
- `
await Promise.all([
mutate({
mutation: createCommentMutation,
From 0f8abe770a1856629efefcbeacb6e6c2eb376fda Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 15 Aug 2022 15:01:26 +0200
Subject: [PATCH 039/374] Move GQL mutation 'loginMutation' in
'user_management.spec.js' into a separate file
---
backend/src/db/graphql/userManagement.js | 13 +++++++++++++
.../src/schema/resolvers/user_management.spec.js | 7 +------
2 files changed, 14 insertions(+), 6 deletions(-)
create mode 100644 backend/src/db/graphql/userManagement.js
diff --git a/backend/src/db/graphql/userManagement.js b/backend/src/db/graphql/userManagement.js
new file mode 100644
index 0000000000..3cb8a05f84
--- /dev/null
+++ b/backend/src/db/graphql/userManagement.js
@@ -0,0 +1,13 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const loginMutation = gql`
+ mutation ($email: String!, $password: String!) {
+ login(email: $email, password: $password)
+ }
+`
+
+// ------ queries
+
+// fill queries in here
diff --git a/backend/src/schema/resolvers/user_management.spec.js b/backend/src/schema/resolvers/user_management.spec.js
index 2dcb148555..15b39e80dd 100644
--- a/backend/src/schema/resolvers/user_management.spec.js
+++ b/backend/src/schema/resolvers/user_management.spec.js
@@ -2,6 +2,7 @@ import jwt from 'jsonwebtoken'
import CONFIG from './../../config'
import Factory, { cleanDatabase } from '../../db/factories'
import { gql } from '../../helpers/jest'
+import { loginMutation } from '../../db/graphql/userManagement'
import { createTestClient } from 'apollo-server-testing'
import createServer, { context } from '../../server'
import encode from '../../jwt/encode'
@@ -177,12 +178,6 @@ describe('currentUser', () => {
})
describe('login', () => {
- const loginMutation = gql`
- mutation ($email: String!, $password: String!) {
- login(email: $email, password: $password)
- }
- `
-
const respondsWith = async (expected) => {
await expect(mutate({ mutation: loginMutation, variables })).resolves.toMatchObject(expected)
}
From bbda8e6dd06bb39725d5c731041c36dd825c5d5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 15 Aug 2022 15:22:49 +0200
Subject: [PATCH 040/374] Seed some groups
---
backend/src/db/seed.js | 54 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 0a0926f06e..64ee3c1dd7 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -5,7 +5,7 @@ import createServer from '../server'
import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
-// import { createGroupMutation } from './graphql/groups'
+import { createGroupMutation } from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
@@ -383,6 +383,58 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
}),
])
+ // Create Groups
+
+ authenticatedUser = await peterLustig.toJson()
+ await Promise.all([
+ mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'g0',
+ name: 'Investigative Journalism',
+ about: 'Investigative journalists share ideas and insights and can collaborate.',
+ description: `English:
This group is hidden.
What is our group for? This group was created to allow investigative journalists to share and collaborate.
How does it work? Here you can internally share posts and comments about them.
Deutsch:
Diese Gruppe ist verborgen.
Wofür ist unsere Gruppe? Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.
Wie funktioniert das? Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.
`,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds: ['cat3', 'cat13', 'cat16'],
+ },
+ }),
+ ])
+
+ authenticatedUser = await jennyRostock.toJson()
+ await Promise.all([
+ mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'g1',
+ name: 'School For Citizens',
+ about: 'Our children shall receive education for life.',
+ description: `English
Our goal Only those who enjoy learning and do not lose their curiosity can obtain a good education for life and continue to learn with joy throughout their lives.
Curiosity For this we need a school that takes up the curiosity of the children, the people, and satisfies it through a lot of experience.
Deutsch
Unser Ziel Nur wer Spaß am Lernen hat und seine Neugier nicht verliert, kann gute Bildung für's Leben erlangen und sein ganzes Leben mit Freude weiter lernen.
Neugier Dazu benötigen wir eine Schule, die die Neugier der Kinder, der Menschen, aufnimmt und durch viel Erfahrung befriedigt.
`,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds: ['cat3', 'cat13', 'cat16'],
+ },
+ }),
+ ])
+
+ authenticatedUser = await bobDerBaumeister.toJson()
+ await Promise.all([
+ mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'g2',
+ name: 'Yoga Practice',
+ about: 'We do yoga around the clock.',
+ description: `What Is yoga? Yoga is not just about practicing asanas. It's about how we do it.
And practicing asanas doesn't have to be yoga, it can be more athletic than yogic.
What makes practicing asanas yogic? The important thing is:
`,
+ groupType: 'public',
+ actionRadius: 'interplanetary',
+ categoryIds: ['cat3', 'cat13', 'cat16'],
+ },
+ }),
+ ])
+
+ // Create Posts
+
const [p0, p1, p3, p4, p5, p6, p9, p10, p11, p13, p14, p15] = await Promise.all([
Factory.build(
'post',
From 936ecf247728456ddc42bb5d3959c79ba03e4bca Mon Sep 17 00:00:00 2001
From: ogerly
Date: Mon, 15 Aug 2022 15:35:33 +0200
Subject: [PATCH 041/374] add mutation createGroup and query GroupList
---
.../CategoriesSelect/CategoriesSelect.vue | 1 +
webapp/components/GroupForm/GroupForm.vue | 91 ++++++++++++-----
webapp/components/GroupList/GroupCard.vue | 28 +++---
webapp/components/GroupList/GroupList.vue | 5 +-
webapp/components/GroupMember/GroupMember.vue | 98 +++++++++----------
webapp/graphql/groups.js | 16 +--
webapp/pages/group/_id.vue | 17 ++--
webapp/pages/group/_id/_slug.vue | 6 +-
webapp/pages/group/create.vue | 43 ++++----
webapp/pages/my-groups.vue | 21 +++-
10 files changed, 183 insertions(+), 143 deletions(-)
diff --git a/webapp/components/CategoriesSelect/CategoriesSelect.vue b/webapp/components/CategoriesSelect/CategoriesSelect.vue
index b7d71de2d9..1fb95a8db8 100644
--- a/webapp/components/CategoriesSelect/CategoriesSelect.vue
+++ b/webapp/components/CategoriesSelect/CategoriesSelect.vue
@@ -46,6 +46,7 @@ export default {
},
methods: {
toggleCategory(id) {
+ console.log('toggleCategory', id)
this.selectedCategoryIds = xor(this.selectedCategoryIds, [id])
if (this.$parentForm) {
this.$parentForm.update(this.model, this.selectedCategoryIds)
diff --git a/webapp/components/GroupForm/GroupForm.vue b/webapp/components/GroupForm/GroupForm.vue
index 8e29bf300a..d52fea0c4b 100644
--- a/webapp/components/GroupForm/GroupForm.vue
+++ b/webapp/components/GroupForm/GroupForm.vue
@@ -1,30 +1,53 @@
-
-
+
+
-
+
-
- {{ form.name }}
- {{ form.status }}
- {{ form.description }}
- {{ form.categoryIds }}
+
+
+ {{ formData }}
Reset form
- Save group
+
+ save
+
@@ -42,29 +65,45 @@ export default {
components: {
CategoriesSelect,
},
- props:{
- model: { type: String, required: true },
- value: { type: String, default: '' },
+ props: {
+ group: {
+ type: Object,
+ default: () => ({}),
+ },
},
data() {
+ const { name, status, description, categories } = this.group
return {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
- form: {
- name: '',
- status: '',
- description: '',
- disable: false,
- categoryIds: [],
- }
-
+ disabled: false,
+ formData: {
+ name: name || '',
+ status: status || '',
+ description: description || '',
+ categoryIds: categories ? categories.map((category) => category.id) : [],
+ },
+ formSchema: {
+ name: { required: true, min: 3, max: 100 },
+ description: { required: true },
+ status: { required: true },
+ categoryIds: {
+ type: 'array',
+ required: this.categoriesActive,
+ validator: (_, value = []) => {
+ if (this.categoriesActive && (value.length === 0 || value.length > 3)) {
+ return [new Error(this.$t('common.validations.categories'))]
+ }
+ return []
+ },
+ },
+ },
}
},
methods: {
submit() {
- console.log('submit', this.form)
- this.$emit('createGroup', this.form)
-
+ console.log('submit', this.formData)
+ this.$emit('createGroup', this.formData)
},
reset() {
console.log('reset')
diff --git a/webapp/components/GroupList/GroupCard.vue b/webapp/components/GroupList/GroupCard.vue
index 4ae9f807fd..f3ff5fab00 100644
--- a/webapp/components/GroupList/GroupCard.vue
+++ b/webapp/components/GroupList/GroupCard.vue
@@ -1,24 +1,21 @@
+ {{ responseGroupListQuery }}
Group Card
-
-
- {{ item.name }}
-
-
-
-
+
+
+ {{ item.name }}
+
+
+
+
@@ -30,6 +27,7 @@ export default {
name: 'GroupList',
props: {
items: { type: Array, default: () => [] },
+ responseGroupListQuery: { type: Array, default: () => [] },
},
methods: {
deleteGroup() {
diff --git a/webapp/components/GroupList/GroupList.vue b/webapp/components/GroupList/GroupList.vue
index 69df8acb72..d16b208dea 100644
--- a/webapp/components/GroupList/GroupList.vue
+++ b/webapp/components/GroupList/GroupList.vue
@@ -10,8 +10,8 @@
@click="deleteGroup(scope.row)"
>
-
- {{ scope.row.name }}
+
+ {{ scope.row.name }}
{{ scope.row.status }}
@@ -26,7 +26,6 @@
-
diff --git a/webapp/components/GroupMember/GroupMember.vue b/webapp/components/GroupMember/GroupMember.vue
index f83b00685e..16f4ebecff 100644
--- a/webapp/components/GroupMember/GroupMember.vue
+++ b/webapp/components/GroupMember/GroupMember.vue
@@ -1,67 +1,65 @@
+
-
Link zur Gruppe
-
- Copy Link for Invite Member please!
-
-
-
-
-
-
Members
-
-
+ Link zur Gruppe
+
+ Copy Link for Invite Member please!
+
+
+
+
+
+
Members
+
+
{{ scope.row.name }} loves {{ scope.row.loves }}
- delete
+ delete
-
+
diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js
index c41f06e4d9..579380b246 100644
--- a/webapp/graphql/groups.js
+++ b/webapp/graphql/groups.js
@@ -3,7 +3,7 @@ import gql from 'graphql-tag'
// ------ mutations
export const createGroupMutation = gql`
- mutation (
+ mutation(
$id: ID
$name: String!
$slug: String
@@ -35,9 +35,6 @@ export const createGroupMutation = gql`
groupType
actionRadius
myRole
- # Wolle: owner {
- # name
- # }
}
}
`
@@ -45,7 +42,7 @@ export const createGroupMutation = gql`
// ------ queries
export const groupQuery = gql`
- query (
+ query(
$isMember: Boolean
$id: ID
$name: String
@@ -54,9 +51,6 @@ export const groupQuery = gql`
$updatedAt: String
$about: String
$description: String
- # $groupType: GroupType!,
- # $actionRadius: GroupActionRadius!,
- # $categoryIds: [ID]
$locationName: String
$first: Int
$offset: Int
@@ -72,9 +66,6 @@ export const groupQuery = gql`
updatedAt: $updatedAt
about: $about
description: $description
- # groupType: $groupType
- # actionRadius: $actionRadius
- # categoryIds: $categoryIds
locationName: $locationName
first: $first
offset: $offset
@@ -99,9 +90,6 @@ export const groupQuery = gql`
name
icon
}
- # Wolle: owner {
- # name
- # }
}
}
`
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
index 7ce25e827c..6e8eed4b43 100644
--- a/webapp/pages/group/_id.vue
+++ b/webapp/pages/group/_id.vue
@@ -1,9 +1,10 @@
- _id groupe page
-
{{$route.params}}
-
- bearbeiten
-
-
-
-
\ No newline at end of file
+
+ _id groupe page
+
{{ $route.params }}
+
+ bearbeiten
+
+
+
+
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index 36f2ccb2be..88c4b85c6c 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -1,6 +1,6 @@
-
+
- Group Page
-
+ Group Page
+
diff --git a/webapp/pages/group/create.vue b/webapp/pages/group/create.vue
index c76be5360f..4526188d7b 100644
--- a/webapp/pages/group/create.vue
+++ b/webapp/pages/group/create.vue
@@ -1,24 +1,24 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
From e2c987f3003f773dc36eb378124917f6fdbf3a11 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 15 Aug 2022 16:14:19 +0200
Subject: [PATCH 042/374] Refine categories of seeded groups
---
backend/src/db/seed.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 64ee3c1dd7..e41ef1abce 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -396,7 +396,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
description: `English:
This group is hidden.
What is our group for? This group was created to allow investigative journalists to share and collaborate.
How does it work? Here you can internally share posts and comments about them.
Deutsch:
Diese Gruppe ist verborgen.
Wofür ist unsere Gruppe? Diese Gruppe wurde geschaffen, um investigativen Journalisten den Austausch und die Zusammenarbeit zu ermöglichen.
Wie funktioniert das? Hier könnt ihr euch intern über Beiträge und Kommentare zu ihnen austauschen.
`,
groupType: 'hidden',
actionRadius: 'global',
- categoryIds: ['cat3', 'cat13', 'cat16'],
+ categoryIds: ['cat6', 'cat9', 'cat14'],
},
}),
])
@@ -412,7 +412,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
description: `English
Our goal Only those who enjoy learning and do not lose their curiosity can obtain a good education for life and continue to learn with joy throughout their lives.
Curiosity For this we need a school that takes up the curiosity of the children, the people, and satisfies it through a lot of experience.
Deutsch
Unser Ziel Nur wer Spaß am Lernen hat und seine Neugier nicht verliert, kann gute Bildung für's Leben erlangen und sein ganzes Leben mit Freude weiter lernen.
Neugier Dazu benötigen wir eine Schule, die die Neugier der Kinder, der Menschen, aufnimmt und durch viel Erfahrung befriedigt.
`,
groupType: 'closed',
actionRadius: 'national',
- categoryIds: ['cat3', 'cat13', 'cat16'],
+ categoryIds: ['cat7', 'cat9', 'cat16'],
},
}),
])
From 27b74eb9e1ba5e41c43f4eea9c40db59e5cdb76e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 17 Aug 2022 12:11:52 +0200
Subject: [PATCH 043/374] Implement 'GroupMember' resolver, a first step
---
.../src/middleware/permissionsMiddleware.js | 32 +++++
backend/src/schema/resolvers/groups.js | 22 ++++
backend/src/schema/resolvers/groups.spec.js | 122 ++++++++++++++++++
backend/src/schema/types/type/Group.gql | 8 ++
backend/src/schema/types/type/User.gql | 2 +
5 files changed, 186 insertions(+)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 99dcfc0cde..4c73624b06 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -52,6 +52,37 @@ const isMySocialMedia = rule({
return socialMedia.ownedBy.node.id === user.id
})
+const isAllowSeeingMembersOfGroup = rule({
+ cache: 'no_cache',
+})(async (_parent, args, { user, driver }) => {
+ if (!user) return false
+ const { id: groupId } = args
+ const session = driver.session()
+ const readTxPromise = session.readTransaction(async (transaction) => {
+ const transactionResponse = await transaction.run(
+ `
+ MATCH (group:Group {id: $groupId})
+ OPTIONAL MATCH (admin {id:User $userId})-[membership:MEMBER_OF]->(group)
+ WHERE membership.role IN ['admin', 'owner']
+ RETURN group, admin
+ `,
+ { groupId, userId: user.id },
+ )
+ return {
+ admin: transactionResponse.records.map((record) => record.get('admin')),
+ group: transactionResponse.records.map((record) => record.get('group')),
+ }
+ })
+ try {
+ const [{ admin, group }] = await readTxPromise
+ return group.groupType === 'public' || !!admin
+ } catch (error) {
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+})
+
const isAuthor = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
@@ -115,6 +146,7 @@ export default shield(
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
+ GroupMember: isAllowSeeingMembersOfGroup,
Post: allow,
profilePagePosts: allow,
Comment: allow,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 5737f5505f..d27d9b5e45 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -46,6 +46,28 @@ export default {
session.close()
}
},
+ GroupMember: async (_object, params, context, _resolveInfo) => {
+ const { id: groupId } = params
+ const session = context.driver.session()
+ const readTxResultPromise = session.readTransaction(async (txc) => {
+ const groupMemberCypher = `
+ MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(:Group {id: $groupId})
+ RETURN user {.*, myRoleInGroup: membership.role}
+ `
+ const result = await txc.run(groupMemberCypher, {
+ groupId,
+ userId: context.user.id,
+ })
+ return result.records.map((record) => record.get('user'))
+ })
+ try {
+ return await readTxResultPromise
+ } catch (error) {
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+ },
},
Mutation: {
CreateGroup: async (_parent, params, context, _resolveInfo) => {
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 707558a063..706e27748f 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -207,6 +207,128 @@ describe('Group', () => {
})
})
+describe('GroupMember', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ let otherUser
+
+ beforeEach(async () => {
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await otherUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'others-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'my-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ })
+
+ describe('query group members', () => {
+ describe('by owner', () => {
+ it.only('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ }),
+ // Wolle: expect.objectContaining({
+ // id: 'others-group',
+ // slug: 'uninteresting-group',
+ // myRole: null,
+ // }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('isMember = true', () => {
+ it('finds only groups where user is member', async () => {
+ const expected = {
+ data: {
+ Group: [
+ {
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ },
+ ],
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('isMember = false', () => {
+ it('finds only groups where user is not(!) member', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+})
+
describe('CreateGroup', () => {
beforeEach(() => {
variables = {
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 3165b4a448..fd53d48b30 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -74,6 +74,14 @@ type Query {
filter: _GroupFilter
): [Group]
+ GroupMember(
+ id: ID
+ first: Int
+ offset: Int
+ orderBy: [_GroupOrdering]
+ filter: _GroupFilter
+ ): [User]
+
AvailableGroupTypes: [GroupType]!
AvailableGroupActionRadii: [GroupActionRadius]!
diff --git a/backend/src/schema/types/type/User.gql b/backend/src/schema/types/type/User.gql
index a25e51079a..4219cd00ed 100644
--- a/backend/src/schema/types/type/User.gql
+++ b/backend/src/schema/types/type/User.gql
@@ -114,6 +114,8 @@ type User {
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
emotions: [EMOTED]
+
+ myRoleInGroup: GroupMemberRole
}
From 25ed30dba168f901f4f09a6c3d03c27c2692d3c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 18 Aug 2022 10:12:11 +0200
Subject: [PATCH 044/374] Implement 'EnterGroup' resolver
---
backend/src/db/graphql/groups.js | 22 +
.../src/middleware/permissionsMiddleware.js | 5 +-
backend/src/schema/resolvers/groups.js | 34 +-
backend/src/schema/resolvers/groups.spec.js | 491 +++++++++++++-----
backend/src/schema/types/type/Group.gql | 12 +-
5 files changed, 429 insertions(+), 135 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 2a611f3247..b169e10fbc 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -39,6 +39,17 @@ export const createGroupMutation = gql`
}
`
+export const enterGroupMutation = gql`
+ mutation ($id: ID!, $userId: ID!) {
+ EnterGroup(id: $id, userId: $userId) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
+ }
+`
+
// ------ queries
export const groupQuery = gql`
@@ -93,3 +104,14 @@ export const groupQuery = gql`
}
}
`
+
+export const groupMemberQuery = gql`
+ query ($id: ID!, $first: Int, $offset: Int, $orderBy: [_UserOrdering], $filter: _UserFilter) {
+ GroupMember(id: $id, first: $first, offset: $offset, orderBy: $orderBy, filter: $filter) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
+ }
+`
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 4c73624b06..9dcf354767 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -62,9 +62,9 @@ const isAllowSeeingMembersOfGroup = rule({
const transactionResponse = await transaction.run(
`
MATCH (group:Group {id: $groupId})
- OPTIONAL MATCH (admin {id:User $userId})-[membership:MEMBER_OF]->(group)
+ OPTIONAL MATCH (admin:User {id: $userId})-[membership:MEMBER_OF]->(group)
WHERE membership.role IN ['admin', 'owner']
- RETURN group, admin
+ RETURN group, admin {.*, myRoleInGroup: membership.role}
`,
{ groupId, userId: user.id },
)
@@ -174,6 +174,7 @@ export default shield(
SignupVerification: allow,
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
+ EnterGroup: isAuthenticated,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index d27d9b5e45..b64484d376 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -109,7 +109,7 @@ export default {
MERGE (owner)-[:CREATED]->(group)
MERGE (owner)-[membership:MEMBER_OF]->(group)
SET membership.createdAt = toString(datetime())
- SET membership.updatedAt = toString(datetime())
+ SET membership.updatedAt = membership.createdAt
SET membership.role = 'owner'
${categoriesCypher}
RETURN group {.*, myRole: membership.role}
@@ -122,8 +122,7 @@ export default {
return group
})
try {
- const group = await writeTxResultPromise
- return group
+ return await writeTxResultPromise
} catch (error) {
if (error.code === 'Neo.ClientError.Schema.ConstraintValidationFailed')
throw new UserInputError('Group with this slug already exists!')
@@ -132,6 +131,35 @@ export default {
session.close()
}
},
+ EnterGroup: async (_parent, params, context, _resolveInfo) => {
+ const { id: groupId, userId } = params
+ const session = context.driver.session()
+ const writeTxResultPromise = session.writeTransaction(async (transaction) => {
+ const enterGroupCypher = `
+ MATCH (member:User {id: $userId}), (group:Group {id: $groupId})
+ MERGE (member)-[membership:MEMBER_OF]->(group)
+ ON CREATE SET
+ membership.createdAt = toString(datetime()),
+ membership.updatedAt = membership.createdAt,
+ membership.role =
+ CASE WHEN group.groupType = 'public'
+ THEN 'usual'
+ ELSE 'pending'
+ END
+ RETURN member {.*, myRoleInGroup: membership.role}
+ `
+ const result = await transaction.run(enterGroupCypher, { groupId, userId })
+ const [member] = await result.records.map((record) => record.get('member'))
+ return member
+ })
+ try {
+ return await writeTxResultPromise
+ } catch (error) {
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+ },
},
Group: {
...Resolver('Group', {
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 706e27748f..81223a5847 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -1,6 +1,11 @@
import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
-import { createGroupMutation, groupQuery } from '../../db/graphql/groups'
+import {
+ createGroupMutation,
+ enterGroupMutation,
+ groupMemberQuery,
+ groupQuery,
+} from '../../db/graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j'
import createServer from '../../server'
import CONFIG from '../../config'
@@ -94,10 +99,6 @@ describe('Group', () => {
})
describe('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
-
let otherUser
beforeEach(async () => {
@@ -207,127 +208,127 @@ describe('Group', () => {
})
})
-describe('GroupMember', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
-
- let otherUser
-
- beforeEach(async () => {
- otherUser = await Factory.build(
- 'user',
- {
- id: 'other-user',
- name: 'Other TestUser',
- },
- {
- email: 'test2@example.org',
- password: '1234',
- },
- )
- authenticatedUser = await otherUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'others-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'global',
- categoryIds,
- },
- })
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'my-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
- })
- })
-
- describe('query group members', () => {
- describe('by owner', () => {
- it.only('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- }),
- // Wolle: expect.objectContaining({
- // id: 'others-group',
- // slug: 'uninteresting-group',
- // myRole: null,
- // }),
- ]),
- },
- errors: undefined,
- }
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
- })
- })
-
- describe('isMember = true', () => {
- it('finds only groups where user is member', async () => {
- const expected = {
- data: {
- Group: [
- {
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- },
- ],
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject(expected)
- })
- })
-
- describe('isMember = false', () => {
- it('finds only groups where user is not(!) member', async () => {
- const expected = {
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject(expected)
- })
- })
- })
- })
-})
+// describe('GroupMember', () => {
+// describe('unauthenticated', () => {
+// it('throws authorization error', async () => {
+// const { errors } = await query({ query: groupMemberQuery, variables: {} })
+// expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+// })
+// })
+
+// describe('authenticated', () => {
+// beforeEach(async () => {
+// authenticatedUser = await user.toJson()
+// })
+
+// let otherUser
+
+// beforeEach(async () => {
+// otherUser = await Factory.build(
+// 'user',
+// {
+// id: 'other-user',
+// name: 'Other TestUser',
+// },
+// {
+// email: 'test2@example.org',
+// password: '1234',
+// },
+// )
+// authenticatedUser = await otherUser.toJson()
+// await mutate({
+// mutation: createGroupMutation,
+// variables: {
+// id: 'others-group',
+// name: 'Uninteresting Group',
+// about: 'We will change nothing!',
+// description: 'We love it like it is!?' + descriptionAdditional100,
+// groupType: 'closed',
+// actionRadius: 'global',
+// categoryIds,
+// },
+// })
+// authenticatedUser = await user.toJson()
+// await mutate({
+// mutation: createGroupMutation,
+// variables: {
+// id: 'my-group',
+// name: 'The Best Group',
+// about: 'We will change the world!',
+// description: 'Some description' + descriptionAdditional100,
+// groupType: 'public',
+// actionRadius: 'regional',
+// categoryIds,
+// },
+// })
+// })
+
+// describe('query group members', () => {
+// describe('by owner', () => {
+// it.only('finds all members', async () => {
+// const expected = {
+// data: {
+// GroupMember: expect.arrayContaining([
+// expect.objectContaining({
+// id: 'my-group',
+// slug: 'the-best-group',
+// myRole: 'owner',
+// }),
+// // Wolle: expect.objectContaining({
+// // id: 'others-group',
+// // slug: 'uninteresting-group',
+// // myRole: null,
+// // }),
+// ]),
+// },
+// errors: undefined,
+// }
+// await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+// })
+// })
+
+// describe('isMember = true', () => {
+// it('finds only groups where user is member', async () => {
+// const expected = {
+// data: {
+// Group: [
+// {
+// id: 'my-group',
+// slug: 'the-best-group',
+// myRole: 'owner',
+// },
+// ],
+// },
+// errors: undefined,
+// }
+// await expect(
+// query({ query: groupQuery, variables: { isMember: true } }),
+// ).resolves.toMatchObject(expected)
+// })
+// })
+
+// describe('isMember = false', () => {
+// it('finds only groups where user is not(!) member', async () => {
+// const expected = {
+// data: {
+// Group: expect.arrayContaining([
+// expect.objectContaining({
+// id: 'others-group',
+// slug: 'uninteresting-group',
+// myRole: null,
+// }),
+// ]),
+// },
+// errors: undefined,
+// }
+// await expect(
+// query({ query: groupQuery, variables: { isMember: false } }),
+// ).resolves.toMatchObject(expected)
+// })
+// })
+// })
+// })
+// })
describe('CreateGroup', () => {
beforeEach(() => {
@@ -440,3 +441,241 @@ describe('CreateGroup', () => {
})
})
})
+
+describe('EnterGroup', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ userId: 'current-user',
+ }
+ const { errors } = await mutate({ mutation: enterGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
+
+ beforeEach(async () => {
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ })
+
+ describe('public group', () => {
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "usual" as membership role', async () => {
+ variables = {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'public-group',
+ userId: 'current-user',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+
+ describe('closed group', () => {
+ describe('entered by "current-user"', () => {
+ it('has "pending" as membership role', async () => {
+ variables = {
+ id: 'closed-group',
+ userId: 'current-user',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'closed-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+
+ describe('hidden group', () => {
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "pending" as membership role', async () => {
+ variables = {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'hidden-group',
+ userId: 'owner-of-hidden-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+ })
+})
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index fd53d48b30..cf44894dbb 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -71,15 +71,14 @@ type Query {
first: Int
offset: Int
orderBy: [_GroupOrdering]
- filter: _GroupFilter
): [Group]
GroupMember(
- id: ID
+ id: ID!
first: Int
offset: Int
- orderBy: [_GroupOrdering]
- filter: _GroupFilter
+ orderBy: [_UserOrdering]
+ filter: _UserFilter
): [User]
AvailableGroupTypes: [GroupType]!
@@ -114,4 +113,9 @@ type Mutation {
): Group
DeleteGroup(id: ID!): Group
+
+ EnterGroup(
+ id: ID!
+ userId: ID!
+ ): User
}
From 613f91b0371825a6b093e1fc0bb48c680709235a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 18 Aug 2022 11:47:27 +0200
Subject: [PATCH 045/374] Add seedings for group memberships
---
backend/src/db/seed.js | 78 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 77 insertions(+), 1 deletion(-)
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index e41ef1abce..ebe8a60209 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -5,7 +5,7 @@ import createServer from '../server'
import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
-import { createGroupMutation } from './graphql/groups'
+import { createGroupMutation, enterGroupMutation } from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
@@ -400,6 +400,22 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
])
+ await Promise.all([
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u2',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u3',
+ },
+ }),
+ ])
authenticatedUser = await jennyRostock.toJson()
await Promise.all([
@@ -416,6 +432,36 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
])
+ await Promise.all([
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g1',
+ userId: 'u1',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g1',
+ userId: 'u5',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g1',
+ userId: 'u6',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g1',
+ userId: 'u7',
+ },
+ }),
+ ])
authenticatedUser = await bobDerBaumeister.toJson()
await Promise.all([
@@ -432,6 +478,36 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
])
+ await Promise.all([
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g2',
+ userId: 'u4',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g2',
+ userId: 'u5',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g2',
+ userId: 'u6',
+ },
+ }),
+ mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'g2',
+ userId: 'u7',
+ },
+ }),
+ ])
// Create Posts
From d36f0eab57887569d439680e3770ba9ea0e41209 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 18 Aug 2022 11:48:26 +0200
Subject: [PATCH 046/374] Add tests for 'GroupMember' resolver, a start
---
.../src/middleware/permissionsMiddleware.js | 17 +-
backend/src/schema/resolvers/groups.js | 4 +-
backend/src/schema/resolvers/groups.spec.js | 424 +++++++++++++-----
3 files changed, 316 insertions(+), 129 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 9dcf354767..dc54d5a298 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -52,11 +52,13 @@ const isMySocialMedia = rule({
return socialMedia.ownedBy.node.id === user.id
})
-const isAllowSeeingMembersOfGroup = rule({
+const isAllowedSeeingMembersOfGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
if (!user) return false
const { id: groupId } = args
+ // Wolle: console.log('groupId: ', groupId)
+ // console.log('user.id: ', user.id)
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
@@ -64,19 +66,22 @@ const isAllowSeeingMembersOfGroup = rule({
MATCH (group:Group {id: $groupId})
OPTIONAL MATCH (admin:User {id: $userId})-[membership:MEMBER_OF]->(group)
WHERE membership.role IN ['admin', 'owner']
- RETURN group, admin {.*, myRoleInGroup: membership.role}
+ RETURN group {.*}, admin {.*, myRoleInGroup: membership.role}
`,
{ groupId, userId: user.id },
)
return {
- admin: transactionResponse.records.map((record) => record.get('admin')),
- group: transactionResponse.records.map((record) => record.get('group')),
+ admin: transactionResponse.records.map((record) => record.get('admin'))[0],
+ group: transactionResponse.records.map((record) => record.get('group'))[0],
}
})
try {
- const [{ admin, group }] = await readTxPromise
+ const { admin, group } = await readTxPromise
+ // Wolle: console.log('admin: ', admin)
+ // console.log('group: ', group)
return group.groupType === 'public' || !!admin
} catch (error) {
+ // Wolle: console.log('error: ', error)
throw new Error(error)
} finally {
session.close()
@@ -146,7 +151,7 @@ export default shield(
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
- GroupMember: isAllowSeeingMembersOfGroup,
+ GroupMember: isAllowedSeeingMembersOfGroup,
Post: allow,
profilePagePosts: allow,
Comment: allow,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index b64484d376..91135a1db3 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -48,15 +48,15 @@ export default {
},
GroupMember: async (_object, params, context, _resolveInfo) => {
const { id: groupId } = params
+ // Wolle: console.log('groupId: ', groupId)
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
const groupMemberCypher = `
- MATCH (user:User {id: $userId})-[membership:MEMBER_OF]->(:Group {id: $groupId})
+ MATCH (user:User)-[membership:MEMBER_OF]->(:Group {id: $groupId})
RETURN user {.*, myRoleInGroup: membership.role}
`
const result = await txc.run(groupMemberCypher, {
groupId,
- userId: context.user.id,
})
return result.records.map((record) => record.get('user'))
})
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 81223a5847..33aeaf2bd6 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -208,127 +208,309 @@ describe('Group', () => {
})
})
-// describe('GroupMember', () => {
-// describe('unauthenticated', () => {
-// it('throws authorization error', async () => {
-// const { errors } = await query({ query: groupMemberQuery, variables: {} })
-// expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
-// })
-// })
-
-// describe('authenticated', () => {
-// beforeEach(async () => {
-// authenticatedUser = await user.toJson()
-// })
-
-// let otherUser
-
-// beforeEach(async () => {
-// otherUser = await Factory.build(
-// 'user',
-// {
-// id: 'other-user',
-// name: 'Other TestUser',
-// },
-// {
-// email: 'test2@example.org',
-// password: '1234',
-// },
-// )
-// authenticatedUser = await otherUser.toJson()
-// await mutate({
-// mutation: createGroupMutation,
-// variables: {
-// id: 'others-group',
-// name: 'Uninteresting Group',
-// about: 'We will change nothing!',
-// description: 'We love it like it is!?' + descriptionAdditional100,
-// groupType: 'closed',
-// actionRadius: 'global',
-// categoryIds,
-// },
-// })
-// authenticatedUser = await user.toJson()
-// await mutate({
-// mutation: createGroupMutation,
-// variables: {
-// id: 'my-group',
-// name: 'The Best Group',
-// about: 'We will change the world!',
-// description: 'Some description' + descriptionAdditional100,
-// groupType: 'public',
-// actionRadius: 'regional',
-// categoryIds,
-// },
-// })
-// })
-
-// describe('query group members', () => {
-// describe('by owner', () => {
-// it.only('finds all members', async () => {
-// const expected = {
-// data: {
-// GroupMember: expect.arrayContaining([
-// expect.objectContaining({
-// id: 'my-group',
-// slug: 'the-best-group',
-// myRole: 'owner',
-// }),
-// // Wolle: expect.objectContaining({
-// // id: 'others-group',
-// // slug: 'uninteresting-group',
-// // myRole: null,
-// // }),
-// ]),
-// },
-// errors: undefined,
-// }
-// await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
-// })
-// })
-
-// describe('isMember = true', () => {
-// it('finds only groups where user is member', async () => {
-// const expected = {
-// data: {
-// Group: [
-// {
-// id: 'my-group',
-// slug: 'the-best-group',
-// myRole: 'owner',
-// },
-// ],
-// },
-// errors: undefined,
-// }
-// await expect(
-// query({ query: groupQuery, variables: { isMember: true } }),
-// ).resolves.toMatchObject(expected)
-// })
-// })
-
-// describe('isMember = false', () => {
-// it('finds only groups where user is not(!) member', async () => {
-// const expected = {
-// data: {
-// Group: expect.arrayContaining([
-// expect.objectContaining({
-// id: 'others-group',
-// slug: 'uninteresting-group',
-// myRole: null,
-// }),
-// ]),
-// },
-// errors: undefined,
-// }
-// await expect(
-// query({ query: groupQuery, variables: { isMember: false } }),
-// ).resolves.toMatchObject(expected)
-// })
-// })
-// })
-// })
-// })
+describe('GroupMember', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ }
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ let otherUser
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
+
+ beforeEach(async () => {
+ // create users
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ // create additional memberships
+ await mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ await mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'current-user',
+ },
+ })
+ await mutate({
+ mutation: enterGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ })
+
+ describe('public group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'public-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner', () => {
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+
+ describe('by "other-user"', () => {
+ it.only('throws authorization error', async () => {
+ authenticatedUser = await otherUser.toJson()
+ const result = await query({ query: groupMemberQuery, variables })
+ console.log('result: ', result)
+ // Wolle: const { errors } = await query({ query: groupMemberQuery, variables })
+ // expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'public-group',
+ userId: 'current-user',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+
+ describe('closed group', () => {
+ describe('entered by "current-user"', () => {
+ it('has "pending" as membership role', async () => {
+ variables = {
+ id: 'closed-group',
+ userId: 'current-user',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'closed-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+
+ describe('hidden group', () => {
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "pending" as membership role', async () => {
+ variables = {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'hidden-group',
+ userId: 'owner-of-hidden-group',
+ }
+ const expected = {
+ data: {
+ EnterGroup: {
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: enterGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+ })
+})
describe('CreateGroup', () => {
beforeEach(() => {
From 14620b00eb1e63b23c3d83399de6a4e735307c8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Fri, 19 Aug 2022 10:47:17 +0200
Subject: [PATCH 047/374] Add tests for 'JoinGroup' and 'GroupMember' resolver
---
backend/src/db/graphql/groups.js | 4 +-
backend/src/db/seed.js | 22 +-
.../src/middleware/permissionsMiddleware.js | 20 +-
backend/src/schema/resolvers/groups.js | 6 +-
backend/src/schema/resolvers/groups.spec.js | 780 ++++++++++--------
backend/src/schema/types/type/Group.gql | 2 +-
6 files changed, 482 insertions(+), 352 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index b169e10fbc..8486288ec5 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -39,9 +39,9 @@ export const createGroupMutation = gql`
}
`
-export const enterGroupMutation = gql`
+export const joinGroupMutation = gql`
mutation ($id: ID!, $userId: ID!) {
- EnterGroup(id: $id, userId: $userId) {
+ JoinGroup(id: $id, userId: $userId) {
id
name
slug
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index ebe8a60209..f9b2d05da0 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -5,7 +5,7 @@ import createServer from '../server'
import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
-import { createGroupMutation, enterGroupMutation } from './graphql/groups'
+import { createGroupMutation, joinGroupMutation } from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
@@ -402,14 +402,14 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g0',
userId: 'u2',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g0',
userId: 'u3',
@@ -434,28 +434,28 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g1',
userId: 'u1',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g1',
userId: 'u5',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g1',
userId: 'u6',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g1',
userId: 'u7',
@@ -480,28 +480,28 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g2',
userId: 'u4',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g2',
userId: 'u5',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g2',
userId: 'u6',
},
}),
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'g2',
userId: 'u7',
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index dc54d5a298..afdc5501e7 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -64,22 +64,26 @@ const isAllowedSeeingMembersOfGroup = rule({
const transactionResponse = await transaction.run(
`
MATCH (group:Group {id: $groupId})
- OPTIONAL MATCH (admin:User {id: $userId})-[membership:MEMBER_OF]->(group)
- WHERE membership.role IN ['admin', 'owner']
- RETURN group {.*}, admin {.*, myRoleInGroup: membership.role}
+ OPTIONAL MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group)
+ RETURN group {.*}, member {.*, myRoleInGroup: membership.role}
`,
{ groupId, userId: user.id },
)
return {
- admin: transactionResponse.records.map((record) => record.get('admin'))[0],
+ member: transactionResponse.records.map((record) => record.get('member'))[0],
group: transactionResponse.records.map((record) => record.get('group'))[0],
}
})
try {
- const { admin, group } = await readTxPromise
- // Wolle: console.log('admin: ', admin)
+ const { member, group } = await readTxPromise
+ // Wolle: console.log('member: ', member)
// console.log('group: ', group)
- return group.groupType === 'public' || !!admin
+ return (
+ group.groupType === 'public' ||
+ (['closed', 'hidden'].includes(group.groupType) &&
+ !!member &&
+ ['usual', 'admin', 'owner'].includes(member.myRoleInGroup))
+ )
} catch (error) {
// Wolle: console.log('error: ', error)
throw new Error(error)
@@ -179,7 +183,7 @@ export default shield(
SignupVerification: allow,
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
- EnterGroup: isAuthenticated,
+ JoinGroup: isAuthenticated,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 91135a1db3..c9a31fdc36 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -131,11 +131,11 @@ export default {
session.close()
}
},
- EnterGroup: async (_parent, params, context, _resolveInfo) => {
+ JoinGroup: async (_parent, params, context, _resolveInfo) => {
const { id: groupId, userId } = params
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
- const enterGroupCypher = `
+ const joinGroupCypher = `
MATCH (member:User {id: $userId}), (group:Group {id: $groupId})
MERGE (member)-[membership:MEMBER_OF]->(group)
ON CREATE SET
@@ -148,7 +148,7 @@ export default {
END
RETURN member {.*, myRoleInGroup: membership.role}
`
- const result = await transaction.run(enterGroupCypher, { groupId, userId })
+ const result = await transaction.run(joinGroupCypher, { groupId, userId })
const [member] = await result.records.map((record) => record.get('member'))
return member
})
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 33aeaf2bd6..87eb02dc0a 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -2,7 +2,7 @@ import { createTestClient } from 'apollo-server-testing'
import Factory, { cleanDatabase } from '../../db/factories'
import {
createGroupMutation,
- enterGroupMutation,
+ joinGroupMutation,
groupMemberQuery,
groupQuery,
} from '../../db/graphql/groups'
@@ -90,6 +90,118 @@ afterEach(async () => {
await cleanDatabase()
})
+describe('CreateGroup', () => {
+ beforeEach(() => {
+ variables = {
+ ...variables,
+ id: 'g589',
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ }
+ })
+
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('creates a group', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+
+ it('assigns the authenticated user as owner', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ myRole: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+
+ it('has "disabled" and "deleted" default to "false"', async () => {
+ const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
+
+ describe('description', () => {
+ describe('length without HTML', () => {
+ describe('less then 100 chars', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ ...variables,
+ description:
+ '0123456789' +
+ '0123456789 ',
+ },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Description too short!')
+ })
+ })
+ })
+ })
+
+ describe('categories', () => {
+ beforeEach(() => {
+ CONFIG.CATEGORIES_ACTIVE = true
+ })
+
+ describe('not even one', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: null },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
+ })
+ })
+
+ describe('four', () => {
+ it('throws error: "Too many categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too many categories!')
+ })
+ })
+ })
+ })
+})
+
describe('Group', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
@@ -208,35 +320,23 @@ describe('Group', () => {
})
})
-describe('GroupMember', () => {
+describe('JoinGroup', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
variables = {
id: 'not-existing-group',
+ userId: 'current-user',
}
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await mutate({ mutation: joinGroupMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
describe('authenticated', () => {
- let otherUser
let ownerOfClosedGroupUser
let ownerOfHiddenGroupUser
beforeEach(async () => {
- // create users
- otherUser = await Factory.build(
- 'user',
- {
- id: 'other-user',
- name: 'Other TestUser',
- },
- {
- email: 'test2@example.org',
- password: '1234',
- },
- )
ownerOfClosedGroupUser = await Factory.build(
'user',
{
@@ -259,7 +359,6 @@ describe('GroupMember', () => {
password: '1234',
},
)
- // create groups
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
mutation: createGroupMutation,
@@ -299,83 +398,30 @@ describe('GroupMember', () => {
categoryIds,
},
})
- // create additional memberships
- await mutate({
- mutation: enterGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-closed-group',
- },
- })
- await mutate({
- mutation: enterGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-hidden-group',
- },
- })
- await mutate({
- mutation: enterGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'current-user',
- },
- })
- await mutate({
- mutation: enterGroupMutation,
- variables: {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- },
- })
})
describe('public group', () => {
- beforeEach(async () => {
- variables = {
- id: 'public-group',
- }
- })
-
- describe('query group members', () => {
- describe('by owner', () => {
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "usual" as membership role', async () => {
+ variables = {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
},
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
variables,
- })
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
-
- describe('by "other-user"', () => {
- it.only('throws authorization error', async () => {
- authenticatedUser = await otherUser.toJson()
- const result = await query({ query: groupMemberQuery, variables })
- console.log('result: ', result)
- // Wolle: const { errors } = await query({ query: groupMemberQuery, variables })
- // expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
+ }),
+ ).resolves.toMatchObject(expected)
})
})
@@ -388,7 +434,7 @@ describe('GroupMember', () => {
}
const expected = {
data: {
- EnterGroup: {
+ JoinGroup: {
id: 'current-user',
myRoleInGroup: 'owner',
},
@@ -397,7 +443,7 @@ describe('GroupMember', () => {
}
await expect(
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -415,7 +461,7 @@ describe('GroupMember', () => {
}
const expected = {
data: {
- EnterGroup: {
+ JoinGroup: {
id: 'current-user',
myRoleInGroup: 'pending',
},
@@ -424,7 +470,7 @@ describe('GroupMember', () => {
}
await expect(
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -440,7 +486,7 @@ describe('GroupMember', () => {
}
const expected = {
data: {
- EnterGroup: {
+ JoinGroup: {
id: 'owner-of-closed-group',
myRoleInGroup: 'owner',
},
@@ -449,7 +495,7 @@ describe('GroupMember', () => {
}
await expect(
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -467,7 +513,7 @@ describe('GroupMember', () => {
}
const expected = {
data: {
- EnterGroup: {
+ JoinGroup: {
id: 'owner-of-closed-group',
myRoleInGroup: 'pending',
},
@@ -476,7 +522,7 @@ describe('GroupMember', () => {
}
await expect(
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -492,7 +538,7 @@ describe('GroupMember', () => {
}
const expected = {
data: {
- EnterGroup: {
+ JoinGroup: {
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
},
@@ -501,7 +547,7 @@ describe('GroupMember', () => {
}
await expect(
mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -512,135 +558,35 @@ describe('GroupMember', () => {
})
})
-describe('CreateGroup', () => {
- beforeEach(() => {
- variables = {
- ...variables,
- id: 'g589',
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- }
- })
-
+describe('GroupMember', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
- const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ variables = {
+ id: 'not-existing-group',
+ }
+ const { errors } = await query({ query: groupMemberQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
describe('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
+ let otherUser
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
- it('creates a group', async () => {
- const expected = {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
- },
+ beforeEach(async () => {
+ // create users
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
},
- errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
- })
-
- it('assigns the authenticated user as owner', async () => {
- const expected = {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- myRole: 'owner',
- },
+ {
+ email: 'test2@example.org',
+ password: '1234',
},
- errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
)
- })
-
- it('has "disabled" and "deleted" default to "false"', async () => {
- const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
- })
-
- describe('description', () => {
- describe('length without HTML', () => {
- describe('less then 100 chars', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: {
- ...variables,
- description:
- '0123456789' +
- '0123456789 ',
- },
- })
- expect(errors[0]).toHaveProperty('message', 'Description too short!')
- })
- })
- })
- })
-
- describe('categories', () => {
- beforeEach(() => {
- CONFIG.CATEGORIES_ACTIVE = true
- })
-
- describe('not even one', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: null },
- })
- expect(errors[0]).toHaveProperty('message', 'Too view categories!')
- })
- })
-
- describe('four', () => {
- it('throws error: "Too many categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
- })
- expect(errors[0]).toHaveProperty('message', 'Too many categories!')
- })
- })
- })
- })
-})
-
-describe('EnterGroup', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- userId: 'current-user',
- }
- const { errors } = await mutate({ mutation: enterGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('authenticated', () => {
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
-
- beforeEach(async () => {
ownerOfClosedGroupUser = await Factory.build(
'user',
{
@@ -663,6 +609,7 @@ describe('EnterGroup', () => {
password: '1234',
},
)
+ // create groups
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
mutation: createGroupMutation,
@@ -702,159 +649,338 @@ describe('EnterGroup', () => {
categoryIds,
},
})
+ // create additional memberships
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'current-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
})
describe('public group', () => {
- describe('entered by "owner-of-closed-group"', () => {
- it('has "usual" as membership role', async () => {
- variables = {
- id: 'public-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
- data: {
- EnterGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
+ beforeEach(async () => {
+ variables = {
+ id: 'public-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
},
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: enterGroupMutation,
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
})
- })
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
- variables = {
- id: 'public-group',
- userId: 'current-user',
+ describe('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
}
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('finds all members', async () => {
const expected = {
data: {
- EnterGroup: {
- id: 'current-user',
- myRoleInGroup: 'owner',
- },
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
},
errors: undefined,
}
- await expect(
- mutate({
- mutation: enterGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
})
})
})
describe('closed group', () => {
- describe('entered by "current-user"', () => {
- it('has "pending" as membership role', async () => {
- variables = {
- id: 'closed-group',
- userId: 'current-user',
- }
- const expected = {
- data: {
- EnterGroup: {
- id: 'current-user',
- myRoleInGroup: 'pending',
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
},
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: enterGroupMutation,
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(2)
+ })
})
- })
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
- variables = {
- id: 'closed-group',
- userId: 'owner-of-closed-group',
- }
+ // needs 'SwitchGroupMemberRole'
+ describe.skip('by usual member "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
const expected = {
data: {
- EnterGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
- },
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
},
errors: undefined,
}
- await expect(
- mutate({
- mutation: enterGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+
+ describe('by pending member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
})
describe('hidden group', () => {
- describe('entered by "owner-of-closed-group"', () => {
- it('has "pending" as membership role', async () => {
- variables = {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
- data: {
- EnterGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'pending',
+ beforeEach(async () => {
+ variables = {
+ id: 'hidden-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
},
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: enterGroupMutation,
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(2)
+ })
})
- })
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
- variables = {
- id: 'hidden-group',
- userId: 'owner-of-hidden-group',
- }
+ // needs 'SwitchGroupMemberRole'
+ describe.skip('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
const expected = {
data: {
- EnterGroup: {
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- },
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
},
errors: undefined,
}
- await expect(
- mutate({
- mutation: enterGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+
+ describe('by pending member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index cf44894dbb..fad1b7e582 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -114,7 +114,7 @@ type Mutation {
DeleteGroup(id: ID!): Group
- EnterGroup(
+ JoinGroup(
id: ID!
userId: ID!
): User
From 695a71bf2574c02ed0c8a5ed78503b375318eb64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Sat, 20 Aug 2022 09:43:37 +0200
Subject: [PATCH 048/374] Implement 'SwitchGroupMemberRole' resolver and write
the beginning to test it
---
backend/src/db/graphql/groups.js | 11 +
.../src/middleware/permissionsMiddleware.js | 85 +++-
backend/src/schema/resolvers/groups.js | 27 ++
backend/src/schema/resolvers/groups.spec.js | 438 +++++++++++++++---
backend/src/schema/types/type/Group.gql | 6 +
5 files changed, 507 insertions(+), 60 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 8486288ec5..41780f7cdb 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -50,6 +50,17 @@ export const joinGroupMutation = gql`
}
`
+export const switchGroupMemberRoleMutation = gql`
+ mutation ($id: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
+ SwitchGroupMemberRole(id: $id, userId: $userId, roleInGroup: $roleInGroup) {
+ id
+ name
+ slug
+ myRoleInGroup
+ }
+ }
+`
+
// ------ queries
export const groupQuery = gql`
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index afdc5501e7..11ca956b6a 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -79,10 +79,82 @@ const isAllowedSeeingMembersOfGroup = rule({
// Wolle: console.log('member: ', member)
// console.log('group: ', group)
return (
- group.groupType === 'public' ||
- (['closed', 'hidden'].includes(group.groupType) &&
- !!member &&
- ['usual', 'admin', 'owner'].includes(member.myRoleInGroup))
+ !!group &&
+ (group.groupType === 'public' ||
+ (['closed', 'hidden'].includes(group.groupType) &&
+ !!member &&
+ ['usual', 'admin', 'owner'].includes(member.myRoleInGroup)))
+ )
+ } catch (error) {
+ // Wolle: console.log('error: ', error)
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+})
+
+const isAllowedToSwitchGroupMemberRole = rule({
+ cache: 'no_cache',
+})(async (_parent, args, { user, driver }) => {
+ if (!user) return false
+ const adminId = user.id
+ const { id: groupId, userId, roleInGroup } = args
+ // Wolle:
+ // console.log('adminId: ', adminId)
+ // console.log('groupId: ', groupId)
+ // console.log('userId: ', userId)
+ // console.log('roleInGroup: ', roleInGroup)
+ const session = driver.session()
+ const readTxPromise = session.readTransaction(async (transaction) => {
+ const transactionResponse = await transaction.run(
+ `
+ MATCH (admin:User {id: $adminId})-[adminMembership:MEMBER_OF]->(group:Group {id: $groupId})<-[userMembership:MEMBER_OF]-(member:User {id: $userId})
+ RETURN group {.*}, admin {.*, myRoleInGroup: adminMembership.role}, member {.*, myRoleInGroup: userMembership.role}
+ `,
+ { groupId, adminId, userId },
+ )
+ // Wolle:
+ // console.log(
+ // 'transactionResponse: ',
+ // transactionResponse,
+ // )
+ // console.log(
+ // 'transaction admins: ',
+ // transactionResponse.records.map((record) => record.get('admin')),
+ // )
+ // console.log(
+ // 'transaction groups: ',
+ // transactionResponse.records.map((record) => record.get('group')),
+ // )
+ // console.log(
+ // 'transaction members: ',
+ // transactionResponse.records.map((record) => record.get('member')),
+ // )
+ return {
+ admin: transactionResponse.records.map((record) => record.get('admin'))[0],
+ group: transactionResponse.records.map((record) => record.get('group'))[0],
+ member: transactionResponse.records.map((record) => record.get('member'))[0],
+ }
+ })
+ try {
+ // Wolle:
+ // console.log('enter try !!!')
+ const { admin, group, member } = await readTxPromise
+ // Wolle:
+ // console.log('after !!!')
+ // console.log('admin: ', admin)
+ // console.log('group: ', group)
+ // console.log('member: ', member)
+ return (
+ !!group &&
+ !!admin &&
+ !!member &&
+ adminId !== userId &&
+ ((['admin'].includes(admin.myRoleInGroup) &&
+ !['owner'].includes(member.myRoleInGroup) &&
+ ['pending', 'usual', 'admin'].includes(roleInGroup)) ||
+ (['owner'].includes(admin.myRoleInGroup) &&
+ ['pending', 'usual', 'admin', 'owner'].includes(roleInGroup)))
)
} catch (error) {
// Wolle: console.log('error: ', error)
@@ -118,7 +190,7 @@ const isAuthor = rule({
const isDeletingOwnAccount = rule({
cache: 'no_cache',
-})(async (parent, args, context, info) => {
+})(async (parent, args, context, _info) => {
return context.user.id === args.id
})
@@ -183,7 +255,8 @@ export default shield(
SignupVerification: allow,
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
- JoinGroup: isAuthenticated,
+ JoinGroup: isAuthenticated, // Wolle: can not be correct
+ SwitchGroupMemberRole: isAllowedToSwitchGroupMemberRole,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index c9a31fdc36..34959908d6 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -160,6 +160,33 @@ export default {
session.close()
}
},
+ SwitchGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
+ const { id: groupId, userId, roleInGroup } = params
+ // Wolle
+ // console.log('groupId: ', groupId)
+ // console.log('userId: ', groupId)
+ // console.log('roleInGroup: ', groupId)
+ const session = context.driver.session()
+ const writeTxResultPromise = session.writeTransaction(async (transaction) => {
+ const joinGroupCypher = `
+ MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId})
+ SET
+ membership.updatedAt = toString(datetime()),
+ membership.role = $roleInGroup
+ RETURN member {.*, myRoleInGroup: membership.role}
+ `
+ const result = await transaction.run(joinGroupCypher, { groupId, userId, roleInGroup })
+ const [member] = await result.records.map((record) => record.get('member'))
+ return member
+ })
+ try {
+ return await writeTxResultPromise
+ } catch (error) {
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+ },
},
Group: {
...Resolver('Group', {
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 87eb02dc0a..e17e4827ac 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -3,6 +3,7 @@ import Factory, { cleanDatabase } from '../../db/factories'
import {
createGroupMutation,
joinGroupMutation,
+ switchGroupMemberRoleMutation,
groupMemberQuery,
groupQuery,
} from '../../db/graphql/groups'
@@ -13,8 +14,8 @@ import CONFIG from '../../config'
const driver = getDriver()
const neode = getNeode()
-let query
-let mutate
+let isCleanDbAfterEach = true
+let isSeedDb = true
let authenticatedUser
let user
@@ -23,20 +24,20 @@ const descriptionAdditional100 =
' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789'
let variables = {}
+const { server } = createServer({
+ context: () => {
+ return {
+ driver,
+ neode,
+ user: authenticatedUser,
+ }
+ },
+})
+const { query } = createTestClient(server)
+const { mutate } = createTestClient(server)
+
beforeAll(async () => {
await cleanDatabase()
-
- const { server } = createServer({
- context: () => {
- return {
- driver,
- neode,
- user: authenticatedUser,
- }
- },
- })
- query = createTestClient(server).query
- mutate = createTestClient(server).mutate
})
afterAll(async () => {
@@ -44,50 +45,55 @@ afterAll(async () => {
})
beforeEach(async () => {
- variables = {}
- user = await Factory.build(
- 'user',
- {
- id: 'current-user',
- name: 'TestUser',
- },
- {
- email: 'test@example.org',
- password: '1234',
- },
- )
- await Promise.all([
- neode.create('Category', {
- id: 'cat9',
- name: 'Democracy & Politics',
- slug: 'democracy-politics',
- icon: 'university',
- }),
- neode.create('Category', {
- id: 'cat4',
- name: 'Environment & Nature',
- slug: 'environment-nature',
- icon: 'tree',
- }),
- neode.create('Category', {
- id: 'cat15',
- name: 'Consumption & Sustainability',
- slug: 'consumption-sustainability',
- icon: 'shopping-cart',
- }),
- neode.create('Category', {
- id: 'cat27',
- name: 'Animal Protection',
- slug: 'animal-protection',
- icon: 'paw',
- }),
- ])
- authenticatedUser = null
+ // Wolle: find a better solution
+ if (isSeedDb) {
+ variables = {}
+ user = await Factory.build(
+ 'user',
+ {
+ id: 'current-user',
+ name: 'TestUser',
+ },
+ {
+ email: 'test@example.org',
+ password: '1234',
+ },
+ )
+ await Promise.all([
+ neode.create('Category', {
+ id: 'cat9',
+ name: 'Democracy & Politics',
+ slug: 'democracy-politics',
+ icon: 'university',
+ }),
+ neode.create('Category', {
+ id: 'cat4',
+ name: 'Environment & Nature',
+ slug: 'environment-nature',
+ icon: 'tree',
+ }),
+ neode.create('Category', {
+ id: 'cat15',
+ name: 'Consumption & Sustainability',
+ slug: 'consumption-sustainability',
+ icon: 'shopping-cart',
+ }),
+ neode.create('Category', {
+ id: 'cat27',
+ name: 'Animal Protection',
+ slug: 'animal-protection',
+ icon: 'paw',
+ }),
+ ])
+ authenticatedUser = null
+ }
})
// 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
afterEach(async () => {
- await cleanDatabase()
+ if (isCleanDbAfterEach) {
+ await cleanDatabase()
+ }
})
describe('CreateGroup', () => {
@@ -558,6 +564,330 @@ describe('JoinGroup', () => {
})
})
+describe('SwitchGroupMemberRole', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ userId: 'current-user',
+ roleInGroup: 'pending',
+ }
+ const { errors } = await mutate({ mutation: switchGroupMemberRoleMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ describe('in building up mode', () => {
+ let usualMemberUser
+ let adminMemberUser
+ let ownerMemberUser
+ // let secondOwnerMemberUser
+
+ beforeEach(async () => {
+ // Wolle: change this to beforeAll?
+ if (isSeedDb) {
+ // create users
+ usualMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
+ },
+ {
+ email: 'usual-member-user@example.org',
+ password: '1234',
+ },
+ )
+ adminMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'admin-member-user',
+ name: 'Admin Member TestUser',
+ },
+ {
+ email: 'admin-member-user@example.org',
+ password: '1234',
+ },
+ )
+ ownerMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-member-user',
+ name: 'Owner Member TestUser',
+ },
+ {
+ email: 'owner-member-user@example.org',
+ password: '1234',
+ },
+ )
+ // secondOwnerMemberUser =
+ await Factory.build(
+ 'user',
+ {
+ id: 'second-owner-member-user',
+ name: 'Second Owner Member TestUser',
+ },
+ {
+ email: 'second-owner-member-user@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ authenticatedUser = await usualMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await ownerMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await adminMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ // create additional memberships
+ // public-group
+ authenticatedUser = await usualMemberUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // closed-group
+ authenticatedUser = await ownerMemberUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'usual-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'admin-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'second-owner-member-user',
+ },
+ })
+ // hidden-group
+ authenticatedUser = await adminMemberUser.toJson()
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'admin-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'second-owner-member-user',
+ },
+ })
+ // Wolle
+ // function sleep(ms) {
+ // return new Promise(resolve => setTimeout(resolve, ms));
+ // }
+ // await sleep(4 * 1000)
+ isCleanDbAfterEach = false
+ isSeedDb = false
+ }
+ })
+ afterAll(async () => {
+ // Wolle: find a better solution
+ await cleanDatabase()
+ isCleanDbAfterEach = true
+ isSeedDb = true
+ })
+
+ describe('in all group types – here "closed-group" for example', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
+ })
+
+ describe('switch role', () => {
+ describe('of owner member "owner-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'owner-member-user',
+ }
+ })
+
+ describe('by owner themself "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+
+ describe('of prospective admin member "admin-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
+ })
+
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMemberQuery,
+ // variables: {
+ // id: 'closed-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by still pending member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
+
+ describe('degrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+ })
+ })
+ })
+ })
+})
+
describe('GroupMember', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index fad1b7e582..270f5c8448 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -118,4 +118,10 @@ type Mutation {
id: ID!
userId: ID!
): User
+
+ SwitchGroupMemberRole(
+ id: ID!
+ userId: ID!
+ roleInGroup: GroupMemberRole!
+ ): User
}
From 0fe609e29452a5f8e3634e3f8e08dbc0d9c6f8fa Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 07:19:07 +0200
Subject: [PATCH 049/374] Add test for find all member of closed and hidden
groups if you are usual member
---
.../src/middleware/permissionsMiddleware.js | 2 +
backend/src/schema/resolvers/groups.spec.js | 79 ++++++++++++++-----
2 files changed, 63 insertions(+), 18 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 11ca956b6a..728385902a 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -55,6 +55,7 @@ const isMySocialMedia = rule({
const isAllowedSeeingMembersOfGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
+ // Wolle: may have a look to 'isAuthenticated'
if (!user) return false
const { id: groupId } = args
// Wolle: console.log('groupId: ', groupId)
@@ -96,6 +97,7 @@ const isAllowedSeeingMembersOfGroup = rule({
const isAllowedToSwitchGroupMemberRole = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
+ // Wolle: may have a look to 'isAuthenticated'
if (!user) return false
const adminId = user.id
const { id: groupId, userId, roleInGroup } = args
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index e17e4827ac..1721e3e00e 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -940,6 +940,19 @@ describe('GroupMember', () => {
},
)
// create groups
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
mutation: createGroupMutation,
@@ -966,38 +979,42 @@ describe('GroupMember', () => {
categoryIds,
},
})
- authenticatedUser = await user.toJson()
+ // create additional memberships
+ // public-group
await mutate({
- mutation: createGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
+ userId: 'owner-of-closed-group',
},
})
- // create additional memberships
await mutate({
mutation: joinGroupMutation,
variables: {
id: 'public-group',
- userId: 'owner-of-closed-group',
+ userId: 'owner-of-hidden-group',
},
})
+ // closed-group
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
- userId: 'owner-of-hidden-group',
+ id: 'closed-group',
+ userId: 'current-user',
},
})
await mutate({
mutation: joinGroupMutation,
variables: {
id: 'closed-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // hidden-group
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
userId: 'current-user',
},
})
@@ -1008,6 +1025,8 @@ describe('GroupMember', () => {
userId: 'owner-of-closed-group',
},
})
+
+ authenticatedUser = await user.toJson()
})
describe('public group', () => {
@@ -1147,6 +1166,10 @@ describe('GroupMember', () => {
id: 'owner-of-closed-group',
myRoleInGroup: 'owner',
}),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'pending',
+ }),
]),
},
errors: undefined,
@@ -1156,13 +1179,21 @@ describe('GroupMember', () => {
variables,
})
expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(2)
+ expect(result.data.GroupMember.length).toBe(3)
})
})
- // needs 'SwitchGroupMemberRole'
- describe.skip('by usual member "owner-of-hidden-group"', () => {
+ describe('by usual member "owner-of-hidden-group"', () => {
beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'owner-of-hidden-group',
+ roleInGroup: 'usual',
+ },
+ })
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
})
@@ -1236,6 +1267,10 @@ describe('GroupMember', () => {
const expected = {
data: {
GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
expect.objectContaining({
id: 'owner-of-closed-group',
myRoleInGroup: 'pending',
@@ -1253,13 +1288,21 @@ describe('GroupMember', () => {
variables,
})
expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(2)
+ expect(result.data.GroupMember.length).toBe(3)
})
})
- // needs 'SwitchGroupMemberRole'
- describe.skip('by usual member "owner-of-closed-group"', () => {
+ describe('by usual member "owner-of-closed-group"', () => {
beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ roleInGroup: 'usual',
+ },
+ })
authenticatedUser = await ownerOfClosedGroupUser.toJson()
})
From 74589d4d3f0a71c50a35ed285429803e614626f0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 07:27:46 +0200
Subject: [PATCH 050/374] Resort creation of groups and its connected joins of
the groups
---
backend/src/schema/resolvers/groups.spec.js | 113 ++++++++++----------
1 file changed, 56 insertions(+), 57 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 1721e3e00e..ea3904d0ac 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -343,6 +343,7 @@ describe('JoinGroup', () => {
let ownerOfHiddenGroupUser
beforeEach(async () => {
+ // create users
ownerOfClosedGroupUser = await Factory.build(
'user',
{
@@ -365,6 +366,8 @@ describe('JoinGroup', () => {
password: '1234',
},
)
+ // create groups
+ // public-group
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
mutation: createGroupMutation,
@@ -634,6 +637,7 @@ describe('SwitchGroupMemberRole', () => {
},
)
// create groups
+ // public-group
authenticatedUser = await usualMemberUser.toJson()
await mutate({
mutation: createGroupMutation,
@@ -647,35 +651,6 @@ describe('SwitchGroupMemberRole', () => {
categoryIds,
},
})
- authenticatedUser = await ownerMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- authenticatedUser = await adminMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- // create additional memberships
- // public-group
- authenticatedUser = await usualMemberUser.toJson()
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -692,6 +667,18 @@ describe('SwitchGroupMemberRole', () => {
})
// closed-group
authenticatedUser = await ownerMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -715,6 +702,18 @@ describe('SwitchGroupMemberRole', () => {
})
// hidden-group
authenticatedUser = await adminMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -729,6 +728,7 @@ describe('SwitchGroupMemberRole', () => {
userId: 'second-owner-member-user',
},
})
+
// Wolle
// function sleep(ms) {
// return new Promise(resolve => setTimeout(resolve, ms));
@@ -940,6 +940,7 @@ describe('GroupMember', () => {
},
)
// create groups
+ // public-group
authenticatedUser = await user.toJson()
await mutate({
mutation: createGroupMutation,
@@ -953,34 +954,6 @@ describe('GroupMember', () => {
categoryIds,
},
})
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- // create additional memberships
- // public-group
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -996,6 +969,19 @@ describe('GroupMember', () => {
},
})
// closed-group
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -1011,6 +997,19 @@ describe('GroupMember', () => {
},
})
// hidden-group
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
From e91394948cda44717edeb1dac7e62fbedef391ec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 10:08:58 +0200
Subject: [PATCH 051/374] Add tests for 'SwitchGroupMemberRole' resolver
---
.../src/middleware/permissionsMiddleware.js | 1 +
backend/src/schema/resolvers/groups.spec.js | 769 +++++++++++++++++-
2 files changed, 737 insertions(+), 33 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 728385902a..e3e3b3eb06 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -152,6 +152,7 @@ const isAllowedToSwitchGroupMemberRole = rule({
!!admin &&
!!member &&
adminId !== userId &&
+ // Wolle: member.myRoleInGroup === roleInGroup &&
((['admin'].includes(admin.myRoleInGroup) &&
!['owner'].includes(member.myRoleInGroup) &&
['pending', 'usual', 'admin'].includes(roleInGroup)) ||
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index ea3904d0ac..a06191baa7 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -582,15 +582,27 @@ describe('SwitchGroupMemberRole', () => {
describe('authenticated', () => {
describe('in building up mode', () => {
+ let pendingMemberUser
let usualMemberUser
let adminMemberUser
let ownerMemberUser
- // let secondOwnerMemberUser
+ let secondOwnerMemberUser
beforeEach(async () => {
// Wolle: change this to beforeAll?
if (isSeedDb) {
// create users
+ pendingMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'pending-member-user',
+ name: 'Pending Member TestUser',
+ },
+ {
+ email: 'pending-member-user@example.org',
+ password: '1234',
+ },
+ )
usualMemberUser = await Factory.build(
'user',
{
@@ -624,8 +636,7 @@ describe('SwitchGroupMemberRole', () => {
password: '1234',
},
)
- // secondOwnerMemberUser =
- await Factory.build(
+ secondOwnerMemberUser = await Factory.build(
'user',
{
id: 'second-owner-member-user',
@@ -679,6 +690,13 @@ describe('SwitchGroupMemberRole', () => {
categoryIds,
},
})
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'pending-member-user',
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -752,8 +770,144 @@ describe('SwitchGroupMemberRole', () => {
}
})
+ describe('give the members their prospective roles', () => {
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('switch role of "usual-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'usual-member-user',
+ }
+ })
+
+ describe('to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('switch role of "admin-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMemberQuery,
+ // variables: {
+ // id: 'closed-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('switch role of "second-owner-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'second-owner-member-user',
+ }
+ })
+
+ describe('to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'second-owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMemberQuery,
+ // variables: {
+ // id: 'closed-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ })
+ })
+ })
+ })
+ })
+
describe('switch role', () => {
- describe('of owner member "owner-member-user"', () => {
+ describe('of owner "owner-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -783,19 +937,12 @@ describe('SwitchGroupMemberRole', () => {
})
})
})
- })
-
- describe('of prospective admin member "admin-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
- })
- describe('by owner "owner-member-user"', () => {
+ // Wolle: shall this be possible for now?
+ // or shall only an owner who gave the second owner the owner role downgrade themself?
+ describe('by second owner "second-owner-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ authenticatedUser = await secondOwnerMemberUser.toJson()
})
describe('to admin', () => {
@@ -807,20 +954,10 @@ describe('SwitchGroupMemberRole', () => {
})
it('has role admin', async () => {
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMemberQuery,
- // variables: {
- // id: 'closed-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
const expected = {
data: {
SwitchGroupMemberRole: {
- id: 'admin-member-user',
+ id: 'owner-member-user',
myRoleInGroup: 'admin',
},
},
@@ -834,18 +971,68 @@ describe('SwitchGroupMemberRole', () => {
).resolves.toMatchObject(expected)
})
})
+
+ describe('back to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by admin "admin-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await adminMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- describe('by still pending member "usual-member-user"', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
authenticatedUser = await usualMemberUser.toJson()
})
- describe('degrade to usual', () => {
+ describe('to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
}
})
@@ -859,16 +1046,532 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('by none member "current-user"', () => {
+ describe('by still pending member "pending-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ authenticatedUser = await pendingMemberUser.toJson()
})
- describe('degrade to pending again', () => {
+ describe('to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'pending',
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+
+ describe('of admin "admin-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
+ })
+
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('back to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
+
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
+
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+
+ describe('of usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'usual-member-user',
+ }
+ })
+
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('back to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
+
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
+
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+
+ describe('of still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'pending-member-user',
+ }
+ })
+
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('back to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('has role usual again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
+
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
+
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
}
})
From 29874e54a146e4174d17a09f6e2f4d451efe2954 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 10:40:16 +0200
Subject: [PATCH 052/374] Add jest dev helper function for sleeping
---
backend/src/helpers/jest.js | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/backend/src/helpers/jest.js b/backend/src/helpers/jest.js
index ecfc1a042a..bb0ab9d7c1 100644
--- a/backend/src/helpers/jest.js
+++ b/backend/src/helpers/jest.js
@@ -7,3 +7,11 @@
export function gql(strings) {
return strings.join('')
}
+
+// sometime we have to wait to check a db state by having a look into the db in a certain moment
+// or we wait a bit to check if we missed to set an await somewhere
+export function sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms))
+}
+// usage – 4 seconds for example
+// await sleep(4 * 1000)
From 41bf1f7d3977b40e9d5b33d7b72fa738ad43e2a2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 10:41:31 +0200
Subject: [PATCH 053/374] Refactor tests in 'groups.spec.js' to have modes
'clean db' and 'building up'
---
backend/src/schema/resolvers/groups.spec.js | 2872 +++++++++----------
1 file changed, 1427 insertions(+), 1445 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index a06191baa7..1a1386118f 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -14,8 +14,6 @@ import CONFIG from '../../config'
const driver = getDriver()
const neode = getNeode()
-let isCleanDbAfterEach = true
-let isSeedDb = true
let authenticatedUser
let user
@@ -36,6 +34,48 @@ const { server } = createServer({
const { query } = createTestClient(server)
const { mutate } = createTestClient(server)
+const seedBasicsAndClearAuthentication = async () => {
+ variables = {}
+ user = await Factory.build(
+ 'user',
+ {
+ id: 'current-user',
+ name: 'TestUser',
+ },
+ {
+ email: 'test@example.org',
+ password: '1234',
+ },
+ )
+ await Promise.all([
+ neode.create('Category', {
+ id: 'cat9',
+ name: 'Democracy & Politics',
+ slug: 'democracy-politics',
+ icon: 'university',
+ }),
+ neode.create('Category', {
+ id: 'cat4',
+ name: 'Environment & Nature',
+ slug: 'environment-nature',
+ icon: 'tree',
+ }),
+ neode.create('Category', {
+ id: 'cat15',
+ name: 'Consumption & Sustainability',
+ slug: 'consumption-sustainability',
+ icon: 'shopping-cart',
+ }),
+ neode.create('Category', {
+ id: 'cat27',
+ name: 'Animal Protection',
+ slug: 'animal-protection',
+ icon: 'paw',
+ }),
+ ])
+ authenticatedUser = null
+}
+
beforeAll(async () => {
await cleanDatabase()
})
@@ -44,408 +84,343 @@ afterAll(async () => {
await cleanDatabase()
})
-beforeEach(async () => {
- // Wolle: find a better solution
- if (isSeedDb) {
- variables = {}
- user = await Factory.build(
- 'user',
- {
- id: 'current-user',
- name: 'TestUser',
- },
- {
- email: 'test@example.org',
- password: '1234',
- },
- )
- await Promise.all([
- neode.create('Category', {
- id: 'cat9',
- name: 'Democracy & Politics',
- slug: 'democracy-politics',
- icon: 'university',
- }),
- neode.create('Category', {
- id: 'cat4',
- name: 'Environment & Nature',
- slug: 'environment-nature',
- icon: 'tree',
- }),
- neode.create('Category', {
- id: 'cat15',
- name: 'Consumption & Sustainability',
- slug: 'consumption-sustainability',
- icon: 'shopping-cart',
- }),
- neode.create('Category', {
- id: 'cat27',
- name: 'Animal Protection',
- slug: 'animal-protection',
- icon: 'paw',
- }),
- ])
- authenticatedUser = null
- }
-})
+describe('in mode: always clean db', () => {
+ beforeEach(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
-// 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
-afterEach(async () => {
- if (isCleanDbAfterEach) {
+ // 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
+ afterEach(async () => {
await cleanDatabase()
- }
-})
-
-describe('CreateGroup', () => {
- beforeEach(() => {
- variables = {
- ...variables,
- id: 'g589',
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- }
})
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await mutate({ mutation: createGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('CreateGroup', () => {
+ beforeEach(() => {
+ variables = {
+ ...variables,
+ id: 'g589',
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ }
})
- })
- describe('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- it('creates a group', async () => {
- const expected = {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('creates a group', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ },
},
- },
- errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
- })
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
- it('assigns the authenticated user as owner', async () => {
- const expected = {
- data: {
- CreateGroup: {
- name: 'The Best Group',
- myRole: 'owner',
+ it('assigns the authenticated user as owner', async () => {
+ const expected = {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ myRole: 'owner',
+ },
},
- },
- errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
- })
+ errors: undefined,
+ }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
- it('has "disabled" and "deleted" default to "false"', async () => {
- const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
- })
+ it('has "disabled" and "deleted" default to "false"', async () => {
+ const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ expected,
+ )
+ })
- describe('description', () => {
- describe('length without HTML', () => {
- describe('less then 100 chars', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: {
- ...variables,
- description:
- '0123456789' +
- '0123456789 ',
- },
+ describe('description', () => {
+ describe('length without HTML', () => {
+ describe('less then 100 chars', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ ...variables,
+ description:
+ '0123456789' +
+ '0123456789 ',
+ },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Description too short!')
})
- expect(errors[0]).toHaveProperty('message', 'Description too short!')
})
})
})
- })
- describe('categories', () => {
- beforeEach(() => {
- CONFIG.CATEGORIES_ACTIVE = true
- })
+ describe('categories', () => {
+ beforeEach(() => {
+ CONFIG.CATEGORIES_ACTIVE = true
+ })
- describe('not even one', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: null },
+ describe('not even one', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: null },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
- expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
- })
- describe('four', () => {
- it('throws error: "Too many categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
+ describe('four', () => {
+ it('throws error: "Too many categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too many categories!')
})
- expect(errors[0]).toHaveProperty('message', 'Too many categories!')
})
})
})
})
-})
-describe('Group', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('Group', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- })
- describe('authenticated', () => {
- let otherUser
+ describe('authenticated', () => {
+ let otherUser
- beforeEach(async () => {
- otherUser = await Factory.build(
- 'user',
- {
- id: 'other-user',
- name: 'Other TestUser',
- },
- {
- email: 'test2@example.org',
- password: '1234',
- },
- )
- authenticatedUser = await otherUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'others-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'global',
- categoryIds,
- },
- })
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'my-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
+ beforeEach(async () => {
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await otherUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'others-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'my-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
})
- })
- describe('query groups', () => {
- describe('without any filters', () => {
- it('finds all groups', async () => {
- const expected = {
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- }),
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
- }
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(expected)
+ describe('query groups', () => {
+ describe('without any filters', () => {
+ it('finds all groups', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(
+ expected,
+ )
+ })
})
- })
- describe('isMember = true', () => {
- it('finds only groups where user is member', async () => {
- const expected = {
- data: {
- Group: [
- {
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- },
- ],
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject(expected)
+ describe('isMember = true', () => {
+ it('finds only groups where user is member', async () => {
+ const expected = {
+ data: {
+ Group: [
+ {
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ },
+ ],
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject(expected)
+ })
})
- })
- describe('isMember = false', () => {
- it('finds only groups where user is not(!) member', async () => {
- const expected = {
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject(expected)
+ describe('isMember = false', () => {
+ it('finds only groups where user is not(!) member', async () => {
+ const expected = {
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject(expected)
+ })
})
})
})
})
-})
-
-describe('JoinGroup', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- userId: 'current-user',
- }
- const { errors } = await mutate({ mutation: joinGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- describe('authenticated', () => {
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
-
- beforeEach(async () => {
- // create users
- ownerOfClosedGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
- },
- {
- email: 'owner-of-closed-group@example.org',
- password: '1234',
- },
- )
- ownerOfHiddenGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
- },
- {
- email: 'owner-of-hidden-group@example.org',
- password: '1234',
- },
- )
- // create groups
- // public-group
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
+ describe('JoinGroup', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ userId: 'current-user',
+ }
+ const { errors } = await mutate({ mutation: joinGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('public group', () => {
- describe('entered by "owner-of-closed-group"', () => {
- it('has "usual" as membership role', async () => {
- variables = {
+ describe('authenticated', () => {
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
+
+ beforeEach(async () => {
+ // create users
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ // public-group
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
id: 'public-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
- data: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
})
})
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
+ describe('public group', () => {
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "usual" as membership role', async () => {
variables = {
id: 'public-group',
- userId: 'current-user',
+ userId: 'owner-of-closed-group',
}
const expected = {
data: {
JoinGroup: {
- id: 'current-user',
- myRoleInGroup: 'owner',
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
},
},
errors: undefined,
@@ -458,46 +433,46 @@ describe('JoinGroup', () => {
).resolves.toMatchObject(expected)
})
})
- })
- })
- describe('closed group', () => {
- describe('entered by "current-user"', () => {
- it('has "pending" as membership role', async () => {
- variables = {
- id: 'closed-group',
- userId: 'current-user',
- }
- const expected = {
- data: {
- JoinGroup: {
- id: 'current-user',
- myRoleInGroup: 'pending',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'public-group',
+ userId: 'current-user',
+ }
+ const expected = {
+ data: {
+ JoinGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
})
})
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
+ describe('closed group', () => {
+ describe('entered by "current-user"', () => {
+ it('has "pending" as membership role', async () => {
variables = {
id: 'closed-group',
- userId: 'owner-of-closed-group',
+ userId: 'current-user',
}
const expected = {
data: {
JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
+ id: 'current-user',
+ myRoleInGroup: 'pending',
},
},
errors: undefined,
@@ -510,46 +485,46 @@ describe('JoinGroup', () => {
).resolves.toMatchObject(expected)
})
})
- })
- })
- describe('hidden group', () => {
- describe('entered by "owner-of-closed-group"', () => {
- it('has "pending" as membership role', async () => {
- variables = {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
- data: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'pending',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'closed-group',
+ userId: 'owner-of-closed-group',
+ }
+ const expected = {
+ data: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
})
})
- describe('entered by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
+ describe('hidden group', () => {
+ describe('entered by "owner-of-closed-group"', () => {
+ it('has "pending" as membership role', async () => {
variables = {
id: 'hidden-group',
- userId: 'owner-of-hidden-group',
+ userId: 'owner-of-closed-group',
}
const expected = {
data: {
JoinGroup: {
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'pending',
},
},
errors: undefined,
@@ -562,568 +537,731 @@ describe('JoinGroup', () => {
).resolves.toMatchObject(expected)
})
})
+
+ describe('entered by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ variables = {
+ id: 'hidden-group',
+ userId: 'owner-of-hidden-group',
+ }
+ const expected = {
+ data: {
+ JoinGroup: {
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
})
})
})
-})
-describe('SwitchGroupMemberRole', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- userId: 'current-user',
- roleInGroup: 'pending',
- }
- const { errors } = await mutate({ mutation: switchGroupMemberRoleMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('GroupMember', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ }
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- })
- describe('authenticated', () => {
- describe('in building up mode', () => {
- let pendingMemberUser
- let usualMemberUser
- let adminMemberUser
- let ownerMemberUser
- let secondOwnerMemberUser
+ describe('authenticated', () => {
+ let otherUser
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
beforeEach(async () => {
- // Wolle: change this to beforeAll?
- if (isSeedDb) {
- // create users
- pendingMemberUser = await Factory.build(
- 'user',
- {
- id: 'pending-member-user',
- name: 'Pending Member TestUser',
- },
- {
- email: 'pending-member-user@example.org',
- password: '1234',
- },
- )
- usualMemberUser = await Factory.build(
- 'user',
- {
- id: 'usual-member-user',
- name: 'Usual Member TestUser',
- },
- {
- email: 'usual-member-user@example.org',
- password: '1234',
- },
- )
- adminMemberUser = await Factory.build(
- 'user',
- {
- id: 'admin-member-user',
- name: 'Admin Member TestUser',
- },
- {
- email: 'admin-member-user@example.org',
- password: '1234',
- },
- )
- ownerMemberUser = await Factory.build(
- 'user',
- {
- id: 'owner-member-user',
- name: 'Owner Member TestUser',
- },
- {
- email: 'owner-member-user@example.org',
- password: '1234',
- },
- )
- secondOwnerMemberUser = await Factory.build(
- 'user',
- {
- id: 'second-owner-member-user',
- name: 'Second Owner Member TestUser',
- },
- {
- email: 'second-owner-member-user@example.org',
- password: '1234',
- },
- )
- // create groups
- // public-group
- authenticatedUser = await usualMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-closed-group',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-hidden-group',
- },
- })
- // closed-group
- authenticatedUser = await ownerMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'pending-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'usual-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'admin-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'second-owner-member-user',
- },
- })
- // hidden-group
- authenticatedUser = await adminMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'hidden-group',
- userId: 'admin-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'hidden-group',
- userId: 'second-owner-member-user',
- },
- })
+ // create users
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ // public-group
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // closed-group
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'current-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // hidden-group
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'current-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
- // Wolle
- // function sleep(ms) {
- // return new Promise(resolve => setTimeout(resolve, ms));
- // }
- // await sleep(4 * 1000)
- isCleanDbAfterEach = false
- isSeedDb = false
- }
- })
- afterAll(async () => {
- // Wolle: find a better solution
- await cleanDatabase()
- isCleanDbAfterEach = true
- isSeedDb = true
+ authenticatedUser = await user.toJson()
})
- describe('in all group types – here "closed-group" for example', () => {
+ describe('public group', () => {
beforeEach(async () => {
variables = {
- id: 'closed-group',
+ id: 'public-group',
}
})
- describe('give the members their prospective roles', () => {
- describe('by owner "owner-member-user"', () => {
+ describe('query group members', () => {
+ describe('by owner "current-user"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ authenticatedUser = await user.toJson()
})
- describe('switch role of "usual-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'usual-member-user',
- }
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
- describe('to usual', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
- })
+ describe('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
- it('has role usual', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
}),
- ).resolves.toMatchObject(expected)
- })
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
+ })
- describe('switch role of "admin-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+ })
+ })
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
+ describe('closed group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
+ })
- it('has role admin', async () => {
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMemberQuery,
- // variables: {
- // id: 'closed-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
+ describe('query group members', () => {
+ describe('by owner "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
}),
- ).resolves.toMatchObject(expected)
- })
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'pending',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
+ })
- describe('switch role of "second-owner-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'second-owner-member-user',
- }
+ describe('by usual member "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'owner-of-hidden-group',
+ roleInGroup: 'usual',
+ },
})
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
- describe('to owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
-
- it('has role owner', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'second-owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
}),
- ).resolves.toMatchObject(expected)
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMemberQuery,
- // variables: {
- // id: 'closed-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
- })
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
})
- })
- describe('switch role', () => {
- describe('of owner "owner-member-user"', () => {
+ describe('by pending member "current-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'owner-member-user',
- }
+ authenticatedUser = await user.toJson()
})
- describe('by owner themself "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
- })
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
+ })
+ })
+ })
- // Wolle: shall this be possible for now?
- // or shall only an owner who gave the second owner the owner role downgrade themself?
- describe('by second owner "second-owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await secondOwnerMemberUser.toJson()
- })
+ describe('hidden group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'hidden-group',
+ }
+ })
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
+ describe('query group members', () => {
+ describe('by owner "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
- it('has role admin', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'owner-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
}),
- ).resolves.toMatchObject(expected)
- })
- })
-
- describe('back to owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
-
- it('has role owner again', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'pending',
}),
- ).resolves.toMatchObject(expected)
- })
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
+ })
- describe('by admin "admin-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await adminMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
+ describe('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ roleInGroup: 'usual',
+ },
})
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
})
- describe('by usual member "usual-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
+ },
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
+ })
- describe('by still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
+ describe('by pending member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('of admin "admin-member-user"', () => {
+ describe('by none member "other-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
+ authenticatedUser = await otherUser.toJson()
})
- describe('by owner "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
- })
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+ })
+ })
+})
- describe('to owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
+describe('in mode: building up', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
- it('has role owner', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
- })
- })
+ afterAll(async () => {
+ await cleanDatabase()
+ })
- describe('back to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
+ describe('SwitchGroupMemberRole', () => {
+ let pendingMemberUser
+ let usualMemberUser
+ let adminMemberUser
+ let ownerMemberUser
+ let secondOwnerMemberUser
- it('has role admin again', async () => {
- const expected = {
- data: {
- SwitchGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'admin',
+ beforeAll(async () => {
+ // create users
+ pendingMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'pending-member-user',
+ name: 'Pending Member TestUser',
+ },
+ {
+ email: 'pending-member-user@example.org',
+ password: '1234',
+ },
+ )
+ usualMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
+ },
+ {
+ email: 'usual-member-user@example.org',
+ password: '1234',
+ },
+ )
+ adminMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'admin-member-user',
+ name: 'Admin Member TestUser',
+ },
+ {
+ email: 'admin-member-user@example.org',
+ password: '1234',
+ },
+ )
+ ownerMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-member-user',
+ name: 'Owner Member TestUser',
+ },
+ {
+ email: 'owner-member-user@example.org',
+ password: '1234',
+ },
+ )
+ secondOwnerMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'second-owner-member-user',
+ name: 'Second Owner Member TestUser',
+ },
+ {
+ email: 'second-owner-member-user@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ // public-group
+ authenticatedUser = await usualMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // closed-group
+ authenticatedUser = await ownerMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'pending-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'usual-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'admin-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'second-owner-member-user',
+ },
+ })
+ // hidden-group
+ authenticatedUser = await adminMemberUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'admin-member-user',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'second-owner-member-user',
+ },
+ })
+ })
+
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ userId: 'current-user',
+ roleInGroup: 'pending',
+ }
+ const { errors } = await mutate({ mutation: switchGroupMemberRoleMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ describe('in all group types – here "closed-group" for example', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
+ })
+
+ describe('give the members their prospective roles', () => {
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('switch role of "usual-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'usual-member-user',
+ }
+ })
+
+ describe('to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
},
},
errors: undefined,
@@ -1138,52 +1276,61 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('by usual member "usual-member-user"', () => {
+ describe('switch role of "admin-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
})
- describe('upgrade to owner', () => {
+ describe('to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'owner',
+ roleInGroup: 'admin',
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('degrade to usual', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
+ it('has role admin', async () => {
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMemberQuery,
+ // variables: {
+ // id: 'closed-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
}
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
})
})
})
- describe('by still pending member "pending-member-user"', () => {
+ describe('switch role of "second-owner-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'second-owner-member-user',
+ }
})
- describe('upgrade to owner', () => {
+ describe('to owner', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1191,25 +1338,52 @@ describe('SwitchGroupMemberRole', () => {
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('degrade to usual', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
+ it('has role owner', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'second-owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+ })
+ })
+
+ describe('switch role', () => {
+ describe('of owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'owner-member-user',
+ }
+ })
+
+ describe('by owner themself "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: switchGroupMemberRoleMutation,
variables,
})
@@ -1218,12 +1392,41 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('by none member "current-user"', () => {
+ // Wolle: shall this be possible for now?
+ // or shall only an owner who gave the second owner the owner role downgrade themself?
+ describe('by second owner "second-owner-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ authenticatedUser = await secondOwnerMemberUser.toJson()
})
- describe('upgrade to owner', () => {
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'owner-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+
+ describe('back to owner', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1231,6 +1434,39 @@ describe('SwitchGroupMemberRole', () => {
}
})
+ it('has role owner again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
+ })
+
+ describe('by admin "admin-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await adminMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
it('throws authorization error', async () => {
const { errors } = await mutate({
mutation: switchGroupMemberRoleMutation,
@@ -1239,12 +1475,41 @@ describe('SwitchGroupMemberRole', () => {
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
+ })
- describe('degrade to pending again', () => {
+ describe('by usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'pending',
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
}
})
@@ -1259,11 +1524,11 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('of usual member "usual-member-user"', () => {
+ describe('of admin "admin-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- userId: 'usual-member-user',
+ userId: 'admin-member-user',
}
})
@@ -1272,20 +1537,20 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await ownerMemberUser.toJson()
})
- describe('to admin', () => {
+ describe('to owner', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ roleInGroup: 'owner',
}
})
- it('has role admin', async () => {
+ it('has role owner', async () => {
const expected = {
data: {
SwitchGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'admin',
+ id: 'admin-member-user',
+ myRoleInGroup: 'owner',
},
},
errors: undefined,
@@ -1299,20 +1564,20 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('back to usual', () => {
+ describe('back to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
}
})
- it('has role usual again', async () => {
+ it('has role admin again', async () => {
const expected = {
data: {
SwitchGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'usual',
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
},
},
errors: undefined,
@@ -1332,11 +1597,11 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await usualMemberUser.toJson()
})
- describe('upgrade to admin', () => {
+ describe('upgrade to owner', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ roleInGroup: 'owner',
}
})
@@ -1349,11 +1614,11 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('degrade to pending', () => {
+ describe('degrade to usual', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'pending',
+ roleInGroup: 'usual',
}
})
@@ -1372,11 +1637,11 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await pendingMemberUser.toJson()
})
- describe('upgrade to admin', () => {
+ describe('upgrade to owner', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ roleInGroup: 'owner',
}
})
@@ -1389,11 +1654,11 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('degrade to pending', () => {
+ describe('degrade to usual', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'pending',
+ roleInGroup: 'usual',
}
})
@@ -1412,11 +1677,11 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await user.toJson()
})
- describe('upgrade to admin', () => {
+ describe('upgrade to owner', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ roleInGroup: 'owner',
}
})
@@ -1448,11 +1713,11 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('of still pending member "pending-member-user"', () => {
+ describe('of usual member "usual-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- userId: 'pending-member-user',
+ userId: 'usual-member-user',
}
})
@@ -1461,20 +1726,20 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await ownerMemberUser.toJson()
})
- describe('to usual', () => {
+ describe('to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
}
})
- it('has role usual', async () => {
+ it('has role admin', async () => {
const expected = {
data: {
SwitchGroupMemberRole: {
- id: 'pending-member-user',
- myRoleInGroup: 'usual',
+ id: 'usual-member-user',
+ myRoleInGroup: 'admin',
},
},
errors: undefined,
@@ -1488,11 +1753,11 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('back to pending', () => {
+ describe('back to usual', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'pending',
+ roleInGroup: 'usual',
}
})
@@ -1500,8 +1765,8 @@ describe('SwitchGroupMemberRole', () => {
const expected = {
data: {
SwitchGroupMemberRole: {
- id: 'pending-member-user',
- myRoleInGroup: 'pending',
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
},
},
errors: undefined,
@@ -1521,11 +1786,28 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await usualMemberUser.toJson()
})
- describe('upgrade to usual', () => {
+ describe('upgrade to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
}
})
@@ -1544,11 +1826,11 @@ describe('SwitchGroupMemberRole', () => {
authenticatedUser = await pendingMemberUser.toJson()
})
- describe('upgrade to usual', () => {
+ describe('upgrade to admin', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'admin',
}
})
@@ -1560,18 +1842,12 @@ describe('SwitchGroupMemberRole', () => {
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- })
-
- describe('by none member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
- describe('upgrade to usual', () => {
+ describe('degrade to pending', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ roleInGroup: 'pending',
}
})
@@ -1584,478 +1860,184 @@ describe('SwitchGroupMemberRole', () => {
})
})
})
- })
- })
- })
- })
- })
-})
-describe('GroupMember', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- }
- const { errors } = await query({ query: groupMemberQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('authenticated', () => {
- let otherUser
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
-
- beforeEach(async () => {
- // create users
- otherUser = await Factory.build(
- 'user',
- {
- id: 'other-user',
- name: 'Other TestUser',
- },
- {
- email: 'test2@example.org',
- password: '1234',
- },
- )
- ownerOfClosedGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
- },
- {
- email: 'owner-of-closed-group@example.org',
- password: '1234',
- },
- )
- ownerOfHiddenGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
- },
- {
- email: 'owner-of-hidden-group@example.org',
- password: '1234',
- },
- )
- // create groups
- // public-group
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-closed-group',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'public-group',
- userId: 'owner-of-hidden-group',
- },
- })
- // closed-group
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'current-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'owner-of-hidden-group',
- },
- })
- // hidden-group
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'hidden-group',
- userId: 'current-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- },
- })
-
- authenticatedUser = await user.toJson()
- })
-
- describe('public group', () => {
- beforeEach(async () => {
- variables = {
- id: 'public-group',
- }
- })
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
- describe('query group members', () => {
- describe('by owner "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
- })
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
- describe('by usual member "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
-
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
})
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
+ describe('of still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ userId: 'pending-member-user',
+ }
})
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
- })
- })
- describe('closed group', () => {
- beforeEach(async () => {
- variables = {
- id: 'closed-group',
- }
- })
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
- describe('query group members', () => {
- describe('by owner "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
+ describe('to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'pending',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
- })
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
+ it('has role usual', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
- describe('by usual member "owner-of-hidden-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables: {
- id: 'closed-group',
- userId: 'owner-of-hidden-group',
- roleInGroup: 'usual',
- },
- })
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- })
+ describe('back to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
+ it('has role usual again', async () => {
+ const expected = {
+ data: {
+ SwitchGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ }
+ await expect(
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject(expected)
+ })
+ })
})
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
- describe('by pending member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- })
-
- describe('hidden group', () => {
- beforeEach(async () => {
- variables = {
- id: 'hidden-group',
- }
- })
+ describe('by usual member "usual-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await usualMemberUser.toJson()
+ })
- describe('query group members', () => {
- describe('by owner "owner-of-hidden-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- })
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
- describe('by usual member "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables: {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- roleInGroup: 'usual',
- },
- })
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
- it('finds all members', async () => {
- const expected = {
- data: {
- GroupMember: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- }),
- ]),
- },
- errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
- })
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
- describe('by pending member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
+ describe('by none member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
- })
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
})
})
})
From f9c8c9b4be2165f1e1187d0b4b2f87e2a73424a9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 11:27:18 +0200
Subject: [PATCH 054/374] Add different group member roles to seeding
---
backend/src/db/seed.js | 113 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 112 insertions(+), 1 deletion(-)
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index f9b2d05da0..a2be78553c 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -5,7 +5,11 @@ import createServer from '../server'
import faker from '@faker-js/faker'
import Factory from '../db/factories'
import { getNeode, getDriver } from '../db/neo4j'
-import { createGroupMutation, joinGroupMutation } from './graphql/groups'
+import {
+ createGroupMutation,
+ joinGroupMutation,
+ switchGroupMemberRoleMutation,
+} from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
@@ -415,6 +419,46 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
userId: 'u3',
},
}),
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u4',
+ },
+ }),
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u6',
+ },
+ }),
+ ])
+ await Promise.all([
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u2',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u4',
+ roleInGroup: 'admin',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u3',
+ roleInGroup: 'owner',
+ },
+ }),
])
authenticatedUser = await jennyRostock.toJson()
@@ -440,6 +484,13 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
userId: 'u1',
},
}),
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'g1',
+ userId: 'u2',
+ },
+ }),
mutate({
mutation: joinGroupMutation,
variables: {
@@ -462,6 +513,40 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
])
+ await Promise.all([
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u1',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u2',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u5',
+ roleInGroup: 'admin',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u6',
+ roleInGroup: 'owner',
+ },
+ }),
+ ])
authenticatedUser = await bobDerBaumeister.toJson()
await Promise.all([
@@ -508,6 +593,32 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
])
+ await Promise.all([
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u4',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u5',
+ roleInGroup: 'usual',
+ },
+ }),
+ mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables: {
+ id: 'g0',
+ userId: 'u6',
+ roleInGroup: 'usual',
+ },
+ }),
+ ])
// Create Posts
From b3c179011a05d30fab24bd925c0f56c5796e0a0d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 11:45:51 +0200
Subject: [PATCH 055/374] Add URL comment for sleep
---
backend/src/helpers/jest.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/backend/src/helpers/jest.js b/backend/src/helpers/jest.js
index bb0ab9d7c1..e3f6a3c84a 100644
--- a/backend/src/helpers/jest.js
+++ b/backend/src/helpers/jest.js
@@ -10,6 +10,7 @@ export function gql(strings) {
// sometime we have to wait to check a db state by having a look into the db in a certain moment
// or we wait a bit to check if we missed to set an await somewhere
+// see: https://www.sitepoint.com/delay-sleep-pause-wait/
export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms))
}
From dee20c25a9c2a4765173ecba8336cca9603fa920 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 11:46:41 +0200
Subject: [PATCH 056/374] Add test todo for 'has "updatedAt" newer as
"createdAt"'
---
backend/src/schema/resolvers/groups.spec.js | 3 +++
1 file changed, 3 insertions(+)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 1a1386118f..22bac6da70 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -1273,6 +1273,9 @@ describe('in mode: building up', () => {
}),
).resolves.toMatchObject(expected)
})
+
+ // the GQL mutation needs this fields in the result for testing
+ it.todo('has "updatedAt" newer as "createdAt"')
})
})
From 91d797dcfa5817dbfa5eae6d2fb4228b4522b284 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 22 Aug 2022 12:14:54 +0200
Subject: [PATCH 057/374] Copy profile Vue pages for the group profile
---
webapp/pages/group/_id.spec.js | 33 ++
webapp/pages/group/_id.vue | 34 ++
webapp/pages/group/_id/_slug.spec.js | 95 ++++++
webapp/pages/group/_id/_slug.vue | 443 +++++++++++++++++++++++++++
4 files changed, 605 insertions(+)
create mode 100644 webapp/pages/group/_id.spec.js
create mode 100644 webapp/pages/group/_id.vue
create mode 100644 webapp/pages/group/_id/_slug.spec.js
create mode 100644 webapp/pages/group/_id/_slug.vue
diff --git a/webapp/pages/group/_id.spec.js b/webapp/pages/group/_id.spec.js
new file mode 100644
index 0000000000..aab216569c
--- /dev/null
+++ b/webapp/pages/group/_id.spec.js
@@ -0,0 +1,33 @@
+import { config, mount } from '@vue/test-utils'
+import _id from './_id.vue'
+
+const localVue = global.localVue
+
+config.stubs['nuxt-child'] = ' '
+
+describe('Profile _id.vue', () => {
+ let wrapper
+ let Wrapper
+ let mocks
+
+ beforeEach(() => {
+ mocks = {}
+ })
+
+ describe('mount', () => {
+ Wrapper = () => {
+ return mount(_id, {
+ mocks,
+ localVue,
+ })
+ }
+
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ it('renders', () => {
+ expect(wrapper.findAll('.nuxt-child')).toHaveLength(1)
+ })
+ })
+})
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
new file mode 100644
index 0000000000..b9bbef83e6
--- /dev/null
+++ b/webapp/pages/group/_id.vue
@@ -0,0 +1,34 @@
+
+
+
+
+
diff --git a/webapp/pages/group/_id/_slug.spec.js b/webapp/pages/group/_id/_slug.spec.js
new file mode 100644
index 0000000000..477174485f
--- /dev/null
+++ b/webapp/pages/group/_id/_slug.spec.js
@@ -0,0 +1,95 @@
+import { config, mount } from '@vue/test-utils'
+import ProfileSlug from './_slug.vue'
+
+const localVue = global.localVue
+
+localVue.filter('date', (d) => d)
+
+config.stubs['client-only'] = ' '
+config.stubs['v-popover'] = ' '
+config.stubs['nuxt-link'] = ' '
+config.stubs['infinite-loading'] = ' '
+config.stubs['follow-list'] = ' '
+
+describe('ProfileSlug', () => {
+ let wrapper
+ let Wrapper
+ let mocks
+
+ beforeEach(() => {
+ mocks = {
+ post: {
+ id: 'p23',
+ name: 'It is a post',
+ },
+ $t: jest.fn(),
+ // If you're mocking router, then don't use VueRouter with localVue: https://vue-test-utils.vuejs.org/guides/using-with-vue-router.html
+ $route: {
+ params: {
+ id: '4711',
+ slug: 'john-doe',
+ },
+ },
+ $router: {
+ history: {
+ push: jest.fn(),
+ },
+ },
+ $toast: {
+ success: jest.fn(),
+ error: jest.fn(),
+ },
+ $apollo: {
+ loading: false,
+ mutate: jest.fn().mockResolvedValue(),
+ },
+ }
+ })
+
+ describe('mount', () => {
+ Wrapper = () => {
+ return mount(ProfileSlug, {
+ mocks,
+ localVue,
+ })
+ }
+
+ describe('given an authenticated user', () => {
+ beforeEach(() => {
+ mocks.$filters = {
+ removeLinks: (c) => c,
+ truncate: (a) => a,
+ }
+ mocks.$store = {
+ getters: {
+ 'auth/isModerator': () => false,
+ 'auth/user': {
+ id: 'u23',
+ },
+ },
+ }
+ })
+
+ describe('given a user for the profile', () => {
+ beforeEach(() => {
+ wrapper = Wrapper()
+ wrapper.setData({
+ User: [
+ {
+ id: 'u3',
+ name: 'Bob the builder',
+ contributionsCount: 6,
+ shoutedCount: 7,
+ commentedCount: 8,
+ },
+ ],
+ })
+ })
+
+ it('displays name of the user', () => {
+ expect(wrapper.text()).toContain('Bob the builder')
+ })
+ })
+ })
+ })
+})
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
new file mode 100644
index 0000000000..4fef7d3aa8
--- /dev/null
+++ b/webapp/pages/group/_id/_slug.vue
@@ -0,0 +1,443 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ userName }}
+
+
+ {{ userSlug }}
+
+
+
+ {{ user.location.name }}
+
+
+ {{ $t('profile.memberSince') }} {{ user.createdAt | date('MMMM yyyy') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('settings.blocked-users.unblock') }}
+
+
+ {{ $t('settings.muted-users.unmute') }}
+
+
+
+
+
+
+ {{ user.about }}
+
+
+
+
+
+ {{ $t('profile.network.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
From e1f4609c25cdb48bea6053fbdfd43f1035827139 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 04:48:38 +0200
Subject: [PATCH 058/374] !!! Temp !!!
---
webapp/graphql/groups.js | 95 ++++++++++++++++++++++++++++++++++
webapp/locales/de.json | 19 +++----
webapp/locales/en.json | 19 +++----
webapp/pages/group/_id.spec.js | 2 +-
webapp/pages/group/_id.vue | 8 +--
5 files changed, 120 insertions(+), 23 deletions(-)
create mode 100644 webapp/graphql/groups.js
diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js
new file mode 100644
index 0000000000..2a611f3247
--- /dev/null
+++ b/webapp/graphql/groups.js
@@ -0,0 +1,95 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createGroupMutation = gql`
+ mutation (
+ $id: ID
+ $name: String!
+ $slug: String
+ $about: String
+ $description: String!
+ $groupType: GroupType!
+ $actionRadius: GroupActionRadius!
+ $categoryIds: [ID]
+ ) {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ }
+ }
+`
+
+// ------ queries
+
+export const groupQuery = gql`
+ query (
+ $isMember: Boolean
+ $id: ID
+ $name: String
+ $slug: String
+ $createdAt: String
+ $updatedAt: String
+ $about: String
+ $description: String
+ $locationName: String
+ $first: Int
+ $offset: Int
+ $orderBy: [_GroupOrdering]
+ $filter: _GroupFilter
+ ) {
+ Group(
+ isMember: $isMember
+ id: $id
+ name: $name
+ slug: $slug
+ createdAt: $createdAt
+ updatedAt: $updatedAt
+ about: $about
+ description: $description
+ locationName: $locationName
+ first: $first
+ offset: $offset
+ orderBy: $orderBy
+ filter: $filter
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ }
+ }
+`
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 297daa511b..07a71dfcf5 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -333,15 +333,16 @@
"placeholder": "Schreib etwas Inspirierendes …"
},
"error-pages": {
- "403-default": "Kein Zugang zu dieser Seite",
- "404-default": "Diese Seite konnte nicht gefunden werden",
- "500-default": "Internal Server Error",
- "503-default": "Dienst steht nicht zur Verfügung",
- "back-to-index": "Zurück zur Startseite",
- "cannot-edit-post": "Dieser Beitrag kann nicht editiert werden",
- "default": "Ein Fehler ist aufgetreten",
- "post-not-found": "Dieser Beitrag konnte nicht gefunden werden",
- "profile-not-found": "Dieses Profil konnte nicht gefunden werden"
+ "403-default": "Kein Zugang zu dieser Seite!",
+ "404-default": "Diese Seite konnte nicht gefunden werden!",
+ "500-default": "Internal Server Error!",
+ "503-default": "Dienst steht nicht zur Verfügung!",
+ "back-to-index": "Zurück zur Startseite!",
+ "cannot-edit-post": "Dieser Beitrag kann nicht editiert werden!",
+ "default": "Ein Fehler ist aufgetreten!",
+ "group-not-found": "Dieses Gruppenprofil konnte nicht gefunden werden!",
+ "post-not-found": "Dieser Beitrag konnte nicht gefunden werden!",
+ "profile-not-found": "Dieses Profil konnte nicht gefunden werden!"
},
"filter-menu": {
"all": "Alle",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 8499b0290c..1b16431e34 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -333,15 +333,16 @@
"placeholder": "Leave your inspirational thoughts …"
},
"error-pages": {
- "403-default": "Not authorized to this page",
- "404-default": "This page could not be found",
- "500-default": "Internal Server Error",
- "503-default": "Service Unavailable",
- "back-to-index": "Back to index page",
- "cannot-edit-post": "This post cannot be edited",
- "default": "An error occurred",
- "post-not-found": "This post could not be found",
- "profile-not-found": "This profile could not be found"
+ "403-default": "Not authorized to this page!",
+ "404-default": "This page could not be found!",
+ "500-default": "Internal Server Error!",
+ "503-default": "Service Unavailable!",
+ "back-to-index": "Back to index page!",
+ "cannot-edit-post": "This post cannot be edited!",
+ "default": "An error occurred!",
+ "group-not-found": "This group profile could not be found!",
+ "post-not-found": "This post could not be found!",
+ "profile-not-found": "This profile could not be found!"
},
"filter-menu": {
"all": "All",
diff --git a/webapp/pages/group/_id.spec.js b/webapp/pages/group/_id.spec.js
index aab216569c..bafd5c3925 100644
--- a/webapp/pages/group/_id.spec.js
+++ b/webapp/pages/group/_id.spec.js
@@ -5,7 +5,7 @@ const localVue = global.localVue
config.stubs['nuxt-child'] = ' '
-describe('Profile _id.vue', () => {
+describe('Group profile _id.vue', () => {
let wrapper
let Wrapper
let mocks
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
index b9bbef83e6..047d12d7f6 100644
--- a/webapp/pages/group/_id.vue
+++ b/webapp/pages/group/_id.vue
@@ -9,7 +9,7 @@ import PersistentLinks from '~/mixins/persistentLinks.js'
const options = {
queryId: gql`
query($idOrSlug: ID) {
- User(id: $idOrSlug) {
+ Group(id: $idOrSlug) {
id
slug
}
@@ -17,14 +17,14 @@ const options = {
`,
querySlug: gql`
query($idOrSlug: String) {
- User(slug: $idOrSlug) {
+ Group(slug: $idOrSlug) {
id
slug
}
}
`,
- message: 'error-pages.profile-not-found',
- path: 'profile',
+ message: 'error-pages.group-not-found',
+ path: 'group',
}
const persistentLinks = PersistentLinks(options)
From e3549d3aa99cefbb21b4e3e5d919613d9a7d56a4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 05:11:10 +0200
Subject: [PATCH 059/374] Rename resolver `SwitchGroupMemberRole` to
`ChangeGroupMemberRole`
Co-Authored-By: Mogge
---
backend/src/db/graphql/groups.js | 4 +-
backend/src/db/seed.js | 22 ++---
.../src/middleware/permissionsMiddleware.js | 2 +-
backend/src/schema/resolvers/groups.js | 2 +-
backend/src/schema/resolvers/groups.spec.js | 92 +++++++++----------
backend/src/schema/types/type/Group.gql | 2 +-
6 files changed, 62 insertions(+), 62 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 41780f7cdb..2a9647860b 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -50,9 +50,9 @@ export const joinGroupMutation = gql`
}
`
-export const switchGroupMemberRoleMutation = gql`
+export const changeGroupMemberRoleMutation = gql`
mutation ($id: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
- SwitchGroupMemberRole(id: $id, userId: $userId, roleInGroup: $roleInGroup) {
+ ChangeGroupMemberRole(id: $id, userId: $userId, roleInGroup: $roleInGroup) {
id
name
slug
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index a2be78553c..0010b09ef0 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -8,7 +8,7 @@ import { getNeode, getDriver } from '../db/neo4j'
import {
createGroupMutation,
joinGroupMutation,
- switchGroupMemberRoleMutation,
+ changeGroupMemberRoleMutation,
} from './graphql/groups'
import { createPostMutation } from './graphql/posts'
import { createCommentMutation } from './graphql/comments'
@@ -436,7 +436,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u2',
@@ -444,7 +444,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u4',
@@ -452,7 +452,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u3',
@@ -515,7 +515,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u1',
@@ -523,7 +523,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u2',
@@ -531,7 +531,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u5',
@@ -539,7 +539,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u6',
@@ -595,7 +595,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
])
await Promise.all([
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u4',
@@ -603,7 +603,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u5',
@@ -611,7 +611,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
},
}),
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'g0',
userId: 'u6',
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index e3e3b3eb06..a3935872e6 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -259,7 +259,7 @@ export default shield(
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
JoinGroup: isAuthenticated, // Wolle: can not be correct
- SwitchGroupMemberRole: isAllowedToSwitchGroupMemberRole,
+ ChangeGroupMemberRole: isAllowedToSwitchGroupMemberRole,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 34959908d6..ca24ef55fc 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -160,7 +160,7 @@ export default {
session.close()
}
},
- SwitchGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
+ ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
const { id: groupId, userId, roleInGroup } = params
// Wolle
// console.log('groupId: ', groupId)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 22bac6da70..23fc5646ea 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -3,7 +3,7 @@ import Factory, { cleanDatabase } from '../../db/factories'
import {
createGroupMutation,
joinGroupMutation,
- switchGroupMemberRoleMutation,
+ changeGroupMemberRoleMutation,
groupMemberQuery,
groupQuery,
} from '../../db/graphql/groups'
@@ -865,7 +865,7 @@ describe('in mode: always clean db', () => {
beforeEach(async () => {
authenticatedUser = await ownerOfClosedGroupUser.toJson()
await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'closed-group',
userId: 'owner-of-hidden-group',
@@ -974,7 +974,7 @@ describe('in mode: always clean db', () => {
beforeEach(async () => {
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'hidden-group',
userId: 'owner-of-closed-group',
@@ -1049,7 +1049,7 @@ describe('in mode: building up', () => {
await cleanDatabase()
})
- describe('SwitchGroupMemberRole', () => {
+ describe('ChangeGroupMemberRole', () => {
let pendingMemberUser
let usualMemberUser
let adminMemberUser
@@ -1221,7 +1221,7 @@ describe('in mode: building up', () => {
userId: 'current-user',
roleInGroup: 'pending',
}
- const { errors } = await mutate({ mutation: switchGroupMemberRoleMutation, variables })
+ const { errors } = await mutate({ mutation: changeGroupMemberRoleMutation, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -1259,7 +1259,7 @@ describe('in mode: building up', () => {
it('has role usual', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'usual',
},
@@ -1268,7 +1268,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1308,7 +1308,7 @@ describe('in mode: building up', () => {
// console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'admin-member-user',
myRoleInGroup: 'admin',
},
@@ -1317,7 +1317,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1344,7 +1344,7 @@ describe('in mode: building up', () => {
it('has role owner', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'second-owner-member-user',
myRoleInGroup: 'owner',
},
@@ -1353,7 +1353,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1387,7 +1387,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1413,7 +1413,7 @@ describe('in mode: building up', () => {
it('has role admin', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'owner-member-user',
myRoleInGroup: 'admin',
},
@@ -1422,7 +1422,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1440,7 +1440,7 @@ describe('in mode: building up', () => {
it('has role owner again', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'owner-member-user',
myRoleInGroup: 'owner',
},
@@ -1449,7 +1449,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1472,7 +1472,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1495,7 +1495,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1518,7 +1518,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1551,7 +1551,7 @@ describe('in mode: building up', () => {
it('has role owner', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'admin-member-user',
myRoleInGroup: 'owner',
},
@@ -1560,7 +1560,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1578,7 +1578,7 @@ describe('in mode: building up', () => {
it('has role admin again', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'admin-member-user',
myRoleInGroup: 'admin',
},
@@ -1587,7 +1587,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1610,7 +1610,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1627,7 +1627,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1650,7 +1650,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1667,7 +1667,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1690,7 +1690,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1707,7 +1707,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1740,7 +1740,7 @@ describe('in mode: building up', () => {
it('has role admin', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'admin',
},
@@ -1749,7 +1749,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1767,7 +1767,7 @@ describe('in mode: building up', () => {
it('has role usual again', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'usual-member-user',
myRoleInGroup: 'usual',
},
@@ -1776,7 +1776,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1799,7 +1799,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1816,7 +1816,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1839,7 +1839,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1856,7 +1856,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1879,7 +1879,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1896,7 +1896,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -1929,7 +1929,7 @@ describe('in mode: building up', () => {
it('has role usual', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'pending-member-user',
myRoleInGroup: 'usual',
},
@@ -1938,7 +1938,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1956,7 +1956,7 @@ describe('in mode: building up', () => {
it('has role usual again', async () => {
const expected = {
data: {
- SwitchGroupMemberRole: {
+ ChangeGroupMemberRole: {
id: 'pending-member-user',
myRoleInGroup: 'pending',
},
@@ -1965,7 +1965,7 @@ describe('in mode: building up', () => {
}
await expect(
mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
}),
).resolves.toMatchObject(expected)
@@ -1988,7 +1988,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -2011,7 +2011,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
@@ -2034,7 +2034,7 @@ describe('in mode: building up', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
+ mutation: changeGroupMemberRoleMutation,
variables,
})
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 270f5c8448..f12b98c079 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -119,7 +119,7 @@ type Mutation {
userId: ID!
): User
- SwitchGroupMemberRole(
+ ChangeGroupMemberRole(
id: ID!
userId: ID!
roleInGroup: GroupMemberRole!
From 3ab33a44f19d17940ec09f9d2a40a1d493100ce3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 05:21:53 +0200
Subject: [PATCH 060/374] Check permission not given for resolver
`ChangeGroupMemberRole` if admin will change their own member role in group
already at the beginning of 'isAllowedToChangeGroupMemberRole'
Co-Authored-By: Mogge
---
backend/src/middleware/permissionsMiddleware.js | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index a3935872e6..a92aacbba7 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -55,8 +55,7 @@ const isMySocialMedia = rule({
const isAllowedSeeingMembersOfGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
- // Wolle: may have a look to 'isAuthenticated'
- if (!user) return false
+ if (!(user && user.id)) return false
const { id: groupId } = args
// Wolle: console.log('groupId: ', groupId)
// console.log('user.id: ', user.id)
@@ -94,13 +93,13 @@ const isAllowedSeeingMembersOfGroup = rule({
}
})
-const isAllowedToSwitchGroupMemberRole = rule({
+const isAllowedToChangeGroupMemberRole = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
- // Wolle: may have a look to 'isAuthenticated'
- if (!user) return false
+ if (!(user && user.id)) return false
const adminId = user.id
const { id: groupId, userId, roleInGroup } = args
+ if (adminId === userId) return false
// Wolle:
// console.log('adminId: ', adminId)
// console.log('groupId: ', groupId)
@@ -151,7 +150,6 @@ const isAllowedToSwitchGroupMemberRole = rule({
!!group &&
!!admin &&
!!member &&
- adminId !== userId &&
// Wolle: member.myRoleInGroup === roleInGroup &&
((['admin'].includes(admin.myRoleInGroup) &&
!['owner'].includes(member.myRoleInGroup) &&
@@ -259,7 +257,7 @@ export default shield(
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
JoinGroup: isAuthenticated, // Wolle: can not be correct
- ChangeGroupMemberRole: isAllowedToSwitchGroupMemberRole,
+ ChangeGroupMemberRole: isAllowedToChangeGroupMemberRole,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
DeletePost: isAuthor,
From 95cebd577d9793a75aef18906516a0ab669c5998 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 10:18:00 +0200
Subject: [PATCH 061/374] Refactor to group joining by `ChangeGroupMemberRole`
and that `JoinGroup` is not possible for hidden groups
Co-Authored-By: Mogge
---
.../src/middleware/permissionsMiddleware.js | 68 +++-
backend/src/schema/resolvers/groups.js | 16 +-
backend/src/schema/resolvers/groups.spec.js | 290 ++++++++++--------
3 files changed, 238 insertions(+), 136 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index a92aacbba7..f0ddd04ca9 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -101,6 +101,7 @@ const isAllowedToChangeGroupMemberRole = rule({
const { id: groupId, userId, roleInGroup } = args
if (adminId === userId) return false
// Wolle:
+ // console.log('isAllowedToChangeGroupMemberRole !!!')
// console.log('adminId: ', adminId)
// console.log('groupId: ', groupId)
// console.log('userId: ', userId)
@@ -109,7 +110,8 @@ const isAllowedToChangeGroupMemberRole = rule({
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
`
- MATCH (admin:User {id: $adminId})-[adminMembership:MEMBER_OF]->(group:Group {id: $groupId})<-[userMembership:MEMBER_OF]-(member:User {id: $userId})
+ MATCH (admin:User {id: $adminId})-[adminMembership:MEMBER_OF]->(group:Group {id: $groupId})
+ OPTIONAL MATCH (group)<-[userMembership:MEMBER_OF]-(member:User {id: $userId})
RETURN group {.*}, admin {.*, myRoleInGroup: adminMembership.role}, member {.*, myRoleInGroup: userMembership.role}
`,
{ groupId, adminId, userId },
@@ -149,14 +151,70 @@ const isAllowedToChangeGroupMemberRole = rule({
return (
!!group &&
!!admin &&
- !!member &&
- // Wolle: member.myRoleInGroup === roleInGroup &&
+ (!member ||
+ (!!member &&
+ (member.myRoleInGroup === roleInGroup || !['owner'].includes(member.myRoleInGroup)))) &&
((['admin'].includes(admin.myRoleInGroup) &&
- !['owner'].includes(member.myRoleInGroup) &&
['pending', 'usual', 'admin'].includes(roleInGroup)) ||
(['owner'].includes(admin.myRoleInGroup) &&
['pending', 'usual', 'admin', 'owner'].includes(roleInGroup)))
)
+ } catch (error) {
+ // Wolle:
+ // console.log('error: ', error)
+ throw new Error(error)
+ } finally {
+ session.close()
+ }
+})
+
+const isAllowedToJoinGroup = rule({
+ cache: 'no_cache',
+})(async (_parent, args, { user, driver }) => {
+ if (!(user && user.id)) return false
+ const { id: groupId, userId } = args
+ // Wolle:
+ // console.log('adminId: ', adminId)
+ // console.log('groupId: ', groupId)
+ // console.log('userId: ', userId)
+ // console.log('roleInGroup: ', roleInGroup)
+ const session = driver.session()
+ const readTxPromise = session.readTransaction(async (transaction) => {
+ const transactionResponse = await transaction.run(
+ `
+ MATCH (group:Group {id: $groupId})
+ OPTIONAL MATCH (group)<-[membership:MEMBER_OF]-(member:User {id: $userId})
+ RETURN group {.*}, member {.*, myRoleInGroup: membership.role}
+ `,
+ { groupId, userId },
+ )
+ // Wolle:
+ // console.log(
+ // 'transactionResponse: ',
+ // transactionResponse,
+ // )
+ // console.log(
+ // 'transaction groups: ',
+ // transactionResponse.records.map((record) => record.get('group')),
+ // )
+ // console.log(
+ // 'transaction members: ',
+ // transactionResponse.records.map((record) => record.get('member')),
+ // )
+ return {
+ group: transactionResponse.records.map((record) => record.get('group'))[0],
+ member: transactionResponse.records.map((record) => record.get('member'))[0],
+ }
+ })
+ try {
+ // Wolle:
+ // console.log('enter try !!!')
+ const { group, member } = await readTxPromise
+ // Wolle:
+ // console.log('after !!!')
+ // console.log('group: ', group)
+ // console.log('member: ', member)
+ return !!group && (group.groupType !== 'hidden' || (!!member && !!member.myRoleInGroup))
} catch (error) {
// Wolle: console.log('error: ', error)
throw new Error(error)
@@ -256,7 +314,7 @@ export default shield(
SignupVerification: allow,
UpdateUser: onlyYourself,
CreateGroup: isAuthenticated,
- JoinGroup: isAuthenticated, // Wolle: can not be correct
+ JoinGroup: isAllowedToJoinGroup,
ChangeGroupMemberRole: isAllowedToChangeGroupMemberRole,
CreatePost: isAuthenticated,
UpdatePost: isAuthor,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index ca24ef55fc..89c136dc52 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -163,20 +163,28 @@ export default {
ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
const { id: groupId, userId, roleInGroup } = params
// Wolle
+ // console.log('ChangeGroupMemberRole !!!')
// console.log('groupId: ', groupId)
- // console.log('userId: ', groupId)
- // console.log('roleInGroup: ', groupId)
+ // console.log('userId: ', userId)
+ // console.log('roleInGroup: ', roleInGroup)
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const joinGroupCypher = `
- MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId})
- SET
+ MATCH (member:User {id: $userId}), (group:Group {id: $groupId})
+ MERGE (member)-[membership:MEMBER_OF]->(group)
+ ON CREATE SET
+ membership.createdAt = toString(datetime()),
+ membership.updatedAt = membership.createdAt,
+ membership.role = $roleInGroup
+ ON MATCH SET
membership.updatedAt = toString(datetime()),
membership.role = $roleInGroup
RETURN member {.*, myRoleInGroup: membership.role}
`
const result = await transaction.run(joinGroupCypher, { groupId, userId, roleInGroup })
const [member] = await result.records.map((record) => record.get('member'))
+ // Wolle
+ // console.log('member: ', member)
return member
})
try {
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 23fc5646ea..d9c2f22e39 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -410,7 +410,7 @@ describe('in mode: always clean db', () => {
})
describe('public group', () => {
- describe('entered by "owner-of-closed-group"', () => {
+ describe('joined by "owner-of-closed-group"', () => {
it('has "usual" as membership role', async () => {
variables = {
id: 'public-group',
@@ -434,7 +434,7 @@ describe('in mode: always clean db', () => {
})
})
- describe('entered by its owner', () => {
+ describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
variables = {
@@ -462,7 +462,7 @@ describe('in mode: always clean db', () => {
})
describe('closed group', () => {
- describe('entered by "current-user"', () => {
+ describe('joined by "current-user"', () => {
it('has "pending" as membership role', async () => {
variables = {
id: 'closed-group',
@@ -486,7 +486,7 @@ describe('in mode: always clean db', () => {
})
})
- describe('entered by its owner', () => {
+ describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
variables = {
@@ -514,31 +514,18 @@ describe('in mode: always clean db', () => {
})
describe('hidden group', () => {
- describe('entered by "owner-of-closed-group"', () => {
- it('has "pending" as membership role', async () => {
+ describe('joined by "owner-of-closed-group"', () => {
+ it('throws authorization error', async () => {
variables = {
id: 'hidden-group',
userId: 'owner-of-closed-group',
}
- const expected = {
- data: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'pending',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ const { errors } = await query({ query: groupMemberQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('entered by its owner', () => {
+ describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
variables = {
@@ -566,8 +553,18 @@ describe('in mode: always clean db', () => {
})
})
})
+})
+describe('in mode: building up – separate for each resolver', () => {
describe('GroupMember', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
+
+ afterAll(async () => {
+ await cleanDatabase()
+ })
+
describe('unauthenticated', () => {
it('throws authorization error', async () => {
variables = {
@@ -580,10 +577,11 @@ describe('in mode: always clean db', () => {
describe('authenticated', () => {
let otherUser
+ let pendingUser
let ownerOfClosedGroupUser
let ownerOfHiddenGroupUser
- beforeEach(async () => {
+ beforeAll(async () => {
// create users
otherUser = await Factory.build(
'user',
@@ -592,7 +590,18 @@ describe('in mode: always clean db', () => {
name: 'Other TestUser',
},
{
- email: 'test2@example.org',
+ email: 'other-user@example.org',
+ password: '1234',
+ },
+ )
+ pendingUser = await Factory.build(
+ 'user',
+ {
+ id: 'pending-user',
+ name: 'Pending TestUser',
+ },
+ {
+ email: 'pending@example.org',
password: '1234',
},
)
@@ -689,22 +698,43 @@ describe('in mode: always clean db', () => {
categoryIds,
},
})
+ // 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
await mutate({
- mutation: joinGroupMutation,
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'pending-user',
+ roleInGroup: 'pending',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'hidden-group',
userId: 'current-user',
+ roleInGroup: 'usual',
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'hidden-group',
userId: 'owner-of-closed-group',
+ roleInGroup: 'admin',
},
})
-
- authenticatedUser = await user.toJson()
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMemberQuery,
+ // variables: {
+ // id: 'hidden-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+
+ authenticatedUser = null
})
describe('public group', () => {
@@ -946,12 +976,16 @@ describe('in mode: always clean db', () => {
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
- id: 'current-user',
+ id: 'pending-user',
myRoleInGroup: 'pending',
}),
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'usual',
+ }),
expect.objectContaining({
id: 'owner-of-closed-group',
- myRoleInGroup: 'pending',
+ myRoleInGroup: 'admin',
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
@@ -966,21 +1000,50 @@ describe('in mode: always clean db', () => {
variables,
})
expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMember.length).toBe(4)
})
})
- describe('by usual member "owner-of-closed-group"', () => {
+ describe('by usual member "current-user"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- roleInGroup: 'usual',
+ authenticatedUser = await user.toJson()
+ })
+
+ it('finds all members', async () => {
+ const expected = {
+ data: {
+ GroupMember: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'pending-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'admin',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
},
+ errors: undefined,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
})
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(4)
+ })
+ })
+
+ describe('by admin member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
authenticatedUser = await ownerOfClosedGroupUser.toJson()
})
@@ -989,13 +1052,17 @@ describe('in mode: always clean db', () => {
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
- id: 'current-user',
+ id: 'pending-user',
myRoleInGroup: 'pending',
}),
expect.objectContaining({
- id: 'owner-of-closed-group',
+ id: 'current-user',
myRoleInGroup: 'usual',
}),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'admin',
+ }),
expect.objectContaining({
id: 'owner-of-hidden-group',
myRoleInGroup: 'owner',
@@ -1009,13 +1076,13 @@ describe('in mode: always clean db', () => {
variables,
})
expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMember.length).toBe(4)
})
})
- describe('by pending member "current-user"', () => {
+ describe('by pending member "pending-user"', () => {
beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ authenticatedUser = await pendingUser.toJson()
})
it('throws authorization error', async () => {
@@ -1038,16 +1105,6 @@ describe('in mode: always clean db', () => {
})
})
})
-})
-
-describe('in mode: building up', () => {
- beforeAll(async () => {
- await seedBasicsAndClearAuthentication()
- })
-
- afterAll(async () => {
- await cleanDatabase()
- })
describe('ChangeGroupMemberRole', () => {
let pendingMemberUser
@@ -1057,6 +1114,7 @@ describe('in mode: building up', () => {
let secondOwnerMemberUser
beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
// create users
pendingMemberUser = await Factory.build(
'user',
@@ -1156,34 +1214,6 @@ describe('in mode: building up', () => {
categoryIds,
},
})
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'pending-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'usual-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'admin-member-user',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- id: 'closed-group',
- userId: 'second-owner-member-user',
- },
- })
// hidden-group
authenticatedUser = await adminMemberUser.toJson()
await mutate({
@@ -1198,20 +1228,45 @@ describe('in mode: building up', () => {
categoryIds,
},
})
+ // 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
await mutate({
- mutation: joinGroupMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'hidden-group',
userId: 'admin-member-user',
+ roleInGroup: 'usual',
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'second-owner-member-user',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'admin-member-user',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
variables: {
id: 'hidden-group',
userId: 'second-owner-member-user',
+ roleInGroup: 'usual',
},
})
+
+ authenticatedUser = null
+ })
+
+ afterAll(async () => {
+ await cleanDatabase()
})
describe('unauthenticated', () => {
@@ -1234,13 +1289,13 @@ describe('in mode: building up', () => {
}
})
- describe('give the members their prospective roles', () => {
+ describe('join the members and give them their prospective roles', () => {
describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
authenticatedUser = await ownerMemberUser.toJson()
})
- describe('switch role of "usual-member-user"', () => {
+ describe('for "usual-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1248,7 +1303,7 @@ describe('in mode: building up', () => {
}
})
- describe('to usual', () => {
+ describe('as usual', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1279,7 +1334,7 @@ describe('in mode: building up', () => {
})
})
- describe('switch role of "admin-member-user"', () => {
+ describe('for "admin-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1287,7 +1342,7 @@ describe('in mode: building up', () => {
}
})
- describe('to admin', () => {
+ describe('as admin', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1325,7 +1380,7 @@ describe('in mode: building up', () => {
})
})
- describe('switch role of "second-owner-member-user"', () => {
+ describe('for "second-owner-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1333,7 +1388,7 @@ describe('in mode: building up', () => {
}
})
- describe('to owner', () => {
+ describe('as owner', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1395,8 +1450,9 @@ describe('in mode: building up', () => {
})
})
- // Wolle: shall this be possible for now?
- // or shall only an owner who gave the second owner the owner role downgrade themself?
+ // shall this be possible in the future?
+ // or shall only an owner who gave the second owner the owner role downgrade themself for savety?
+ // otherwise the first owner who downgrades the other one has the victory over the group!
describe('by second owner "second-owner-member-user"', () => {
beforeEach(async () => {
authenticatedUser = await secondOwnerMemberUser.toJson()
@@ -1410,26 +1466,16 @@ describe('in mode: building up', () => {
}
})
- it('has role admin', async () => {
- const expected = {
- data: {
- ChangeGroupMemberRole: {
- id: 'owner-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('back to owner', () => {
+ describe('to same role owner', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1437,7 +1483,7 @@ describe('in mode: building up', () => {
}
})
- it('has role owner again', async () => {
+ it('has role owner still', async () => {
const expected = {
data: {
ChangeGroupMemberRole: {
@@ -1575,22 +1621,12 @@ describe('in mode: building up', () => {
}
})
- it('has role admin again', async () => {
- const expected = {
- data: {
- ChangeGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
From 9dd819f8d23bbed03d1d797a98b465190741a7f2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 10:25:39 +0200
Subject: [PATCH 062/374] Set not 'membership.updatedAt' on relation creation
Co-Authored-By: Mogge
---
backend/src/schema/resolvers/groups.js | 3 ---
1 file changed, 3 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 89c136dc52..04ef6d7042 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -109,7 +109,6 @@ export default {
MERGE (owner)-[:CREATED]->(group)
MERGE (owner)-[membership:MEMBER_OF]->(group)
SET membership.createdAt = toString(datetime())
- SET membership.updatedAt = membership.createdAt
SET membership.role = 'owner'
${categoriesCypher}
RETURN group {.*, myRole: membership.role}
@@ -140,7 +139,6 @@ export default {
MERGE (member)-[membership:MEMBER_OF]->(group)
ON CREATE SET
membership.createdAt = toString(datetime()),
- membership.updatedAt = membership.createdAt,
membership.role =
CASE WHEN group.groupType = 'public'
THEN 'usual'
@@ -174,7 +172,6 @@ export default {
MERGE (member)-[membership:MEMBER_OF]->(group)
ON CREATE SET
membership.createdAt = toString(datetime()),
- membership.updatedAt = membership.createdAt,
membership.role = $roleInGroup
ON MATCH SET
membership.updatedAt = toString(datetime()),
From 356fac9d948642379dfecce2b0163bb198ba1983 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 10:36:37 +0200
Subject: [PATCH 063/374] Destruction of 'mutate' and 'query' in
'groups.spec.js' in one line now
Co-Authored-By: Mogge
---
backend/src/schema/resolvers/groups.spec.js | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index d9c2f22e39..6787e866ea 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -31,8 +31,7 @@ const { server } = createServer({
}
},
})
-const { query } = createTestClient(server)
-const { mutate } = createTestClient(server)
+const { mutate, query } = createTestClient(server)
const seedBasicsAndClearAuthentication = async () => {
variables = {}
From b703621eb7a8db37cbd4de1aab0be955aea6c462 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 10:52:15 +0200
Subject: [PATCH 064/374] Remove local variables 'expected' and some 'variable'
and put the values directly in the 'expect' calls in 'groups.spec.js'
Co-Authored-By: Mogge
---
backend/src/schema/resolvers/groups.spec.js | 379 +++++++++-----------
1 file changed, 170 insertions(+), 209 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 6787e866ea..df4c5388be 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -121,7 +121,7 @@ describe('in mode: always clean db', () => {
})
it('creates a group', async () => {
- const expected = {
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
data: {
CreateGroup: {
name: 'The Best Group',
@@ -130,14 +130,11 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
+ })
})
it('assigns the authenticated user as owner', async () => {
- const expected = {
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
data: {
CreateGroup: {
name: 'The Best Group',
@@ -145,17 +142,13 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
+ })
})
it('has "disabled" and "deleted" default to "false"', async () => {
- const expected = { data: { CreateGroup: { disabled: false, deleted: false } } }
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
- expected,
- )
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
+ data: { CreateGroup: { disabled: false, deleted: false } },
+ })
})
describe('description', () => {
@@ -259,7 +252,7 @@ describe('in mode: always clean db', () => {
describe('query groups', () => {
describe('without any filters', () => {
it('finds all groups', async () => {
- const expected = {
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({
data: {
Group: expect.arrayContaining([
expect.objectContaining({
@@ -275,16 +268,15 @@ describe('in mode: always clean db', () => {
]),
},
errors: undefined,
- }
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject(
- expected,
- )
+ })
})
})
describe('isMember = true', () => {
it('finds only groups where user is member', async () => {
- const expected = {
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject({
data: {
Group: [
{
@@ -295,16 +287,15 @@ describe('in mode: always clean db', () => {
],
},
errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject(expected)
+ })
})
})
describe('isMember = false', () => {
it('finds only groups where user is not(!) member', async () => {
- const expected = {
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject({
data: {
Group: expect.arrayContaining([
expect.objectContaining({
@@ -315,10 +306,7 @@ describe('in mode: always clean db', () => {
]),
},
errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -411,11 +399,15 @@ describe('in mode: always clean db', () => {
describe('public group', () => {
describe('joined by "owner-of-closed-group"', () => {
it('has "usual" as membership role', async () => {
- variables = {
- id: 'public-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ }),
+ ).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-closed-group',
@@ -423,24 +415,22 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
- variables = {
- id: 'public-group',
- userId: 'current-user',
- }
- const expected = {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'current-user',
+ },
+ }),
+ ).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'current-user',
@@ -448,13 +438,7 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -463,11 +447,15 @@ describe('in mode: always clean db', () => {
describe('closed group', () => {
describe('joined by "current-user"', () => {
it('has "pending" as membership role', async () => {
- variables = {
- id: 'closed-group',
- userId: 'current-user',
- }
- const expected = {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'current-user',
+ },
+ }),
+ ).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'current-user',
@@ -475,24 +463,22 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
- variables = {
- id: 'closed-group',
- userId: 'owner-of-closed-group',
- }
- const expected = {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'closed-group',
+ userId: 'owner-of-closed-group',
+ },
+ }),
+ ).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-closed-group',
@@ -500,13 +486,7 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -527,11 +507,15 @@ describe('in mode: always clean db', () => {
describe('joined by its owner', () => {
describe('does not create additional "MEMBER_OF" relation and therefore', () => {
it('has still "owner" as membership role', async () => {
- variables = {
- id: 'hidden-group',
- userId: 'owner-of-hidden-group',
- }
- const expected = {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ userId: 'owner-of-hidden-group',
+ },
+ }),
+ ).resolves.toMatchObject({
data: {
JoinGroup: {
id: 'owner-of-hidden-group',
@@ -539,13 +523,7 @@ describe('in mode: always clean db', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -750,7 +728,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -768,12 +750,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(3)
})
})
@@ -784,7 +761,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -802,12 +783,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(3)
})
})
@@ -818,7 +794,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -836,12 +816,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(3)
})
})
@@ -862,7 +837,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -880,12 +859,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(3)
})
})
@@ -905,7 +879,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -923,12 +901,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(3)
})
})
@@ -971,7 +944,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -993,12 +970,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(4)
})
})
@@ -1009,7 +981,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -1031,12 +1007,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(4)
})
})
@@ -1047,7 +1018,11 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('finds all members', async () => {
- const expected = {
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
data: {
GroupMember: expect.arrayContaining([
expect.objectContaining({
@@ -1069,12 +1044,7 @@ describe('in mode: building up – separate for each resolver', () => {
]),
},
errors: undefined,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
})
- expect(result).toMatchObject(expected)
expect(result.data.GroupMember.length).toBe(4)
})
})
@@ -1311,7 +1281,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role usual', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
@@ -1319,13 +1294,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
// the GQL mutation needs this fields in the result for testing
@@ -1360,7 +1329,12 @@ describe('in mode: building up – separate for each resolver', () => {
// },
// })
// console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'admin-member-user',
@@ -1368,13 +1342,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -1396,7 +1364,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role owner', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'second-owner-member-user',
@@ -1404,13 +1377,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -1483,7 +1450,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role owner still', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'owner-member-user',
@@ -1491,13 +1463,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -1594,7 +1560,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role owner', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'admin-member-user',
@@ -1602,13 +1573,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
@@ -1773,7 +1738,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role admin', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
@@ -1781,13 +1751,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
@@ -1800,7 +1764,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role usual again', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'usual-member-user',
@@ -1808,13 +1777,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
@@ -1962,7 +1925,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role usual', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'pending-member-user',
@@ -1970,13 +1938,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
@@ -1989,7 +1951,12 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('has role usual again', async () => {
- const expected = {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
data: {
ChangeGroupMemberRole: {
id: 'pending-member-user',
@@ -1997,13 +1964,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
},
errors: undefined,
- }
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ })
})
})
})
From 826cf5a8e0364845f4c92b7d870e389f02f5a700 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 11:03:47 +0200
Subject: [PATCH 065/374] Renamed 'GroupMember' to 'GroupMembers'
Co-Authored-By: Mogge
---
backend/src/db/graphql/groups.js | 4 +-
.../src/middleware/permissionsMiddleware.js | 2 +-
backend/src/schema/resolvers/groups.js | 2 +-
backend/src/schema/resolvers/groups.spec.js | 72 +++++++++----------
backend/src/schema/types/type/Group.gql | 2 +-
5 files changed, 41 insertions(+), 41 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 2a9647860b..3a7f047d2b 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -116,9 +116,9 @@ export const groupQuery = gql`
}
`
-export const groupMemberQuery = gql`
+export const groupMembersQuery = gql`
query ($id: ID!, $first: Int, $offset: Int, $orderBy: [_UserOrdering], $filter: _UserFilter) {
- GroupMember(id: $id, first: $first, offset: $offset, orderBy: $orderBy, filter: $filter) {
+ GroupMembers(id: $id, first: $first, offset: $offset, orderBy: $orderBy, filter: $filter) {
id
name
slug
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index f0ddd04ca9..c8f97cd17a 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -286,7 +286,7 @@ export default shield(
statistics: allow,
currentUser: allow,
Group: isAuthenticated,
- GroupMember: isAllowedSeeingMembersOfGroup,
+ GroupMembers: isAllowedSeeingMembersOfGroup,
Post: allow,
profilePagePosts: allow,
Comment: allow,
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 04ef6d7042..5e401c28e7 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -46,7 +46,7 @@ export default {
session.close()
}
},
- GroupMember: async (_object, params, context, _resolveInfo) => {
+ GroupMembers: async (_object, params, context, _resolveInfo) => {
const { id: groupId } = params
// Wolle: console.log('groupId: ', groupId)
const session = context.driver.session()
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index df4c5388be..c81f976be4 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -4,7 +4,7 @@ import {
createGroupMutation,
joinGroupMutation,
changeGroupMemberRoleMutation,
- groupMemberQuery,
+ groupMembersQuery,
groupQuery,
} from '../../db/graphql/groups'
import { getNeode, getDriver } from '../../db/neo4j'
@@ -499,7 +499,7 @@ describe('in mode: always clean db', () => {
id: 'hidden-group',
userId: 'owner-of-closed-group',
}
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -533,7 +533,7 @@ describe('in mode: always clean db', () => {
})
describe('in mode: building up – separate for each resolver', () => {
- describe('GroupMember', () => {
+ describe('GroupMembers', () => {
beforeAll(async () => {
await seedBasicsAndClearAuthentication()
})
@@ -547,7 +547,7 @@ describe('in mode: building up – separate for each resolver', () => {
variables = {
id: 'not-existing-group',
}
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -704,12 +704,12 @@ describe('in mode: building up – separate for each resolver', () => {
// const groups = await query({ query: groupQuery, variables: {} })
// console.log('groups.data.Group: ', groups.data.Group)
// const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMemberQuery,
+ // mutation: groupMembersQuery,
// variables: {
// id: 'hidden-group',
// },
// })
- // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
authenticatedUser = null
})
@@ -729,12 +729,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
@@ -751,7 +751,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMembers.length).toBe(3)
})
})
@@ -762,12 +762,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
@@ -784,7 +784,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMembers.length).toBe(3)
})
})
@@ -795,12 +795,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'owner',
@@ -817,7 +817,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMembers.length).toBe(3)
})
})
})
@@ -838,12 +838,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'pending',
@@ -860,7 +860,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMembers.length).toBe(3)
})
})
@@ -880,12 +880,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'current-user',
myRoleInGroup: 'pending',
@@ -902,7 +902,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(3)
+ expect(result.data.GroupMembers.length).toBe(3)
})
})
@@ -912,7 +912,7 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -923,7 +923,7 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -945,12 +945,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
@@ -971,7 +971,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(4)
+ expect(result.data.GroupMembers.length).toBe(4)
})
})
@@ -982,12 +982,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
@@ -1008,7 +1008,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(4)
+ expect(result.data.GroupMembers.length).toBe(4)
})
})
@@ -1019,12 +1019,12 @@ describe('in mode: building up – separate for each resolver', () => {
it('finds all members', async () => {
const result = await mutate({
- mutation: groupMemberQuery,
+ mutation: groupMembersQuery,
variables,
})
expect(result).toMatchObject({
data: {
- GroupMember: expect.arrayContaining([
+ GroupMembers: expect.arrayContaining([
expect.objectContaining({
id: 'pending-user',
myRoleInGroup: 'pending',
@@ -1045,7 +1045,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
errors: undefined,
})
- expect(result.data.GroupMember.length).toBe(4)
+ expect(result.data.GroupMembers.length).toBe(4)
})
})
@@ -1055,7 +1055,7 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -1066,7 +1066,7 @@ describe('in mode: building up – separate for each resolver', () => {
})
it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMemberQuery, variables })
+ const { errors } = await query({ query: groupMembersQuery, variables })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -1323,12 +1323,12 @@ describe('in mode: building up – separate for each resolver', () => {
// const groups = await query({ query: groupQuery, variables: {} })
// console.log('groups.data.Group: ', groups.data.Group)
// const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMemberQuery,
+ // mutation: groupMembersQuery,
// variables: {
// id: 'closed-group',
// },
// })
- // console.log('groupMemberOfClosedGroup.data.GroupMember: ', groupMemberOfClosedGroup.data.GroupMember)
+ // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
await expect(
mutate({
mutation: changeGroupMemberRoleMutation,
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index f12b98c079..1f59d7cc4f 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -73,7 +73,7 @@ type Query {
orderBy: [_GroupOrdering]
): [Group]
- GroupMember(
+ GroupMembers(
id: ID!
first: Int
offset: Int
From 813c072dc7d0ba9983c780c194ebfacd90185671 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 11:32:22 +0200
Subject: [PATCH 066/374] Change GQL parameter for 'JoinGroup' from 'id' to
'groupId'
Co-Authored-By: Mogge
---
backend/src/db/graphql/groups.js | 4 +-
backend/src/db/seed.js | 26 +++++------
.../src/middleware/permissionsMiddleware.js | 2 +-
backend/src/schema/resolvers/groups.js | 2 +-
backend/src/schema/resolvers/groups.spec.js | 46 ++++++++++---------
backend/src/schema/types/type/Group.gql | 2 +-
6 files changed, 43 insertions(+), 39 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 3a7f047d2b..a0aef97b2e 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -40,8 +40,8 @@ export const createGroupMutation = gql`
`
export const joinGroupMutation = gql`
- mutation ($id: ID!, $userId: ID!) {
- JoinGroup(id: $id, userId: $userId) {
+ mutation ($groupId: ID!, $userId: ID!) {
+ JoinGroup(groupId: $groupId, userId: $userId) {
id
name
slug
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 0010b09ef0..1bd5354568 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -408,28 +408,28 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u2',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u3',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u4',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u6',
},
}),
@@ -480,35 +480,35 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g1',
+ groupId: 'g1',
userId: 'u1',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g1',
+ groupId: 'g1',
userId: 'u2',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g1',
+ groupId: 'g1',
userId: 'u5',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g1',
+ groupId: 'g1',
userId: 'u6',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g1',
+ groupId: 'g1',
userId: 'u7',
},
}),
@@ -567,28 +567,28 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g2',
+ groupId: 'g2',
userId: 'u4',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g2',
+ groupId: 'g2',
userId: 'u5',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g2',
+ groupId: 'g2',
userId: 'u6',
},
}),
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'g2',
+ groupId: 'g2',
userId: 'u7',
},
}),
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index c8f97cd17a..bbaff67d9c 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -172,7 +172,7 @@ const isAllowedToJoinGroup = rule({
cache: 'no_cache',
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
- const { id: groupId, userId } = args
+ const { groupId, userId } = args
// Wolle:
// console.log('adminId: ', adminId)
// console.log('groupId: ', groupId)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 5e401c28e7..d34d62eb75 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -131,7 +131,7 @@ export default {
}
},
JoinGroup: async (_parent, params, context, _resolveInfo) => {
- const { id: groupId, userId } = params
+ const { groupId, userId } = params
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const joinGroupCypher = `
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index c81f976be4..74f806b7b7 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -316,11 +316,13 @@ describe('in mode: always clean db', () => {
describe('JoinGroup', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- userId: 'current-user',
- }
- const { errors } = await mutate({ mutation: joinGroupMutation, variables })
+ const { errors } = await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'not-existing-group',
+ userId: 'current-user',
+ },
+ })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -403,7 +405,7 @@ describe('in mode: always clean db', () => {
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'owner-of-closed-group',
},
}),
@@ -426,7 +428,7 @@ describe('in mode: always clean db', () => {
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'current-user',
},
}),
@@ -451,7 +453,7 @@ describe('in mode: always clean db', () => {
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'closed-group',
+ groupId: 'closed-group',
userId: 'current-user',
},
}),
@@ -474,7 +476,7 @@ describe('in mode: always clean db', () => {
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'closed-group',
+ groupId: 'closed-group',
userId: 'owner-of-closed-group',
},
}),
@@ -495,11 +497,13 @@ describe('in mode: always clean db', () => {
describe('hidden group', () => {
describe('joined by "owner-of-closed-group"', () => {
it('throws authorization error', async () => {
- variables = {
- id: 'hidden-group',
- userId: 'owner-of-closed-group',
- }
- const { errors } = await query({ query: groupMembersQuery, variables })
+ const { errors } = await query({
+ query: joinGroupMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -511,7 +515,7 @@ describe('in mode: always clean db', () => {
mutate({
mutation: joinGroupMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'owner-of-hidden-group',
},
}),
@@ -622,14 +626,14 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'owner-of-closed-group',
},
})
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'owner-of-hidden-group',
},
})
@@ -650,14 +654,14 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'closed-group',
+ groupId: 'closed-group',
userId: 'current-user',
},
})
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'closed-group',
+ groupId: 'closed-group',
userId: 'owner-of-hidden-group',
},
})
@@ -1158,14 +1162,14 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'owner-of-closed-group',
},
})
await mutate({
mutation: joinGroupMutation,
variables: {
- id: 'public-group',
+ groupId: 'public-group',
userId: 'owner-of-hidden-group',
},
})
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 1f59d7cc4f..9d2210a1aa 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -115,7 +115,7 @@ type Mutation {
DeleteGroup(id: ID!): Group
JoinGroup(
- id: ID!
+ groupId: ID!
userId: ID!
): User
From fd497a03aae62b0296c7f1ffc5129c2facf25c7b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 11:58:23 +0200
Subject: [PATCH 067/374] Change GQL parameter for 'ChangeGroupMemberRole' from
'id' to 'groupId'
Co-Authored-By: Mogge
---
backend/src/db/graphql/groups.js | 4 +-
backend/src/db/seed.js | 20 ++++-----
.../src/middleware/permissionsMiddleware.js | 2 +-
backend/src/schema/resolvers/groups.js | 2 +-
backend/src/schema/resolvers/groups.spec.js | 44 ++++++++-----------
backend/src/schema/types/type/Group.gql | 2 +-
6 files changed, 34 insertions(+), 40 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index a0aef97b2e..c6f110ed1e 100644
--- a/backend/src/db/graphql/groups.js
+++ b/backend/src/db/graphql/groups.js
@@ -51,8 +51,8 @@ export const joinGroupMutation = gql`
`
export const changeGroupMemberRoleMutation = gql`
- mutation ($id: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
- ChangeGroupMemberRole(id: $id, userId: $userId, roleInGroup: $roleInGroup) {
+ mutation ($groupId: ID!, $userId: ID!, $roleInGroup: GroupMemberRole!) {
+ ChangeGroupMemberRole(groupId: $groupId, userId: $userId, roleInGroup: $roleInGroup) {
id
name
slug
diff --git a/backend/src/db/seed.js b/backend/src/db/seed.js
index 1bd5354568..a71b111317 100644
--- a/backend/src/db/seed.js
+++ b/backend/src/db/seed.js
@@ -438,7 +438,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u2',
roleInGroup: 'usual',
},
@@ -446,7 +446,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u4',
roleInGroup: 'admin',
},
@@ -454,7 +454,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u3',
roleInGroup: 'owner',
},
@@ -517,7 +517,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u1',
roleInGroup: 'usual',
},
@@ -525,7 +525,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u2',
roleInGroup: 'usual',
},
@@ -533,7 +533,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u5',
roleInGroup: 'admin',
},
@@ -541,7 +541,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u6',
roleInGroup: 'owner',
},
@@ -597,7 +597,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u4',
roleInGroup: 'usual',
},
@@ -605,7 +605,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u5',
roleInGroup: 'usual',
},
@@ -613,7 +613,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'g0',
+ groupId: 'g0',
userId: 'u6',
roleInGroup: 'usual',
},
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index bbaff67d9c..926f824e4d 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -98,7 +98,7 @@ const isAllowedToChangeGroupMemberRole = rule({
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
const adminId = user.id
- const { id: groupId, userId, roleInGroup } = args
+ const { groupId, userId, roleInGroup } = args
if (adminId === userId) return false
// Wolle:
// console.log('isAllowedToChangeGroupMemberRole !!!')
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index d34d62eb75..6ca09d72af 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -159,7 +159,7 @@ export default {
}
},
ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
- const { id: groupId, userId, roleInGroup } = params
+ const { groupId, userId, roleInGroup } = params
// Wolle
// console.log('ChangeGroupMemberRole !!!')
// console.log('groupId: ', groupId)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 74f806b7b7..a660f6ce8d 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -659,10 +659,11 @@ describe('in mode: building up – separate for each resolver', () => {
},
})
await mutate({
- mutation: joinGroupMutation,
+ mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'closed-group',
userId: 'owner-of-hidden-group',
+ roleInGroup: 'usual',
},
})
// hidden-group
@@ -683,7 +684,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'pending-user',
roleInGroup: 'pending',
},
@@ -691,7 +692,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'current-user',
roleInGroup: 'usual',
},
@@ -699,7 +700,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'owner-of-closed-group',
roleInGroup: 'admin',
},
@@ -858,7 +859,7 @@ describe('in mode: building up – separate for each resolver', () => {
}),
expect.objectContaining({
id: 'owner-of-hidden-group',
- myRoleInGroup: 'pending',
+ myRoleInGroup: 'usual',
}),
]),
},
@@ -870,15 +871,6 @@ describe('in mode: building up – separate for each resolver', () => {
describe('by usual member "owner-of-hidden-group"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- id: 'closed-group',
- userId: 'owner-of-hidden-group',
- roleInGroup: 'usual',
- },
- })
authenticatedUser = await ownerOfHiddenGroupUser.toJson()
})
@@ -1205,7 +1197,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'admin-member-user',
roleInGroup: 'usual',
},
@@ -1213,7 +1205,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'second-owner-member-user',
roleInGroup: 'usual',
},
@@ -1221,7 +1213,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'admin-member-user',
roleInGroup: 'usual',
},
@@ -1229,7 +1221,7 @@ describe('in mode: building up – separate for each resolver', () => {
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
- id: 'hidden-group',
+ groupId: 'hidden-group',
userId: 'second-owner-member-user',
roleInGroup: 'usual',
},
@@ -1244,12 +1236,14 @@ describe('in mode: building up – separate for each resolver', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- userId: 'current-user',
- roleInGroup: 'pending',
- }
- const { errors } = await mutate({ mutation: changeGroupMemberRoleMutation, variables })
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'not-existing-group',
+ userId: 'current-user',
+ roleInGroup: 'pending',
+ },
+ })
expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
@@ -1258,7 +1252,7 @@ describe('in mode: building up – separate for each resolver', () => {
describe('in all group types – here "closed-group" for example', () => {
beforeEach(async () => {
variables = {
- id: 'closed-group',
+ groupId: 'closed-group',
}
})
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 9d2210a1aa..5c98e49e8a 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -120,7 +120,7 @@ type Mutation {
): User
ChangeGroupMemberRole(
- id: ID!
+ groupId: ID!
userId: ID!
roleInGroup: GroupMemberRole!
): User
From c7c2ebdeb7544078a3a6bf4441965e775294640d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 12:20:27 +0200
Subject: [PATCH 068/374] Refine clean db mode in 'groups.spec.js'
---
backend/src/schema/resolvers/groups.spec.js | 3106 ++++++++++---------
1 file changed, 1554 insertions(+), 1552 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index a660f6ce8d..054e3abb05 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -83,450 +83,452 @@ afterAll(async () => {
await cleanDatabase()
})
-describe('in mode: always clean db', () => {
- beforeEach(async () => {
- await seedBasicsAndClearAuthentication()
- })
-
- // 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
- afterEach(async () => {
- await cleanDatabase()
- })
+describe('in mode', () => {
+ describe('clean db after each single test', () => {
+ beforeEach(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
- describe('CreateGroup', () => {
- beforeEach(() => {
- variables = {
- ...variables,
- id: 'g589',
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- }
+ // 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
+ afterEach(async () => {
+ await cleanDatabase()
})
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await mutate({ mutation: createGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('CreateGroup', () => {
+ beforeEach(() => {
+ variables = {
+ ...variables,
+ id: 'g589',
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ }
})
- })
- describe('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({ mutation: createGroupMutation, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- it('creates a group', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: {
- CreateGroup: {
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
+ describe('authenticated', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('creates a group', async () => {
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ },
},
- },
- errors: undefined,
+ errors: undefined,
+ })
})
- })
- it('assigns the authenticated user as owner', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: {
- CreateGroup: {
- name: 'The Best Group',
- myRole: 'owner',
+ it('assigns the authenticated user as owner', async () => {
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ myRole: 'owner',
+ },
},
- },
- errors: undefined,
+ errors: undefined,
+ })
})
- })
- it('has "disabled" and "deleted" default to "false"', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: { CreateGroup: { disabled: false, deleted: false } },
+ it('has "disabled" and "deleted" default to "false"', async () => {
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
+ data: { CreateGroup: { disabled: false, deleted: false } },
+ })
})
- })
- describe('description', () => {
- describe('length without HTML', () => {
- describe('less then 100 chars', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: {
- ...variables,
- description:
- '0123456789' +
- '0123456789 ',
- },
+ describe('description', () => {
+ describe('length without HTML', () => {
+ describe('less then 100 chars', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ ...variables,
+ description:
+ '0123456789' +
+ '0123456789 ',
+ },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Description too short!')
})
- expect(errors[0]).toHaveProperty('message', 'Description too short!')
})
})
})
- })
- describe('categories', () => {
- beforeEach(() => {
- CONFIG.CATEGORIES_ACTIVE = true
- })
+ describe('categories', () => {
+ beforeEach(() => {
+ CONFIG.CATEGORIES_ACTIVE = true
+ })
- describe('not even one', () => {
- it('throws error: "Too view categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: null },
+ describe('not even one', () => {
+ it('throws error: "Too view categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: null },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
- expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
- })
- describe('four', () => {
- it('throws error: "Too many categories!"', async () => {
- const { errors } = await mutate({
- mutation: createGroupMutation,
- variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
+ describe('four', () => {
+ it('throws error: "Too many categories!"', async () => {
+ const { errors } = await mutate({
+ mutation: createGroupMutation,
+ variables: { ...variables, categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'] },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Too many categories!')
})
- expect(errors[0]).toHaveProperty('message', 'Too many categories!')
})
})
})
})
- })
- describe('Group', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('Group', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- })
- describe('authenticated', () => {
- let otherUser
+ describe('authenticated', () => {
+ let otherUser
- beforeEach(async () => {
- otherUser = await Factory.build(
- 'user',
- {
- id: 'other-user',
- name: 'Other TestUser',
- },
- {
- email: 'test2@example.org',
- password: '1234',
- },
- )
- authenticatedUser = await otherUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'others-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'global',
- categoryIds,
- },
- })
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'my-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
+ beforeEach(async () => {
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'test2@example.org',
+ password: '1234',
+ },
+ )
+ authenticatedUser = await otherUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'others-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'my-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
})
- })
- describe('query groups', () => {
- describe('without any filters', () => {
- it('finds all groups', async () => {
- await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- }),
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
+ describe('query groups', () => {
+ describe('without any filters', () => {
+ it('finds all groups', async () => {
+ await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
})
})
- })
- describe('isMember = true', () => {
- it('finds only groups where user is member', async () => {
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject({
- data: {
- Group: [
- {
- id: 'my-group',
- slug: 'the-best-group',
- myRole: 'owner',
- },
- ],
- },
- errors: undefined,
+ describe('isMember = true', () => {
+ it('finds only groups where user is member', async () => {
+ await expect(
+ query({ query: groupQuery, variables: { isMember: true } }),
+ ).resolves.toMatchObject({
+ data: {
+ Group: [
+ {
+ id: 'my-group',
+ slug: 'the-best-group',
+ myRole: 'owner',
+ },
+ ],
+ },
+ errors: undefined,
+ })
})
})
- })
- describe('isMember = false', () => {
- it('finds only groups where user is not(!) member', async () => {
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject({
- data: {
- Group: expect.arrayContaining([
- expect.objectContaining({
- id: 'others-group',
- slug: 'uninteresting-group',
- myRole: null,
- }),
- ]),
- },
- errors: undefined,
+ describe('isMember = false', () => {
+ it('finds only groups where user is not(!) member', async () => {
+ await expect(
+ query({ query: groupQuery, variables: { isMember: false } }),
+ ).resolves.toMatchObject({
+ data: {
+ Group: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'others-group',
+ slug: 'uninteresting-group',
+ myRole: null,
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
})
})
})
})
})
- })
- describe('JoinGroup', () => {
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'not-existing-group',
- userId: 'current-user',
- },
+ describe('JoinGroup', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'not-existing-group',
+ userId: 'current-user',
+ },
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- })
-
- describe('authenticated', () => {
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
- beforeEach(async () => {
- // create users
- ownerOfClosedGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
- },
- {
- email: 'owner-of-closed-group@example.org',
- password: '1234',
- },
- )
- ownerOfHiddenGroupUser = await Factory.build(
- 'user',
- {
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
- },
- {
- email: 'owner-of-hidden-group@example.org',
- password: '1234',
- },
- )
- // create groups
- // public-group
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- authenticatedUser = await user.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
- })
- })
+ describe('authenticated', () => {
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
- describe('public group', () => {
- describe('joined by "owner-of-closed-group"', () => {
- it('has "usual" as membership role', async () => {
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'public-group',
- userId: 'owner-of-closed-group',
- },
- }),
- ).resolves.toMatchObject({
- data: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
- })
+ beforeEach(async () => {
+ // create users
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ // public-group
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
})
})
- describe('joined by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
+ describe('public group', () => {
+ describe('joined by "owner-of-closed-group"', () => {
+ it('has "usual" as membership role', async () => {
await expect(
mutate({
mutation: joinGroupMutation,
variables: {
groupId: 'public-group',
- userId: 'current-user',
+ userId: 'owner-of-closed-group',
},
}),
).resolves.toMatchObject({
data: {
JoinGroup: {
- id: 'current-user',
- myRoleInGroup: 'owner',
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
},
},
errors: undefined,
})
})
})
- })
- })
- describe('closed group', () => {
- describe('joined by "current-user"', () => {
- it('has "pending" as membership role', async () => {
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'closed-group',
- userId: 'current-user',
- },
- }),
- ).resolves.toMatchObject({
- data: {
- JoinGroup: {
- id: 'current-user',
- myRoleInGroup: 'pending',
- },
- },
- errors: undefined,
+ describe('joined by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'public-group',
+ userId: 'current-user',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ JoinGroup: {
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
})
})
})
- describe('joined by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
+ describe('closed group', () => {
+ describe('joined by "current-user"', () => {
+ it('has "pending" as membership role', async () => {
await expect(
mutate({
mutation: joinGroupMutation,
variables: {
groupId: 'closed-group',
- userId: 'owner-of-closed-group',
+ userId: 'current-user',
},
}),
).resolves.toMatchObject({
data: {
JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
+ id: 'current-user',
+ myRoleInGroup: 'pending',
},
},
errors: undefined,
})
})
})
- })
- })
- describe('hidden group', () => {
- describe('joined by "owner-of-closed-group"', () => {
- it('throws authorization error', async () => {
- const { errors } = await query({
- query: joinGroupMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'owner-of-closed-group',
- },
+ describe('joined by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'closed-group',
+ userId: 'owner-of-closed-group',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('joined by its owner', () => {
- describe('does not create additional "MEMBER_OF" relation and therefore', () => {
- it('has still "owner" as membership role', async () => {
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'owner-of-hidden-group',
- },
- }),
- ).resolves.toMatchObject({
- data: {
- JoinGroup: {
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- },
+ describe('hidden group', () => {
+ describe('joined by "owner-of-closed-group"', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({
+ query: joinGroupMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'owner-of-closed-group',
},
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('joined by its owner', () => {
+ describe('does not create additional "MEMBER_OF" relation and therefore', () => {
+ it('has still "owner" as membership role', async () => {
+ await expect(
+ mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'owner-of-hidden-group',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ JoinGroup: {
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
})
})
})
@@ -534,83 +536,610 @@ describe('in mode: always clean db', () => {
})
})
})
-})
-describe('in mode: building up – separate for each resolver', () => {
- describe('GroupMembers', () => {
- beforeAll(async () => {
- await seedBasicsAndClearAuthentication()
- })
+ describe('building up – clean db after each resolver', () => {
+ describe('GroupMembers', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
- afterAll(async () => {
- await cleanDatabase()
- })
+ afterAll(async () => {
+ await cleanDatabase()
+ })
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- variables = {
- id: 'not-existing-group',
- }
- const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ variables = {
+ id: 'not-existing-group',
+ }
+ const { errors } = await query({ query: groupMembersQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('authenticated', () => {
+ let otherUser
+ let pendingUser
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
+
+ beforeAll(async () => {
+ // create users
+ otherUser = await Factory.build(
+ 'user',
+ {
+ id: 'other-user',
+ name: 'Other TestUser',
+ },
+ {
+ email: 'other-user@example.org',
+ password: '1234',
+ },
+ )
+ pendingUser = await Factory.build(
+ 'user',
+ {
+ id: 'pending-user',
+ name: 'Pending TestUser',
+ },
+ {
+ email: 'pending@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfClosedGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-closed-group',
+ name: 'Owner Of Closed Group',
+ },
+ {
+ email: 'owner-of-closed-group@example.org',
+ password: '1234',
+ },
+ )
+ ownerOfHiddenGroupUser = await Factory.build(
+ 'user',
+ {
+ id: 'owner-of-hidden-group',
+ name: 'Owner Of Hidden Group',
+ },
+ {
+ email: 'owner-of-hidden-group@example.org',
+ password: '1234',
+ },
+ )
+ // create groups
+ // public-group
+ authenticatedUser = await user.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'public-group',
+ name: 'The Best Group',
+ about: 'We will change the world!',
+ description: 'Some description' + descriptionAdditional100,
+ groupType: 'public',
+ actionRadius: 'regional',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'public-group',
+ userId: 'owner-of-hidden-group',
+ },
+ })
+ // closed-group
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'closed-group',
+ name: 'Uninteresting Group',
+ about: 'We will change nothing!',
+ description: 'We love it like it is!?' + descriptionAdditional100,
+ groupType: 'closed',
+ actionRadius: 'national',
+ categoryIds,
+ },
+ })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ groupId: 'closed-group',
+ userId: 'current-user',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'closed-group',
+ userId: 'owner-of-hidden-group',
+ roleInGroup: 'usual',
+ },
+ })
+ // hidden-group
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ await mutate({
+ mutation: createGroupMutation,
+ variables: {
+ id: 'hidden-group',
+ name: 'Investigative Journalism Group',
+ about: 'We will change all.',
+ description: 'We research …' + descriptionAdditional100,
+ groupType: 'hidden',
+ actionRadius: 'global',
+ categoryIds,
+ },
+ })
+ // 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'pending-user',
+ roleInGroup: 'pending',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'current-user',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'owner-of-closed-group',
+ roleInGroup: 'admin',
+ },
+ })
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMembersQuery,
+ // variables: {
+ // id: 'hidden-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
+
+ authenticatedUser = null
+ })
+
+ describe('public group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'public-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(3)
+ })
+ })
+
+ describe('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(3)
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(3)
+ })
+ })
+ })
+ })
+
+ describe('closed group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(3)
+ })
+ })
+
+ describe('by usual member "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'usual',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(3)
+ })
+ })
+
+ describe('by pending member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMembersQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMembersQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
+
+ describe('hidden group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'hidden-group',
+ }
+ })
+
+ describe('query group members', () => {
+ describe('by owner "owner-of-hidden-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'pending-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'admin',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(4)
+ })
+ })
+
+ describe('by usual member "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'pending-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'admin',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(4)
+ })
+ })
+
+ describe('by admin member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ })
+
+ it('finds all members', async () => {
+ const result = await mutate({
+ mutation: groupMembersQuery,
+ variables,
+ })
+ expect(result).toMatchObject({
+ data: {
+ GroupMembers: expect.arrayContaining([
+ expect.objectContaining({
+ id: 'pending-user',
+ myRoleInGroup: 'pending',
+ }),
+ expect.objectContaining({
+ id: 'current-user',
+ myRoleInGroup: 'usual',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'admin',
+ }),
+ expect.objectContaining({
+ id: 'owner-of-hidden-group',
+ myRoleInGroup: 'owner',
+ }),
+ ]),
+ },
+ errors: undefined,
+ })
+ expect(result.data.GroupMembers.length).toBe(4)
+ })
+ })
+
+ describe('by pending member "pending-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingUser.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMembersQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupMembersQuery, variables })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
})
})
- describe('authenticated', () => {
- let otherUser
- let pendingUser
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
+ describe('ChangeGroupMemberRole', () => {
+ let pendingMemberUser
+ let usualMemberUser
+ let adminMemberUser
+ let ownerMemberUser
+ let secondOwnerMemberUser
beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
// create users
- otherUser = await Factory.build(
+ pendingMemberUser = await Factory.build(
+ 'user',
+ {
+ id: 'pending-member-user',
+ name: 'Pending Member TestUser',
+ },
+ {
+ email: 'pending-member-user@example.org',
+ password: '1234',
+ },
+ )
+ usualMemberUser = await Factory.build(
'user',
{
- id: 'other-user',
- name: 'Other TestUser',
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
},
{
- email: 'other-user@example.org',
+ email: 'usual-member-user@example.org',
password: '1234',
},
)
- pendingUser = await Factory.build(
+ adminMemberUser = await Factory.build(
'user',
{
- id: 'pending-user',
- name: 'Pending TestUser',
+ id: 'admin-member-user',
+ name: 'Admin Member TestUser',
},
{
- email: 'pending@example.org',
+ email: 'admin-member-user@example.org',
password: '1234',
},
)
- ownerOfClosedGroupUser = await Factory.build(
+ ownerMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
+ id: 'owner-member-user',
+ name: 'Owner Member TestUser',
},
{
- email: 'owner-of-closed-group@example.org',
+ email: 'owner-member-user@example.org',
password: '1234',
},
)
- ownerOfHiddenGroupUser = await Factory.build(
+ secondOwnerMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
+ id: 'second-owner-member-user',
+ name: 'Second Owner Member TestUser',
},
{
- email: 'owner-of-hidden-group@example.org',
+ email: 'second-owner-member-user@example.org',
password: '1234',
},
)
// create groups
// public-group
- authenticatedUser = await user.toJson()
+ authenticatedUser = await usualMemberUser.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
@@ -638,7 +1167,7 @@ describe('in mode: building up – separate for each resolver', () => {
},
})
// closed-group
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
+ authenticatedUser = await ownerMemberUser.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
@@ -651,23 +1180,8 @@ describe('in mode: building up – separate for each resolver', () => {
categoryIds,
},
})
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'closed-group',
- userId: 'current-user',
- },
- })
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'closed-group',
- userId: 'owner-of-hidden-group',
- roleInGroup: 'usual',
- },
- })
// hidden-group
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
+ authenticatedUser = await adminMemberUser.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
@@ -685,15 +1199,15 @@ describe('in mode: building up – separate for each resolver', () => {
mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'hidden-group',
- userId: 'pending-user',
- roleInGroup: 'pending',
+ userId: 'admin-member-user',
+ roleInGroup: 'usual',
},
})
await mutate({
mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'hidden-group',
- userId: 'current-user',
+ userId: 'second-owner-member-user',
roleInGroup: 'usual',
},
})
@@ -701,1337 +1215,825 @@ describe('in mode: building up – separate for each resolver', () => {
mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'hidden-group',
- userId: 'owner-of-closed-group',
- roleInGroup: 'admin',
+ userId: 'admin-member-user',
+ roleInGroup: 'usual',
+ },
+ })
+ await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'hidden-group',
+ userId: 'second-owner-member-user',
+ roleInGroup: 'usual',
},
})
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMembersQuery,
- // variables: {
- // id: 'hidden-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
authenticatedUser = null
})
- describe('public group', () => {
- beforeEach(async () => {
- variables = {
- id: 'public-group',
- }
- })
-
- describe('query group members', () => {
- describe('by owner "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
+ afterAll(async () => {
+ await cleanDatabase()
+ })
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(3)
- })
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables: {
+ groupId: 'not-existing-group',
+ userId: 'current-user',
+ roleInGroup: 'pending',
+ },
})
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
- describe('by usual member "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(3)
- })
- })
-
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(3)
- })
- })
- })
- })
-
- describe('closed group', () => {
- beforeEach(async () => {
- variables = {
- id: 'closed-group',
- }
- })
-
- describe('query group members', () => {
- describe('by owner "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(3)
- })
- })
-
- describe('by usual member "owner-of-hidden-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'usual',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(3)
- })
- })
-
- describe('by pending member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- })
-
- describe('hidden group', () => {
- beforeEach(async () => {
- variables = {
- id: 'hidden-group',
- }
- })
-
- describe('query group members', () => {
- describe('by owner "owner-of-hidden-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfHiddenGroupUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'pending-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'admin',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(4)
- })
- })
-
- describe('by usual member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'pending-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'admin',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(4)
- })
- })
-
- describe('by admin member "owner-of-closed-group"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerOfClosedGroupUser.toJson()
- })
-
- it('finds all members', async () => {
- const result = await mutate({
- mutation: groupMembersQuery,
- variables,
- })
- expect(result).toMatchObject({
- data: {
- GroupMembers: expect.arrayContaining([
- expect.objectContaining({
- id: 'pending-user',
- myRoleInGroup: 'pending',
- }),
- expect.objectContaining({
- id: 'current-user',
- myRoleInGroup: 'usual',
- }),
- expect.objectContaining({
- id: 'owner-of-closed-group',
- myRoleInGroup: 'admin',
- }),
- expect.objectContaining({
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- }),
- ]),
- },
- errors: undefined,
- })
- expect(result.data.GroupMembers.length).toBe(4)
- })
- })
-
- describe('by pending member "pending-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingUser.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('by none member "other-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await otherUser.toJson()
- })
-
- it('throws authorization error', async () => {
- const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- })
- })
- })
-
- describe('ChangeGroupMemberRole', () => {
- let pendingMemberUser
- let usualMemberUser
- let adminMemberUser
- let ownerMemberUser
- let secondOwnerMemberUser
-
- beforeAll(async () => {
- await seedBasicsAndClearAuthentication()
- // create users
- pendingMemberUser = await Factory.build(
- 'user',
- {
- id: 'pending-member-user',
- name: 'Pending Member TestUser',
- },
- {
- email: 'pending-member-user@example.org',
- password: '1234',
- },
- )
- usualMemberUser = await Factory.build(
- 'user',
- {
- id: 'usual-member-user',
- name: 'Usual Member TestUser',
- },
- {
- email: 'usual-member-user@example.org',
- password: '1234',
- },
- )
- adminMemberUser = await Factory.build(
- 'user',
- {
- id: 'admin-member-user',
- name: 'Admin Member TestUser',
- },
- {
- email: 'admin-member-user@example.org',
- password: '1234',
- },
- )
- ownerMemberUser = await Factory.build(
- 'user',
- {
- id: 'owner-member-user',
- name: 'Owner Member TestUser',
- },
- {
- email: 'owner-member-user@example.org',
- password: '1234',
- },
- )
- secondOwnerMemberUser = await Factory.build(
- 'user',
- {
- id: 'second-owner-member-user',
- name: 'Second Owner Member TestUser',
- },
- {
- email: 'second-owner-member-user@example.org',
- password: '1234',
- },
- )
- // create groups
- // public-group
- authenticatedUser = await usualMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'public-group',
- name: 'The Best Group',
- about: 'We will change the world!',
- description: 'Some description' + descriptionAdditional100,
- groupType: 'public',
- actionRadius: 'regional',
- categoryIds,
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'public-group',
- userId: 'owner-of-closed-group',
- },
- })
- await mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'public-group',
- userId: 'owner-of-hidden-group',
- },
- })
- // closed-group
- authenticatedUser = await ownerMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'closed-group',
- name: 'Uninteresting Group',
- about: 'We will change nothing!',
- description: 'We love it like it is!?' + descriptionAdditional100,
- groupType: 'closed',
- actionRadius: 'national',
- categoryIds,
- },
- })
- // hidden-group
- authenticatedUser = await adminMemberUser.toJson()
- await mutate({
- mutation: createGroupMutation,
- variables: {
- id: 'hidden-group',
- name: 'Investigative Journalism Group',
- about: 'We will change all.',
- description: 'We research …' + descriptionAdditional100,
- groupType: 'hidden',
- actionRadius: 'global',
- categoryIds,
- },
- })
- // 'JoinGroup' mutation does not work in hidden groups so we join them by 'ChangeGroupMemberRole' through the owner
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'admin-member-user',
- roleInGroup: 'usual',
- },
- })
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'second-owner-member-user',
- roleInGroup: 'usual',
- },
- })
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'admin-member-user',
- roleInGroup: 'usual',
- },
- })
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'second-owner-member-user',
- roleInGroup: 'usual',
- },
- })
-
- authenticatedUser = null
- })
-
- afterAll(async () => {
- await cleanDatabase()
- })
-
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'not-existing-group',
- userId: 'current-user',
- roleInGroup: 'pending',
- },
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('authenticated', () => {
- describe('in all group types – here "closed-group" for example', () => {
- beforeEach(async () => {
- variables = {
- groupId: 'closed-group',
- }
- })
-
- describe('join the members and give them their prospective roles', () => {
- describe('by owner "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
- })
-
- describe('for "usual-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'usual-member-user',
- }
- })
-
- describe('as usual', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
- })
-
- it('has role usual', async () => {
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
- })
- })
-
- // the GQL mutation needs this fields in the result for testing
- it.todo('has "updatedAt" newer as "createdAt"')
- })
- })
-
- describe('for "admin-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
- })
-
- describe('as admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('has role admin', async () => {
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMembersQuery,
- // variables: {
- // id: 'closed-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
- })
- })
- })
- })
-
- describe('for "second-owner-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'second-owner-member-user',
- }
- })
-
- describe('as owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
-
- it('has role owner', async () => {
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'second-owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- })
- })
- })
- })
- })
- })
-
- describe('switch role', () => {
- describe('of owner "owner-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'owner-member-user',
- }
- })
-
- describe('by owner themself "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
-
- // shall this be possible in the future?
- // or shall only an owner who gave the second owner the owner role downgrade themself for savety?
- // otherwise the first owner who downgrades the other one has the victory over the group!
- describe('by second owner "second-owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await secondOwnerMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- describe('to same role owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
-
- it('has role owner still', async () => {
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- })
- })
- })
- })
-
- describe('by admin "admin-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await adminMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
-
- describe('by usual member "usual-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
-
- describe('by still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
- })
-
- describe('to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- })
-
- describe('of admin "admin-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
- })
-
- describe('by owner "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
- })
-
- describe('to owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
- })
-
- it('has role owner', async () => {
- await expect(
- mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
- })
- })
+ describe('authenticated', () => {
+ describe('in all group types – here "closed-group" for example', () => {
+ beforeEach(async () => {
+ variables = {
+ groupId: 'closed-group',
+ }
+ })
+
+ describe('join the members and give them their prospective roles', () => {
+ describe('by owner "owner-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerMemberUser.toJson()
})
- describe('back to admin', () => {
+ describe('for "usual-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ userId: 'usual-member-user',
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('as usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- describe('by usual member "usual-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
+ it('has role usual', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ })
+ })
+
+ // the GQL mutation needs this fields in the result for testing
+ it.todo('has "updatedAt" newer as "createdAt"')
+ })
})
- describe('upgrade to owner', () => {
+ describe('for "admin-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'owner',
+ userId: 'admin-member-user',
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('as admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ // Wolle:
+ // const groups = await query({ query: groupQuery, variables: {} })
+ // console.log('groups.data.Group: ', groups.data.Group)
+ // const groupMemberOfClosedGroup = await mutate({
+ // mutation: groupMembersQuery,
+ // variables: {
+ // id: 'closed-group',
+ // },
+ // })
+ // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to usual', () => {
+ describe('for "second-owner-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ userId: 'second-owner-member-user',
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('as owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'second-owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
+ })
- describe('by still pending member "pending-member-user"', () => {
+ describe('switch role', () => {
+ describe('of owner "owner-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'owner-member-user',
+ }
})
- describe('upgrade to owner', () => {
+ describe('by owner themself "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to usual', () => {
+ // shall this be possible in the future?
+ // or shall only an owner who gave the second owner the owner role downgrade themself for savety?
+ // otherwise the first owner who downgrades the other one has the victory over the group!
+ describe('by second owner "second-owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await secondOwnerMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
-
- describe('by none member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
- })
- describe('upgrade to owner', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('to same role owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner still', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'owner-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to pending again', () => {
+ describe('by admin "admin-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await adminMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- })
-
- describe('of usual member "usual-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'usual-member-user',
- }
- })
- describe('by owner "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- describe('to admin', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
+ authenticatedUser = await usualMemberUser.toJson()
})
- it('has role admin', async () => {
- await expect(
- mutate({
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
- describe('back to usual', () => {
+ describe('by still pending member "pending-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await pendingMemberUser.toJson()
})
- it('has role usual again', async () => {
- await expect(
- mutate({
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
})
- describe('by usual member "usual-member-user"', () => {
+ describe('of admin "admin-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
})
- describe('upgrade to admin', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('has role owner', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'admin-member-user',
+ myRoleInGroup: 'owner',
+ },
+ },
+ errors: undefined,
+ })
+ })
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('back to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to pending', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await usualMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- })
- })
- describe('by still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
+ describe('degrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- describe('upgrade to admin', () => {
+ describe('by still pending member "pending-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
+ authenticatedUser = await pendingMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to pending', () => {
+ describe('by none member "current-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await user.toJson()
+ })
+
+ describe('upgrade to owner', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'owner',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
- describe('by none member "current-user"', () => {
+ describe('of usual member "usual-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ variables = {
+ ...variables,
+ userId: 'usual-member-user',
+ }
})
- describe('upgrade to admin', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
+ })
+
+ describe('to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('has role admin', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'admin',
+ },
+ },
+ errors: undefined,
+ })
+ })
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('back to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual again', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'usual-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- describe('degrade to pending again', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await usualMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- })
- })
- })
- describe('of still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'pending-member-user',
- }
- })
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
- describe('by owner "owner-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- describe('to usual', () => {
+ describe('by still pending member "pending-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await pendingMemberUser.toJson()
})
- it('has role usual', async () => {
- await expect(
- mutate({
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'pending-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
- describe('back to pending', () => {
+ describe('by none member "current-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await user.toJson()
})
- it('has role usual again', async () => {
- await expect(
- mutate({
+ describe('upgrade to admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'pending-member-user',
- myRoleInGroup: 'pending',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
})
- describe('by usual member "usual-member-user"', () => {
+ describe('of still pending member "pending-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'pending-member-user',
+ }
})
- describe('upgrade to usual', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('has role usual', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'usual',
+ },
+ },
+ errors: undefined,
+ })
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- })
- })
- describe('by still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
+ describe('back to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
+ })
+
+ it('has role usual again', async () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'pending',
+ },
+ },
+ errors: undefined,
+ })
+ })
+ })
})
- describe('upgrade to usual', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await usualMemberUser.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- })
- describe('by none member "current-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ describe('by still pending member "pending-member-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await pendingMemberUser.toJson()
+ })
+
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
})
- describe('upgrade to usual', () => {
+ describe('by none member "current-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await user.toJson()
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
From cd090420cf318f5b9fbd7618ac3a0f8d65d67700 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 12:30:01 +0200
Subject: [PATCH 069/374] Move 'JoinGroup' to building up mode in
'groups.spec.js'
---
backend/src/schema/resolvers/groups.spec.js | 56 +++++++++++++--------
1 file changed, 35 insertions(+), 21 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 054e3abb05..e966506a71 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -122,34 +122,40 @@ describe('in mode', () => {
})
it('creates a group', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: {
- CreateGroup: {
- name: 'The Best Group',
- slug: 'the-group',
- about: 'We will change the world!',
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ slug: 'the-group',
+ about: 'We will change the world!',
+ },
},
+ errors: undefined,
},
- errors: undefined,
- })
+ )
})
it('assigns the authenticated user as owner', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: {
- CreateGroup: {
- name: 'The Best Group',
- myRole: 'owner',
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ {
+ data: {
+ CreateGroup: {
+ name: 'The Best Group',
+ myRole: 'owner',
+ },
},
+ errors: undefined,
},
- errors: undefined,
- })
+ )
})
it('has "disabled" and "deleted" default to "false"', async () => {
- await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject({
- data: { CreateGroup: { disabled: false, deleted: false } },
- })
+ await expect(mutate({ mutation: createGroupMutation, variables })).resolves.toMatchObject(
+ {
+ data: { CreateGroup: { disabled: false, deleted: false } },
+ },
+ )
})
describe('description', () => {
@@ -313,8 +319,18 @@ describe('in mode', () => {
})
})
})
+ })
+ describe('building up – clean db after each resolver', () => {
describe('JoinGroup', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
+
+ afterAll(async () => {
+ await cleanDatabase()
+ })
+
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({
@@ -332,7 +348,7 @@ describe('in mode', () => {
let ownerOfClosedGroupUser
let ownerOfHiddenGroupUser
- beforeEach(async () => {
+ beforeAll(async () => {
// create users
ownerOfClosedGroupUser = await Factory.build(
'user',
@@ -535,9 +551,7 @@ describe('in mode', () => {
})
})
})
- })
- describe('building up – clean db after each resolver', () => {
describe('GroupMembers', () => {
beforeAll(async () => {
await seedBasicsAndClearAuthentication()
From 47027ad86bca4e82b4d314f880d5f78260669a06 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 12:47:57 +0200
Subject: [PATCH 070/374] Move 'Group' resolver to building up mode in
'groups.spec.js'
---
backend/src/schema/resolvers/groups.spec.js | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index e966506a71..7a5f9504a9 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -204,8 +204,18 @@ describe('in mode', () => {
})
})
})
+ })
+ describe('building up – clean db after each resolver', () => {
describe('Group', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
+ })
+
+ afterAll(async () => {
+ await cleanDatabase()
+ })
+
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupQuery, variables: {} })
@@ -216,7 +226,7 @@ describe('in mode', () => {
describe('authenticated', () => {
let otherUser
- beforeEach(async () => {
+ beforeAll(async () => {
otherUser = await Factory.build(
'user',
{
@@ -319,9 +329,7 @@ describe('in mode', () => {
})
})
})
- })
- describe('building up – clean db after each resolver', () => {
describe('JoinGroup', () => {
beforeAll(async () => {
await seedBasicsAndClearAuthentication()
From 72eedef8d0d5b5f2e53858e6fee9c15f2e17b15e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 23 Aug 2022 13:12:38 +0200
Subject: [PATCH 071/374] Cleanup
---
.../src/middleware/permissionsMiddleware.js | 62 -------------------
backend/src/schema/resolvers/groups.js | 8 ---
backend/src/schema/resolvers/groups.spec.js | 20 ------
3 files changed, 90 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 926f824e4d..bfd10537a1 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -57,8 +57,6 @@ const isAllowedSeeingMembersOfGroup = rule({
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
const { id: groupId } = args
- // Wolle: console.log('groupId: ', groupId)
- // console.log('user.id: ', user.id)
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
@@ -76,8 +74,6 @@ const isAllowedSeeingMembersOfGroup = rule({
})
try {
const { member, group } = await readTxPromise
- // Wolle: console.log('member: ', member)
- // console.log('group: ', group)
return (
!!group &&
(group.groupType === 'public' ||
@@ -86,7 +82,6 @@ const isAllowedSeeingMembersOfGroup = rule({
['usual', 'admin', 'owner'].includes(member.myRoleInGroup)))
)
} catch (error) {
- // Wolle: console.log('error: ', error)
throw new Error(error)
} finally {
session.close()
@@ -100,12 +95,6 @@ const isAllowedToChangeGroupMemberRole = rule({
const adminId = user.id
const { groupId, userId, roleInGroup } = args
if (adminId === userId) return false
- // Wolle:
- // console.log('isAllowedToChangeGroupMemberRole !!!')
- // console.log('adminId: ', adminId)
- // console.log('groupId: ', groupId)
- // console.log('userId: ', userId)
- // console.log('roleInGroup: ', roleInGroup)
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
@@ -116,23 +105,6 @@ const isAllowedToChangeGroupMemberRole = rule({
`,
{ groupId, adminId, userId },
)
- // Wolle:
- // console.log(
- // 'transactionResponse: ',
- // transactionResponse,
- // )
- // console.log(
- // 'transaction admins: ',
- // transactionResponse.records.map((record) => record.get('admin')),
- // )
- // console.log(
- // 'transaction groups: ',
- // transactionResponse.records.map((record) => record.get('group')),
- // )
- // console.log(
- // 'transaction members: ',
- // transactionResponse.records.map((record) => record.get('member')),
- // )
return {
admin: transactionResponse.records.map((record) => record.get('admin'))[0],
group: transactionResponse.records.map((record) => record.get('group'))[0],
@@ -140,14 +112,7 @@ const isAllowedToChangeGroupMemberRole = rule({
}
})
try {
- // Wolle:
- // console.log('enter try !!!')
const { admin, group, member } = await readTxPromise
- // Wolle:
- // console.log('after !!!')
- // console.log('admin: ', admin)
- // console.log('group: ', group)
- // console.log('member: ', member)
return (
!!group &&
!!admin &&
@@ -160,8 +125,6 @@ const isAllowedToChangeGroupMemberRole = rule({
['pending', 'usual', 'admin', 'owner'].includes(roleInGroup)))
)
} catch (error) {
- // Wolle:
- // console.log('error: ', error)
throw new Error(error)
} finally {
session.close()
@@ -173,11 +136,6 @@ const isAllowedToJoinGroup = rule({
})(async (_parent, args, { user, driver }) => {
if (!(user && user.id)) return false
const { groupId, userId } = args
- // Wolle:
- // console.log('adminId: ', adminId)
- // console.log('groupId: ', groupId)
- // console.log('userId: ', userId)
- // console.log('roleInGroup: ', roleInGroup)
const session = driver.session()
const readTxPromise = session.readTransaction(async (transaction) => {
const transactionResponse = await transaction.run(
@@ -188,35 +146,15 @@ const isAllowedToJoinGroup = rule({
`,
{ groupId, userId },
)
- // Wolle:
- // console.log(
- // 'transactionResponse: ',
- // transactionResponse,
- // )
- // console.log(
- // 'transaction groups: ',
- // transactionResponse.records.map((record) => record.get('group')),
- // )
- // console.log(
- // 'transaction members: ',
- // transactionResponse.records.map((record) => record.get('member')),
- // )
return {
group: transactionResponse.records.map((record) => record.get('group'))[0],
member: transactionResponse.records.map((record) => record.get('member'))[0],
}
})
try {
- // Wolle:
- // console.log('enter try !!!')
const { group, member } = await readTxPromise
- // Wolle:
- // console.log('after !!!')
- // console.log('group: ', group)
- // console.log('member: ', member)
return !!group && (group.groupType !== 'hidden' || (!!member && !!member.myRoleInGroup))
} catch (error) {
- // Wolle: console.log('error: ', error)
throw new Error(error)
} finally {
session.close()
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 6ca09d72af..c47cf60858 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -48,7 +48,6 @@ export default {
},
GroupMembers: async (_object, params, context, _resolveInfo) => {
const { id: groupId } = params
- // Wolle: console.log('groupId: ', groupId)
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
const groupMemberCypher = `
@@ -160,11 +159,6 @@ export default {
},
ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => {
const { groupId, userId, roleInGroup } = params
- // Wolle
- // console.log('ChangeGroupMemberRole !!!')
- // console.log('groupId: ', groupId)
- // console.log('userId: ', userId)
- // console.log('roleInGroup: ', roleInGroup)
const session = context.driver.session()
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
const joinGroupCypher = `
@@ -180,8 +174,6 @@ export default {
`
const result = await transaction.run(joinGroupCypher, { groupId, userId, roleInGroup })
const [member] = await result.records.map((record) => record.get('member'))
- // Wolle
- // console.log('member: ', member)
return member
})
try {
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 7a5f9504a9..be9ba4f3d2 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -728,16 +728,6 @@ describe('in mode', () => {
roleInGroup: 'admin',
},
})
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMembersQuery,
- // variables: {
- // id: 'hidden-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
authenticatedUser = null
})
@@ -1340,16 +1330,6 @@ describe('in mode', () => {
})
it('has role admin', async () => {
- // Wolle:
- // const groups = await query({ query: groupQuery, variables: {} })
- // console.log('groups.data.Group: ', groups.data.Group)
- // const groupMemberOfClosedGroup = await mutate({
- // mutation: groupMembersQuery,
- // variables: {
- // id: 'closed-group',
- // },
- // })
- // console.log('groupMemberOfClosedGroup.data.GroupMembers: ', groupMemberOfClosedGroup.data.GroupMembers)
await expect(
mutate({
mutation: changeGroupMemberRoleMutation,
From c3ab014d9c7f371bf96cdbc641d918ed681eb1e5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 25 Aug 2022 13:44:05 +0200
Subject: [PATCH 072/374] Set 'updatedAt' to 'null' on 'MEMBER_OF' creation in
all group resolvers
---
backend/src/schema/resolvers/groups.js | 8 +++++--
backend/src/schema/types/type/Group.gql | 30 ++++++++++++-------------
2 files changed, 21 insertions(+), 17 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index c47cf60858..abaa1716f1 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -107,8 +107,10 @@ export default {
MATCH (owner:User {id: $userId})
MERGE (owner)-[:CREATED]->(group)
MERGE (owner)-[membership:MEMBER_OF]->(group)
- SET membership.createdAt = toString(datetime())
- SET membership.role = 'owner'
+ SET
+ membership.createdAt = toString(datetime()),
+ membership.updatedAt = null,
+ membership.role = 'owner'
${categoriesCypher}
RETURN group {.*, myRole: membership.role}
`,
@@ -138,6 +140,7 @@ export default {
MERGE (member)-[membership:MEMBER_OF]->(group)
ON CREATE SET
membership.createdAt = toString(datetime()),
+ membership.updatedAt = null,
membership.role =
CASE WHEN group.groupType = 'public'
THEN 'usual'
@@ -166,6 +169,7 @@ export default {
MERGE (member)-[membership:MEMBER_OF]->(group)
ON CREATE SET
membership.createdAt = toString(datetime()),
+ membership.updatedAt = null,
membership.role = $roleInGroup
ON MATCH SET
membership.updatedAt = toString(datetime()),
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 5c98e49e8a..e254e50869 100644
--- a/backend/src/schema/types/type/Group.gql
+++ b/backend/src/schema/types/type/Group.gql
@@ -59,7 +59,7 @@ input _GroupFilter {
type Query {
Group(
- isMember: Boolean # if 'undefined' or 'null' then all groups
+ isMember: Boolean # if 'undefined' or 'null' then get all groups
id: ID
name: String
slug: String
@@ -81,11 +81,11 @@ type Query {
filter: _UserFilter
): [User]
- AvailableGroupTypes: [GroupType]!
+ # AvailableGroupTypes: [GroupType]!
- AvailableGroupActionRadii: [GroupActionRadius]!
+ # AvailableGroupActionRadii: [GroupActionRadius]!
- AvailableGroupMemberRoles: [GroupMemberRole]!
+ # AvailableGroupMemberRoles: [GroupMemberRole]!
}
type Mutation {
@@ -102,17 +102,17 @@ type Mutation {
locationName: String
): Group
- UpdateGroup(
- id: ID!
- name: String
- slug: String
- avatar: ImageInput
- locationName: String
- about: String
- description: String
- ): Group
-
- DeleteGroup(id: ID!): Group
+ # UpdateGroup(
+ # id: ID!
+ # name: String
+ # slug: String
+ # avatar: ImageInput
+ # locationName: String
+ # about: String
+ # description: String
+ # ): Group
+
+ # DeleteGroup(id: ID!): Group
JoinGroup(
groupId: ID!
From 921adb9d8b785d73eb6ec734cc3646dda63368da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 25 Aug 2022 13:47:37 +0200
Subject: [PATCH 073/374] Fixed all tests
---
backend/src/schema/resolvers/groups.spec.js | 62 ++++++++++-----------
1 file changed, 31 insertions(+), 31 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index be9ba4f3d2..1d272de2be 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -112,7 +112,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: createGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -219,7 +219,7 @@ describe('in mode', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -348,7 +348,7 @@ describe('in mode', () => {
userId: 'current-user',
},
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -529,7 +529,7 @@ describe('in mode', () => {
userId: 'owner-of-closed-group',
},
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -575,7 +575,7 @@ describe('in mode', () => {
id: 'not-existing-group',
}
const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -922,7 +922,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -933,7 +933,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1065,7 +1065,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1076,7 +1076,7 @@ describe('in mode', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupMembersQuery, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1257,7 +1257,7 @@ describe('in mode', () => {
roleInGroup: 'pending',
},
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1412,7 +1412,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1438,7 +1438,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1487,7 +1487,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1510,7 +1510,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1533,7 +1533,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1591,7 +1591,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1614,7 +1614,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1631,7 +1631,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1654,7 +1654,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1671,7 +1671,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1694,7 +1694,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1711,7 +1711,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1801,7 +1801,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1818,7 +1818,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1841,7 +1841,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1858,7 +1858,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1881,7 +1881,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -1898,7 +1898,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -1988,7 +1988,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -2011,7 +2011,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
@@ -2034,7 +2034,7 @@ describe('in mode', () => {
mutation: changeGroupMemberRoleMutation,
variables,
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
})
From 512784e580dee2b995bb5c8f09c0278b247a74f1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Thu, 25 Aug 2022 20:17:23 +0200
Subject: [PATCH 074/374] Fix group tests
---
backend/src/schema/resolvers/groups.spec.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index 707558a063..6890e9147b 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -89,7 +89,7 @@ describe('Group', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
@@ -225,7 +225,7 @@ describe('CreateGroup', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
const { errors } = await mutate({ mutation: createGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ expect(errors[0]).toHaveProperty('message', 'Not Authorized!')
})
})
From 623af5f47ba11fc71abda14848c1fffbde38bc0f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 11:18:40 +0200
Subject: [PATCH 075/374] Query for group 'id' directly in resolver
---
backend/src/schema/resolvers/groups.js | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index 5737f5505f..2d9dde1ff7 100644
--- a/backend/src/schema/resolvers/groups.js
+++ b/backend/src/schema/resolvers/groups.js
@@ -9,25 +9,26 @@ import Resolver from './helpers/Resolver'
export default {
Query: {
Group: async (_object, params, context, _resolveInfo) => {
- const { isMember } = params
+ const { id: groupId, isMember } = params
const session = context.driver.session()
const readTxResultPromise = session.readTransaction(async (txc) => {
+ const groupIdCypher = groupId ? ` {id: "${groupId}"}` : ''
let groupCypher
if (isMember === true) {
groupCypher = `
- MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group)
+ MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group${groupIdCypher})
RETURN group {.*, myRole: membership.role}
`
} else {
if (isMember === false) {
groupCypher = `
- MATCH (group:Group)
+ MATCH (group:Group${groupIdCypher})
WHERE NOT (:User {id: $userId})-[:MEMBER_OF]->(group)
RETURN group {.*, myRole: NULL}
`
} else {
groupCypher = `
- MATCH (group:Group)
+ MATCH (group:Group${groupIdCypher})
OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group)
RETURN group {.*, myRole: membership.role}
`
From 39487778bf096fe4c3ad068c5bec0e311d2058d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 11:26:27 +0200
Subject: [PATCH 076/374] Implement group profile, first step
---
webapp/graphql/groups.js | 95 ++++++++
webapp/locales/de.json | 19 +-
webapp/locales/en.json | 19 +-
webapp/pages/group/_id.spec.js | 2 +-
webapp/pages/group/_id.vue | 8 +-
webapp/pages/group/_id/_slug.vue | 401 ++++++++++++++++---------------
6 files changed, 325 insertions(+), 219 deletions(-)
create mode 100644 webapp/graphql/groups.js
diff --git a/webapp/graphql/groups.js b/webapp/graphql/groups.js
new file mode 100644
index 0000000000..579380b246
--- /dev/null
+++ b/webapp/graphql/groups.js
@@ -0,0 +1,95 @@
+import gql from 'graphql-tag'
+
+// ------ mutations
+
+export const createGroupMutation = gql`
+ mutation(
+ $id: ID
+ $name: String!
+ $slug: String
+ $about: String
+ $description: String!
+ $groupType: GroupType!
+ $actionRadius: GroupActionRadius!
+ $categoryIds: [ID]
+ ) {
+ CreateGroup(
+ id: $id
+ name: $name
+ slug: $slug
+ about: $about
+ description: $description
+ groupType: $groupType
+ actionRadius: $actionRadius
+ categoryIds: $categoryIds
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ }
+ }
+`
+
+// ------ queries
+
+export const groupQuery = gql`
+ query(
+ $isMember: Boolean
+ $id: ID
+ $name: String
+ $slug: String
+ $createdAt: String
+ $updatedAt: String
+ $about: String
+ $description: String
+ $locationName: String
+ $first: Int
+ $offset: Int
+ $orderBy: [_GroupOrdering]
+ $filter: _GroupFilter
+ ) {
+ Group(
+ isMember: $isMember
+ id: $id
+ name: $name
+ slug: $slug
+ createdAt: $createdAt
+ updatedAt: $updatedAt
+ about: $about
+ description: $description
+ locationName: $locationName
+ first: $first
+ offset: $offset
+ orderBy: $orderBy
+ filter: $filter
+ ) {
+ id
+ name
+ slug
+ createdAt
+ updatedAt
+ disabled
+ deleted
+ about
+ description
+ groupType
+ actionRadius
+ myRole
+ categories {
+ id
+ slug
+ name
+ icon
+ }
+ }
+ }
+`
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 297daa511b..07a71dfcf5 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -333,15 +333,16 @@
"placeholder": "Schreib etwas Inspirierendes …"
},
"error-pages": {
- "403-default": "Kein Zugang zu dieser Seite",
- "404-default": "Diese Seite konnte nicht gefunden werden",
- "500-default": "Internal Server Error",
- "503-default": "Dienst steht nicht zur Verfügung",
- "back-to-index": "Zurück zur Startseite",
- "cannot-edit-post": "Dieser Beitrag kann nicht editiert werden",
- "default": "Ein Fehler ist aufgetreten",
- "post-not-found": "Dieser Beitrag konnte nicht gefunden werden",
- "profile-not-found": "Dieses Profil konnte nicht gefunden werden"
+ "403-default": "Kein Zugang zu dieser Seite!",
+ "404-default": "Diese Seite konnte nicht gefunden werden!",
+ "500-default": "Internal Server Error!",
+ "503-default": "Dienst steht nicht zur Verfügung!",
+ "back-to-index": "Zurück zur Startseite!",
+ "cannot-edit-post": "Dieser Beitrag kann nicht editiert werden!",
+ "default": "Ein Fehler ist aufgetreten!",
+ "group-not-found": "Dieses Gruppenprofil konnte nicht gefunden werden!",
+ "post-not-found": "Dieser Beitrag konnte nicht gefunden werden!",
+ "profile-not-found": "Dieses Profil konnte nicht gefunden werden!"
},
"filter-menu": {
"all": "Alle",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 8499b0290c..1b16431e34 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -333,15 +333,16 @@
"placeholder": "Leave your inspirational thoughts …"
},
"error-pages": {
- "403-default": "Not authorized to this page",
- "404-default": "This page could not be found",
- "500-default": "Internal Server Error",
- "503-default": "Service Unavailable",
- "back-to-index": "Back to index page",
- "cannot-edit-post": "This post cannot be edited",
- "default": "An error occurred",
- "post-not-found": "This post could not be found",
- "profile-not-found": "This profile could not be found"
+ "403-default": "Not authorized to this page!",
+ "404-default": "This page could not be found!",
+ "500-default": "Internal Server Error!",
+ "503-default": "Service Unavailable!",
+ "back-to-index": "Back to index page!",
+ "cannot-edit-post": "This post cannot be edited!",
+ "default": "An error occurred!",
+ "group-not-found": "This group profile could not be found!",
+ "post-not-found": "This post could not be found!",
+ "profile-not-found": "This profile could not be found!"
},
"filter-menu": {
"all": "All",
diff --git a/webapp/pages/group/_id.spec.js b/webapp/pages/group/_id.spec.js
index aab216569c..bafd5c3925 100644
--- a/webapp/pages/group/_id.spec.js
+++ b/webapp/pages/group/_id.spec.js
@@ -5,7 +5,7 @@ const localVue = global.localVue
config.stubs['nuxt-child'] = ' '
-describe('Profile _id.vue', () => {
+describe('Group profile _id.vue', () => {
let wrapper
let Wrapper
let mocks
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
index b9bbef83e6..047d12d7f6 100644
--- a/webapp/pages/group/_id.vue
+++ b/webapp/pages/group/_id.vue
@@ -9,7 +9,7 @@ import PersistentLinks from '~/mixins/persistentLinks.js'
const options = {
queryId: gql`
query($idOrSlug: ID) {
- User(id: $idOrSlug) {
+ Group(id: $idOrSlug) {
id
slug
}
@@ -17,14 +17,14 @@ const options = {
`,
querySlug: gql`
query($idOrSlug: String) {
- User(slug: $idOrSlug) {
+ Group(slug: $idOrSlug) {
id
slug
}
}
`,
- message: 'error-pages.profile-not-found',
- path: 'profile',
+ message: 'error-pages.group-not-found',
+ path: 'group',
}
const persistentLinks = PersistentLinks(options)
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index 4fef7d3aa8..365fcc5577 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -1,23 +1,24 @@
-
+
-
-
+
+
+
-
+
-
+
- {{ userName }}
+ {{ groupName }}
- {{ userSlug }}
+ {{ groupSlug }}
-
+
- {{ $t('profile.memberSince') }} {{ user.createdAt | date('MMMM yyyy') }}
+ {{ $t('profile.memberSince') }} {{ group.createdAt | date('MMMM yyyy') }}
-
+
-
+
-
+
-
+
+
- {{ user.about }}
+ {{ group.about }}
@@ -90,7 +91,7 @@
{{ $t('profile.network.title') }}
-
-
+ /> -->
+
-
+
+
-
+
@@ -184,19 +185,20 @@ import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
import TabNavigation from '~/components/_new/generic/TabNavigation/TabNavigation'
import { profilePagePosts } from '~/graphql/PostQuery'
-import UserQuery from '~/graphql/User'
+import { groupQuery } from '~/graphql/groups'
import { muteUser, unmuteUser } from '~/graphql/settings/MutedUsers'
import { blockUser, unblockUser } from '~/graphql/settings/BlockedUsers'
import UpdateQuery from '~/components/utils/UpdateQuery'
import SocialMedia from '~/components/SocialMedia/SocialMedia'
-const tabToFilterMapping = ({ tab, id }) => {
- return {
- post: { author: { id } },
- comment: { comments_some: { author: { id } } },
- shout: { shoutedBy_some: { id } },
- }[tab]
-}
+// Wolle:
+// const tabToFilterMapping = ({ tab, id }) => {
+// return {
+// post: { author: { id } },
+// comment: { comments_some: { author: { id } } },
+// shout: { shoutedBy_some: { id } },
+// }[tab]
+// }
export default {
components: {
@@ -220,194 +222,201 @@ export default {
mode: 'out-in',
},
data() {
- const filter = tabToFilterMapping({ tab: 'post', id: this.$route.params.id })
+ // Wolle:
+ // const filter = tabToFilterMapping({ tab: 'post', id: this.$route.params.id })
return {
- User: [],
+ Group: [],
posts: [],
hasMore: true,
offset: 0,
pageSize: 6,
tabActive: 'post',
- filter,
+ // Wolle: filter,
followedByCountStartValue: 0,
followedByCount: 7,
followingCount: 7,
}
},
computed: {
- myProfile() {
- return this.$route.params.id === this.$store.getters['auth/user'].id
+ isMyGroup() {
+ return this.group.myRole
},
- user() {
- return this.User ? this.User[0] : {}
+ group() {
+ return this.Group ? this.Group[0] : {}
},
- userName() {
- const { name } = this.user || {}
+ groupName() {
+ const { name } = this.group || {}
return name || this.$t('profile.userAnonym')
},
- userSlug() {
- const { slug } = this.user || {}
+ groupSlug() {
+ const { slug } = this.group || {}
return slug && `@${slug}`
},
- tabOptions() {
- return [
- {
- type: 'post',
- title: this.$t('common.post', null, this.user.contributionsCount),
- count: this.user.contributionsCount,
- disabled: this.user.contributionsCount === 0,
- },
- {
- type: 'comment',
- title: this.$t('profile.commented'),
- count: this.user.commentedCount,
- disabled: this.user.commentedCount === 0,
- },
- {
- type: 'shout',
- title: this.$t('profile.shouted'),
- count: this.user.shoutedCount,
- disabled: this.user.shoutedCount === 0,
- },
- ]
- },
+ // tabOptions() {
+ // return [
+ // {
+ // type: 'post',
+ // title: this.$t('common.post', null, this.user.contributionsCount),
+ // count: this.user.contributionsCount,
+ // disabled: this.user.contributionsCount === 0,
+ // },
+ // {
+ // type: 'comment',
+ // title: this.$t('profile.commented'),
+ // count: this.user.commentedCount,
+ // disabled: this.user.commentedCount === 0,
+ // },
+ // {
+ // type: 'shout',
+ // title: this.$t('profile.shouted'),
+ // count: this.user.shoutedCount,
+ // disabled: this.user.shoutedCount === 0,
+ // },
+ // ]
+ // },
},
methods: {
- handleTab(tab) {
- if (this.tabActive !== tab) {
- this.tabActive = tab
- this.filter = tabToFilterMapping({ tab, id: this.$route.params.id })
- this.resetPostList()
- }
- },
+ // Wolle: handleTab(tab) {
+ // if (this.tabActive !== tab) {
+ // this.tabActive = tab
+ // this.filter = tabToFilterMapping({ tab, id: this.$route.params.id })
+ // this.resetPostList()
+ // }
+ // },
uniq(items, field = 'id') {
return uniqBy(items, field)
},
- showMoreContributions($state) {
- const { profilePagePosts: PostQuery } = this.$apollo.queries
- if (!PostQuery) return // seems this can be undefined on subpages
- this.offset += this.pageSize
+ // Wolle:
+ // showMoreContributions($state) {
+ // const { profilePagePosts: PostQuery } = this.$apollo.queries
+ // if (!PostQuery) return // seems this can be undefined on subpages
+ // this.offset += this.pageSize
- PostQuery.fetchMore({
- variables: {
- offset: this.offset,
- filter: this.filter,
- first: this.pageSize,
- orderBy: 'createdAt_desc',
- },
- updateQuery: UpdateQuery(this, { $state, pageKey: 'profilePagePosts' }),
- })
- },
- resetPostList() {
- this.offset = 0
- this.posts = []
- this.hasMore = true
- },
- refetchPostList() {
- this.resetPostList()
- this.$apollo.queries.profilePagePosts.refetch()
- },
- async muteUser(user) {
- try {
- await this.$apollo.mutate({ mutation: muteUser(), variables: { id: user.id } })
- } catch (error) {
- this.$toast.error(error.message)
- } finally {
- this.$apollo.queries.User.refetch()
- this.resetPostList()
- this.$apollo.queries.profilePagePosts.refetch()
- }
- },
- async unmuteUser(user) {
- try {
- this.$apollo.mutate({ mutation: unmuteUser(), variables: { id: user.id } })
- } catch (error) {
- this.$toast.error(error.message)
- } finally {
- this.$apollo.queries.User.refetch()
- this.resetPostList()
- this.$apollo.queries.profilePagePosts.refetch()
- }
- },
- async blockUser(user) {
- try {
- await this.$apollo.mutate({ mutation: blockUser(), variables: { id: user.id } })
- } catch (error) {
- this.$toast.error(error.message)
- } finally {
- this.$apollo.queries.User.refetch()
- }
- },
- async unblockUser(user) {
- try {
- this.$apollo.mutate({ mutation: unblockUser(), variables: { id: user.id } })
- } catch (error) {
- this.$toast.error(error.message)
- } finally {
- this.$apollo.queries.User.refetch()
- }
- },
- async deleteUser(userdata) {
- this.$store.commit('modal/SET_OPEN', {
- name: 'delete',
- data: {
- userdata: userdata,
- },
- })
- },
- optimisticFollow({ followedByCurrentUser }) {
- /*
- * Note: followedByCountStartValue is updated to avoid counting from 0 when follow/unfollow
- */
- this.followedByCountStartValue = this.user.followedByCount
- const currentUser = this.$store.getters['auth/user']
- if (followedByCurrentUser) {
- this.user.followedByCount++
- this.user.followedBy = [currentUser, ...this.user.followedBy]
- } else {
- this.user.followedByCount--
- this.user.followedBy = this.user.followedBy.filter((user) => user.id !== currentUser.id)
- }
- this.user.followedByCurrentUser = followedByCurrentUser
- },
- updateFollow({ followedByCurrentUser, followedBy, followedByCount }) {
- this.followedByCountStartValue = this.user.followedByCount
- this.user.followedByCount = followedByCount
- this.user.followedByCurrentUser = followedByCurrentUser
- this.user.followedBy = followedBy
- },
- fetchAllConnections(type) {
- if (type === 'following') this.followingCount = Infinity
- if (type === 'followedBy') this.followedByCount = Infinity
- },
+ // PostQuery.fetchMore({
+ // variables: {
+ // offset: this.offset,
+ // filter: this.filter,
+ // first: this.pageSize,
+ // orderBy: 'createdAt_desc',
+ // },
+ // updateQuery: UpdateQuery(this, { $state, pageKey: 'profilePagePosts' }),
+ // })
+ // },
+ // resetPostList() {
+ // this.offset = 0
+ // this.posts = []
+ // this.hasMore = true
+ // },
+ // refetchPostList() {
+ // this.resetPostList()
+ // this.$apollo.queries.profilePagePosts.refetch()
+ // },
+ // async muteUser(user) {
+ // try {
+ // await this.$apollo.mutate({ mutation: muteUser(), variables: { id: user.id } })
+ // } catch (error) {
+ // this.$toast.error(error.message)
+ // } finally {
+ // this.$apollo.queries.User.refetch()
+ // this.resetPostList()
+ // this.$apollo.queries.profilePagePosts.refetch()
+ // }
+ // },
+ // async unmuteUser(user) {
+ // try {
+ // this.$apollo.mutate({ mutation: unmuteUser(), variables: { id: user.id } })
+ // } catch (error) {
+ // this.$toast.error(error.message)
+ // } finally {
+ // this.$apollo.queries.User.refetch()
+ // this.resetPostList()
+ // this.$apollo.queries.profilePagePosts.refetch()
+ // }
+ // },
+ // async blockUser(user) {
+ // try {
+ // await this.$apollo.mutate({ mutation: blockUser(), variables: { id: user.id } })
+ // } catch (error) {
+ // this.$toast.error(error.message)
+ // } finally {
+ // this.$apollo.queries.User.refetch()
+ // }
+ // },
+ // async unblockUser(user) {
+ // try {
+ // this.$apollo.mutate({ mutation: unblockUser(), variables: { id: user.id } })
+ // } catch (error) {
+ // this.$toast.error(error.message)
+ // } finally {
+ // this.$apollo.queries.User.refetch()
+ // }
+ // },
+ // async deleteUser(userdata) {
+ // this.$store.commit('modal/SET_OPEN', {
+ // name: 'delete',
+ // data: {
+ // userdata: userdata,
+ // },
+ // })
+ // },
+ // Wolle:
+ // optimisticFollow({ followedByCurrentUser }) {
+ // /*
+ // * Note: followedByCountStartValue is updated to avoid counting from 0 when follow/unfollow
+ // */
+ // this.followedByCountStartValue = this.user.followedByCount
+ // const currentUser = this.$store.getters['auth/user']
+ // if (followedByCurrentUser) {
+ // this.user.followedByCount++
+ // this.user.followedBy = [currentUser, ...this.user.followedBy]
+ // } else {
+ // this.user.followedByCount--
+ // this.user.followedBy = this.user.followedBy.filter((user) => user.id !== currentUser.id)
+ // }
+ // this.user.followedByCurrentUser = followedByCurrentUser
+ // },
+ // Wolle:
+ // updateFollow({ followedByCurrentUser, followedBy, followedByCount }) {
+ // this.followedByCountStartValue = this.user.followedByCount
+ // this.user.followedByCount = followedByCount
+ // this.user.followedByCurrentUser = followedByCurrentUser
+ // this.user.followedBy = followedBy
+ // },
+ // Wolle:
+ // fetchAllConnections(type) {
+ // if (type === 'following') this.followingCount = Infinity
+ // if (type === 'followedBy') this.followedByCount = Infinity
+ // },
},
apollo: {
- profilePagePosts: {
- query() {
- return profilePagePosts(this.$i18n)
- },
- variables() {
- return {
- filter: this.filter,
- first: this.pageSize,
- offset: 0,
- orderBy: 'createdAt_desc',
- }
- },
- update({ profilePagePosts }) {
- this.posts = profilePagePosts
- },
- fetchPolicy: 'cache-and-network',
- },
- User: {
+ // Wolle:
+ // profilePagePosts: {
+ // query() {
+ // return profilePagePosts(this.$i18n)
+ // },
+ // variables() {
+ // return {
+ // filter: this.filter,
+ // first: this.pageSize,
+ // offset: 0,
+ // orderBy: 'createdAt_desc',
+ // }
+ // },
+ // update({ profilePagePosts }) {
+ // this.posts = profilePagePosts
+ // },
+ // fetchPolicy: 'cache-and-network',
+ // },
+ Group: {
query() {
- return UserQuery(this.$i18n)
+ // Wolle: return groupQuery(this.$i18n) // language will be needed for lacations
+ return groupQuery
},
variables() {
return {
id: this.$route.params.id,
- followedByCount: this.followedByCount,
- followingCount: this.followingCount,
+ // followedByCount: this.followedByCount,
+ // followingCount: this.followingCount,
}
},
fetchPolicy: 'cache-and-network',
From f678dad50a7201b14df29a1c69ac963ff17cafeb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 11:32:20 +0200
Subject: [PATCH 077/374] Fix linting
---
webapp/pages/group/_id.vue | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/webapp/pages/group/_id.vue b/webapp/pages/group/_id.vue
index 047d12d7f6..d743633d1f 100644
--- a/webapp/pages/group/_id.vue
+++ b/webapp/pages/group/_id.vue
@@ -8,7 +8,7 @@ import PersistentLinks from '~/mixins/persistentLinks.js'
const options = {
queryId: gql`
- query($idOrSlug: ID) {
+ query ($idOrSlug: ID) {
Group(id: $idOrSlug) {
id
slug
@@ -16,7 +16,7 @@ const options = {
}
`,
querySlug: gql`
- query($idOrSlug: String) {
+ query ($idOrSlug: String) {
Group(slug: $idOrSlug) {
id
slug
From d1b940b3320b6348e4d44eebaffae038685f1335 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 11:50:06 +0200
Subject: [PATCH 078/374] Implement 'groupSince' and 'groupGoal' in group
profile
---
webapp/locales/de.json | 2 ++
webapp/locales/en.json | 2 ++
webapp/pages/group/_id/_slug.vue | 6 ++++--
3 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 07a71dfcf5..db2046ae31 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -521,6 +521,8 @@
"follow": "Folgen",
"followers": "Folgen",
"following": "Folge Ich",
+ "groupGoal": "Ziel:",
+ "groupSince": "Gründung",
"invites": {
"description": "Zur Einladung die E-Mail-Adresse hier eintragen.",
"emailPlaceholder": "E-Mail-Adresse für die Einladung",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 1b16431e34..5b49c96ee6 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -521,6 +521,8 @@
"follow": "Follow",
"followers": "Followers",
"following": "Following",
+ "groupGoal": "Goal:",
+ "groupSince": "Foundation",
"invites": {
"description": "Enter their e-mail address for invitation.",
"emailPlaceholder": "E-mail to invite",
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index 365fcc5577..326cce74a4 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -39,7 +39,7 @@
{{ user.location.name }}
-->
- {{ $t('profile.memberSince') }} {{ group.createdAt | date('MMMM yyyy') }}
+ {{ $t('profile.groupSince') }} {{ group.createdAt | date('MMMM yyyy') }}
-
+
+
-
+
@@ -178,7 +178,7 @@ import HcBadges from '~/components/Badges.vue'
import FollowList from '~/components/features/FollowList/FollowList'
import HcEmpty from '~/components/Empty/Empty'
import ContentMenu from '~/components/ContentMenu/ContentMenu'
-import HcUpload from '~/components/Upload'
+import AvatarUploader from '~/components/Uploader/AvatarUploader'
import UserAvatar from '~/components/_new/generic/UserAvatar/UserAvatar'
import MasonryGrid from '~/components/MasonryGrid/MasonryGrid.vue'
import MasonryGridItem from '~/components/MasonryGrid/MasonryGridItem.vue'
@@ -208,7 +208,7 @@ export default {
HcEmpty,
UserAvatar,
ContentMenu,
- HcUpload,
+ AvatarUploader,
MasonryGrid,
MasonryGridItem,
FollowList,
From a9c52bfcaee52d5acece0a3dc0e2e92d6cf73651 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 12:48:11 +0200
Subject: [PATCH 080/374] Renamed 'AvatarUploader' property from 'user' to
'profile'
- Rename several CSS.
---
.../Uploader/AvatarUploader.spec.js | 2 +-
webapp/components/Uploader/AvatarUploader.vue | 21 ++++++++++---------
webapp/locales/de.json | 8 +++----
webapp/locales/en.json | 8 +++----
webapp/locales/es.json | 8 +++----
webapp/locales/fr.json | 8 +++----
webapp/locales/it.json | 8 +++----
webapp/locales/nl.json | 3 +++
webapp/locales/pl.json | 8 +++----
webapp/locales/pt.json | 8 +++----
webapp/locales/ru.json | 8 +++----
webapp/pages/group/_id/_slug.vue | 3 +--
webapp/pages/profile/_id/_slug.vue | 2 +-
13 files changed, 41 insertions(+), 54 deletions(-)
diff --git a/webapp/components/Uploader/AvatarUploader.spec.js b/webapp/components/Uploader/AvatarUploader.spec.js
index 568e4f3a8a..8fc313c43d 100644
--- a/webapp/components/Uploader/AvatarUploader.spec.js
+++ b/webapp/components/Uploader/AvatarUploader.spec.js
@@ -26,7 +26,7 @@ describe('AvatarUploader', () => {
}
const propsData = {
- user: {
+ profile: {
avatar: { url: '/api/generic.jpg' },
},
}
diff --git a/webapp/components/Uploader/AvatarUploader.vue b/webapp/components/Uploader/AvatarUploader.vue
index f2ef4e712a..6937aeabc9 100644
--- a/webapp/components/Uploader/AvatarUploader.vue
+++ b/webapp/components/Uploader/AvatarUploader.vue
@@ -10,8 +10,8 @@
>
-
-
+
@@ -24,11 +24,12 @@ import vueDropzone from 'nuxt-dropzone'
import { updateUserMutation } from '~/graphql/User.js'
export default {
+ name: 'AvatarUploader',
components: {
vueDropzone,
},
props: {
- user: { type: Object, default: null },
+ profile: { type: Object, default: null },
},
data() {
return {
@@ -43,7 +44,7 @@ export default {
},
computed: {
avatarUrl() {
- const { avatar } = this.user
+ const { avatar } = this.profile
return avatar && avatar.url
},
},
@@ -73,11 +74,11 @@ export default {
avatar: {
upload: avatarUpload,
},
- id: this.user.id,
+ id: this.profile.id,
},
})
.then(() => {
- this.$toast.success(this.$t('user.avatar.submitted'))
+ this.$toast.success(this.$t('profile.avatar.submitted'))
})
.catch((error) => this.$toast.error(error.message))
},
@@ -115,7 +116,7 @@ export default {
width: 100%;
}
-.hc-attachments-upload-area {
+.avatar-attachments-upload-area {
position: relative;
display: flex;
align-items: center;
@@ -123,11 +124,11 @@ export default {
cursor: pointer;
}
-.hc-attachments-upload-button {
+.avatar-attachments-upload-button {
pointer-events: none;
}
-.hc-drag-marker {
+.avatar-drag-marker {
position: relative;
width: 122px;
height: 122px;
@@ -165,7 +166,7 @@ export default {
border-radius: 100%;
border: 1px dashed hsl(0, 0%, 25%);
}
- .hc-attachments-upload-area:hover & {
+ .avatar-attachments-upload-area:hover & {
opacity: 1;
}
}
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index db2046ae31..134368ad3a 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -517,6 +517,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Erfolgreich hochgeladen!"
+ },
"commented": "Kommentiert",
"follow": "Folgen",
"followers": "Folgen",
@@ -820,10 +823,5 @@
"newTermsAndConditions": "Neue Nutzungsbedingungen",
"termsAndConditionsNewConfirm": "Ich habe die neuen Nutzungsbedingungen durchgelesen und stimme zu.",
"termsAndConditionsNewConfirmText": "Bitte lies Dir die neuen Nutzungsbedingungen jetzt durch!"
- },
- "user": {
- "avatar": {
- "submitted": "Erfolgreich hochgeladen!"
- }
}
}
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 5b49c96ee6..fe12ef4370 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -517,6 +517,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Upload successful!"
+ },
"commented": "Commented",
"follow": "Follow",
"followers": "Followers",
@@ -820,10 +823,5 @@
"newTermsAndConditions": "New Terms and Conditions",
"termsAndConditionsNewConfirm": "I have read and agree to the new terms of conditions.",
"termsAndConditionsNewConfirmText": "Please read the new terms of use now!"
- },
- "user": {
- "avatar": {
- "submitted": "Upload successful!"
- }
}
}
diff --git a/webapp/locales/es.json b/webapp/locales/es.json
index 900b2fa5da..55d6542310 100644
--- a/webapp/locales/es.json
+++ b/webapp/locales/es.json
@@ -435,6 +435,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Carga con éxito"
+ },
"commented": "Comentado",
"follow": "Seguir",
"followers": "Seguidores",
@@ -715,10 +718,5 @@
"newTermsAndConditions": "Nuevos términos de uso",
"termsAndConditionsNewConfirm": "He leído y acepto los nuevos términos de uso.",
"termsAndConditionsNewConfirmText": "¡Por favor, lea los nuevos términos de uso ahora!"
- },
- "user": {
- "avatar": {
- "submitted": "Carga con éxito"
- }
}
}
diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json
index 684f080b7d..8c5a731a31 100644
--- a/webapp/locales/fr.json
+++ b/webapp/locales/fr.json
@@ -423,6 +423,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Téléchargement réussi"
+ },
"commented": "Commentais",
"follow": "Suivre",
"followers": "Suiveurs",
@@ -683,10 +686,5 @@
"newTermsAndConditions": "Nouvelles conditions générales",
"termsAndConditionsNewConfirm": "J'ai lu et accepté les nouvelles conditions générales.",
"termsAndConditionsNewConfirmText": "Veuillez lire les nouvelles conditions d'utilisation dès maintenant !"
- },
- "user": {
- "avatar": {
- "submitted": "Téléchargement réussi"
- }
}
}
diff --git a/webapp/locales/it.json b/webapp/locales/it.json
index 71994167be..694dee5326 100644
--- a/webapp/locales/it.json
+++ b/webapp/locales/it.json
@@ -376,6 +376,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": null
+ },
"commented": "Commentato",
"follow": "Seguire",
"followers": "Seguenti",
@@ -633,10 +636,5 @@
"newTermsAndConditions": "Nuovi Termini e Condizioni",
"termsAndConditionsNewConfirm": "Ho letto e accetto le nuove condizioni generali di contratto.",
"termsAndConditionsNewConfirmText": "Si prega di leggere le nuove condizioni d'uso ora!"
- },
- "user": {
- "avatar": {
- "submitted": null
- }
}
}
diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json
index 3c1a8902d6..fc2e90a343 100644
--- a/webapp/locales/nl.json
+++ b/webapp/locales/nl.json
@@ -100,6 +100,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": null
+ },
"follow": "Volgen",
"followers": "Volgelingen",
"following": "Volgt",
diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json
index 6ae3e32f7e..49405d0a9e 100644
--- a/webapp/locales/pl.json
+++ b/webapp/locales/pl.json
@@ -208,6 +208,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Przesłano pomyślnie"
+ },
"commented": "Skomentuj",
"follow": "Obserwuj",
"followers": "Obserwujący",
@@ -356,10 +359,5 @@
"taxident": "Numer identyfikacyjny podatku od wartości dodanej zgodnie z § 27 a Ustawa o podatku od wartości dodanej (Niemcy)",
"termsAc": "Warunki użytkowania",
"tribunal": "sąd rejestrowy"
- },
- "user": {
- "avatar": {
- "submitted": "Przesłano pomyślnie"
- }
}
}
diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json
index f5f89374a4..f85e7b0e23 100644
--- a/webapp/locales/pt.json
+++ b/webapp/locales/pt.json
@@ -412,6 +412,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Carregado com sucesso!"
+ },
"commented": "Comentou",
"follow": "Seguir",
"followers": "Seguidores",
@@ -668,10 +671,5 @@
"newTermsAndConditions": "Novos Termos e Condições",
"termsAndConditionsNewConfirm": "Eu li e concordo com os novos termos de condições.",
"termsAndConditionsNewConfirmText": "Por favor, leia os novos termos de uso agora!"
- },
- "user": {
- "avatar": {
- "submitted": "Carregado com sucesso!"
- }
}
}
diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json
index 5f1368820d..de7b063268 100644
--- a/webapp/locales/ru.json
+++ b/webapp/locales/ru.json
@@ -449,6 +449,9 @@
}
},
"profile": {
+ "avatar": {
+ "submitted": "Успешная загрузка!"
+ },
"commented": "Прокомментированные",
"follow": "Подписаться",
"followers": "Подписчики",
@@ -729,10 +732,5 @@
"newTermsAndConditions": "Новые условия и положения",
"termsAndConditionsNewConfirm": "Я прочитал(а) и согласен(на) с новыми условиями.",
"termsAndConditionsNewConfirmText": "Пожалуйста, ознакомьтесь с новыми условиями использования!"
- },
- "user": {
- "avatar": {
- "submitted": "Успешная загрузка!"
- }
}
}
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index b048740476..80c4ef6ca3 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -7,8 +7,7 @@
:class="{ 'disabled-content': group.disabled }"
style="position: relative; height: auto; overflow: visible"
>
-
-
+
diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue
index e25cb8fd57..98ff88a963 100644
--- a/webapp/pages/profile/_id/_slug.vue
+++ b/webapp/pages/profile/_id/_slug.vue
@@ -7,7 +7,7 @@
:class="{ 'disabled-content': user.disabled }"
style="position: relative; height: auto; overflow: visible"
>
-
+
From 2fc84f9c366d002f86e11c0e6e067b8e59b3c6e9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 29 Aug 2022 13:45:30 +0200
Subject: [PATCH 081/374] Rename class 'profile-avatar' to
'profile-page-avatar'
---
.../I_should_be_able_to_change_my_profile_picture.js | 2 +-
webapp/pages/group/_id/_slug.vue | 6 +++---
webapp/pages/profile/_id/_slug.vue | 6 +++---
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/cypress/integration/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js b/cypress/integration/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js
index f92789ef82..366fd82928 100644
--- a/cypress/integration/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js
+++ b/cypress/integration/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js
@@ -9,7 +9,7 @@ Then("I should be able to change my profile picture", () => {
{ subjectType: "drag-n-drop", force: true }
);
});
- cy.get(".profile-avatar img")
+ cy.get(".profile-page-avatar img")
.should("have.attr", "src")
.and("contains", "onourjourney");
cy.contains(".iziToast-message", "Upload successful")
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index 80c4ef6ca3..53da683ceb 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -8,9 +8,9 @@
style="position: relative; height: auto; overflow: visible"
>
-
+
-
+
diff --git a/webapp/components/features/ProfileList/ProfileList.vue b/webapp/components/features/ProfileList/ProfileList.vue
new file mode 100644
index 0000000000..c619da8fcf
--- /dev/null
+++ b/webapp/components/features/ProfileList/ProfileList.vue
@@ -0,0 +1,158 @@
+
+
+
+
+ {{ title }}
+
+
+
+ {{
+ $t('profile.network.andMore', {
+ number: allConnectionsCount - connections.length,
+ })
+ }}
+
+
+
+ {{ titleNobody }}
+
+
+
+
+
+
From 9e544b0e64ed65959fb198ea6702327af2f9c3f7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 12 Sep 2022 08:35:52 +0200
Subject: [PATCH 141/374] Rename props and classes in 'ProfileList'
---
.../features/ProfileList/FollowList.vue | 6 ++---
.../features/ProfileList/ProfileList.vue | 24 +++++++++----------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/webapp/components/features/ProfileList/FollowList.vue b/webapp/components/features/ProfileList/FollowList.vue
index 635eccd5b7..366c01be55 100644
--- a/webapp/components/features/ProfileList/FollowList.vue
+++ b/webapp/components/features/ProfileList/FollowList.vue
@@ -3,10 +3,10 @@
:uniqueName="`${type}Filter`"
:title="$filters.truncate(userName, 15) + ' ' + $t(`profile.network.${type}`)"
:titleNobody="$filters.truncate(userName, 15) + ' ' + $t(`profile.network.${type}Nobody`)"
- :allConnectionsCount="allConnectionsCount"
- :connections="connections"
+ :allProfilesCount="allConnectionsCount"
+ :profiles="connections"
:loading="loading"
- @fetchAllConnections="$emit('fetchAllConnections', type)"
+ @fetchAllProfiles="$emit('fetchAllConnections', type)"
/>
diff --git a/webapp/components/features/ProfileList/ProfileList.vue b/webapp/components/features/ProfileList/ProfileList.vue
index c619da8fcf..44de2eab58 100644
--- a/webapp/components/features/ProfileList/ProfileList.vue
+++ b/webapp/components/features/ProfileList/ProfileList.vue
@@ -1,6 +1,6 @@
-
-
+
+
{{ title }}
@@ -18,11 +18,11 @@
:loading="loading"
class="spacer-x-small"
size="small"
- @click="$emit('fetchAllConnections')"
+ @click="$emit('fetchAllProfiles')"
>
{{
$t('profile.network.andMore', {
- number: allConnectionsCount - connections.length,
+ number: allProfilesCount - profiles.length,
})
}}
@@ -53,8 +53,8 @@ export default {
uniqueName: { type: String, required: true },
title: { type: String, required: true },
titleNobody: { type: String, required: true },
- allConnectionsCount: { type: Number, required: true },
- connections: { type: Array, required: true },
+ allProfilesCount: { type: Number, required: true },
+ profiles: { type: Array, required: true },
type: { type: String, default: 'following' },
loading: { type: Boolean, default: false },
},
@@ -65,14 +65,14 @@ export default {
},
computed: {
hasMore() {
- return this.allConnectionsCount > this.connections.length
+ return this.allProfilesCount > this.profiles.length
},
connectionsClass() {
- return `connections${this.hasMore ? '' : ' --overflow'}`
+ return `profiles${this.hasMore ? '' : ' --overflow'}`
},
filteredConnections() {
if (!this.filter) {
- return this.connections
+ return this.profiles
}
// @example
@@ -83,7 +83,7 @@ export default {
'i',
)
- const fuzzyScores = this.connections
+ const fuzzyScores = this.profiles
.map((user) => {
const match = user.name.match(fuzzyExpression)
@@ -116,7 +116,7 @@ export default {
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 86602a7c18..1fc4e3fa72 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -373,6 +373,10 @@
"group": {
"foundation": "Gründung",
"goal": "Ziel:",
+ "joinLeaveButton": {
+ "iAmMember": "Bin Mitglied",
+ "join": "Beitreten"
+ },
"membersCount": "Mitglieder",
"membersListTitle": "Gruppenmitglieder"
},
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 7e0def985d..b1a9356a9a 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -373,6 +373,10 @@
"group": {
"foundation": "Foundation",
"goal": "Goal:",
+ "joinLeaveButton": {
+ "iAmMember": "I'm a member",
+ "join": "Join"
+ },
"membersCount": "Members",
"membersListTitle": "Group Members"
},
diff --git a/webapp/locales/es.json b/webapp/locales/es.json
index 791874729d..63070ee4b5 100644
--- a/webapp/locales/es.json
+++ b/webapp/locales/es.json
@@ -300,6 +300,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/fr.json b/webapp/locales/fr.json
index 66675942c2..91b91abe99 100644
--- a/webapp/locales/fr.json
+++ b/webapp/locales/fr.json
@@ -289,6 +289,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/it.json b/webapp/locales/it.json
index c9ff7870b2..342550a147 100644
--- a/webapp/locales/it.json
+++ b/webapp/locales/it.json
@@ -297,6 +297,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/nl.json b/webapp/locales/nl.json
index 02039e0db4..c7e474a3a8 100644
--- a/webapp/locales/nl.json
+++ b/webapp/locales/nl.json
@@ -85,6 +85,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/pl.json b/webapp/locales/pl.json
index c371a88315..840698487f 100644
--- a/webapp/locales/pl.json
+++ b/webapp/locales/pl.json
@@ -169,6 +169,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/pt.json b/webapp/locales/pt.json
index fe419f0538..bec922edb1 100644
--- a/webapp/locales/pt.json
+++ b/webapp/locales/pt.json
@@ -335,6 +335,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/locales/ru.json b/webapp/locales/ru.json
index bb0f340860..f50121635a 100644
--- a/webapp/locales/ru.json
+++ b/webapp/locales/ru.json
@@ -314,6 +314,10 @@
"group": {
"foundation": null,
"goal": null,
+ "joinLeaveButton": {
+ "iAmMember": null,
+ "join": null
+ },
"membersCount": null,
"membersListTitle": null
},
diff --git a/webapp/pages/group/_id/_slug.vue b/webapp/pages/group/_id/_slug.vue
index 2011e0a8c8..f21a719425 100644
--- a/webapp/pages/group/_id/_slug.vue
+++ b/webapp/pages/group/_id/_slug.vue
@@ -49,7 +49,11 @@
-
+
@@ -72,7 +76,7 @@
-->
-
diff --git a/webapp/pages/profile/_id/_slug.vue b/webapp/pages/profile/_id/_slug.vue
index 668c271d6c..81bb5c49bc 100644
--- a/webapp/pages/profile/_id/_slug.vue
+++ b/webapp/pages/profile/_id/_slug.vue
@@ -31,6 +31,7 @@
{{ userName }}
+
{{ userSlug }}
@@ -253,7 +254,7 @@ export default {
},
userSlug() {
const { slug } = this.user || {}
- return slug && `@${slug}`
+ return slug
},
tabOptions() {
return [
From 0d44d8ae043e297e9974014a5726bcbead1a6b83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 26 Sep 2022 11:39:14 +0200
Subject: [PATCH 246/374] Cleanup
- Reduce tooltip delay to 750ms.
---
webapp/components/Group/GroupContentMenu.vue | 37 +++++---------------
webapp/components/Group/GroupForm.vue | 13 ++++---
webapp/components/Group/GroupTeaser.vue | 27 +-------------
webapp/locales/de.json | 10 +++---
webapp/locales/en.json | 10 +++---
webapp/pages/group/edit/_id.vue | 4 ---
webapp/pages/group/edit/_id/index.vue | 2 +-
webapp/pages/settings.vue | 4 ---
webapp/plugins/v-tooltip.js | 2 +-
9 files changed, 27 insertions(+), 82 deletions(-)
diff --git a/webapp/components/Group/GroupContentMenu.vue b/webapp/components/Group/GroupContentMenu.vue
index e61786fe76..cfebe65a42 100644
--- a/webapp/components/Group/GroupContentMenu.vue
+++ b/webapp/components/Group/GroupContentMenu.vue
@@ -42,7 +42,7 @@ export default {
props: {
placement: { type: String, default: 'bottom-end' },
resource: { type: Object, required: true },
- isOwner: { type: String, default: null },
+ group: { type: Object, required: true },
resourceType: {
type: String,
required: true,
@@ -56,40 +56,19 @@ export default {
const routes = []
if (this.resourceType === 'group') {
- if (this.isOwner === 'owner') {
+ routes.push({
+ label: this.$t('group.contentMenu.visitGroupPage'),
+ icon: 'home',
+ name: 'group-id-slug',
+ params: { id: this.group.id, slug: this.group.slug },
+ })
+ if (this.group.myRole === 'owner') {
routes.push({
label: this.$t('admin.settings.name'),
path: `/group/edit/${this.resource.id}`,
icon: 'edit',
})
}
- if (this.isOwner === 'usual') {
- routes.push({
- label: this.$t('group.unfollowing'),
- callback: () => {
- // this.$emit('join-group', this.resource)
- },
- icon: 'minus',
- })
- }
- if (this.isOwner === 'pending') {
- routes.push({
- label: this.$t('group.unfollowing'),
- callback: () => {
- // this.removePending(this.resource)
- },
- icon: 'minus',
- })
- }
- if (this.isOwner === null) {
- routes.push({
- label: this.$t('group.follow'),
- callback: () => {
- this.$emit('joinGroup', this.resource)
- },
- icon: 'plus',
- })
- }
}
return routes
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index 87b46091bb..daf0c1d2a3 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -46,11 +46,18 @@
{{ $t(`group.types.${groupType}`) }}
+
+
-
@@ -120,10 +127,6 @@
-
-
diff --git a/webapp/components/Group/GroupTeaser.vue b/webapp/components/Group/GroupTeaser.vue
index 9ebbbcdcec..9a6eb9bcc7 100644
--- a/webapp/components/Group/GroupTeaser.vue
+++ b/webapp/components/Group/GroupTeaser.vue
@@ -58,13 +58,7 @@
-
+
diff --git a/webapp/pages/group/edit/_id/index.vue b/webapp/pages/group/edit/_id/index.vue
index c50eaa4c43..e3c934dc50 100644
--- a/webapp/pages/group/edit/_id/index.vue
+++ b/webapp/pages/group/edit/_id/index.vue
@@ -58,7 +58,7 @@ export default {
responseSlug = groupSlug
},
})
- this.$toast.success(this.$t('group.group-updated'))
+ this.$toast.success(this.$t('group.updatedGroup'))
this.$router.history.push({
name: 'group-id-slug',
params: { id: responseId, slug: responseSlug },
diff --git a/webapp/pages/settings.vue b/webapp/pages/settings.vue
index 5b87cf12d6..1fce64d8f3 100644
--- a/webapp/pages/settings.vue
+++ b/webapp/pages/settings.vue
@@ -42,10 +42,6 @@ export default {
name: this.$t('settings.social-media.name'),
path: `/settings/my-social-media`,
},
- // Wolle: {
- // name: this.$t('settings.myGroups'),
- // path: `/my-groups`,
- // },
{
name: this.$t('settings.muted-users.name'),
path: `/settings/muted-users`,
diff --git a/webapp/plugins/v-tooltip.js b/webapp/plugins/v-tooltip.js
index 73681e9b32..0c0cbc8d61 100644
--- a/webapp/plugins/v-tooltip.js
+++ b/webapp/plugins/v-tooltip.js
@@ -3,7 +3,7 @@ import VTooltip from 'v-tooltip'
Vue.use(VTooltip, {
defaultDelay: {
- show: 1500,
+ show: 750,
hide: 50,
},
defaultOffset: 2,
From 211a4b9130ff9a7ad25ed39c3db56870f95a69b4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 26 Sep 2022 11:52:09 +0200
Subject: [PATCH 247/374] Fix webapp tests
---
webapp/pages/group/_id/_slug.spec.js | 36 ++++++++++++++++++----------
1 file changed, 24 insertions(+), 12 deletions(-)
diff --git a/webapp/pages/group/_id/_slug.spec.js b/webapp/pages/group/_id/_slug.spec.js
index 0cca122b32..1f92212ab7 100644
--- a/webapp/pages/group/_id/_slug.spec.js
+++ b/webapp/pages/group/_id/_slug.spec.js
@@ -238,7 +238,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@yoga-practice')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('yoga-practice')
})
describe('displays no(!) group location – because is "null"', () => {
@@ -385,7 +386,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@yoga-practice')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('yoga-practice')
})
describe('displays no(!) group location – because is "null"', () => {
@@ -492,7 +494,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@yoga-practice')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('yoga-practice')
})
describe('displays no(!) group location – because is "null"', () => {
@@ -599,7 +602,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@yoga-practice')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('yoga-practice')
})
describe('displays no(!) group location – because is "null"', () => {
@@ -710,7 +714,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@school-for-citizens')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('school-for-citizens')
})
describe('displays group location', () => {
@@ -821,7 +826,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@school-for-citizens')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('school-for-citizens')
})
describe('displays group location', () => {
@@ -932,7 +938,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@school-for-citizens')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('school-for-citizens')
})
describe('displays group location', () => {
@@ -1043,7 +1050,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@school-for-citizens')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('school-for-citizens')
})
describe('displays group location', () => {
@@ -1158,7 +1166,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@investigative-journalism')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('investigative-journalism')
})
describe('displays group location', () => {
@@ -1272,7 +1281,8 @@ describe('GroupProfileSlug', () => {
})
it('has group slug', () => {
- expect(wrapper.text()).toContain('@investigative-journalism')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(true)
+ expect(wrapper.text()).toContain('investigative-journalism')
})
describe('displays group location', () => {
@@ -1386,7 +1396,8 @@ describe('GroupProfileSlug', () => {
})
it('has no(!) group slug', () => {
- expect(wrapper.text()).not.toContain('@investigative-journalism')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(false)
+ expect(wrapper.text()).not.toContain('investigative-journalism')
})
describe('displays not(!) group location', () => {
@@ -1488,7 +1499,8 @@ describe('GroupProfileSlug', () => {
})
it('has no(!) group slug', () => {
- expect(wrapper.text()).not.toContain('@investigative-journalism')
+ expect(wrapper.find('[data-test="at"]').exists()).toBe(false)
+ expect(wrapper.text()).not.toContain('investigative-journalism')
})
describe('displays not(!) group location', () => {
From 8e19c04b44d5549199997f6197caba59ec39eb3f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 26 Sep 2022 13:30:58 +0200
Subject: [PATCH 248/374] Implement automatic deployment for branch
'5059-epic-groups'
---
.github/workflows/publish.yml | 50 +++++++++++++++++------------------
1 file changed, 25 insertions(+), 25 deletions(-)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index ac333604ec..b25e19070c 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -4,7 +4,7 @@ on:
push:
branches:
- master
- # - 5093-fix-automatic-deployment # for testing while developing
+ - 5059-epic-groups # for testing while developing
jobs:
##############################################################################
@@ -94,16 +94,16 @@ jobs:
##########################################################################
- name: Backend | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/backend:latest-base" -t "ocelotsocialnetwork/backend:${VERSION}-base" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- docker build --target code -t "ocelotsocialnetwork/backend:latest-code" -t "ocelotsocialnetwork/backend:${VERSION}-code" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- docker build --target production -t "ocelotsocialnetwork/backend:latest" -t "ocelotsocialnetwork/backend:${VERSION}" -t "ocelotsocialnetwork/backend:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target base -t "ocelotsocialnetwork/backend-groups:latest-base" -t "ocelotsocialnetwork/backend-groups:${VERSION}-base" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target code -t "ocelotsocialnetwork/backend-groups:latest-code" -t "ocelotsocialnetwork/backend-groups:${VERSION}-code" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
+ docker build --target production -t "ocelotsocialnetwork/backend-groups:latest" -t "ocelotsocialnetwork/backend-groups:${VERSION}" -t "ocelotsocialnetwork/backend-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT backend/
- name: Backend | Save docker image
- run: docker save "ocelotsocialnetwork/backend" > /tmp/backend.tar
+ run: docker save "ocelotsocialnetwork/backend-groups" > /tmp/backend-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-backend-production
- path: /tmp/backend.tar
+ path: /tmp/backend-groups.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION WEBAPP ########################################
@@ -134,16 +134,16 @@ jobs:
##########################################################################
- name: Webapp | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/webapp:latest-base" -t "ocelotsocialnetwork/webapp:${VERSION}-base" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- docker build --target code -t "ocelotsocialnetwork/webapp:latest-code" -t "ocelotsocialnetwork/webapp:${VERSION}-code" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- docker build --target production -t "ocelotsocialnetwork/webapp:latest" -t "ocelotsocialnetwork/webapp:${VERSION}" -t "ocelotsocialnetwork/webapp:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target base -t "ocelotsocialnetwork/webapp-groups:latest-base" -t "ocelotsocialnetwork/webapp-groups:${VERSION}-base" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target code -t "ocelotsocialnetwork/webapp-groups:latest-code" -t "ocelotsocialnetwork/webapp-groups:${VERSION}-code" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
+ docker build --target production -t "ocelotsocialnetwork/webapp-groups:latest" -t "ocelotsocialnetwork/webapp-groups:${VERSION}" -t "ocelotsocialnetwork/webapp-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/
- name: Webapp | Save docker image
- run: docker save "ocelotsocialnetwork/webapp" > /tmp/webapp.tar
+ run: docker save "ocelotsocialnetwork/webapp-groups" > /tmp/webapp-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-webapp-production
- path: /tmp/webapp.tar
+ path: /tmp/webapp-groups.tar
##############################################################################
# JOB: DOCKER BUILD PRODUCTION MAINTENANCE ###################################
@@ -174,16 +174,16 @@ jobs:
##########################################################################
- name: Maintenance | Build `production` image
run: |
- docker build --target base -t "ocelotsocialnetwork/maintenance:latest-base" -t "ocelotsocialnetwork/maintenance:${VERSION}-base" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- docker build --target code -t "ocelotsocialnetwork/maintenance:latest-code" -t "ocelotsocialnetwork/maintenance:${VERSION}-code" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- docker build --target production -t "ocelotsocialnetwork/maintenance:latest" -t "ocelotsocialnetwork/maintenance:${VERSION}" -t "ocelotsocialnetwork/maintenance:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target base -t "ocelotsocialnetwork/maintenance-groups:latest-base" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}-base" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}-base" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target code -t "ocelotsocialnetwork/maintenance-groups:latest-code" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}-code" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}-code" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
+ docker build --target production -t "ocelotsocialnetwork/maintenance-groups:latest" -t "ocelotsocialnetwork/maintenance-groups:${VERSION}" -t "ocelotsocialnetwork/maintenance-groups:${BUILD_VERSION}" --build-arg BBUILD_DATE=$BUILD_DATE --build-arg BBUILD_VERSION=$BUILD_VERSION --build-arg BBUILD_COMMIT=$BUILD_COMMIT webapp/ -f webapp/Dockerfile.maintenance
- name: Maintenance | Save docker image
- run: docker save "ocelotsocialnetwork/maintenance" > /tmp/maintenance.tar
+ run: docker save "ocelotsocialnetwork/maintenance-groups" > /tmp/maintenance-groups.tar
- name: Upload Artifact
uses: actions/upload-artifact@v2
with:
name: docker-maintenance-production
- path: /tmp/maintenance.tar
+ path: /tmp/maintenance-groups.tar
##############################################################################
# JOB: UPLOAD TO DOCKERHUB ###################################################
@@ -217,21 +217,21 @@ jobs:
name: docker-backend-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/backend.tar
+ run: docker load < /tmp/backend-groups.tar
- name: Download Docker Image (WebApp)
uses: actions/download-artifact@v2
with:
name: docker-webapp-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/webapp.tar
+ run: docker load < /tmp/webapp-groups.tar
- name: Download Docker Image (Maintenance)
uses: actions/download-artifact@v2
with:
name: docker-maintenance-production
path: /tmp
- name: Load Docker Image
- run: docker load < /tmp/maintenance.tar
+ run: docker load < /tmp/maintenance-groups.tar
##########################################################################
# Upload #################################################################
##########################################################################
@@ -240,11 +240,11 @@ jobs:
- name: Push neo4j
run: docker push --all-tags ocelotsocialnetwork/neo4j-community
- name: Push backend
- run: docker push --all-tags ocelotsocialnetwork/backend
+ run: docker push --all-tags ocelotsocialnetwork/backend-groups
- name: Push webapp
- run: docker push --all-tags ocelotsocialnetwork/webapp
+ run: docker push --all-tags ocelotsocialnetwork/webapp-groups
- name: Push maintenance
- run: docker push --all-tags ocelotsocialnetwork/maintenance
+ run: docker push --all-tags ocelotsocialnetwork/maintenance-groups
##############################################################################
# JOB: KUBERNETES DEPLOY ACTUAL/LATEST VERSION ######################################
@@ -292,11 +292,11 @@ jobs:
# kubectl -n default rollout restart deployment/ocelot-neo4j
- name: Deploy actual version '$BUILD_VERSION' to DigitalOcean Kubernetes
run: |
- kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-webapp container-ocelot-webapp=ocelotsocialnetwork/webapp-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-webapp
- kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-backend container-ocelot-backend=ocelotsocialnetwork/backend-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-backend
- kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance:$BUILD_VERSION
+ kubectl -n default set image deployment/ocelot-maintenance container-ocelot-maintenance=ocelotsocialnetwork/maintenance-groups:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-maintenance
kubectl -n default set image deployment/ocelot-neo4j container-ocelot-neo4j=ocelotsocialnetwork/neo4j-community:$BUILD_VERSION
kubectl -n default rollout restart deployment/ocelot-neo4j
From 9c0cc267a0a6b7e1e04649ed2092e04d4ba99b4a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 26 Sep 2022 13:35:03 +0200
Subject: [PATCH 249/374] Disable push on master in 'publishing.yml'
---
.github/workflows/publish.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index b25e19070c..d4ecca7f5d 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -3,7 +3,7 @@ name: ocelot.social publish CI
on:
push:
branches:
- - master
+ # - master
- 5059-epic-groups # for testing while developing
jobs:
From 8ff8d55d6d35bd602da0172c73d89539a6249308 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Mon, 26 Sep 2022 21:40:10 +0200
Subject: [PATCH 250/374] Implement 'GroupButton' in 'default' layout
---
webapp/components/Group/GroupButton.vue | 5 +++++
webapp/layouts/default.vue | 7 ++++++-
2 files changed, 11 insertions(+), 1 deletion(-)
create mode 100644 webapp/components/Group/GroupButton.vue
diff --git a/webapp/components/Group/GroupButton.vue b/webapp/components/Group/GroupButton.vue
new file mode 100644
index 0000000000..2000e3046d
--- /dev/null
+++ b/webapp/components/Group/GroupButton.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue
index 3e2a4aa693..b23ed98ea8 100644
--- a/webapp/layouts/default.vue
+++ b/webapp/layouts/default.vue
@@ -88,6 +88,9 @@
+
+
+
@@ -125,7 +128,8 @@ import FilterMenu from '~/components/FilterMenu/FilterMenu.vue'
import PageFooter from '~/components/PageFooter/PageFooter'
import AvatarMenu from '~/components/AvatarMenu/AvatarMenu'
import InviteButton from '~/components/InviteButton/InviteButton'
-import CategoriesMenu from '~/components/FilterMenu/CategoriesMenu.vue'
+import CategoriesMenu from '~/components/FilterMenu/CategoriesMenu'
+import GroupButton from '~/components/Group/GroupButton'
export default {
components: {
@@ -139,6 +143,7 @@ export default {
PageFooter,
InviteButton,
CategoriesMenu,
+ GroupButton,
},
mixins: [seo],
data() {
From e430f22392608340865734f5db65b9ccea1a5cdc Mon Sep 17 00:00:00 2001
From: ogerly
Date: Tue, 27 Sep 2022 11:24:02 +0200
Subject: [PATCH 251/374] add test GroupContentMenu ans GroupForm
---
.../components/Group/GroupContentMenu.spec.js | 36 +++++++++++++++++
webapp/components/Group/GroupForm.spec.js | 39 +++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 webapp/components/Group/GroupContentMenu.spec.js
create mode 100644 webapp/components/Group/GroupForm.spec.js
diff --git a/webapp/components/Group/GroupContentMenu.spec.js b/webapp/components/Group/GroupContentMenu.spec.js
new file mode 100644
index 0000000000..6639c77e7f
--- /dev/null
+++ b/webapp/components/Group/GroupContentMenu.spec.js
@@ -0,0 +1,36 @@
+import { mount } from '@vue/test-utils'
+import GroupContentMenu from './GroupContentMenu.vue'
+
+const localVue = global.localVue
+
+const propsData = {
+ resource: {},
+ group: {},
+ resourceType: 'group',
+
+}
+
+describe('GroupContentMenu', () => {
+ let wrapper
+ let mocks
+
+ beforeEach(() => {
+ mocks = {
+ $t: jest.fn(),
+ }
+ })
+
+ describe('mount', () => {
+ const Wrapper = () => {
+ return mount(GroupContentMenu, { propsData, mocks, localVue })
+ }
+
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ it('renders', () => {
+ expect(wrapper.findAll('.group-menu')).toHaveLength(1)
+ })
+ })
+})
diff --git a/webapp/components/Group/GroupForm.spec.js b/webapp/components/Group/GroupForm.spec.js
new file mode 100644
index 0000000000..83a16745c6
--- /dev/null
+++ b/webapp/components/Group/GroupForm.spec.js
@@ -0,0 +1,39 @@
+import { config, mount } from '@vue/test-utils'
+import GroupForm from './GroupForm.vue'
+
+const localVue = global.localVue
+
+config.stubs['nuxt-link'] = ' '
+
+const propsData = {
+ update: false,
+ group: {},
+}
+
+describe('GroupForm', () => {
+ let wrapper
+ let mocks
+
+ beforeEach(() => {
+ mocks = {
+ $t: jest.fn(),
+ $env: {
+ CATEGORIES_ACTIVE: true,
+ },
+ }
+ })
+
+ describe('mount', () => {
+ const Wrapper = () => {
+ return mount(GroupForm, { propsData, mocks, localVue })
+ }
+
+ beforeEach(() => {
+ wrapper = Wrapper()
+ })
+
+ it('renders', () => {
+ expect(wrapper.findAll('.group-form')).toHaveLength(1)
+ })
+ })
+})
From c2dc8b47a0479230aa899f60e274bcf6209e0b2f Mon Sep 17 00:00:00 2001
From: ogerly
Date: Tue, 27 Sep 2022 11:24:48 +0200
Subject: [PATCH 252/374] fix lint
---
webapp/components/Group/GroupContentMenu.spec.js | 7 +++----
webapp/components/Group/GroupForm.spec.js | 4 ++--
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/webapp/components/Group/GroupContentMenu.spec.js b/webapp/components/Group/GroupContentMenu.spec.js
index 6639c77e7f..69c13e6f00 100644
--- a/webapp/components/Group/GroupContentMenu.spec.js
+++ b/webapp/components/Group/GroupContentMenu.spec.js
@@ -4,10 +4,9 @@ import GroupContentMenu from './GroupContentMenu.vue'
const localVue = global.localVue
const propsData = {
- resource: {},
- group: {},
- resourceType: 'group',
-
+ resource: {},
+ group: {},
+ resourceType: 'group',
}
describe('GroupContentMenu', () => {
diff --git a/webapp/components/Group/GroupForm.spec.js b/webapp/components/Group/GroupForm.spec.js
index 83a16745c6..0300bacd02 100644
--- a/webapp/components/Group/GroupForm.spec.js
+++ b/webapp/components/Group/GroupForm.spec.js
@@ -6,8 +6,8 @@ const localVue = global.localVue
config.stubs['nuxt-link'] = ' '
const propsData = {
- update: false,
- group: {},
+ update: false,
+ group: {},
}
describe('GroupForm', () => {
From 60b68ed63c8f1993095b78c1b8fe76cbdc8fb581 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 27 Sep 2022 13:47:01 +0200
Subject: [PATCH 253/374] Add 'SHOW_GROUP_BUTTON_IN_HEADER' in 'groups.js',
still unused
---
webapp/constants/groups.js | 1 +
1 file changed, 1 insertion(+)
diff --git a/webapp/constants/groups.js b/webapp/constants/groups.js
index 3abf0d12ad..1c49d3ff35 100644
--- a/webapp/constants/groups.js
+++ b/webapp/constants/groups.js
@@ -2,3 +2,4 @@
export const NAME_LENGTH_MIN = 3
export const NAME_LENGTH_MAX = 50
export const DESCRIPTION_WITHOUT_HTML_LENGTH_MIN = 100 // with removed HTML tags
+export const SHOW_GROUP_BUTTON_IN_HEADER = true
From 8867f8674f318d614c8cf9c13a36ebd1ac466a20 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Tue, 27 Sep 2022 16:09:33 +0200
Subject: [PATCH 254/374] Make group button in header configurable
---
webapp/layouts/default.vue | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue
index b23ed98ea8..6fb3a2f541 100644
--- a/webapp/layouts/default.vue
+++ b/webapp/layouts/default.vue
@@ -88,7 +88,7 @@
-
+
@@ -115,21 +115,22 @@
+
+
From 6364974b71ef407dc8ddeea5b726e02c796bfccb Mon Sep 17 00:00:00 2001
From: ogerly
Date: Wed, 28 Sep 2022 10:15:39 +0200
Subject: [PATCH 258/374] add editor component for GroupForm.vue
---
webapp/components/Group/GroupForm.vue | 28 ++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index 8a9638b1ae..0923d6fc28 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -24,12 +24,16 @@
-
+
+
+ {{ `${contentLength} / ${descriptionMin}` }}
+
+
Date: Wed, 28 Sep 2022 10:17:29 +0200
Subject: [PATCH 259/374] delete unused code
---
webapp/components/Group/GroupForm.vue | 2 --
1 file changed, 2 deletions(-)
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index 0923d6fc28..45fd428e16 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -48,8 +48,6 @@
:existingCategoryIds="formData.categoryIds"
/>
- {{ formData }}
-
Reset form
Date: Wed, 28 Sep 2022 12:01:27 +0200
Subject: [PATCH 260/374] Remove 'gost' property from 'ContentMenu' button
---
webapp/components/ContentMenu/ContentMenu.vue | 1 -
1 file changed, 1 deletion(-)
diff --git a/webapp/components/ContentMenu/ContentMenu.vue b/webapp/components/ContentMenu/ContentMenu.vue
index 88d83d059e..60bcccf433 100644
--- a/webapp/components/ContentMenu/ContentMenu.vue
+++ b/webapp/components/ContentMenu/ContentMenu.vue
@@ -7,7 +7,6 @@
icon="ellipsis-v"
size="small"
circle
- ghost
@click.prevent="toggleMenu()"
/>
From 1e01ccfa7184a866fb21cd2f1f19242bfae130b6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 28 Sep 2022 12:02:41 +0200
Subject: [PATCH 261/374] Add 'GroupContentMenu' on group profile page
---
webapp/components/Group/GroupContentMenu.vue | 9 ++++---
webapp/pages/group/_id/_slug.vue | 28 ++++++++++----------
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/webapp/components/Group/GroupContentMenu.vue b/webapp/components/Group/GroupContentMenu.vue
index cfebe65a42..97a8846986 100644
--- a/webapp/components/Group/GroupContentMenu.vue
+++ b/webapp/components/Group/GroupContentMenu.vue
@@ -1,5 +1,5 @@
- -->
-
- {{ $t('group.type') }}
-
-
-
-
- {{ $t(`group.types.${groupType}`) }}
-
-
+
+
+ {{ $t('group.type') }}
+
+
+
+
+ {{ $t(`group.types.${groupType}`) }}
+
+
+
-
+
+
+
+
+
+ {{ $t('group.description') }}
+
+
@@ -79,9 +91,9 @@
{{ $t('group.actionRadius') }}
-
+
- {{ $t(`group.actionRadii.${actionRadius}`) }}
+ {{ $t(`group.actionRadii.${actionRadius}`) }}
+
+
+
@@ -167,8 +182,17 @@ export default {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
disabled: false,
descriptionMin: 50,
- groupTypeOptions: ['public', 'closed', 'hidden'],
- actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
+ groupTypeOptions: [
+ 'public',
+ 'closed',
+ 'hidden'
+ ],
+ actionRadiusOptions: [
+ 'regional',
+ 'national',
+ 'continental',
+ 'global'
+ ],
loadingGeo: false,
cities: [],
formData: {
@@ -317,16 +341,12 @@ export default {
From f21796b5e2f681f9e6e3f679205707f6274eef0f Mon Sep 17 00:00:00 2001
From: ogerly
Date: Wed, 28 Sep 2022 15:20:51 +0200
Subject: [PATCH 264/374] delete location entry per button
---
webapp/components/Group/GroupForm.vue | 49 ++++++++++++---------------
1 file changed, 21 insertions(+), 28 deletions(-)
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index 516e0895f4..b82b3953ca 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -8,11 +8,14 @@
@submit="submit"
>
+
+
+
+
-
-
-
{{ $t('group.type') }}
-
+
+
@@ -77,23 +71,13 @@
+
-
-
{{ $t('group.actionRadius') }}
-
+
-
-
+
+
-.ds-input {
+.appearance--auto {
-webkit-appearance: auto;
-moz-appearance: auto;
appearance: auto;
From 1199c5d77a16d4f527aa524e16e37a078e87d858 Mon Sep 17 00:00:00 2001
From: ogerly
Date: Wed, 28 Sep 2022 15:21:40 +0200
Subject: [PATCH 265/374] fix lint
---
webapp/components/Group/GroupForm.vue | 259 ++++++++++++--------------
1 file changed, 122 insertions(+), 137 deletions(-)
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index b82b3953ca..6454d44939 100644
--- a/webapp/components/Group/GroupForm.vue
+++ b/webapp/components/Group/GroupForm.vue
@@ -7,136 +7,134 @@
:schema="formSchema"
@submit="submit"
>
-
-
-
-
-
-
+
+
-
-
-
-
- {{ $t('group.type') }}
-
-
-
- {{ $t(`group.types.${groupType}`) }}
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+ {{ $t('group.type') }}
+
+
+
+ {{ $t(`group.types.${groupType}`) }}
+
+
+
-
-
-
- {{ $t('group.description') }}
-
-
-
- {{ `${contentLength} / ${descriptionMin}` }}
-
-
+
+
+
-
-
-
- {{ $t('group.actionRadius') }}
-
-
-
+
+
+ {{ $t('group.description') }}
+
+
+
+ {{ `${contentLength} / ${descriptionMin}` }}
+
+
+
+
+
+
+ {{ $t('group.actionRadius') }}
+
+
- {{ $t(`group.actionRadii.${actionRadius}`) }}
-
-
-
+
+ {{ $t(`group.actionRadii.${actionRadius}`) }}
+
+
+
-
-
-
-
-
-
-
-
-
- {{ $t('actions.cancel') }}
-
-
- {{ update ? $t('group.update') : $t('group.save') }}
-
-
-
+
+
+
+
+
+
+
+
+ {{ $t('actions.cancel') }}
+
+
+ {{ update ? $t('group.update') : $t('group.save') }}
+
+
+
@@ -175,17 +173,8 @@ export default {
categoriesActive: this.$env.CATEGORIES_ACTIVE,
disabled: false,
descriptionMin: 50,
- groupTypeOptions: [
- 'public',
- 'closed',
- 'hidden'
- ],
- actionRadiusOptions: [
- 'regional',
- 'national',
- 'continental',
- 'global'
- ],
+ groupTypeOptions: ['public', 'closed', 'hidden'],
+ actionRadiusOptions: ['regional', 'national', 'continental', 'global'],
loadingGeo: false,
cities: [],
formData: {
@@ -223,7 +212,6 @@ export default {
}
},
computed: {
-
contentLength() {
return this.$filters.removeHtml(this.formData.description).length
},
@@ -334,12 +322,9 @@ export default {
From 2eda102a71c2b2465effdea7ccf33c6789f5ac0c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 28 Sep 2022 16:37:46 +0200
Subject: [PATCH 266/374] Remove doubled 'GroupButton'
---
webapp/layouts/default.vue | 3 ---
1 file changed, 3 deletions(-)
diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue
index 9a1e87fe1f..6fb3a2f541 100644
--- a/webapp/layouts/default.vue
+++ b/webapp/layouts/default.vue
@@ -91,9 +91,6 @@
-
-
-
From ac9be9e2a3fd74f5a02fa194273491f8b9ba67db Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Wed, 28 Sep 2022 16:38:56 +0200
Subject: [PATCH 267/374] Refactor 'GroupContentMenu' component
---
webapp/components/Group/GroupContentMenu.vue | 27 ++++++++------------
webapp/components/Group/GroupTeaser.vue | 7 +----
webapp/pages/group/_id/_slug.vue | 4 +--
3 files changed, 13 insertions(+), 25 deletions(-)
diff --git a/webapp/components/Group/GroupContentMenu.vue b/webapp/components/Group/GroupContentMenu.vue
index 929232bfe1..7a77373208 100644
--- a/webapp/components/Group/GroupContentMenu.vue
+++ b/webapp/components/Group/GroupContentMenu.vue
@@ -47,34 +47,27 @@ export default {
return value.match(/(groupTeaser|groupProfile)/)
},
},
- resource: { type: Object, required: true },
- resourceType: {
- type: String,
- required: true,
- validator: (value) => {
- return value.match(/(group)/)
- },
- },
+ group: { type: Object, required: true },
placement: { type: String, default: 'bottom-end' },
},
computed: {
routes() {
const routes = []
- if (this.resourceType === 'group') {
+ if (this.usage !== 'groupProfile') {
routes.push({
label: this.$t('group.contentMenu.visitGroupPage'),
icon: 'home',
name: 'group-id-slug',
- params: { id: this.resource.id, slug: this.resource.slug },
+ params: { id: this.group.id, slug: this.group.slug },
+ })
+ }
+ if (this.group.myRole === 'owner') {
+ routes.push({
+ label: this.$t('admin.settings.name'),
+ path: `/group/edit/${this.group.id}`,
+ icon: 'edit',
})
- if (this.resource.myRole === 'owner') {
- routes.push({
- label: this.$t('admin.settings.name'),
- path: `/group/edit/${this.resource.id}`,
- icon: 'edit',
- })
- }
}
return routes
diff --git a/webapp/components/Group/GroupTeaser.vue b/webapp/components/Group/GroupTeaser.vue
index 1b9ede71e4..4d50c11811 100644
--- a/webapp/components/Group/GroupTeaser.vue
+++ b/webapp/components/Group/GroupTeaser.vue
@@ -58,12 +58,7 @@
-
+
+
+
+
{
if (!this.isTag(item)) {
this.$router.push({
- name: this.isPost(item) ? 'post-id-slug' : 'profile-id-slug',
+ name: this.getRouteName(item),
params: { id: item.id, slug: item.slug },
})
} else {
From 61c2250c03452b3c54771accbf60feb219ba2839 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Fri, 21 Oct 2022 17:54:25 +0200
Subject: [PATCH 355/374] respect & symbol in searches
---
backend/src/schema/resolvers/searches.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/searches.js b/backend/src/schema/resolvers/searches.js
index 21363365f4..c0354731bc 100644
--- a/backend/src/schema/resolvers/searches.js
+++ b/backend/src/schema/resolvers/searches.js
@@ -223,8 +223,8 @@ export default {
let userId = null
if (context.user) userId = context.user.id
- const searchType = query.replace(/^([!@#]?).*$/, '$1')
- const searchString = query.replace(/^([!@#])/, '')
+ const searchType = query.replace(/^([!@#&]?).*$/, '$1')
+ const searchString = query.replace(/^([!@#&])/, '')
const params = {
query: queryString(searchString),
From 2b8b50af48de391acf3b273bdfa5d5aec0d64a24 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Fri, 21 Oct 2022 17:59:11 +0200
Subject: [PATCH 356/374] add group hint to search hint
---
webapp/locales/de.json | 2 +-
webapp/locales/en.json | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/webapp/locales/de.json b/webapp/locales/de.json
index 837cfc75c3..036aed766c 100644
--- a/webapp/locales/de.json
+++ b/webapp/locales/de.json
@@ -749,7 +749,7 @@
"Tag": "Hashtag ::: Hashtags",
"User": "Benutzer ::: Benutzer"
},
- "hint": "Wonach suchst Du? Nutze !… für Beiträge, @… für Mitglieder, #… für Hashtags",
+ "hint": "Wonach suchst Du? Nutze !… für Beiträge, @… für Mitglieder, &… für Gruppen, #… für Hashtags",
"no-results": "Keine Ergebnisse für \"{search}\" gefunden. Versuch' es mit einem anderen Begriff!",
"page": "Seite",
"placeholder": "Suchen",
diff --git a/webapp/locales/en.json b/webapp/locales/en.json
index 3b18e1a453..91acdb8f63 100644
--- a/webapp/locales/en.json
+++ b/webapp/locales/en.json
@@ -749,7 +749,7 @@
"Tag": "Hashtag ::: Hashtags",
"User": "User ::: Users"
},
- "hint": "What are you searching for? Use !… for posts, @… for users, #… for hashtags.",
+ "hint": "What are you searching for? Use !… for posts, @… for users, &… for groups, #… for hashtags.",
"no-results": "No results found for \"{search}\". Try a different search term!",
"page": "Page",
"placeholder": "Search",
From 57bbe4c6e81360471c92ea3a85a8c81fc1f2fcb4 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Fri, 21 Oct 2022 18:34:48 +0200
Subject: [PATCH 357/374] allow search groups in permission middleware, add
user id to search groups query
---
backend/src/middleware/permissionsMiddleware.js | 1 +
backend/src/schema/resolvers/searches.js | 4 ++++
2 files changed, 5 insertions(+)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index d77363c292..3d698810e7 100644
--- a/backend/src/middleware/permissionsMiddleware.js
+++ b/backend/src/middleware/permissionsMiddleware.js
@@ -302,6 +302,7 @@ export default shield(
searchResults: allow,
searchPosts: allow,
searchUsers: allow,
+ searchGroups: allow,
searchHashtags: allow,
embed: allow,
Category: allow,
diff --git a/backend/src/schema/resolvers/searches.js b/backend/src/schema/resolvers/searches.js
index c0354731bc..c72be11bec 100644
--- a/backend/src/schema/resolvers/searches.js
+++ b/backend/src/schema/resolvers/searches.js
@@ -201,6 +201,8 @@ export default {
},
searchGroups: async (_parent, args, context, _resolveInfo) => {
const { query, groupsOffset, firstGroups } = args
+ let userId = null
+ if (context.user) userId = context.user.id
return {
groupCount: getSearchResults(
context,
@@ -208,6 +210,7 @@ export default {
{
query: queryString(query),
skip: 0,
+ userId,
},
countResultCallback,
),
@@ -215,6 +218,7 @@ export default {
query: queryString(query),
skip: groupsOffset,
limit: firstGroups,
+ userId,
}),
}
},
From d1b05b7e02ac2e5d7d60abfdbee90790f6913111 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Fri, 21 Oct 2022 18:35:21 +0200
Subject: [PATCH 358/374] add groups to search result page
---
.../features/SearchResults/SearchResults.vue | 73 ++++++++++++++++++-
webapp/graphql/Search.js | 14 ++++
2 files changed, 84 insertions(+), 3 deletions(-)
diff --git a/webapp/components/_new/features/SearchResults/SearchResults.vue b/webapp/components/_new/features/SearchResults/SearchResults.vue
index f3730f61df..dadbd5aac3 100644
--- a/webapp/components/_new/features/SearchResults/SearchResults.vue
+++ b/webapp/components/_new/features/SearchResults/SearchResults.vue
@@ -59,6 +59,14 @@
+
+
+
+
+
+
+
+
@@ -100,13 +108,14 @@
@@ -365,6 +428,10 @@ export default {
opacity: 0.8;
}
}
+
+ .group-teaser-card-wrapper {
+ padding: 0;
+ }
}
.grid-total-search-results {
diff --git a/webapp/graphql/Search.js b/webapp/graphql/Search.js
index 71dd18c07c..d2131ddc01 100644
--- a/webapp/graphql/Search.js
+++ b/webapp/graphql/Search.js
@@ -61,6 +61,20 @@ export const searchPosts = gql`
}
`
+export const searchGroups = gql`
+ ${groupFragment}
+
+ query ($query: String!, $firstGroups: Int, $groupsOffset: Int) {
+ searchGroups(query: $query, firstGroups: $firstGroups, groupsOffset: $groupsOffset) {
+ groupCount
+ groups {
+ __typename
+ ...group
+ }
+ }
+ }
+`
+
export const searchUsers = gql`
${userFragment}
From 1421973a9fd76345749f9f05fe2962f6aeeb8aae Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Sat, 22 Oct 2022 08:41:27 +0200
Subject: [PATCH 359/374] Fix Cypress test 'I_click_on_the_author.js'
---
.../Moderation.ReportContent/I_click_on_the_author.js | 2 +-
webapp/components/UserTeaser/UserTeaser.vue | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/cypress/integration/Moderation.ReportContent/I_click_on_the_author.js b/cypress/integration/Moderation.ReportContent/I_click_on_the_author.js
index 3a6600ff68..fad21e1a61 100644
--- a/cypress/integration/Moderation.ReportContent/I_click_on_the_author.js
+++ b/cypress/integration/Moderation.ReportContent/I_click_on_the_author.js
@@ -1,7 +1,7 @@
import { When } from "cypress-cucumber-preprocessor/steps";
When('I click on the author', () => {
- cy.get('.user-teaser')
+ cy.get('[data-test="avatarUserLink"]')
.click()
.url().should('include', '/profile/')
})
\ No newline at end of file
diff --git a/webapp/components/UserTeaser/UserTeaser.vue b/webapp/components/UserTeaser/UserTeaser.vue
index 96507672f1..c7d3df131c 100644
--- a/webapp/components/UserTeaser/UserTeaser.vue
+++ b/webapp/components/UserTeaser/UserTeaser.vue
@@ -5,7 +5,7 @@
-
+
From 2b0ea92f093d2b85aabfd3c977ea49cfa58c9f98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?=
Date: Sat, 22 Oct 2022 12:30:48 +0200
Subject: [PATCH 360/374] Fix master merge by adding groups button to mobile
menu
---
webapp/layouts/default.vue | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/webapp/layouts/default.vue b/webapp/layouts/default.vue
index 98b6b39781..e173602235 100644
--- a/webapp/layouts/default.vue
+++ b/webapp/layouts/default.vue
@@ -95,6 +95,7 @@
+
@@ -163,6 +164,16 @@
+
+
+
+
+
+
Date: Sat, 22 Oct 2022 15:28:01 +0200
Subject: [PATCH 361/374] add db:migrate up to CI
---
.github/workflows/test.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index de171cc61f..cc4d2deb93 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -202,6 +202,8 @@ jobs:
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps neo4j backend
- name: backend | Initialize Database
run: docker-compose exec -T backend yarn db:migrate init
+ - name: backend | Migrate Database Up
+ run: docker-compose exec -T backend yarn db:migrate up
- name: backend | Unit test
run: docker-compose exec -T backend yarn test
##########################################################################
From 6f4bde93f62d2c42e4e90e71fb2d47a056b0827d Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Sun, 23 Oct 2022 00:52:48 +0200
Subject: [PATCH 362/374] Update backend/src/schema/resolvers/searches.js
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Wolfgang Huß
---
backend/src/schema/resolvers/searches.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/backend/src/schema/resolvers/searches.js b/backend/src/schema/resolvers/searches.js
index c72be11bec..466fae08fa 100644
--- a/backend/src/schema/resolvers/searches.js
+++ b/backend/src/schema/resolvers/searches.js
@@ -76,8 +76,8 @@ const searchGroupsSetup = {
AND NOT (resource.deleted = true OR resource.disabled = true)
AND (resource.groupType IN ['public', 'closed']
OR membership.role IN ['usual', 'admin', 'owner'])`,
- withClause: 'WITH resource',
- returnClause: 'resource { .*, __typename: labels(resource)[0] }',
+ withClause: 'WITH resource, membership',
+ returnClause: 'resource { .*, myRole: membership.role, __typename: labels(resource)[0] }',
limit: 'LIMIT $limit',
}
From 19aa01f83f6e24b2648617dd927abe4dfb52edcd Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Sun, 23 Oct 2022 00:53:22 +0200
Subject: [PATCH 363/374] Update
webapp/components/generic/SearchGroup/SearchGroup.vue
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Wolfgang Huß
---
webapp/components/generic/SearchGroup/SearchGroup.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webapp/components/generic/SearchGroup/SearchGroup.vue b/webapp/components/generic/SearchGroup/SearchGroup.vue
index 8dc3d2c133..b5903615e0 100644
--- a/webapp/components/generic/SearchGroup/SearchGroup.vue
+++ b/webapp/components/generic/SearchGroup/SearchGroup.vue
@@ -1,5 +1,5 @@
-
+
{{ option.groupName | truncate(70) }}
From 7102de6e521e092c94c17094246dfc23af4cab80 Mon Sep 17 00:00:00 2001
From: Moriz Wahl
Date: Sun, 23 Oct 2022 00:53:41 +0200
Subject: [PATCH 364/374] Update
webapp/components/generic/SearchGroup/SearchGroup.vue
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: Wolfgang Huß
---
webapp/components/generic/SearchGroup/SearchGroup.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/webapp/components/generic/SearchGroup/SearchGroup.vue b/webapp/components/generic/SearchGroup/SearchGroup.vue
index b5903615e0..3bac21f7b3 100644
--- a/webapp/components/generic/SearchGroup/SearchGroup.vue
+++ b/webapp/components/generic/SearchGroup/SearchGroup.vue
@@ -14,7 +14,7 @@ export default {
}