From 8f117f23b412055a11b21c2fc09fe7696632824f Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 10:13:11 +0200 Subject: [PATCH 01/43] Add new right CREATE_CONTRIBUTION_MESSAGE --- backend/src/auth/RIGHTS.ts | 1 + backend/src/auth/ROLES.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 0d8252402..c38315de4 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -33,6 +33,7 @@ export enum RIGHTS { LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS', SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS', + CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE', // Admin SEARCH_USERS = 'SEARCH_USERS', SET_USER_ROLE = 'SET_USER_ROLE', diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index f14e77b17..bad18db9e 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -31,6 +31,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.SEARCH_ADMIN_USERS, RIGHTS.LIST_CONTRIBUTION_LINKS, RIGHTS.COMMUNITY_STATISTICS, + RIGHTS.CREATE_CONTRIBUTION_MESSAGE, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights From a59967d767d0f9aab702f8d501785f2b49b9566d Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 10:30:14 +0200 Subject: [PATCH 02/43] Add new Arg for ContributionMessage --- backend/src/graphql/arg/ContributionMessageArgs.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 backend/src/graphql/arg/ContributionMessageArgs.ts diff --git a/backend/src/graphql/arg/ContributionMessageArgs.ts b/backend/src/graphql/arg/ContributionMessageArgs.ts new file mode 100644 index 000000000..963824350 --- /dev/null +++ b/backend/src/graphql/arg/ContributionMessageArgs.ts @@ -0,0 +1,11 @@ +import { ArgsType, Field, InputType } from 'type-graphql' + +@InputType() +@ArgsType() +export default class ContributionArgs { + @Field(() => Number) + contributionId: number + + @Field(() => String) + message: string +} From 0376cffb193f230faedeaaeb4dbf4c273abed229 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 10:30:48 +0200 Subject: [PATCH 03/43] First steps createContributionMessage method of ContributionMessageResolver. --- .../resolver/ContributionMessageResolver.ts | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 backend/src/graphql/resolver/ContributionMessageResolver.ts diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts new file mode 100644 index 000000000..e10bcf853 --- /dev/null +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -0,0 +1,33 @@ +import { RIGHTS } from '@/auth/RIGHTS' +import { Context, getUser } from '@/server/context' +import { ContributionMessage } from '@entity/ContributionMessage' +import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' +import ContributionMessageArgs from '@arg/ContributionMessageArgs' +import { Contribution } from '@entity/Contribution' +import { ContributionMessageType } from '@enum/MessageType' + +@Resolver() +export class ContributionResolver { + @Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE]) + @Mutation(() => ContributionMessage) + async createContributionMessage( + @Args() { contributionId, message }: ContributionMessageArgs, + @Ctx() context: Context, + ): Promise { + const user = getUser(context) + const contribution = await Contribution.findOneOrFail({ id: contributionId }) + if (!user.isAdmin && contribution.userId !== user.id) { + throw new Error('Can not send message to contribution of another user') + } + + const contributionMessage = new ContributionMessage() + contributionMessage.contributionId = contributionId + contributionMessage.createdAt = new Date() + contributionMessage.message = message + contributionMessage.userId = user.id + contributionMessage.type = ContributionMessageType.DIALOG + + ContributionMessage.save(contributionMessage) + return contributionMessage + } +} From 281f6cde3feb3cd04b054aeaf94564f8d6d74b98 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 10:39:56 +0200 Subject: [PATCH 04/43] Update contributionStatus to new state. --- .../graphql/resolver/ContributionMessageResolver.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index e10bcf853..b6df23488 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -5,6 +5,7 @@ import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { Contribution } from '@entity/Contribution' import { ContributionMessageType } from '@enum/MessageType' +import { ContributionStatus } from '../enum/ContributionStatus' @Resolver() export class ContributionResolver { @@ -26,8 +27,18 @@ export class ContributionResolver { contributionMessage.message = message contributionMessage.userId = user.id contributionMessage.type = ContributionMessageType.DIALOG - ContributionMessage.save(contributionMessage) + + if (user.isAdmin && contribution.contributionStatus === ContributionStatus.PENDING) { + contribution.contributionStatus = ContributionStatus.IN_PROGRESS + } else if ( + !user.isAdmin && + contribution.contributionStatus === ContributionStatus.IN_PROGRESS + ) { + contribution.contributionStatus = ContributionStatus.PENDING + } + Contribution.save(contribution) + return contributionMessage } } From c5b84694d04111fe6ce316594141ec24ab19b0f4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 12:16:20 +0200 Subject: [PATCH 05/43] Withdrew admin part of user createContributionMessage. --- .../resolver/ContributionMessageResolver.ts | 49 +++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index b6df23488..ea80dd7d4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -1,3 +1,4 @@ +import { backendLogger as logger } from '@/server/logger' import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { ContributionMessage } from '@entity/ContributionMessage' @@ -6,9 +7,11 @@ import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { Contribution } from '@entity/Contribution' import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '../enum/ContributionStatus' +import { getConnection } from '@dbTools/typeorm' @Resolver() export class ContributionResolver { + // TODO: Have two method one for admin on for @Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE]) @Mutation(() => ContributionMessage) async createContributionMessage( @@ -16,29 +19,35 @@ export class ContributionResolver { @Ctx() context: Context, ): Promise { const user = getUser(context) - const contribution = await Contribution.findOneOrFail({ id: contributionId }) - if (!user.isAdmin && contribution.userId !== user.id) { - throw new Error('Can not send message to contribution of another user') - } - + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('READ UNCOMMITTED') const contributionMessage = new ContributionMessage() - contributionMessage.contributionId = contributionId - contributionMessage.createdAt = new Date() - contributionMessage.message = message - contributionMessage.userId = user.id - contributionMessage.type = ContributionMessageType.DIALOG - ContributionMessage.save(contributionMessage) + try { + const contribution = await Contribution.findOneOrFail({ id: contributionId }) + if (contribution.userId !== user.id) { + throw new Error('Can not send message to contribution of another user') + } - if (user.isAdmin && contribution.contributionStatus === ContributionStatus.PENDING) { - contribution.contributionStatus = ContributionStatus.IN_PROGRESS - } else if ( - !user.isAdmin && - contribution.contributionStatus === ContributionStatus.IN_PROGRESS - ) { - contribution.contributionStatus = ContributionStatus.PENDING + contributionMessage.contributionId = contributionId + contributionMessage.createdAt = new Date() + contributionMessage.message = message + contributionMessage.userId = user.id + contributionMessage.type = ContributionMessageType.DIALOG + await queryRunner.manager.insert(ContributionMessage, contributionMessage) + + if (contribution.contributionStatus === ContributionStatus.IN_PROGRESS) { + contribution.contributionStatus = ContributionStatus.PENDING + await queryRunner.manager.update(Contribution, { id: contributionId }, contribution) + } + await queryRunner.commitTransaction() + } catch (e) { + await queryRunner.rollbackTransaction() + logger.error(`ContributionMessage was not successful: ${e}`) + throw new Error(`Transaction was not successful: ${e}`) + } finally { + await queryRunner.release() } - Contribution.save(contribution) - return contributionMessage } } From bca2f49483115f062076abb84e9f0a018de58f51 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 12:17:11 +0200 Subject: [PATCH 06/43] Add admin right to create contribution message. --- backend/src/auth/RIGHTS.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index c38315de4..a62a09bee 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -51,4 +51,5 @@ export enum RIGHTS { CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', + ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', } From 710d47b594c6158b81f631e095fa9659a7a3de82 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 12:21:38 +0200 Subject: [PATCH 07/43] Add adminCreateContributionMessage. --- backend/src/graphql/resolver/AdminResolver.ts | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index e70fe71ee..01d70cd3c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -62,6 +62,9 @@ import { MEMO_MAX_CHARS, MEMO_MIN_CHARS, } from './const/const' +import { ContributionMessage } from '@entity/ContributionMessage' +import ContributionMessageArgs from '../arg/ContributionMessageArgs' +import { ContributionMessageType } from '../enum/MessageType' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -696,4 +699,44 @@ export class AdminResolver { logger.debug(`updateContributionLink successful!`) return new ContributionLink(dbContributionLink) } + + @Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTION_MESSAGE]) + @Mutation(() => ContributionMessage) + async adminCreateContributionMessage( + @Args() { contributionId, message }: ContributionMessageArgs, + @Ctx() context: Context, + ): Promise { + const user = getUser(context) + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('READ UNCOMMITTED') + const contributionMessage = new ContributionMessage() + try { + const contribution = await Contribution.findOneOrFail({ id: contributionId }) + + contributionMessage.contributionId = contributionId + contributionMessage.createdAt = new Date() + contributionMessage.message = message + contributionMessage.userId = user.id + contributionMessage.type = ContributionMessageType.DIALOG + await queryRunner.manager.insert(ContributionMessage, contributionMessage) + + if ( + contribution.contributionStatus === ContributionStatus.DELETED || + contribution.contributionStatus === ContributionStatus.DENIED || + contribution.contributionStatus === ContributionStatus.PENDING + ) { + contribution.contributionStatus = ContributionStatus.IN_PROGRESS + await queryRunner.manager.update(Contribution, { id: contributionId }, contribution) + } + await queryRunner.commitTransaction() + } catch (e) { + await queryRunner.rollbackTransaction() + logger.error(`ContributionMessage was not successful: ${e}`) + throw new Error(`Transaction was not successful: ${e}`) + } finally { + await queryRunner.release() + } + return contributionMessage + } } From 5ca422c6d3aaa1ce30cacfe3b4055627fc339bb4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 12:27:06 +0200 Subject: [PATCH 08/43] Change import for enum and arg. --- backend/src/graphql/resolver/AdminResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 01d70cd3c..48acada1f 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -63,8 +63,8 @@ import { MEMO_MIN_CHARS, } from './const/const' import { ContributionMessage } from '@entity/ContributionMessage' -import ContributionMessageArgs from '../arg/ContributionMessageArgs' -import { ContributionMessageType } from '../enum/MessageType' +import ContributionMessageArgs from '@arg/ContributionMessageArgs' +import { ContributionMessageType } from '@enum/MessageType' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? From 27fd42f097e39597b6efdf95ca7e9f392b115aec Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 22 Aug 2022 12:27:46 +0200 Subject: [PATCH 09/43] Change import for enum. --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index ea80dd7d4..158b3fafb 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -6,7 +6,7 @@ import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { Contribution } from '@entity/Contribution' import { ContributionMessageType } from '@enum/MessageType' -import { ContributionStatus } from '../enum/ContributionStatus' +import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' @Resolver() From e70a18c6bb42bc4b022b26616e46a6d309957ba4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Aug 2022 11:09:41 +0200 Subject: [PATCH 10/43] Change thrown error message. --- backend/src/graphql/resolver/AdminResolver.ts | 2 +- backend/src/graphql/resolver/ContributionMessageResolver.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 48acada1f..68d3d4a8d 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -733,7 +733,7 @@ export class AdminResolver { } catch (e) { await queryRunner.rollbackTransaction() logger.error(`ContributionMessage was not successful: ${e}`) - throw new Error(`Transaction was not successful: ${e}`) + throw new Error(`ContributionMessage was not successful: ${e}`) } finally { await queryRunner.release() } diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 158b3fafb..a9529dfe7 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -44,7 +44,7 @@ export class ContributionResolver { } catch (e) { await queryRunner.rollbackTransaction() logger.error(`ContributionMessage was not successful: ${e}`) - throw new Error(`Transaction was not successful: ${e}`) + throw new Error(`ContributionMessage was not successful: ${e}`) } finally { await queryRunner.release() } From 1c433e2d57269ce9decdddf6f26c728f71413888 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Aug 2022 11:53:19 +0200 Subject: [PATCH 11/43] Add graphql object that will be returned instead of dbEntity. --- .../src/graphql/model/ContributionMessage.ts | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 backend/src/graphql/model/ContributionMessage.ts diff --git a/backend/src/graphql/model/ContributionMessage.ts b/backend/src/graphql/model/ContributionMessage.ts new file mode 100644 index 000000000..caf5eddc4 --- /dev/null +++ b/backend/src/graphql/model/ContributionMessage.ts @@ -0,0 +1,37 @@ +import { Field, ObjectType } from 'type-graphql' +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' +import { User } from './User' + +@ObjectType() +export class ContributionMessage { + constructor(contributionMessage: DbContributionMessage, user: User) { + this.id = contributionMessage.id + this.message = contributionMessage.message + this.createdAt = contributionMessage.createdAt + this.updatedAt = contributionMessage.updatedAt + this.type = contributionMessage.type + this.userFirstName = user.firstName + this.userLastName = user.lastName + } + + @Field(() => Number) + id: number + + @Field(() => String) + message: string + + @Field(() => Date) + createdAt: Date + + @Field(() => Date, { nullable: true }) + updatedAt?: Date | null + + @Field(() => String) + type: string + + @Field(() => String, { nullable: true }) + userFirstName: string | null + + @Field(() => String, { nullable: true }) + userLastName: string | null +} From 2c459a8fae7e4ee0e7acf98ac641a6bac72b2010 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Aug 2022 11:55:34 +0200 Subject: [PATCH 12/43] Changed the Contribution.find so that we can throw a specific error, added an GraphQL Model as return value instead of DbEntity. --- backend/src/graphql/resolver/AdminResolver.ts | 15 +++++++++------ .../resolver/ContributionMessageResolver.ts | 16 ++++++++++------ 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 68d3d4a8d..9a5609b75 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -62,9 +62,10 @@ import { MEMO_MAX_CHARS, MEMO_MIN_CHARS, } from './const/const' -import { ContributionMessage } from '@entity/ContributionMessage' +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { ContributionMessageType } from '@enum/MessageType' +import { ContributionMessage } from '@model/ContributionMessage' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -710,16 +711,18 @@ export class AdminResolver { const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') - const contributionMessage = new ContributionMessage() + const contributionMessage = DbContributionMessage.create() try { - const contribution = await Contribution.findOneOrFail({ id: contributionId }) - + const contribution = await Contribution.findOne({ id: contributionId }) + if (!contribution) { + throw new Error('Contribution not found') + } contributionMessage.contributionId = contributionId contributionMessage.createdAt = new Date() contributionMessage.message = message contributionMessage.userId = user.id contributionMessage.type = ContributionMessageType.DIALOG - await queryRunner.manager.insert(ContributionMessage, contributionMessage) + await queryRunner.manager.insert(DbContributionMessage, contributionMessage) if ( contribution.contributionStatus === ContributionStatus.DELETED || @@ -737,6 +740,6 @@ export class AdminResolver { } finally { await queryRunner.release() } - return contributionMessage + return new ContributionMessage(contributionMessage, new User(user)) } } diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index a9529dfe7..dbffc77ce 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -1,17 +1,18 @@ import { backendLogger as logger } from '@/server/logger' import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' -import { ContributionMessage } from '@entity/ContributionMessage' +import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { Contribution } from '@entity/Contribution' import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' +import { ContributionMessage } from '@model/ContributionMessage' +import { User } from '@model/User' @Resolver() export class ContributionResolver { - // TODO: Have two method one for admin on for @Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE]) @Mutation(() => ContributionMessage) async createContributionMessage( @@ -22,9 +23,12 @@ export class ContributionResolver { const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() await queryRunner.startTransaction('READ UNCOMMITTED') - const contributionMessage = new ContributionMessage() + const contributionMessage = DbContributionMessage.create() try { - const contribution = await Contribution.findOneOrFail({ id: contributionId }) + const contribution = await Contribution.findOne({ id: contributionId }) + if (!contribution) { + throw new Error('Contribution not found') + } if (contribution.userId !== user.id) { throw new Error('Can not send message to contribution of another user') } @@ -34,7 +38,7 @@ export class ContributionResolver { contributionMessage.message = message contributionMessage.userId = user.id contributionMessage.type = ContributionMessageType.DIALOG - await queryRunner.manager.insert(ContributionMessage, contributionMessage) + await queryRunner.manager.insert(DbContributionMessage, contributionMessage) if (contribution.contributionStatus === ContributionStatus.IN_PROGRESS) { contribution.contributionStatus = ContributionStatus.PENDING @@ -48,6 +52,6 @@ export class ContributionResolver { } finally { await queryRunner.release() } - return contributionMessage + return new ContributionMessage(contributionMessage, new User(user)) } } From e3e4b61c57176c724a63c2e32d274c1f82674721 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Aug 2022 11:59:24 +0200 Subject: [PATCH 13/43] Rename export of ContributionArgs to ContributionMessageArgs. --- backend/src/graphql/arg/ContributionMessageArgs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/arg/ContributionMessageArgs.ts b/backend/src/graphql/arg/ContributionMessageArgs.ts index 963824350..dd75baed0 100644 --- a/backend/src/graphql/arg/ContributionMessageArgs.ts +++ b/backend/src/graphql/arg/ContributionMessageArgs.ts @@ -2,7 +2,7 @@ import { ArgsType, Field, InputType } from 'type-graphql' @InputType() @ArgsType() -export default class ContributionArgs { +export default class ContributionMessageArgs { @Field(() => Number) contributionId: number From 41fb0d2a9f5f0b50cc7096eaf8be917b206ea442 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 23 Aug 2022 13:46:55 +0200 Subject: [PATCH 14/43] Change export name of ContributionMessageResolver class. --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index dbffc77ce..7c4779e63 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -12,7 +12,7 @@ import { ContributionMessage } from '@model/ContributionMessage' import { User } from '@model/User' @Resolver() -export class ContributionResolver { +export class ContributionMessageResolver { @Authorized([RIGHTS.CREATE_CONTRIBUTION_MESSAGE]) @Mutation(() => ContributionMessage) async createContributionMessage( From 0271a4274121fd7f137091604fd73d401888ded8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 10:56:19 +0200 Subject: [PATCH 15/43] Use user entity instead of user model. --- backend/src/graphql/model/ContributionMessage.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 2 +- .../src/graphql/resolver/ContributionMessageResolver.ts | 3 +-- backend/src/graphql/resolver/ContributionResolver.ts | 7 +++---- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/backend/src/graphql/model/ContributionMessage.ts b/backend/src/graphql/model/ContributionMessage.ts index caf5eddc4..98b6b2721 100644 --- a/backend/src/graphql/model/ContributionMessage.ts +++ b/backend/src/graphql/model/ContributionMessage.ts @@ -1,6 +1,6 @@ import { Field, ObjectType } from 'type-graphql' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' -import { User } from './User' +import { User } from '@entity/User' @ObjectType() export class ContributionMessage { diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 9a5609b75..63da1baed 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -740,6 +740,6 @@ export class AdminResolver { } finally { await queryRunner.release() } - return new ContributionMessage(contributionMessage, new User(user)) + return new ContributionMessage(contributionMessage, user) } } diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 7c4779e63..c73301598 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -9,7 +9,6 @@ import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' import { ContributionMessage } from '@model/ContributionMessage' -import { User } from '@model/User' @Resolver() export class ContributionMessageResolver { @@ -52,6 +51,6 @@ export class ContributionMessageResolver { } finally { await queryRunner.release() } - return new ContributionMessage(contributionMessage, new User(user)) + return new ContributionMessage(contributionMessage, user) } } diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 8056ffde3..a59ba0a86 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -96,13 +96,14 @@ export class ContributionResolver { order: { createdAt: order, }, + relations: ['messages'], withDeleted: true, skip: (currentPage - 1) * pageSize, take: pageSize, }) return new ContributionListResult( count, - contributions.map((contribution) => new Contribution(contribution, new User(user))), + contributions.map((contribution) => new Contribution(contribution, user)), ) } @@ -123,9 +124,7 @@ export class ContributionResolver { .getManyAndCount() return new ContributionListResult( count, - dbContributions.map( - (contribution) => new Contribution(contribution, new User(contribution.user)), - ), + dbContributions.map((contribution) => new Contribution(contribution, contribution.user)), ) } From bda04f87d26886e67bd91c937a4510bbe09def64 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 10:57:03 +0200 Subject: [PATCH 16/43] Add messages and state to the contribution model. --- backend/src/graphql/model/Contribution.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index aa878990c..741debbbb 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -1,7 +1,8 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { Contribution as dbContribution } from '@entity/Contribution' -import { User } from './User' +import { User } from '@entity/User' +import { ContributionMessage } from './ContributionMessage' @ObjectType() export class Contribution { @@ -16,6 +17,10 @@ export class Contribution { this.confirmedAt = contribution.confirmedAt this.confirmedBy = contribution.confirmedBy this.contributionDate = contribution.contributionDate + this.state = contribution.contributionStatus + this.messages = contribution.messages + ? contribution.messages.map((message) => new ContributionMessage(message, user)) + : [] } @Field(() => Number) @@ -47,6 +52,12 @@ export class Contribution { @Field(() => Date) contributionDate: Date + + @Field(() => [ContributionMessage]) + messages: ContributionMessage[] + + @Field(() => String) + state: string } @ObjectType() From dd2022df25c10a1acbdacdc6ff508f97336d9981 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 10:57:52 +0200 Subject: [PATCH 17/43] Add new values to query for contributions in frontend. --- frontend/src/graphql/queries.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index 03299dd49..f701ad8cf 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -206,6 +206,16 @@ export const listContributions = gql` confirmedAt confirmedBy deletedAt + state + messages { + id + message + createdAt + updatedAt + type + userFirstName + userLastName + } } } } From 59df924e1d6b9210edbf0a61ff84e2713acc04c4 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 16:27:28 +0200 Subject: [PATCH 18/43] Add User to Contribution link, query the messages user in the listContributions query. --- backend/src/graphql/model/Contribution.ts | 2 +- .../graphql/resolver/ContributionResolver.ts | 24 +++++++++++-------- .../User.ts | 5 ++++ .../ContributionMessage.ts | 6 +++++ 4 files changed, 26 insertions(+), 11 deletions(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index 741debbbb..c0e6d66f3 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -19,7 +19,7 @@ export class Contribution { this.contributionDate = contribution.contributionDate this.state = contribution.contributionStatus this.messages = contribution.messages - ? contribution.messages.map((message) => new ContributionMessage(message, user)) + ? contribution.messages.map((message) => new ContributionMessage(message, message.user)) : [] } diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index a59ba0a86..f9538851b 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -14,6 +14,7 @@ import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import { User } from '@model/User' import { validateContribution, getUserCreation, updateCreations } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' +import { ContributionMessage } from '@entity/ContributionMessage' @Resolver() export class ContributionResolver { @@ -90,17 +91,20 @@ export class ContributionResolver { userId: number confirmedBy?: FindOperator | null } = { userId: user.id } + if (filterConfirmed) where.confirmedBy = IsNull() - const [contributions, count] = await dbContribution.findAndCount({ - where, - order: { - createdAt: order, - }, - relations: ['messages'], - withDeleted: true, - skip: (currentPage - 1) * pageSize, - take: pageSize, - }) + + const [contributions, count] = await getConnection() + .createQueryBuilder() + .select('c') + .from(dbContribution, 'c') + .innerJoinAndSelect('c.messages', 'm') + .innerJoinAndSelect('m.user', 'u') + .orderBy('c.createdAt', order) + .limit(pageSize) + .offset((currentPage - 1) * pageSize) + .getManyAndCount() + return new ContributionListResult( count, contributions.map((contribution) => new Contribution(contribution, user)), diff --git a/database/entity/0046-adapt_users_table_for_gradidoid/User.ts b/database/entity/0046-adapt_users_table_for_gradidoid/User.ts index 3f2547cad..a772a3c99 100644 --- a/database/entity/0046-adapt_users_table_for_gradidoid/User.ts +++ b/database/entity/0046-adapt_users_table_for_gradidoid/User.ts @@ -8,6 +8,7 @@ import { JoinColumn, } from 'typeorm' import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -108,4 +109,8 @@ export class User extends BaseEntity { @OneToMany(() => Contribution, (contribution) => contribution.user) @JoinColumn({ name: 'user_id' }) contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] } diff --git a/database/entity/0047-messages_tables/ContributionMessage.ts b/database/entity/0047-messages_tables/ContributionMessage.ts index d9ac124dd..3d9c04d89 100644 --- a/database/entity/0047-messages_tables/ContributionMessage.ts +++ b/database/entity/0047-messages_tables/ContributionMessage.ts @@ -5,9 +5,11 @@ import { Entity, JoinColumn, ManyToOne, + OneToMany, PrimaryGeneratedColumn, } from 'typeorm' import { Contribution } from '../Contribution' +import { User } from '../User' @Entity('contribution_messages', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci', @@ -26,6 +28,10 @@ export class ContributionMessage extends BaseEntity { @Column({ name: 'user_id', unsigned: true, nullable: false }) userId: number + @ManyToOne(() => User, (user) => user.messages) + @JoinColumn({ name: 'user_id' }) + user: User + @Column({ length: 2000, nullable: false, collation: 'utf8mb4_unicode_ci' }) message: string From e0fcdb2cee35352187b914fd113abdb2083cb6f0 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 16:31:36 +0200 Subject: [PATCH 19/43] Add new RIGHT for LIST_ALL_CONTRIBUTION_MESSAGES. --- backend/src/auth/RIGHTS.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index a62a09bee..4f144f1e9 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -34,6 +34,7 @@ export enum RIGHTS { COMMUNITY_STATISTICS = 'COMMUNITY_STATISTICS', SEARCH_ADMIN_USERS = 'SEARCH_ADMIN_USERS', CREATE_CONTRIBUTION_MESSAGE = 'CREATE_CONTRIBUTION_MESSAGE', + LIST_ALL_CONTRIBUTION_MESSAGES = 'LIST_ALL_CONTRIBUTION_MESSAGES', // Admin SEARCH_USERS = 'SEARCH_USERS', SET_USER_ROLE = 'SET_USER_ROLE', From a9c3dd25edc2ed85237242df2b439a8c9b619cd8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 16:32:58 +0200 Subject: [PATCH 20/43] Add RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES to the ROLES of Users. --- backend/src/auth/ROLES.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index bad18db9e..eabaf8e99 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -32,6 +32,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.LIST_CONTRIBUTION_LINKS, RIGHTS.COMMUNITY_STATISTICS, RIGHTS.CREATE_CONTRIBUTION_MESSAGE, + RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights From 057816e4c0a2df9052fbe0b416d8929388866e9a Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 16:43:32 +0200 Subject: [PATCH 21/43] Change innerJoin to leftJoin, so that every contribution is shown not only the one with messages. --- backend/src/graphql/resolver/ContributionResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index f9538851b..1b63c90d2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -98,8 +98,8 @@ export class ContributionResolver { .createQueryBuilder() .select('c') .from(dbContribution, 'c') - .innerJoinAndSelect('c.messages', 'm') - .innerJoinAndSelect('m.user', 'u') + .leftJoinAndSelect('c.messages', 'm') + .leftJoinAndSelect('m.user', 'u') .orderBy('c.createdAt', order) .limit(pageSize) .offset((currentPage - 1) * pageSize) From 4dbcabf96dad19d717a2ad2eb32115ad846dd8b2 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 24 Aug 2022 16:52:42 +0200 Subject: [PATCH 22/43] Add where clause. --- backend/src/graphql/resolver/ContributionResolver.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 1b63c90d2..1b7a8f630 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -100,6 +100,7 @@ export class ContributionResolver { .from(dbContribution, 'c') .leftJoinAndSelect('c.messages', 'm') .leftJoinAndSelect('m.user', 'u') + .where(where) .orderBy('c.createdAt', order) .limit(pageSize) .offset((currentPage - 1) * pageSize) From d2790588ba4518aa7de0d4d009903cf82e8edc31 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:09:13 +0200 Subject: [PATCH 23/43] Add new model ContributionMessageListResult. --- backend/src/graphql/model/ContributionMessage.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/graphql/model/ContributionMessage.ts b/backend/src/graphql/model/ContributionMessage.ts index 98b6b2721..863813da9 100644 --- a/backend/src/graphql/model/ContributionMessage.ts +++ b/backend/src/graphql/model/ContributionMessage.ts @@ -35,3 +35,11 @@ export class ContributionMessage { @Field(() => String, { nullable: true }) userLastName: string | null } +@ObjectType() +export class ContributionMessageListResult { + @Field(() => Number) + count: number + + @Field(() => [ContributionMessage]) + messages: ContributionMessage[] +} From 3fbf8b6444a449b3b74bf3c76498097decb62e2c Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:10:13 +0200 Subject: [PATCH 24/43] Query ContributionMessages for a specific contributionId. --- .../resolver/ContributionMessageResolver.ts | 32 +++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index c73301598..f5dee91f6 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -2,13 +2,15 @@ import { backendLogger as logger } from '@/server/logger' import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' -import { Args, Authorized, Ctx, Mutation, Resolver } from 'type-graphql' +import { Arg, Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql' import ContributionMessageArgs from '@arg/ContributionMessageArgs' import { Contribution } from '@entity/Contribution' import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' -import { ContributionMessage } from '@model/ContributionMessage' +import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' +import Paginated from '../arg/Paginated' +import { Order } from '../enum/Order' @Resolver() export class ContributionMessageResolver { @@ -53,4 +55,30 @@ export class ContributionMessageResolver { } return new ContributionMessage(contributionMessage, user) } + + @Authorized([RIGHTS.LIST_ALL_CONTRIBUTION_MESSAGES]) + @Query(() => ContributionMessageListResult) + async listContributionMessages( + @Arg('contributionId') contributionId: number, + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + ): Promise { + const [contributionMessages, count] = await getConnection() + .createQueryBuilder() + .select('cm') + .from(DbContributionMessage, 'cm') + .leftJoinAndSelect('cm.user', 'u') + .where({ contributionId: contributionId }) + .orderBy('cm.createdAt', order) + .limit(pageSize) + .offset((currentPage - 1) * pageSize) + .getManyAndCount() + + return { + count, + messages: contributionMessages.map( + (message) => new ContributionMessage(message, message.user), + ), + } + } } From 4b9bdb0f155f6f5963bd4384dd87807e1e241ca3 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:11:24 +0200 Subject: [PATCH 25/43] Add userId to the ContributionMessage model. --- backend/src/graphql/model/ContributionMessage.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/graphql/model/ContributionMessage.ts b/backend/src/graphql/model/ContributionMessage.ts index 863813da9..1357c02cb 100644 --- a/backend/src/graphql/model/ContributionMessage.ts +++ b/backend/src/graphql/model/ContributionMessage.ts @@ -12,6 +12,7 @@ export class ContributionMessage { this.type = contributionMessage.type this.userFirstName = user.firstName this.userLastName = user.lastName + this.userId = user.id } @Field(() => Number) @@ -34,6 +35,9 @@ export class ContributionMessage { @Field(() => String, { nullable: true }) userLastName: string | null + + @Field(() => Number, { nullable: true }) + userId: number | null } @ObjectType() export class ContributionMessageListResult { From f3f0c27063053bed076e76442f98cfc10973a18d Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:12:49 +0200 Subject: [PATCH 26/43] Fix imports. --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 4 ++-- backend/src/graphql/resolver/ContributionResolver.ts | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index f5dee91f6..408481513 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -9,8 +9,8 @@ import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' -import Paginated from '../arg/Paginated' -import { Order } from '../enum/Order' +import Paginated from '@arg/Paginated' +import { Order } from '@enum/Order' @Resolver() export class ContributionMessageResolver { diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 1b7a8f630..f8300f164 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -11,10 +11,8 @@ import { ContributionType } from '@enum/ContributionType' import { ContributionStatus } from '@enum/ContributionStatus' import { Contribution, ContributionListResult } from '@model/Contribution' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' -import { User } from '@model/User' import { validateContribution, getUserCreation, updateCreations } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' -import { ContributionMessage } from '@entity/ContributionMessage' @Resolver() export class ContributionResolver { From 0163eac52fe14de60fc8936d6a14acbec0803abc Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:51:58 +0200 Subject: [PATCH 27/43] Add contribution state and contribution messages to the model --- .../src/graphql/model/UnconfirmedContribution.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/model/UnconfirmedContribution.ts b/backend/src/graphql/model/UnconfirmedContribution.ts index 1d697a971..b7dc4bd72 100644 --- a/backend/src/graphql/model/UnconfirmedContribution.ts +++ b/backend/src/graphql/model/UnconfirmedContribution.ts @@ -2,10 +2,11 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { Contribution } from '@entity/Contribution' import { User } from '@entity/User' +import { ContributionMessage } from '@model/ContributionMessage' @ObjectType() export class UnconfirmedContribution { - constructor(contribution: Contribution, user: User, creations: Decimal[]) { + constructor(contribution: Contribution, user: User | undefined, creations: Decimal[]) { this.id = contribution.id this.userId = contribution.userId this.amount = contribution.amount @@ -15,6 +16,10 @@ export class UnconfirmedContribution { this.lastName = user ? user.lastName : '' this.email = user ? user.email : '' this.creation = creations + this.state = contribution.contributionStatus + this.messages = contribution.messages + ? contribution.messages.map((message) => new ContributionMessage(message, message.user)) + : [] } @Field(() => String) @@ -46,4 +51,10 @@ export class UnconfirmedContribution { @Field(() => [Decimal]) creation: Decimal[] + + @Field(() => String) + state: string + + @Field(() => [ContributionMessage]) + messages: ContributionMessage[] } From cdb306dc8579989a7c7a827e16237193ee4a8a28 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 09:52:33 +0200 Subject: [PATCH 28/43] Change the query to get the messages and the user of a message. --- backend/src/graphql/resolver/AdminResolver.ts | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 63da1baed..a0eed646f 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -361,7 +361,15 @@ export class AdminResolver { @Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS]) @Query(() => [UnconfirmedContribution]) async listUnconfirmedContributions(): Promise { - const contributions = await Contribution.find({ where: { confirmedAt: IsNull() } }) + const contributions = await getConnection() + .createQueryBuilder() + .select('c') + .from(Contribution, 'c') + .leftJoinAndSelect('c.messages', 'm') + .leftJoinAndSelect('m.user', 'u') + .where({ confirmedAt: IsNull() }) + .getMany() + if (contributions.length === 0) { return [] } @@ -374,18 +382,11 @@ export class AdminResolver { const user = users.find((u) => u.id === contribution.userId) const creation = userCreations.find((c) => c.id === contribution.userId) - return { - id: contribution.id, - userId: contribution.userId, - date: contribution.contributionDate, - memo: contribution.memo, - amount: contribution.amount, - moderator: contribution.moderatorId, - firstName: user ? user.firstName : '', - lastName: user ? user.lastName : '', - email: user ? user.email : '', - creation: creation ? creation.creations : FULL_CREATION_AVAILABLE, - } + return new UnconfirmedContribution( + contribution, + user, + creation ? creation.creations : FULL_CREATION_AVAILABLE, + ) }) } From c27d64245b9f929439180147c41b6e4a075cc376 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 13:51:38 +0200 Subject: [PATCH 29/43] Add moderatorId to the object. --- backend/src/graphql/model/UnconfirmedContribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/src/graphql/model/UnconfirmedContribution.ts b/backend/src/graphql/model/UnconfirmedContribution.ts index b7dc4bd72..3b88bec1c 100644 --- a/backend/src/graphql/model/UnconfirmedContribution.ts +++ b/backend/src/graphql/model/UnconfirmedContribution.ts @@ -15,6 +15,7 @@ export class UnconfirmedContribution { this.firstName = user ? user.firstName : '' this.lastName = user ? user.lastName : '' this.email = user ? user.email : '' + this.moderator = contribution.moderatorId this.creation = creations this.state = contribution.contributionStatus this.messages = contribution.messages From 84ee364051897fef67507455f644caa4b6c5dfff Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 15:00:21 +0200 Subject: [PATCH 30/43] Revert "Fix imports." This reverts commit f3f0c27063053bed076e76442f98cfc10973a18d. --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 4 ++-- backend/src/graphql/resolver/ContributionResolver.ts | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index 408481513..f5dee91f6 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -9,8 +9,8 @@ import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' -import Paginated from '@arg/Paginated' -import { Order } from '@enum/Order' +import Paginated from '../arg/Paginated' +import { Order } from '../enum/Order' @Resolver() export class ContributionMessageResolver { diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index f8300f164..1b7a8f630 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -11,8 +11,10 @@ import { ContributionType } from '@enum/ContributionType' import { ContributionStatus } from '@enum/ContributionStatus' import { Contribution, ContributionListResult } from '@model/Contribution' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' +import { User } from '@model/User' import { validateContribution, getUserCreation, updateCreations } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' +import { ContributionMessage } from '@entity/ContributionMessage' @Resolver() export class ContributionResolver { From ead31344f5593018067cf5111e4ee032e0efaea9 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 15:16:44 +0200 Subject: [PATCH 31/43] Remove leftJoin on user and remove messages from UnconfirmedContribution model added messageCount instead to the model. --- backend/src/graphql/model/UnconfirmedContribution.ts | 9 +++------ backend/src/graphql/resolver/AdminResolver.ts | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/model/UnconfirmedContribution.ts b/backend/src/graphql/model/UnconfirmedContribution.ts index 3b88bec1c..5847b08d0 100644 --- a/backend/src/graphql/model/UnconfirmedContribution.ts +++ b/backend/src/graphql/model/UnconfirmedContribution.ts @@ -2,7 +2,6 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { Contribution } from '@entity/Contribution' import { User } from '@entity/User' -import { ContributionMessage } from '@model/ContributionMessage' @ObjectType() export class UnconfirmedContribution { @@ -18,9 +17,7 @@ export class UnconfirmedContribution { this.moderator = contribution.moderatorId this.creation = creations this.state = contribution.contributionStatus - this.messages = contribution.messages - ? contribution.messages.map((message) => new ContributionMessage(message, message.user)) - : [] + this.messageCount = contribution.messages ? contribution.messages.length : 0 } @Field(() => String) @@ -56,6 +53,6 @@ export class UnconfirmedContribution { @Field(() => String) state: string - @Field(() => [ContributionMessage]) - messages: ContributionMessage[] + @Field(() => Number) + messageCount: number } diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index a0eed646f..f4656aec8 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -366,7 +366,6 @@ export class AdminResolver { .select('c') .from(Contribution, 'c') .leftJoinAndSelect('c.messages', 'm') - .leftJoinAndSelect('m.user', 'u') .where({ confirmedAt: IsNull() }) .getMany() From e510c2ddbf2c048dbca8e2e50f1131f1de74c193 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 25 Aug 2022 15:27:12 +0200 Subject: [PATCH 32/43] Change import for enum. --- backend/src/graphql/resolver/ContributionMessageResolver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.ts b/backend/src/graphql/resolver/ContributionMessageResolver.ts index f5dee91f6..408481513 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.ts @@ -9,8 +9,8 @@ import { ContributionMessageType } from '@enum/MessageType' import { ContributionStatus } from '@enum/ContributionStatus' import { getConnection } from '@dbTools/typeorm' import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage' -import Paginated from '../arg/Paginated' -import { Order } from '../enum/Order' +import Paginated from '@arg/Paginated' +import { Order } from '@enum/Order' @Resolver() export class ContributionMessageResolver { From 1090d17fdb365da3e592c20ea86b7f2062c0535c Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 26 Aug 2022 08:32:15 +0200 Subject: [PATCH 33/43] Remove OneToMany import. --- database/entity/0047-messages_tables/ContributionMessage.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/database/entity/0047-messages_tables/ContributionMessage.ts b/database/entity/0047-messages_tables/ContributionMessage.ts index 3d9c04d89..e5226043d 100644 --- a/database/entity/0047-messages_tables/ContributionMessage.ts +++ b/database/entity/0047-messages_tables/ContributionMessage.ts @@ -5,7 +5,6 @@ import { Entity, JoinColumn, ManyToOne, - OneToMany, PrimaryGeneratedColumn, } from 'typeorm' import { Contribution } from '../Contribution' From 894140b41837e8ab2a0a3c7a7d1dcfcb97ed456a Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 26 Aug 2022 08:50:08 +0200 Subject: [PATCH 34/43] Remove unused imports. --- backend/src/graphql/resolver/ContributionResolver.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 1b7a8f630..f8300f164 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -11,10 +11,8 @@ import { ContributionType } from '@enum/ContributionType' import { ContributionStatus } from '@enum/ContributionStatus' import { Contribution, ContributionListResult } from '@model/Contribution' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' -import { User } from '@model/User' import { validateContribution, getUserCreation, updateCreations } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' -import { ContributionMessage } from '@entity/ContributionMessage' @Resolver() export class ContributionResolver { From 423989777b91898b5a9b698956c72537aacd1a8f Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 12:19:25 +0200 Subject: [PATCH 35/43] Remove leftJoin on user, changed ContributionMessage array to count of ContributionMessage. --- backend/src/graphql/model/Contribution.ts | 9 +++------ backend/src/graphql/resolver/ContributionResolver.ts | 1 - 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index c0e6d66f3..1f690a3d8 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -2,7 +2,6 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { Contribution as dbContribution } from '@entity/Contribution' import { User } from '@entity/User' -import { ContributionMessage } from './ContributionMessage' @ObjectType() export class Contribution { @@ -18,9 +17,7 @@ export class Contribution { this.confirmedBy = contribution.confirmedBy this.contributionDate = contribution.contributionDate this.state = contribution.contributionStatus - this.messages = contribution.messages - ? contribution.messages.map((message) => new ContributionMessage(message, message.user)) - : [] + this.messagesCount = contribution.messages ? contribution.messages.length : 0 } @Field(() => Number) @@ -53,8 +50,8 @@ export class Contribution { @Field(() => Date) contributionDate: Date - @Field(() => [ContributionMessage]) - messages: ContributionMessage[] + @Field(() => Number) + messagesCount: number @Field(() => String) state: string diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index f8300f164..fc93880f1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -97,7 +97,6 @@ export class ContributionResolver { .select('c') .from(dbContribution, 'c') .leftJoinAndSelect('c.messages', 'm') - .leftJoinAndSelect('m.user', 'u') .where(where) .orderBy('c.createdAt', order) .limit(pageSize) From 214f85544b64c82911c1a5c85d8ac09578bb61ae Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 12:24:09 +0200 Subject: [PATCH 36/43] ContributionMessage is only defined in 0047 so remove the OneToMany relation from 0046 User and add it to 0047 User. --- .../User.ts | 5 - database/entity/0047-messages_tables/User.ts | 116 ++++++++++++++++++ database/entity/User.ts | 2 +- 3 files changed, 117 insertions(+), 6 deletions(-) create mode 100644 database/entity/0047-messages_tables/User.ts diff --git a/database/entity/0046-adapt_users_table_for_gradidoid/User.ts b/database/entity/0046-adapt_users_table_for_gradidoid/User.ts index a772a3c99..3f2547cad 100644 --- a/database/entity/0046-adapt_users_table_for_gradidoid/User.ts +++ b/database/entity/0046-adapt_users_table_for_gradidoid/User.ts @@ -8,7 +8,6 @@ import { JoinColumn, } from 'typeorm' import { Contribution } from '../Contribution' -import { ContributionMessage } from '../ContributionMessage' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -109,8 +108,4 @@ export class User extends BaseEntity { @OneToMany(() => Contribution, (contribution) => contribution.user) @JoinColumn({ name: 'user_id' }) contributions?: Contribution[] - - @OneToMany(() => ContributionMessage, (message) => message.user) - @JoinColumn({ name: 'user_id' }) - messages?: ContributionMessage[] } diff --git a/database/entity/0047-messages_tables/User.ts b/database/entity/0047-messages_tables/User.ts new file mode 100644 index 000000000..a772a3c99 --- /dev/null +++ b/database/entity/0047-messages_tables/User.ts @@ -0,0 +1,116 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, +} from 'typeorm' +import { Contribution } from '../Contribution' +import { ContributionMessage } from '../ContributionMessage' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ + name: 'gradido_id', + length: 36, + nullable: false, + unique: true, + collation: 'utf8mb4_unicode_ci', + }) + gradidoID: string + + @Column({ + name: 'alias', + length: 20, + nullable: true, + unique: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + alias: string + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @DeleteDateColumn() + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null }) + isAdmin: Date | null + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ + name: 'contribution_link_id', + type: 'int', + unsigned: true, + nullable: true, + default: null, + }) + contributionLinkId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ + type: 'text', + name: 'passphrase', + collation: 'utf8mb4_unicode_ci', + nullable: true, + default: null, + }) + passphrase: string + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] + + @OneToMany(() => ContributionMessage, (message) => message.user) + @JoinColumn({ name: 'user_id' }) + messages?: ContributionMessage[] +} diff --git a/database/entity/User.ts b/database/entity/User.ts index 02a99fcd1..7d15bf559 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0046-adapt_users_table_for_gradidoid/User' +export { User } from './0047-messages_tables/User' From 7156f1afd5a0c9a3166acc441eaf7fc61bd286d6 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 12:58:01 +0200 Subject: [PATCH 37/43] Setup Test file for ContributionMessageResolver. --- .../ContributionMessageResolver.test.ts | 47 +++++++++++++++++++ backend/src/seeds/graphql/mutations.ts | 28 +++++++++++ backend/src/seeds/graphql/queries.ts | 23 +++++++++ 3 files changed, 98 insertions(+) create mode 100644 backend/src/graphql/resolver/ContributionMessageResolver.test.ts diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts new file mode 100644 index 000000000..f98e4f211 --- /dev/null +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -0,0 +1,47 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ + +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { cleanDB, resetToken, testEnvironment } from '@test/helpers' +import { GraphQLError } from 'graphql' +import { userFactory } from '@/seeds/factory/user' +import { creationFactory } from '@/seeds/factory/creation' +import { creations } from '@/seeds/creation/index' +import { peterLustig } from '@/seeds/users/peter-lustig' +import { createContributionMessage } from '@/seeds/graphql/mutations' + +let mutate: any, query: any, con: any +let testEnv: any +let result: any + +beforeAll(async () => { + testEnv = await testEnvironment() + mutate = testEnv.mutate + query = testEnv.query + con = testEnv.con + await cleanDB() +}) + +afterAll(async () => { + await cleanDB() + await con.close() +}) + +describe('ContributionMessageResolver', () => { + describe('createContributionMessage', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: createContributionMessage, + variables: { contributionId: 1, message: 'This is a test message' }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) +}) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index bf898bd7d..e5f290645 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -261,3 +261,31 @@ export const deleteContribution = gql` deleteContribution(id: $id) } ` + +export const createContributionMessage = gql` + mutation ($contributionId: Float!, $message: String!) { + createContributionMessage(contributionId: $contributionId, message: $message) { + id + message + createdAt + updatedAt + type + userFirstName + userLastName + } + } +` + +export const adminCreateContributionMessage = gql` + mutation ($contributionId: Float!, $message: String!) { + adminCreateContributionMessage(contributionId: $contributionId, message: $message) { + id + message + createdAt + updatedAt + type + userFirstName + userLastName + } + } +` diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 3bd042ac2..60dffa21b 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -292,3 +292,26 @@ export const searchAdminUsers = gql` } } ` + +export const listContributionMessages = gql` + query ($contributionId: Float!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) { + listContributionMessages( + contributionId: $contributionId + pageSize: $pageSize + currentPage: $currentPage + order: $order + ) { + count + messages { + id + message + createdAt + updatedAt + type + userFirstName + userLastName + userId + } + } + } +` From 8b62696c91aaa8116dc5960360d586f99a6dc136 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 13:07:24 +0200 Subject: [PATCH 38/43] Add test that listContributionMessages throws error for non authenticated user. --- .../ContributionMessageResolver.test.ts | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index f98e4f211..8d65db306 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -1,18 +1,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { cleanDB, resetToken, testEnvironment } from '@test/helpers' +import { cleanDB, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' -import { userFactory } from '@/seeds/factory/user' -import { creationFactory } from '@/seeds/factory/creation' -import { creations } from '@/seeds/creation/index' -import { peterLustig } from '@/seeds/users/peter-lustig' import { createContributionMessage } from '@/seeds/graphql/mutations' +import { listContributionMessages } from '@/seeds/graphql/queries' let mutate: any, query: any, con: any let testEnv: any -let result: any beforeAll(async () => { testEnv = await testEnvironment() @@ -44,4 +39,21 @@ describe('ContributionMessageResolver', () => { }) }) }) + + describe('listContributionMessages', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: listContributionMessages, + variables: { contributionId: 1 }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) }) From ac4f7dc04a193b4594cbf37740d3e81bf85d4794 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 13:16:44 +0200 Subject: [PATCH 39/43] Add test with non existing contributionId. --- .../ContributionMessageResolver.test.ts | 54 ++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 8d65db306..c66a85700 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -1,10 +1,12 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { cleanDB, testEnvironment } from '@test/helpers' +import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' import { createContributionMessage } from '@/seeds/graphql/mutations' -import { listContributionMessages } from '@/seeds/graphql/queries' +import { listContributionMessages, login } from '@/seeds/graphql/queries' +import { userFactory } from '@/seeds/factory/user' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' let mutate: any, query: any, con: any let testEnv: any @@ -38,6 +40,39 @@ describe('ContributionMessageResolver', () => { ) }) }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('input not valid', () => { + it('throws error when contribution does not exist', async () => { + await expect( + mutate({ + mutation: createContributionMessage, + variables: { + contributionId: -1, + message: 'Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Contribution not found')], + }), + ) + }) + }) + }) }) describe('listContributionMessages', () => { @@ -55,5 +90,20 @@ describe('ContributionMessageResolver', () => { ) }) }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + }) }) }) From 4ce8ec5bedf59573aa2fb9cd0b1955a3853b1991 Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 29 Aug 2022 13:25:42 +0200 Subject: [PATCH 40/43] Change error message from createContributionMessage, remove describe block without tests. --- .../ContributionMessageResolver.test.ts | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index c66a85700..dcf7d983e 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -67,7 +67,11 @@ describe('ContributionMessageResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Contribution not found')], + errors: [ + new GraphQLError( + 'ContributionMessage was not successful: Error: Contribution not found', + ), + ], }), ) }) @@ -90,20 +94,5 @@ describe('ContributionMessageResolver', () => { ) }) }) - - describe('authenticated', () => { - beforeAll(async () => { - await userFactory(testEnv, bibiBloxberg) - await query({ - query: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - }) - - afterAll(async () => { - await cleanDB() - resetToken() - }) - }) }) }) From d402b871e45e32f4955b9f5ba1eb408bb2fa7db3 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 31 Aug 2022 12:26:29 +0200 Subject: [PATCH 41/43] Test that adminCreateContributionMessage, createContributionMessage & listContributionMessages gives the right values back. --- .../ContributionMessageResolver.test.ts | 251 +++++++++++++++++- 1 file changed, 250 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index dcf7d983e..bf61da47f 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -3,13 +3,19 @@ import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' -import { createContributionMessage } from '@/seeds/graphql/mutations' +import { + adminCreateContributionMessage, + createContribution, + createContributionMessage, +} from '@/seeds/graphql/mutations' import { listContributionMessages, login } from '@/seeds/graphql/queries' import { userFactory } from '@/seeds/factory/user' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' +import { peterLustig } from '@/seeds/users/peter-lustig' let mutate: any, query: any, con: any let testEnv: any +let result: any beforeAll(async () => { testEnv = await testEnvironment() @@ -25,6 +31,99 @@ afterAll(async () => { }) describe('ContributionMessageResolver', () => { + describe('adminCreateContributionMessage', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { contributionId: 1, message: 'This is a test message' }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + result = await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('input not valid', () => { + it('throws error when contribution does not exist', async () => { + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: -1, + message: 'Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'ContributionMessage was not successful: Error: Contribution not found', + ), + ], + }), + ) + }) + }) + + describe('valid input', () => { + it('creates ContributionMessage', async () => { + await expect( + mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Admin Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + adminCreateContributionMessage: expect.objectContaining({ + id: expect.any(Number), + message: 'Admin Test', + type: 'DIALOG', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), + }, + }), + ) + }) + }) + }) + }) + describe('createContributionMessage', () => { describe('unauthenticated', () => { it('returns an error', async () => { @@ -44,10 +143,19 @@ describe('ContributionMessageResolver', () => { describe('authenticated', () => { beforeAll(async () => { await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) + result = await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) }) afterAll(async () => { @@ -75,6 +183,68 @@ describe('ContributionMessageResolver', () => { }), ) }) + + it('throws error when other user tries to send createContributionMessage', async () => { + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + await expect( + mutate({ + mutation: createContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'ContributionMessage was not successful: Error: Can not send message to contribution of another user', + ), + ], + }), + ) + }) + }) + + describe('valid input', () => { + beforeAll(async () => { + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('creates ContributionMessage', async () => { + await expect( + mutate({ + mutation: createContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Test', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + createContributionMessage: expect.objectContaining({ + id: expect.any(Number), + message: 'Test', + type: 'DIALOG', + userFirstName: 'Bibi', + userLastName: 'Bloxberg', + }), + }, + }), + ) + }) }) }) }) @@ -94,5 +264,84 @@ describe('ContributionMessageResolver', () => { ) }) }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + result = await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: adminCreateContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'Admin Test', + }, + }) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: createContributionMessage, + variables: { + contributionId: result.data.createContribution.id, + message: 'User Test', + }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns a list of contributionmessages', async () => { + await expect( + mutate({ + mutation: listContributionMessages, + variables: { contributionId: result.data.createContribution.id }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listContributionMessages: { + count: 2, + messages: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + message: 'Admin Test', + type: 'DIALOG', + userFirstName: 'Peter', + userLastName: 'Lustig', + }), + expect.objectContaining({ + id: expect.any(Number), + message: 'User Test', + type: 'DIALOG', + userFirstName: 'Bibi', + userLastName: 'Bloxberg', + }), + ]), + }, + }, + }), + ) + }) + }) }) }) From 2177c501c37b9f809ea14673b6aefb4ea8b20fe7 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 1 Sep 2022 10:24:42 +0200 Subject: [PATCH 42/43] Remove cleanDB in most afterAll so we don't need to seed contribution and contribution messages again in later tests. --- .../ContributionMessageResolver.test.ts | 60 ++----------------- 1 file changed, 5 insertions(+), 55 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index bf61da47f..6c617acb4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -69,8 +69,7 @@ describe('ContributionMessageResolver', () => { }) }) - afterAll(async () => { - await cleanDB() + afterAll(() => { resetToken() }) @@ -142,24 +141,13 @@ describe('ContributionMessageResolver', () => { describe('authenticated', () => { beforeAll(async () => { - await userFactory(testEnv, bibiBloxberg) - await userFactory(testEnv, peterLustig) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) - result = await mutate({ - mutation: createContribution, - variables: { - amount: 100.0, - memo: 'Test env contribution', - creationDate: new Date().toString(), - }, - }) }) - afterAll(async () => { - await cleanDB() + afterAll(() => { resetToken() }) @@ -217,18 +205,13 @@ describe('ContributionMessageResolver', () => { }) }) - afterAll(async () => { - await cleanDB() - resetToken() - }) - it('creates ContributionMessage', async () => { await expect( mutate({ mutation: createContributionMessage, variables: { contributionId: result.data.createContribution.id, - message: 'Test', + message: 'User Test', }, }), ).resolves.toEqual( @@ -236,7 +219,7 @@ describe('ContributionMessageResolver', () => { data: { createContributionMessage: expect.objectContaining({ id: expect.any(Number), - message: 'Test', + message: 'User Test', type: 'DIALOG', userFirstName: 'Bibi', userLastName: 'Bloxberg', @@ -267,46 +250,13 @@ describe('ContributionMessageResolver', () => { describe('authenticated', () => { beforeAll(async () => { - await userFactory(testEnv, bibiBloxberg) - await userFactory(testEnv, peterLustig) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) - result = await mutate({ - mutation: createContribution, - variables: { - amount: 100.0, - memo: 'Test env contribution', - creationDate: new Date().toString(), - }, - }) - await query({ - query: login, - variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, - }) - await mutate({ - mutation: adminCreateContributionMessage, - variables: { - contributionId: result.data.createContribution.id, - message: 'Admin Test', - }, - }) - await query({ - query: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - await mutate({ - mutation: createContributionMessage, - variables: { - contributionId: result.data.createContribution.id, - message: 'User Test', - }, - }) }) - afterAll(async () => { - await cleanDB() + afterAll(() => { resetToken() }) From fbd3d412101ac917a13c65d00022b00a6b8fcc04 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 1 Sep 2022 10:25:41 +0200 Subject: [PATCH 43/43] Remove messages and replace it with messagesCount in the query listContributions. --- frontend/src/graphql/queries.js | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index f701ad8cf..88c312a3f 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -207,15 +207,7 @@ export const listContributions = gql` confirmedBy deletedAt state - messages { - id - message - createdAt - updatedAt - type - userFirstName - userLastName - } + messagesCount } } }