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 6d837856c..3601dd2fa 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 377caf0b0..7a8be0b94 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 000000000..aa6f5767e
--- /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 8d6a021ab..d476e5f9b 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 000000000..afc421133
--- /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 000000000..2cf298474
--- /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 000000000..655a251d6
--- /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 772dedf6b..aca08df0e 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 aa6f5767e..b26f7779c 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 5858a5d1e..dd46846a9 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 6ad8c578b..7df780cfc 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 000000000..a29cfa112
--- /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 7a8be0b94..78960be6b 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 40a6a6ae4..cfaf7f1b0 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 b10389f50..7e23cfe0f 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 165235be9..25c7c21a4 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 7cfb89c19..41d58ece3 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 b26f7779c..651c2983e 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 000000000..0ed5d1356
--- /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 000000000..aba718159
--- /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 655a251d6..2dbe3c8c6 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 7373255a9..9776fcea2 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 00df85bac..db030e929 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 25c7c21a4..2a965c87f 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 78960be6b..938ebef02 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 000000000..b87e5632a
--- /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 a29cfa112..5fc554ee2 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 7c6f18ab1..fbf03c675 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('CreateGroup', () => {
+ const categoryIds = ['cat9']
+
+ 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']
- const createPostMutation = gql`
- mutation ($title: String!, $content: String!, $categoryIds: [ID]!, $slug: String) {
- CreatePost(title: $title, content: $content, categoryIds: $categoryIds, slug: $slug) {
- slug
- }
- }
- `
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 102acde6a..c64d1fd37 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 aba718159..8d8c7c3d8 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 | 32 ++++++++++++-------------
1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql
index 2dbe3c8c6..310df9dbc 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
+ ): # Wolle: add group settings
+ # Wolle:
+ # showShoutsPublicly: Boolean
+ # sendNotificationEmails: Boolean
+ # locale: String
+ Group
- UpdateGroup (
+ 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 fbf03c675..44701970d 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 000000000..76d659b96
--- /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 000000000..e9b3ba670
--- /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 297daa511..162897883 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 8499b0290..4043eefd8 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 000000000..6b52167f2
--- /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 000000000..386bb1e51
--- /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 201d68c14..14317642b 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 5fc554ee2..c49856f2a 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 44701970d..af6ff25b0 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 651c2983e..53b02fbec 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 8d8c7c3d8..4932c2c5e 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 310df9dbc..ee71f3e1f 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 c49856f2a..4f07e0f1e 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 53b02fbec..0cec02bf8 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 0ed5d1356..b202c6037 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 4932c2c5e..17fc4b1da 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 000000000..dacdd4b52
--- /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 ee71f3e1f..72ac9b57a 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 000000000..edda989f6
--- /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 7e23cfe0f..99dcfc0cd 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 b202c6037..9a55c9f4d 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 000000000..f05970650
--- /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 000000000..80b599658
--- /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 4f07e0f1e..000000000
--- 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 000000000..3277af820
--- /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 af6ff25b0..3c18e70b0 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 17fc4b1da..8860f87f2 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 72ac9b57a..cd15689ec 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 80b599658..e8da8e90b 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 9a55c9f4d..be07fecc6 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 8860f87f2..58e5f37be 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 cd15689ec..b8e00f0ee 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 76d659b96..e730a3c13 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 b77096859..9463083f1 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 a199106a5..22df641e9 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 386bb1e51..a861403ed 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 3c18e70b0..59fa72ba7 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 e6a8c3a18..f6d482421 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 f6d482421..dadbcd2a1 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 25149e9c3..a75ad518f 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 dadbcd2a1..d1af98513 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 bae530c61..5354f5ebe 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 afc421133..221ed7f87 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 59fa72ba7..9605aada9 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 5354f5ebe..b3327d44a 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 1bd4455d7..703adc45c 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')
+ submit() {
+ console.log('handleSubmit')
+ },
+ handleSubmit() {
+ console.log('handleSubmit')
+ },
+ reset() {
+ 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 000000000..4ae9f807f
--- /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 bfaceed21..69df8acb7 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 000000000..f83b00685
--- /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 22df641e9..33a50dca7 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 000000000..7ce25e827
--- /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 000000000..36f2ccb2b
--- /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 9463083f1..e82fadd6e 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 a861403ed..81843f5d2 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 703adc45c..2cbc67464 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 000000000..c41f06e4d
--- /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 e82fadd6e..c76be5360 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 2cbc67464..8e29bf300 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 9651cbb95..9aa7f539f 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 cfaf7f1b0..ca061609a 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 9605aada9..3fea526ee 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 d1af98513..5737f5505 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 b3327d44a..707558a06 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 000000000..b408c5e95
--- /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 3277af820..237446d41 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 46c5870e0..0a0926f06 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 000000000..3cb8a05f8
--- /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 2dcb14855..15b39e80d 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 0a0926f06..64ee3c1dd 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 b7d71de2d..1fb95a8db 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 8e29bf300..d52fea0c4 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 4ae9f807f..f3ff5fab0 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 69df8acb7..d16b208de 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 f83b00685..16f4ebecf 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 c41f06e4d..579380b24 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 7ce25e827..6e8eed4b4 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 36f2ccb2b..88c4b85c6 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 c76be5360..4526188d7 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 64ee3c1dd..e41ef1abc 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 99dcfc0cd..4c73624b0 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 5737f5505..d27d9b5e4 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 707558a06..706e27748 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 3165b4a44..fd53d48b3 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 a25e51079..4219cd00e 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 | 479 +++++++++++++-----
backend/src/schema/types/type/Group.gql | 12 +-
5 files changed, 423 insertions(+), 129 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index 2a611f324..b169e10fb 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 4c73624b0..9dcf35476 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 d27d9b5e4..b64484d37 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 706e27748..81223a584 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('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()
- })
+// describe('authenticated', () => {
+// beforeEach(async () => {
+// authenticatedUser = await user.toJson()
+// })
- let otherUser
+// 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 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('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 = 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('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 fd53d48b3..cf44894db 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 e41ef1abc..ebe8a6020 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 | 412 +++++++++++++-----
3 files changed, 310 insertions(+), 123 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 9dcf35476..dc54d5a29 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 b64484d37..91135a1db 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 81223a584..33aeaf2bd 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('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', () => {
-// beforeEach(async () => {
-// authenticatedUser = await user.toJson()
-// })
+ describe('authenticated', () => {
+ let otherUser
+ let ownerOfClosedGroupUser
+ let ownerOfHiddenGroupUser
-// let otherUser
+ 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',
+ },
+ })
+ })
-// 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('public group', () => {
+ beforeEach(async () => {
+ variables = {
+ id: 'public-group',
+ }
+ })
-// 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('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('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('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('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('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 | 1022 +++++++++--------
backend/src/schema/types/type/Group.gql | 2 +-
6 files changed, 603 insertions(+), 473 deletions(-)
diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js
index b169e10fb..8486288ec 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 ebe8a6020..f9b2d05da 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 dc54d5a29..afdc5501e 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 91135a1db..c9a31fdc3 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 33aeaf2bd..87eb02dc0 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,6 +320,244 @@ describe('Group', () => {
})
})
+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 () => {
+ 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: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
+ },
+ },
+ 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('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: '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('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: '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('GroupMember', () => {
describe('unauthenticated', () => {
it('throws authorization error', async () => {
@@ -301,28 +651,28 @@ describe('GroupMember', () => {
})
// create additional memberships
await mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'public-group',
userId: 'owner-of-closed-group',
},
})
await mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'public-group',
userId: 'owner-of-hidden-group',
},
})
await mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'closed-group',
userId: 'current-user',
},
})
await mutate({
- mutation: enterGroupMutation,
+ mutation: joinGroupMutation,
variables: {
id: 'hidden-group',
userId: 'owner-of-closed-group',
@@ -338,7 +688,11 @@ describe('GroupMember', () => {
})
describe('query group members', () => {
- describe('by owner', () => {
+ describe('by owner "current-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await user.toJson()
+ })
+
it('finds all members', async () => {
const expected = {
data: {
@@ -368,493 +722,265 @@ describe('GroupMember', () => {
})
})
- 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('by usual member "owner-of-closed-group"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await ownerOfClosedGroupUser.toJson()
})
- })
- })
- 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',
- }
+ 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)
- })
- })
- })
- })
-
- 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,
+ const result = await mutate({
+ mutation: groupMemberQuery,
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(() => {
- 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!')
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
})
})
- })
- })
- 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('by none member "other-user"', () => {
+ beforeEach(async () => {
+ authenticatedUser = await otherUser.toJson()
})
- 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',
- {
- 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',
- }
+ 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',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: enterGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
- })
+ beforeEach(async () => {
+ variables = {
+ id: 'closed-group',
+ }
})
- 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',
- }
+ 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: {
- 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',
+ }),
+ ]),
},
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(2)
+ })
+ })
+
+ // 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: {
+ 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,
+ })
+ 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',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: enterGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
- })
+ beforeEach(async () => {
+ variables = {
+ id: 'hidden-group',
+ }
})
- 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',
- }
+ 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: {
- EnterGroup: {
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
- },
+ 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,
- variables,
- }),
- ).resolves.toMatchObject(expected)
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(2)
+ })
+ })
+
+ // 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: {
+ 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 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 cf44894db..fad1b7e58 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 8486288ec..41780f7cd 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 afdc5501e..11ca956b6 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 c9a31fdc3..34959908d 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 87eb02dc0..e17e4827a 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 fad1b7e58..270f5c844 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 | 81 ++++++++++++++-----
2 files changed, 64 insertions(+), 19 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 11ca956b6..728385902 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 e17e4827a..1721e3e00 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,20 +979,8 @@ describe('GroupMember', () => {
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
+ // public-group
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -994,6 +995,7 @@ describe('GroupMember', () => {
userId: 'owner-of-hidden-group',
},
})
+ // closed-group
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -1001,6 +1003,21 @@ describe('GroupMember', () => {
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',
+ },
+ })
await mutate({
mutation: joinGroupMutation,
variables: {
@@ -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 1721e3e00..ea3904d0a 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 | 765 +++++++++++++++++-
2 files changed, 735 insertions(+), 31 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index 728385902..e3e3b3eb0 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 ea3904d0a..a06191baa 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,50 +770,54 @@ describe('SwitchGroupMemberRole', () => {
}
})
- describe('switch role', () => {
- describe('of owner member "owner-member-user"', () => {
+ describe('give the members their prospective roles', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'owner-member-user',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- describe('by owner themself "owner-member-user"', () => {
+ describe('switch role of "usual-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'usual-member-user',
+ }
})
- describe('to admin', () => {
+ describe('to usual', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'admin',
+ roleInGroup: 'usual',
}
})
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: switchGroupMemberRoleMutation,
- variables,
- })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ 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('of prospective admin member "admin-member-user"', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'admin-member-user',
- }
- })
-
- describe('by owner "owner-member-user"', () => {
+ describe('switch role of "admin-member-user"', () => {
beforeEach(async () => {
- authenticatedUser = await ownerMemberUser.toJson()
+ variables = {
+ ...variables,
+ userId: 'admin-member-user',
+ }
})
describe('to admin', () => {
@@ -836,11 +858,348 @@ describe('SwitchGroupMemberRole', () => {
})
})
- describe('by still pending member "usual-member-user"', () => {
+ 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 "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!')
+ })
+ })
+ })
+
+ // 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('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,
+ 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 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!')
+ })
+ })
+ })
+
+ 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: 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 = {
@@ -864,6 +1223,23 @@ describe('SwitchGroupMemberRole', () => {
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 = {
@@ -882,6 +1258,333 @@ describe('SwitchGroupMemberRole', () => {
})
})
})
+
+ 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',
+ }
+ })
+
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
+ mutation: switchGroupMemberRoleMutation,
+ variables,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+ })
})
})
})
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 ecfc1a042..bb0ab9d7c 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 | 2228 +++++++++----------
1 file changed, 1105 insertions(+), 1123 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index a06191baa..1a1386118 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,357 +84,1038 @@ 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('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,
+ 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,
}
- 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',
+ 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,
- )
- })
+ 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('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,
+ )
+ })
- describe('description', () => {
- describe('length without HTML', () => {
- describe('less then 100 chars', () => {
+ 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,
- description:
- '0123456789' +
- '0123456789 ',
- },
+ variables: { ...variables, categoryIds: null },
})
- expect(errors[0]).toHaveProperty('message', 'Description too short!')
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
})
- })
- })
- 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('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 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 () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- 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,
- },
+ describe('Group', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- 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('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,
+ },
})
})
- 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',
+ 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 = 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('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: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'usual',
},
- ],
- },
- errors: undefined,
- }
- await expect(
- query({ query: groupQuery, variables: { isMember: true } }),
- ).resolves.toMatchObject(expected)
+ },
+ 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('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,
+ 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: '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,
}),
- ]),
- },
- errors: undefined,
+ ).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: '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('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',
}
- await expect(
- query({ query: groupQuery, variables: { isMember: false } }),
- ).resolves.toMatchObject(expected)
+ })
+
+ 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,
+ }
+ const result = await mutate({
+ mutation: groupMemberQuery,
+ variables,
+ })
+ expect(result).toMatchObject(expected)
+ expect(result.data.GroupMember.length).toBe(3)
+ })
+ })
+
+ 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: {
+ 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('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 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)
+ })
+ })
+
+ 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()
+ })
+
+ 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,
+ })
+ 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('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: '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,
+ })
+ 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()
+ })
+
+ 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 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('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('in mode: building up', () => {
+ beforeAll(async () => {
+ await seedBasicsAndClearAuthentication()
})
- describe('authenticated', () => {
- let ownerOfClosedGroupUser
- let ownerOfHiddenGroupUser
+ afterAll(async () => {
+ await cleanDatabase()
+ })
- beforeEach(async () => {
+ describe('SwitchGroupMemberRole', () => {
+ let pendingMemberUser
+ let usualMemberUser
+ let adminMemberUser
+ let ownerMemberUser
+ let secondOwnerMemberUser
+
+ beforeAll(async () => {
// create users
- ownerOfClosedGroupUser = await Factory.build(
+ pendingMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
+ id: 'pending-member-user',
+ name: 'Pending Member TestUser',
},
{
- email: 'owner-of-closed-group@example.org',
+ email: 'pending-member-user@example.org',
password: '1234',
},
)
- ownerOfHiddenGroupUser = await Factory.build(
+ usualMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
},
{
- email: 'owner-of-hidden-group@example.org',
+ 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 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()
+ authenticatedUser = await usualMemberUser.toJson()
await mutate({
mutation: createGroupMutation,
variables: {
@@ -407,362 +1128,105 @@ describe('JoinGroup', () => {
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: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
- }
- await expect(
- mutate({
- mutation: joinGroupMutation,
- variables,
- }),
- ).resolves.toMatchObject(expected)
- })
+ await mutate({
+ mutation: joinGroupMutation,
+ variables: {
+ id: 'public-group',
+ userId: 'owner-of-closed-group',
+ },
})
-
- 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)
- })
- })
+ 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('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: '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('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: '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('authenticated', () => {
- describe('in building up mode', () => {
- let pendingMemberUser
- let usualMemberUser
- let adminMemberUser
- let ownerMemberUser
- 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',
- {
- 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',
- },
- })
-
- // Wolle
- // function sleep(ms) {
- // return new Promise(resolve => setTimeout(resolve, ms));
- // }
- // await sleep(4 * 1000)
- isCleanDbAfterEach = false
- isSeedDb = false
+ 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!')
})
- afterAll(async () => {
- // Wolle: find a better solution
- await cleanDatabase()
- isCleanDbAfterEach = true
- isSeedDb = true
- })
+ })
+ describe('authenticated', () => {
describe('in all group types – here "closed-group" for example', () => {
beforeEach(async () => {
variables = {
@@ -890,16 +1354,6 @@ describe('SwitchGroupMemberRole', () => {
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)
})
})
})
@@ -1590,475 +2044,3 @@ 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('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,
- }
- const result = await mutate({
- mutation: groupMemberQuery,
- variables,
- })
- expect(result).toMatchObject(expected)
- expect(result.data.GroupMember.length).toBe(3)
- })
- })
-
- 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: {
- 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('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 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)
- })
- })
-
- 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()
- })
-
- 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,
- })
- 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('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: '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,
- })
- 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()
- })
-
- 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 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!')
- })
- })
- })
- })
- })
-})
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 f9b2d05da..a2be78553 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 bb0ab9d7c..e3f6a3c84 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 1a1386118..22bac6da7 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 000000000..aab216569
--- /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 000000000..b9bbef83e
--- /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 000000000..477174485
--- /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 000000000..4fef7d3aa
--- /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 000000000..2a611f324
--- /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 297daa511..07a71dfcf 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 8499b0290..1b16431e3 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 aab216569..bafd5c392 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 b9bbef83e..047d12d7f 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 41780f7cd..2a9647860 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 a2be78553..0010b09ef 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 e3e3b3eb0..a3935872e 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 34959908d..ca24ef55f 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 22bac6da7..23fc5646e 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 270f5c844..f12b98c07 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 a3935872e..a92aacbba 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 | 288 ++++++++++--------
3 files changed, 237 insertions(+), 135 deletions(-)
diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js
index a92aacbba..f0ddd04ca 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 ca24ef55f..89c136dc5 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 23fc5646e..d9c2f22e3 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',
},
})
+ // 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 = await user.toJson()
+ 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 89c136dc5..04ef6d704 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 d9c2f22e3..6787e866e 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 6787e866e..df4c5388b 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 2a9647860..3a7f047d2 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 f0ddd04ca..c8f97cd17 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 04ef6d704..5e401c28e 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 df4c5388b..c81f976be 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 f12b98c07..1f59d7cc4 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 3a7f047d2..a0aef97b2 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 0010b09ef..1bd535456 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 c8f97cd17..bbaff67d9 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 5e401c28e..d34d62eb7 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 c81f976be..74f806b7b 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 1f59d7cc4..9d2210a1a 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 a0aef97b2..c6f110ed1 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 1bd535456..a71b11131 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 bbaff67d9..926f824e4 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 d34d62eb7..6ca09d72a 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 74f806b7b..a660f6ce8 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 9d2210a1a..5c98e49e8 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 | 3264 ++++++++++---------
1 file changed, 1633 insertions(+), 1631 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js
index a660f6ce8..054e3abb0 100644
--- a/backend/src/schema/resolvers/groups.spec.js
+++ b/backend/src/schema/resolvers/groups.spec.js
@@ -83,361 +83,228 @@ 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('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('in mode', () => {
+ describe('clean db after each single test', () => {
+ beforeEach(async () => {
+ await seedBasicsAndClearAuthentication()
})
- describe('unauthenticated', () => {
- it('throws authorization error', async () => {
- const { errors } = await mutate({ mutation: createGroupMutation, variables })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
+ // 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('authenticated', () => {
- beforeEach(async () => {
- authenticatedUser = await user.toJson()
+ 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,
+ }
})
- 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('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 () => {
+ 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', () => {
+ 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,
- description:
- '0123456789' +
- '0123456789 ',
- },
+ variables: { ...variables, categoryIds: null },
})
- expect(errors[0]).toHaveProperty('message', 'Description too short!')
+ expect(errors[0]).toHaveProperty('message', 'Too view categories!')
})
})
- })
- })
- 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 () => {
- const { errors } = await query({ query: groupQuery, variables: {} })
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
-
- 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,
- },
- })
- })
-
- 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 = 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('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('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!')
- })
- })
-
- 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('Group', () => {
+ describe('unauthenticated', () => {
+ it('throws authorization error', async () => {
+ const { errors } = await query({ query: groupQuery, variables: {} })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- 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',
+ 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,
+ },
+ })
+ })
+
+ 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,
+ }),
+ ]),
},
- }),
- ).resolves.toMatchObject({
- data: {
- JoinGroup: {
- id: 'owner-of-closed-group',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
+ 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('isMember = true', () => {
+ it('finds only groups where user is member', async () => {
await expect(
- mutate({
- mutation: joinGroupMutation,
- variables: {
- groupId: 'public-group',
- userId: 'current-user',
- },
- }),
+ query({ query: groupQuery, variables: { isMember: true } }),
).resolves.toMatchObject({
data: {
- JoinGroup: {
- id: 'current-user',
- myRoleInGroup: 'owner',
- },
+ 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,
})
@@ -445,38 +312,101 @@ describe('in mode: always clean db', () => {
})
})
})
+ })
- 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('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!')
+ })
+ })
+
+ 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('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: 'closed-group',
+ groupId: 'public-group',
userId: 'owner-of-closed-group',
},
}),
@@ -484,133 +414,732 @@ describe('in mode: always clean db', () => {
data: {
JoinGroup: {
id: 'owner-of-closed-group',
- myRoleInGroup: 'owner',
+ myRoleInGroup: 'usual',
},
},
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: 'public-group',
+ userId: 'current-user',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ JoinGroup: {
+ id: 'current-user',
+ 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 () => {
+ describe('closed group', () => {
+ describe('joined by "current-user"', () => {
+ it('has "pending" as membership role', async () => {
await expect(
mutate({
mutation: joinGroupMutation,
variables: {
- groupId: 'hidden-group',
- userId: 'owner-of-hidden-group',
+ groupId: 'closed-group',
+ userId: 'current-user',
},
}),
).resolves.toMatchObject({
data: {
JoinGroup: {
- id: 'owner-of-hidden-group',
- myRoleInGroup: 'owner',
+ 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: 'closed-group',
+ userId: 'owner-of-closed-group',
+ },
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ JoinGroup: {
+ id: 'owner-of-closed-group',
+ myRoleInGroup: 'owner',
+ },
+ },
+ 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',
+ },
+ })
+ 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,
+ })
+ })
+ })
+ })
})
})
})
})
-})
-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: 'other-user',
- name: 'Other TestUser',
+ id: 'pending-member-user',
+ name: 'Pending Member TestUser',
},
{
- email: 'other-user@example.org',
+ email: 'pending-member-user@example.org',
password: '1234',
},
)
- pendingUser = await Factory.build(
+ usualMemberUser = await Factory.build(
'user',
{
- id: 'pending-user',
- name: 'Pending TestUser',
+ id: 'usual-member-user',
+ name: 'Usual Member TestUser',
},
{
- email: 'pending@example.org',
+ email: 'usual-member-user@example.org',
password: '1234',
},
)
- ownerOfClosedGroupUser = await Factory.build(
+ adminMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-closed-group',
- name: 'Owner Of Closed Group',
+ id: 'admin-member-user',
+ name: 'Admin Member TestUser',
},
{
- email: 'owner-of-closed-group@example.org',
+ email: 'admin-member-user@example.org',
password: '1234',
},
)
- ownerOfHiddenGroupUser = await Factory.build(
+ ownerMemberUser = await Factory.build(
'user',
{
- id: 'owner-of-hidden-group',
- name: 'Owner Of Hidden Group',
+ id: 'owner-member-user',
+ name: 'Owner Member TestUser',
},
{
- email: 'owner-of-hidden-group@example.org',
+ 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 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,7 @@ describe('in mode: building up – separate for each resolver', () => {
mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'hidden-group',
- userId: 'pending-user',
- roleInGroup: 'pending',
- },
- })
- await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables: {
- groupId: 'hidden-group',
- userId: 'current-user',
+ userId: 'admin-member-user',
roleInGroup: 'usual',
},
})
@@ -701,606 +1207,337 @@ describe('in mode: building up – separate for each resolver', () => {
mutation: changeGroupMemberRoleMutation,
variables: {
groupId: 'hidden-group',
- userId: 'owner-of-closed-group',
- roleInGroup: 'admin',
+ 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',
},
})
- // 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',
- }
- })
+ afterAll(async () => {
+ await cleanDatabase()
+ })
- 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('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('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('authenticated', () => {
+ describe('in all group types – here "closed-group" for example', () => {
+ beforeEach(async () => {
+ variables = {
+ groupId: 'closed-group',
+ }
})
- 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"', () => {
+ describe('join the members and give them their prospective roles', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- userId: 'usual-member-user',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- describe('as usual', () => {
+ describe('for "usual-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- roleInGroup: 'usual',
+ userId: 'usual-member-user',
}
})
- it('has role usual', async () => {
- await expect(
- mutate({
+ 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,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'usual-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
+ })
+ 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!')
})
})
- // the GQL mutation needs this fields in the result for testing
- it.todo('has "updatedAt" newer as "createdAt"')
+ 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('for "admin-member-user"', () => {
+ describe('of admin "admin-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
@@ -1308,730 +1545,495 @@ describe('in mode: building up – separate for each resolver', () => {
}
})
- describe('as admin', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- 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({
+ 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('back 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: 'admin-member-user',
- myRoleInGroup: 'admin',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
+ })
+ })
+ })
+
+ 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: 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!')
+ })
+ })
+ })
+
+ 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: 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!')
+ })
+ })
+ })
+
+ 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!')
+ })
+ })
+
+ 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('for "second-owner-member-user"', () => {
+ describe('of usual member "usual-member-user"', () => {
beforeEach(async () => {
variables = {
...variables,
- userId: 'second-owner-member-user',
+ userId: 'usual-member-user',
}
})
- describe('as owner', () => {
+ describe('by owner "owner-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'owner',
- }
+ authenticatedUser = await ownerMemberUser.toJson()
})
- it('has role owner', async () => {
- await expect(
- mutate({
+ 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,
+ })
+ })
+ })
+
+ 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,
+ })
+ })
+ })
+ })
+
+ 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: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'second-owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- })
- })
- })
- })
- 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,
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
})
- 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({
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'owner-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
})
- })
- describe('by admin "admin-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await adminMemberUser.toJson()
- })
-
- describe('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 admin', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'admin',
+ }
})
- 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({
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'admin-member-user',
- myRoleInGroup: 'owner',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- })
- describe('back to admin', () => {
- beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'admin',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ describe('degrade to pending', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
})
- expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
- })
- })
- })
- 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: 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!')
- })
- })
- })
-
- 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: 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!')
- })
- })
- })
-
- 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!')
- })
- })
-
- 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('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 () => {
- await expect(
- mutate({
+ 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 none member "current-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ 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: 'usual-member-user',
- myRoleInGroup: 'usual',
- },
- },
- errors: undefined,
+ })
+ expect(errors[0]).toHaveProperty('message', 'Not Authorised!')
})
})
- })
- })
- 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: changeGroupMemberRoleMutation,
- variables,
+ describe('degrade to pending again', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'pending',
+ }
})
- 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('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: changeGroupMemberRoleMutation,
- 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: changeGroupMemberRoleMutation,
- 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: changeGroupMemberRoleMutation,
- 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: changeGroupMemberRoleMutation,
- 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 () => {
- await expect(
- mutate({
+ it('throws authorization error', async () => {
+ const { errors } = await mutate({
mutation: changeGroupMemberRoleMutation,
variables,
- }),
- ).resolves.toMatchObject({
- data: {
- ChangeGroupMemberRole: {
- id: 'pending-member-user',
- myRoleInGroup: 'usual',
+ })
+ 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 () => {
+ await expect(
+ mutate({
+ mutation: changeGroupMemberRoleMutation,
+ variables,
+ }),
+ ).resolves.toMatchObject({
+ data: {
+ ChangeGroupMemberRole: {
+ id: 'pending-member-user',
+ myRoleInGroup: 'usual',
+ },
},
- },
- errors: undefined,
+ errors: undefined,
+ })
+ })
+ })
+
+ 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('back to pending', () => {
+ describe('by usual member "usual-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'pending',
- }
+ authenticatedUser = await usualMemberUser.toJson()
})
- it('has role usual again', async () => {
- await expect(
- mutate({
+ describe('upgrade to usual', () => {
+ beforeEach(async () => {
+ variables = {
+ ...variables,
+ roleInGroup: 'usual',
+ }
+ })
+
+ 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('by usual member "usual-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await usualMemberUser.toJson()
- })
-
- describe('upgrade to usual', () => {
+ describe('by still pending member "pending-member-user"', () => {
beforeEach(async () => {
- variables = {
- ...variables,
- roleInGroup: 'usual',
- }
+ authenticatedUser = await pendingMemberUser.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 still pending member "pending-member-user"', () => {
- beforeEach(async () => {
- authenticatedUser = await pendingMemberUser.toJson()
- })
-
- 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',
+ }
})
- 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',
- }
- })
-
- it('throws authorization error', async () => {
- const { errors } = await mutate({
- mutation: changeGroupMemberRoleMutation,
- variables,
+ 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 054e3abb0..e966506a7 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 e966506a7..7a5f9504a 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 926f824e4..bfd10537a 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 6ca09d72a..c47cf6085 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 7a5f9504a..be9ba4f3d 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 | 28 ++++++++++++-------------
2 files changed, 20 insertions(+), 16 deletions(-)
diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js
index c47cf6085..abaa1716f 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 5c98e49e8..e254e5086 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
+ # UpdateGroup(
+ # id: ID!
+ # name: String
+ # slug: String
+ # avatar: ImageInput
+ # locationName: String
+ # about: String
+ # description: String
+ # ): Group
- DeleteGroup(id: ID!): 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 be9ba4f3d..1d272de2b 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 707558a06..6890e9147 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 5737f5505..2d9dde1ff 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 000000000..579380b24
--- /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 297daa511..07a71dfcf 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 8499b0290..1b16431e3 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 aab216569..bafd5c392 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 b9bbef83e..047d12d7f 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 4fef7d3aa..365fcc557 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: {
+ // 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 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: {
- 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 047d12d7f..d743633d1 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 07a71dfcf..db2046ae3 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 1b16431e3..5b49c96ee 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 365fcc557..326cce74a 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 568e4f3a8..8fc313c43 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 f2ef4e712..6937aeabc 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 db2046ae3..134368ad3 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 5b49c96ee..fe12ef437 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 900b2fa5d..55d654231 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 684f080b7..8c5a731a3 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 71994167b..694dee532 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 3c1a8902d..fc2e90a34 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 6ae3e32f7..49405d0a9 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 f5f89374a..f85e7b0e2 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 5f1368820..de7b06326 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 b04874047..80c4ef6ca 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 e25cb8fd5..98ff88a96 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 f92789ef8..366fd8292 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 80c4ef6ca..53da683ce 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 000000000..c619da8fc
--- /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 635eccd5b..366c01be5 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 c619da8fc..44de2eab5 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 86602a7c1..1fc4e3fa7 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 7e0def985..b1a9356a9 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 791874729..63070ee4b 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 66675942c..91b91abe9 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 c9ff7870b..342550a14 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 02039e0db..c7e474a3a 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 c371a8831..840698487 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 fe419f053..bec922edb 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 bb0f34086..f50121635 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 2011e0a8c..f21a71942 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 668c271d6..81bb5c49b 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 e61786fe7..cfebe65a4 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 87b46091b..daf0c1d2a 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 9ebbbcdce..9a6eb9bcc 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 c50eaa4c4..e3c934dc5 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 5b87cf12d..1fce64d8f 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 73681e9b3..0c0cbc8d6 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 0cca122b3..1f92212ab 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 ac333604e..b25e19070 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 b25e19070..d4ecca7f5 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 000000000..2000e3046
--- /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 3e2a4aa69..b23ed98ea 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 000000000..6639c77e7
--- /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 000000000..83a16745c
--- /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 6639c77e7..69c13e6f0 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 83a16745c..0300bacd0 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 3abf0d12a..1c49d3ff3 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 b23ed98ea..6fb3a2f54 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 8a9638b1a..0923d6fc2 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 0923d6fc2..45fd428e1 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 88d83d059..60bcccf43 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 cfebe65a4..97a884698 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 516e0895f..b82b3953c 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 | 263 ++++++++++++--------------
1 file changed, 124 insertions(+), 139 deletions(-)
diff --git a/webapp/components/Group/GroupForm.vue b/webapp/components/Group/GroupForm.vue
index b82b3953c..6454d4493 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.description') }}
-
-
-
- {{ `${contentLength} / ${descriptionMin}` }}
-
-
+
+
+
-
-
-
- {{ $t('group.actionRadius') }}
-
-
-
+
+
+ {{ $t('group.type') }}
+
+
- {{ $t(`group.actionRadii.${actionRadius}`) }}
-
-
-
+
+ {{ $t(`group.types.${groupType}`) }}
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('group.description') }}
+
+
+
+ {{ `${contentLength} / ${descriptionMin}` }}
+
+
+
+
+
+
+ {{ $t('group.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 9a1e87fe1..6fb3a2f54 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 929232bfe..7a7737320 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 1b9ede71e..4d50c1181 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 21363365f..c0354731b 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 837cfc75c..036aed766 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 3b18e1a45..91acdb8f6 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 d77363c29..3d698810e 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 c0354731b..c72be11be 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 f3730f61d..dadbd5aac 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 71dd18c07..d2131ddc0 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 3a6600ff6..fad21e1a6 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 96507672f..c7d3df131 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 98b6b3978..e17360223 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 de171cc61..cc4d2deb9 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 c72be11be..466fae08f 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 8dc3d2c13..b5903615e 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 b5903615e..3bac21f7b 100644
--- a/webapp/components/generic/SearchGroup/SearchGroup.vue
+++ b/webapp/components/generic/SearchGroup/SearchGroup.vue
@@ -14,7 +14,7 @@ export default {
}