From 78195612c2c4abbf83151f7faed47ad4f4d1767f Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 4 Jul 2022 07:39:11 +0200 Subject: [PATCH 01/49] Add ROLES and RIGHTS to updateContribution. --- 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 6a6f8b7c0..1d0fd2856 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -27,6 +27,7 @@ export enum RIGHTS { GDT_BALANCE = 'GDT_BALANCE', CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION', LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS', + UPDATE_CONTRIBUTION = 'UPDATE_CONTRIBUTION', // 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 f56106664..75e552e79 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -25,6 +25,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.GDT_BALANCE, RIGHTS.CREATE_CONTRIBUTION, RIGHTS.LIST_CONTRIBUTIONS, + RIGHTS.UPDATE_CONTRIBUTION, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights From b580f8755da0a0034f58eb6b8871bab68c366d3e Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 4 Jul 2022 07:40:10 +0200 Subject: [PATCH 02/49] Refactor updateCreations. --- backend/src/graphql/resolver/AdminResolver.ts | 11 +---------- .../src/graphql/resolver/util/isContributionValid.ts | 11 +++++++++++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 5476fd8a1..962c13e9b 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -52,6 +52,7 @@ import { getUserCreations, isContributionValid, isStartEndDateValid, + updateCreations, } from './util/isContributionValid' import { CONTRIBUTIONLINK_MEMO_MAX_CHARS, @@ -688,13 +689,3 @@ export class AdminResolver { return new ContributionLink(dbContributionLink) } } - -function updateCreations(creations: Decimal[], contribution: Contribution): Decimal[] { - const index = getCreationIndex(contribution.contributionDate.getMonth()) - - if (index < 0) { - throw new Error('You cannot create GDD for a month older than the last three months.') - } - creations[index] = creations[index].plus(contribution.amount.toString()) - return creations -} diff --git a/backend/src/graphql/resolver/util/isContributionValid.ts b/backend/src/graphql/resolver/util/isContributionValid.ts index 0a9b57170..552370f4c 100644 --- a/backend/src/graphql/resolver/util/isContributionValid.ts +++ b/backend/src/graphql/resolver/util/isContributionValid.ts @@ -1,6 +1,7 @@ import { TransactionTypeId } from '@/graphql/enum/TransactionTypeId' import { backendLogger as logger } from '@/server/logger' import { getConnection } from '@dbTools/typeorm' +import { Contribution } from '@entity/Contribution' import Decimal from 'decimal.js-light' import { FULL_CREATION_AVAILABLE, MAX_CREATION_AMOUNT } from '../const/const' @@ -117,3 +118,13 @@ export const isStartEndDateValid = ( throw new Error(`The value of validFrom must before or equals the validTo!`) } } + +export const updateCreations = (creations: Decimal[], contribution: Contribution): Decimal[] => { + const index = getCreationIndex(contribution.contributionDate.getMonth()) + + if (index < 0) { + throw new Error('You cannot create GDD for a month older than the last three months.') + } + creations[index] = creations[index].plus(contribution.amount.toString()) + return creations +} From 03db4ed125cd2bcd7d7288f081bc25fa08b071af Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 4 Jul 2022 07:41:21 +0200 Subject: [PATCH 03/49] Add mutation to updateContribution. --- .../resolver/ContributionResolver.test.ts | 24 ++++++++++++++++++- backend/src/seeds/graphql/mutations.ts | 14 +++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 2308cd4e7..c9302cf64 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { createContribution } from '@/seeds/graphql/mutations' +import { createContribution, updateContribution } from '@/seeds/graphql/mutations' import { listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' @@ -212,4 +212,26 @@ describe('ContributionResolver', () => { }) }) }) + + describe('updateContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: 1, + amount: 100.0, + memo: 'Test Contribution', + creationDate: 'not-valid', + }, + }), + ).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 4926f706f..45adeacc8 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -239,3 +239,17 @@ export const createContribution = gql` } } ` + +export const updateContribution = gql` + mutation ($contributionId: Int!, $amount: Decimal!, $memo: String!, $creationDate: String!) { + updateContribution( + contributionId: $contributionId + amount: $amount + memo: $memo + creationDate: $creationDate + ) { + amount + memo + } + } +` From 7b38fe7f931ad45697d67d1d3ce4d06cb0e535ec Mon Sep 17 00:00:00 2001 From: elweyn Date: Mon, 4 Jul 2022 07:41:41 +0200 Subject: [PATCH 04/49] Function for updateContribution. --- .../graphql/resolver/ContributionResolver.ts | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 6669ef20d..2399593c2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -2,7 +2,7 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { backendLogger as logger } from '@/server/logger' import { Contribution as dbContribution } from '@entity/Contribution' -import { Arg, Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql' +import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' import { IsNull } from '../../../../database/node_modules/typeorm' import ContributionArgs from '../arg/ContributionArgs' import Paginated from '../arg/Paginated' @@ -10,7 +10,7 @@ import { Order } from '../enum/Order' import { Contribution } from '../model/Contribution' import { UnconfirmedContribution } from '../model/UnconfirmedContribution' import { User } from '../model/User' -import { isContributionValid, getUserCreation } from './util/isContributionValid' +import { isContributionValid, getUserCreation, updateCreations } from './util/isContributionValid' @Resolver() export class ContributionResolver { @@ -75,4 +75,41 @@ export class ContributionResolver { } return contribution.map((contr) => new Contribution(contr, new User(user))) } + + @Authorized([RIGHTS.UPDATE_CONTRIBUTION]) + @Mutation(() => UnconfirmedContribution) + async updateContribution( + @Arg('contributionId', () => Int) + contributionId: number, + @Args() { amount, memo, creationDate }: ContributionArgs, + @Ctx() context: Context, + ): Promise { + const user = getUser(context) + + const contributionToUpdate = await dbContribution.findOne({ + where: { id: contributionId, confirmedAt: IsNull() }, + }) + if (!contributionToUpdate) { + throw new Error('No contribution found to given id.') + } + if (contributionToUpdate.userId !== user.id) { + throw new Error('user of the pending contribution and send user does not correspond') + } + + const creationDateObj = new Date(creationDate) + let creations = await getUserCreation(user.id) + if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) { + creations = updateCreations(creations, contributionToUpdate) + } + + // all possible cases not to be true are thrown in this function + isContributionValid(creations, amount, creationDateObj) + contributionToUpdate.amount = amount + contributionToUpdate.memo = memo + contributionToUpdate.contributionDate = new Date(creationDate) + dbContribution.save(contributionToUpdate) + + const result = new UnconfirmedContribution(contributionToUpdate, user, creations) + return result + } } From 8e7476ecb07d0ede3ff6270f1077f18d93ec17a8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 5 Jul 2022 09:07:29 +0200 Subject: [PATCH 05/49] Refactor return value so that their is only one return value. --- backend/src/graphql/resolver/ContributionResolver.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 2399593c2..f14aa4f36 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -109,7 +109,6 @@ export class ContributionResolver { contributionToUpdate.contributionDate = new Date(creationDate) dbContribution.save(contributionToUpdate) - const result = new UnconfirmedContribution(contributionToUpdate, user, creations) - return result + return new UnconfirmedContribution(contributionToUpdate, user, creations) } } From 61d4c138e1d196bd02f2c360555ec19866c80187 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 5 Jul 2022 09:17:48 +0200 Subject: [PATCH 06/49] Remove unused import. --- backend/src/graphql/resolver/AdminResolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 962c13e9b..291ee6ed8 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -47,7 +47,6 @@ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver' import CONFIG from '@/config' import { - getCreationIndex, getUserCreation, getUserCreations, isContributionValid, From 5369aced43ee60b6788c6a00ca492efaa6f0452e Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:37:06 +0200 Subject: [PATCH 07/49] Change mutation to include id in the result, write test to update contribution with inexistent id. --- .../resolver/ContributionResolver.test.ts | 53 +++++++++++++++++++ backend/src/seeds/graphql/mutations.ts | 1 + 2 files changed, 54 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index c9302cf64..697c5ae43 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -13,6 +13,7 @@ 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() @@ -159,6 +160,14 @@ describe('ContributionResolver', () => { query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) + await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) }) afterAll(async () => { @@ -233,5 +242,49 @@ describe('ContributionResolver', () => { ) }) }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, peterLustig) + await userFactory(testEnv, bibiBloxberg) + // bibi needs GDDs + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + // await userFactory(testEnv, bibiBloxberg) + 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(), + }, + }) + }) + + describe('wrong contribution id', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: -1, + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No contribution found to given id.')], + }), + ) + }) + }) + }) }) }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 45adeacc8..bfa9847e0 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -234,6 +234,7 @@ export const deleteContributionLink = gql` export const createContribution = gql` mutation ($amount: Decimal!, $memo: String!, $creationDate: String!) { createContribution(amount: $amount, memo: $memo, creationDate: $creationDate) { + id amount memo } From 86433478ec24dd58de4f6dcbd6f8560836eabe30 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:38:19 +0200 Subject: [PATCH 08/49] Test that only the user that created the contribution can update it with the updateContribution mutation. --- .../resolver/ContributionResolver.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 697c5ae43..41b5808a1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -285,6 +285,37 @@ describe('ContributionResolver', () => { ) }) }) + + describe('wrong user tries to update the contribution', () => { + beforeAll(async () => { + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 10.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'user of the pending contribution and send user does not correspond', + ), + ], + }), + ) + }) + }) }) }) }) From 6348756ab31a18fd1fcb7946918d8fc20a911487 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:39:24 +0200 Subject: [PATCH 09/49] Test that the updateContribution mutation can not update more gdd's than allowed in the month. --- .../resolver/ContributionResolver.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 41b5808a1..bdb8453da 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -316,6 +316,37 @@ describe('ContributionResolver', () => { ) }) }) + + describe('update to much so that the limit is exceeded', () => { + beforeAll(async () => { + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + it('throws an error', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 1019.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError( + 'The amount (1019 GDD) to be created exceeds the amount (1000 GDD) still available for this month.', + ), + ], + }), + ) + }) + }) }) }) }) From 9e355f9202d05e208dcaf36438bfae489d3c8521 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:43:47 +0200 Subject: [PATCH 10/49] Test that the updateContribution mutation can not update contribution to a date that is older than 3 month. --- .../resolver/ContributionResolver.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index bdb8453da..d3d42be86 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -347,6 +347,29 @@ describe('ContributionResolver', () => { ) }) }) + + describe('update creation to a date that is older than 3 month', () => { + it('throws an error', async () => { + const date = new Date() // , + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 1019.0, + memo: 'Test env contribution', + creationDate: date.setMonth(date.getMonth() - 3).toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [ + new GraphQLError('No information for available creations for the given date'), + ], + }), + ) + }) + }) }) }) }) From 9ee40767bbf019e5ed44ed732909d287b00c6fb0 Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:45:48 +0200 Subject: [PATCH 11/49] Change test so that only the date is to old and the amount could be right. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index d3d42be86..31d46e2ae 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -356,7 +356,7 @@ describe('ContributionResolver', () => { mutation: updateContribution, variables: { contributionId: result.data.createContribution.id, - amount: 1019.0, + amount: 10.0, memo: 'Test env contribution', creationDate: date.setMonth(date.getMonth() - 3).toString(), }, From c6ae5760c3072bb89d4d68f395062f1b7eff208a Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 09:55:03 +0200 Subject: [PATCH 12/49] Test that the updateContribution mutation updates the contribution with the right values. --- .../resolver/ContributionResolver.test.ts | 26 +++++++++++++++++++ backend/src/seeds/graphql/mutations.ts | 1 + 2 files changed, 27 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 31d46e2ae..3c8046bc2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -370,6 +370,32 @@ describe('ContributionResolver', () => { ) }) }) + + describe('valid input', () => { + it('updates contribution', async () => { + await expect( + mutate({ + mutation: updateContribution, + variables: { + contributionId: result.data.createContribution.id, + amount: 10.0, + memo: 'Test contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + updateContribution: { + id: result.data.createContribution.id, + amount: '10', + memo: 'Test contribution', + }, + }, + }), + ) + }) + }) }) }) }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index bfa9847e0..4e7fa8a90 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -249,6 +249,7 @@ export const updateContribution = gql` memo: $memo creationDate: $creationDate ) { + id amount memo } From b991bb0467035a5c18aa409ca99b93292fea41cd Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 10:11:28 +0200 Subject: [PATCH 13/49] Delete datas after all listContributions and updateContribution Tests. --- .../src/graphql/resolver/ContributionResolver.test.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index f690e10b1..a1f274fc6 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -169,6 +169,11 @@ describe('ContributionResolver', () => { }) }) + afterAll(async () => { + await cleanDB() + resetToken() + }) + describe('filter confirmed is false', () => { it('returns creations', async () => { await expect( @@ -276,6 +281,11 @@ describe('ContributionResolver', () => { }) }) + afterAll(async () => { + await cleanDB() + resetToken() + }) + describe('wrong contribution id', () => { it('throws an error', async () => { await expect( From 0380609deeea78f4713205c3e0b217814ac2b018 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Tue, 12 Jul 2022 12:54:46 +0200 Subject: [PATCH 14/49] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index a1f274fc6..a5c9b2f55 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -368,7 +368,7 @@ describe('ContributionResolver', () => { }) }) - describe('update creation to a date that is older than 3 month', () => { + describe('update creation to a date that is older than 3 months', () => { it('throws an error', async () => { const date = new Date() // , await expect( From 90d2ffbff3bc91713510a1817dcb10f901197a66 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Tue, 12 Jul 2022 12:54:55 +0200 Subject: [PATCH 15/49] Update backend/src/graphql/resolver/ContributionResolver.test.ts Co-authored-by: Moriz Wahl --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index a5c9b2f55..731a897c1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -370,7 +370,7 @@ describe('ContributionResolver', () => { describe('update creation to a date that is older than 3 months', () => { it('throws an error', async () => { - const date = new Date() // , + const date = new Date() await expect( mutate({ mutation: updateContribution, From 32d79bb61cdf19adee86e0403977e3f54255773f Mon Sep 17 00:00:00 2001 From: elweyn Date: Tue, 12 Jul 2022 13:01:46 +0200 Subject: [PATCH 16/49] Corrected the spelling errors and withdrew admin creation on update scenario. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 731a897c1..394d9fc7d 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -262,11 +262,6 @@ describe('ContributionResolver', () => { beforeAll(async () => { await userFactory(testEnv, peterLustig) await userFactory(testEnv, bibiBloxberg) - // bibi needs GDDs - const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await creationFactory(testEnv, bibisCreation!) - // await userFactory(testEnv, bibiBloxberg) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, @@ -337,7 +332,7 @@ describe('ContributionResolver', () => { }) }) - describe('update to much so that the limit is exceeded', () => { + describe('update too much so that the limit is exceeded', () => { beforeAll(async () => { await query({ query: login, From 3e471eb81ee0037e40c6d127e90ef546250378aa Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 12 Jul 2022 16:54:37 +0200 Subject: [PATCH 17/49] add creation on login query and store --- frontend/src/graphql/queries.js | 2 ++ frontend/src/store/store.js | 6 ++++++ frontend/src/store/store.test.js | 25 +++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/frontend/src/graphql/queries.js b/frontend/src/graphql/queries.js index adcd653a4..27e63d568 100644 --- a/frontend/src/graphql/queries.js +++ b/frontend/src/graphql/queries.js @@ -13,6 +13,7 @@ export const login = gql` hasElopage publisherId isAdmin + creation } } ` @@ -30,6 +31,7 @@ export const verifyLogin = gql` hasElopage publisherId isAdmin + creation } } ` diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index e95eec7b9..8fdbc519e 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -47,6 +47,9 @@ export const mutations = { hasElopage: (state, hasElopage) => { state.hasElopage = hasElopage }, + creation: (state, creation) => { + state.creation = creation + }, } export const actions = { @@ -60,6 +63,7 @@ export const actions = { commit('hasElopage', data.hasElopage) commit('publisherId', data.publisherId) commit('isAdmin', data.isAdmin) + commit('creation', data.creation) }, logout: ({ commit, state }) => { commit('token', null) @@ -71,6 +75,7 @@ export const actions = { commit('hasElopage', false) commit('publisherId', null) commit('isAdmin', false) + commit('creation', null) localStorage.clear() }, } @@ -96,6 +101,7 @@ try { newsletterState: null, hasElopage: false, publisherId: null, + creation: null, }, getters: {}, // Syncronous mutation of the state diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 3f942fa35..0493d7f53 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -30,6 +30,7 @@ const { publisherId, isAdmin, hasElopage, + creation, } = mutations const { login, logout } = actions @@ -139,6 +140,14 @@ describe('Vuex store', () => { expect(state.hasElopage).toBeTruthy() }) }) + + describe('creation', () => { + it('sets the state of creation', () => { + const state = { creation: null } + creation(state, true) + expect(state.creation).toEqual(true) + }) + }) }) describe('actions', () => { @@ -156,11 +165,12 @@ describe('Vuex store', () => { hasElopage: false, publisherId: 1234, isAdmin: true, + creation: ['1000', '1000', '1000'], } it('calls nine commits', () => { login({ commit, state }, commitedData) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(9) }) it('commits email', () => { @@ -202,6 +212,11 @@ describe('Vuex store', () => { login({ commit, state }, commitedData) expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', true) }) + + it('commits creation', () => { + login({ commit, state }, commitedData) + expect(commit).toHaveBeenNthCalledWith(9, 'creation', ['1000', '1000', '1000']) + }) }) describe('logout', () => { @@ -210,7 +225,7 @@ describe('Vuex store', () => { it('calls nine commits', () => { logout({ commit, state }) - expect(commit).toHaveBeenCalledTimes(8) + expect(commit).toHaveBeenCalledTimes(9) }) it('commits token', () => { @@ -253,6 +268,12 @@ describe('Vuex store', () => { expect(commit).toHaveBeenNthCalledWith(8, 'isAdmin', false) }) + it('commits creation', () => { + logout({ commit, state }) + expect(commit).toHaveBeenNthCalledWith(9, 'creation', null) + }) + + // how to get this working? it.skip('calls localStorage.clear()', () => { const clearStorageMock = jest.fn() From bc8078c427883aa5192a5d1b54556fb8f285e5d6 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 12 Jul 2022 16:55:50 +0200 Subject: [PATCH 18/49] fix lint --- frontend/src/store/store.test.js | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/store/store.test.js b/frontend/src/store/store.test.js index 0493d7f53..651a3ccc5 100644 --- a/frontend/src/store/store.test.js +++ b/frontend/src/store/store.test.js @@ -273,7 +273,6 @@ describe('Vuex store', () => { expect(commit).toHaveBeenNthCalledWith(9, 'creation', null) }) - // how to get this working? it.skip('calls localStorage.clear()', () => { const clearStorageMock = jest.fn() From c6bab99a2c96e8fcdf80e72c7878ec8e9f5502c2 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 08:26:30 +0200 Subject: [PATCH 19/49] Add Test that adminUpdateContribution is not allowed on creation made by createContribution mutation. --- .../resolver/ContributionResolver.test.ts | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 394d9fc7d..75d6d1128 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { createContribution, updateContribution } from '@/seeds/graphql/mutations' +import { adminUpdateContribution, createContribution, updateContribution } from '@/seeds/graphql/mutations' import { listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' @@ -332,6 +332,27 @@ describe('ContributionResolver', () => { }) }) + describe('admin tries to update a user contribution', () => { + it('throws an error', async () => { + await expect( + mutate({ + mutation: adminUpdateContribution, + variables: { + id: result.data.createContribution.id, + email: 'bibi@bloxberg.de', + amount: 10.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('An admin is not allowed to update a user contribution.')], + }), + ) + }) + }) + describe('update too much so that the limit is exceeded', () => { beforeAll(async () => { await query({ From 801e223cfd29398004b267ced763eb9d530421f7 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 08:27:05 +0200 Subject: [PATCH 20/49] Implement that adminUpdateContribution is not allowed when moderator is null. --- backend/src/graphql/resolver/AdminResolver.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index e3c61c300..12cad529c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -321,6 +321,10 @@ export class AdminResolver { throw new Error('user of the pending contribution and send user does not correspond') } + if (contributionToUpdate.moderatorId === null) { + throw new Error('An admin is not allowed to update a user contribution.') + } + const creationDateObj = new Date(creationDate) let creations = await getUserCreation(user.id) if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) { From e1d50cacb521b3fabec9eaae67cdf29987531139 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 09:55:21 +0200 Subject: [PATCH 21/49] Fix linting. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 75d6d1128..7afae08f6 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2,7 +2,11 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -import { adminUpdateContribution, createContribution, updateContribution } from '@/seeds/graphql/mutations' +import { + adminUpdateContribution, + createContribution, + updateContribution, +} from '@/seeds/graphql/mutations' import { listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' From 9d9d1a78f18abda66333139ac1b4ffd66e631607 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 11:01:44 +0200 Subject: [PATCH 22/49] Add confirmedBy and confirmedAt for the contribution query. --- backend/src/graphql/model/Contribution.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index dc1dd39e9..348a6eb98 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -12,6 +12,8 @@ export class Contribution { this.memo = contribution.memo this.createdAt = contribution.createdAt this.deletedAt = contribution.deletedAt + this.confirmedAt = contribution.confirmedAt + this.confirmedBy = contribution.confirmedBy } @Field(() => Number) @@ -31,6 +33,12 @@ export class Contribution { @Field(() => Date, { nullable: true }) deletedAt: Date | null + + @Field(() => Date, { nullable: true }) + confirmedAt: Date | null + + @Field(() => Number, { nullable: true }) + confirmedBy: number | null } @ObjectType() From 99c4e8367048fe1d100a307be5febd84b6036177 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 11:26:18 +0200 Subject: [PATCH 23/49] Add deleted contribution to the list of contribution. --- 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 4424b40d0..562859116 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -58,6 +58,7 @@ export class ContributionResolver { order: { createdAt: order, }, + withDeleted: true, skip: (currentPage - 1) * pageSize, take: pageSize, }) From b7c24978392ae1f74baf3442ed32521d167c6637 Mon Sep 17 00:00:00 2001 From: elweyn Date: Wed, 13 Jul 2022 12:18:47 +0200 Subject: [PATCH 24/49] Add new right LIST_ALL_CONFIRMED_CONTRIBUTIONS. --- 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 6a6f8b7c0..975c2006a 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -27,6 +27,7 @@ export enum RIGHTS { GDT_BALANCE = 'GDT_BALANCE', CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION', LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS', + LIST_ALL_CONFIRMED_CONTRIBUTIONS = 'LIST_ALL_CONFIRMED_CONTRIBUTIONS', // 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 f56106664..e5628bb62 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -25,6 +25,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.GDT_BALANCE, RIGHTS.CREATE_CONTRIBUTION, RIGHTS.LIST_CONTRIBUTIONS, + RIGHTS.LIST_ALL_CONFIRMED_CONTRIBUTIONS, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights From ed10d7d1a00b8cc8ae65d6efa1323921e800e649 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 13 Jul 2022 14:54:17 +0200 Subject: [PATCH 25/49] set session timer to 0, if expiration is greater then 0 seconds --- frontend/src/components/SessionLogoutTimeout.vue | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/SessionLogoutTimeout.vue b/frontend/src/components/SessionLogoutTimeout.vue index 1e5a27998..113362abf 100644 --- a/frontend/src/components/SessionLogoutTimeout.vue +++ b/frontend/src/components/SessionLogoutTimeout.vue @@ -65,7 +65,7 @@ export default { this.$timer.restart('tokenExpires') this.$bvModal.show('modalSessionTimeOut') } - if (this.tokenExpiresInSeconds <= 0) { + if (this.tokenExpiresInSeconds === 0) { this.$timer.stop('tokenExpires') this.$emit('logout') } @@ -90,7 +90,11 @@ export default { }, computed: { tokenExpiresInSeconds() { - return Math.floor((new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000) + const remainingSecs = Math.floor( + (new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000, + ) + if (remainingSecs <= 0) return 0 + return remainingSecs }, }, beforeDestroy() { From b2164e6d1dc90fed257fcbb95601b5b5cc218e46 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 13 Jul 2022 14:54:47 +0200 Subject: [PATCH 26/49] add unit test for this fix --- frontend/src/components/SessionLogoutTimeout.spec.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/SessionLogoutTimeout.spec.js b/frontend/src/components/SessionLogoutTimeout.spec.js index 0f5d21d36..94751f9cb 100644 --- a/frontend/src/components/SessionLogoutTimeout.spec.js +++ b/frontend/src/components/SessionLogoutTimeout.spec.js @@ -62,12 +62,16 @@ describe('SessionLogoutTimeout', () => { }) }) - describe('token is expired', () => { + describe('token is expired for several seconds', () => { beforeEach(() => { mocks.$store.state.tokenTime = setTokenTime(-60) wrapper = Wrapper() }) + it('value for remaining seconds is 0', () => { + expect(wrapper.tokenExpiresInSeconds === 0) + }) + it('emits logout', () => { expect(wrapper.emitted('logout')).toBeTruthy() }) From 1d1cc7e960aa845de7a659f6e5f4f25442c52e1b Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 13 Jul 2022 15:36:15 +0200 Subject: [PATCH 27/49] Update frontend/src/components/SessionLogoutTimeout.spec.js Co-authored-by: Moriz Wahl --- frontend/src/components/SessionLogoutTimeout.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/SessionLogoutTimeout.spec.js b/frontend/src/components/SessionLogoutTimeout.spec.js index 94751f9cb..bd6911d13 100644 --- a/frontend/src/components/SessionLogoutTimeout.spec.js +++ b/frontend/src/components/SessionLogoutTimeout.spec.js @@ -68,7 +68,7 @@ describe('SessionLogoutTimeout', () => { wrapper = Wrapper() }) - it('value for remaining seconds is 0', () => { + it('has value for remaining seconds equal 0', () => { expect(wrapper.tokenExpiresInSeconds === 0) }) From 2302da9054785b8b4a7ca330fd1ddbdd42826ce0 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 13 Jul 2022 15:36:40 +0200 Subject: [PATCH 28/49] Update frontend/src/components/SessionLogoutTimeout.vue Co-authored-by: Moriz Wahl --- frontend/src/components/SessionLogoutTimeout.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/SessionLogoutTimeout.vue b/frontend/src/components/SessionLogoutTimeout.vue index 113362abf..f10b8114a 100644 --- a/frontend/src/components/SessionLogoutTimeout.vue +++ b/frontend/src/components/SessionLogoutTimeout.vue @@ -93,8 +93,7 @@ export default { const remainingSecs = Math.floor( (new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000, ) - if (remainingSecs <= 0) return 0 - return remainingSecs + return remainingSecs <= 0 ? 0 : remainingSecs }, }, beforeDestroy() { From 90d90ca9bd9960457aa6a3267e73628d21675519 Mon Sep 17 00:00:00 2001 From: mahula Date: Wed, 13 Jul 2022 19:04:38 +0200 Subject: [PATCH 29/49] fix linting --- frontend/src/components/SessionLogoutTimeout.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/SessionLogoutTimeout.vue b/frontend/src/components/SessionLogoutTimeout.vue index f10b8114a..1ebff752a 100644 --- a/frontend/src/components/SessionLogoutTimeout.vue +++ b/frontend/src/components/SessionLogoutTimeout.vue @@ -93,7 +93,7 @@ export default { const remainingSecs = Math.floor( (new Date(this.$store.state.tokenTime * 1000).getTime() - this.now) / 1000, ) - return remainingSecs <= 0 ? 0 : remainingSecs + return remainingSecs <= 0 ? 0 : remainingSecs }, }, beforeDestroy() { From 7c698de325294811d144f6ac7bac058d44200735 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 12:25:45 +0200 Subject: [PATCH 30/49] Add constructor to ContributionListResult. --- backend/src/graphql/model/Contribution.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index dc1dd39e9..989296848 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -35,6 +35,11 @@ export class Contribution { @ObjectType() export class ContributionListResult { + constructor(count: number, list: Contribution[]) { + this.linkCount = count + this.linkList = list + } + @Field(() => Int) linkCount: number From 2f90bd981126ef77f744beacb5fa8d27bc3cc225 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 12:58:01 +0200 Subject: [PATCH 31/49] Withdrew User from Contribution object and replaced it with userId, firstName and lastName. --- backend/src/graphql/model/Contribution.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index 989296848..324b0ff07 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -7,7 +7,9 @@ import { User } from './User' export class Contribution { constructor(contribution: dbContribution, user: User) { this.id = contribution.id - this.user = user + this.userId = user ? user.id : null + this.firstName = user ? user.firstName : null + this.lastName = user ? user.lastName : null this.amount = contribution.amount this.memo = contribution.memo this.createdAt = contribution.createdAt @@ -17,8 +19,14 @@ export class Contribution { @Field(() => Number) id: number - @Field(() => User) - user: User + @Field(() => Number, { nullable: true }) + userId: number | null + + @Field(() => String, { nullable: true }) + firstName: string | null + + @Field(() => String, { nullable: true }) + lastName: string | null @Field(() => Decimal) amount: Decimal From c02f575780166a8f2e6c29df4a83ffac59fcd36d Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 12:59:21 +0200 Subject: [PATCH 32/49] Withdrew userid from contribution object. --- backend/src/graphql/model/Contribution.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index 324b0ff07..a1537244d 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -7,7 +7,6 @@ import { User } from './User' export class Contribution { constructor(contribution: dbContribution, user: User) { this.id = contribution.id - this.userId = user ? user.id : null this.firstName = user ? user.firstName : null this.lastName = user ? user.lastName : null this.amount = contribution.amount @@ -19,9 +18,6 @@ export class Contribution { @Field(() => Number) id: number - @Field(() => Number, { nullable: true }) - userId: number | null - @Field(() => String, { nullable: true }) firstName: string | null From 8c6ec08e88aa4350134c2efd812a77ddfbeea2d6 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 16:42:55 +0200 Subject: [PATCH 33/49] Gives a list of allCreations and calls the User one time in the creation. --- .../graphql/resolver/ContributionResolver.ts | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 4424b40d0..8a23b3150 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -2,12 +2,13 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { backendLogger as logger } from '@/server/logger' import { Contribution as dbContribution } from '@entity/Contribution' +import { User as dbUser } from '@entity/User' import { Arg, Args, Authorized, Ctx, Mutation, Query, Resolver } from 'type-graphql' -import { FindOperator, IsNull } from '@dbTools/typeorm' +import { FindOperator, IsNull, Not } from '@dbTools/typeorm' import ContributionArgs from '@arg/ContributionArgs' import Paginated from '@arg/Paginated' import { Order } from '@enum/Order' -import { Contribution } from '@model/Contribution' +import { Contribution, ContributionListResult } from '@model/Contribution' import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import { User } from '@model/User' import { validateContribution, getUserCreation } from './util/creations' @@ -63,4 +64,37 @@ export class ContributionResolver { }) return contributions.map((contribution) => new Contribution(contribution, new User(user))) } + + @Authorized([RIGHTS.LIST_ALL_CONFIRMED_CONTRIBUTIONS]) + @Query(() => ContributionListResult) + async listAllConfirmedContributions( + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + ): Promise { + const dbContributions = await dbContribution.find({ + order: { + createdAt: order, + }, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) + const contributions: Contribution[] = [] + const userIds: number[] = [] + dbContributions.forEach(async (dbContribution) => { + userIds.push(dbContribution.userId) + }) + userIds.filter((elem, index, self) => { + return index === self.indexOf(elem) + }) + const users = new Map() + for (let i = 0; i < userIds.length; i++) { + const id = userIds[i] + const user = await dbUser.findOneOrFail({ id }) + users.set(id, user) + } + dbContributions.forEach((dbContribution) => { + contributions.push(new Contribution(dbContribution, users.get(dbContribution.userId))) + }) + return new ContributionListResult(contributions.length, contributions) + } } From fdea5f68ef8fd78176ce3ea5707c6cc270d2abbd Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 16:43:20 +0200 Subject: [PATCH 34/49] Add confirmedBy and confirmedAt for the contribution query. --- backend/src/graphql/model/Contribution.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backend/src/graphql/model/Contribution.ts b/backend/src/graphql/model/Contribution.ts index a1537244d..34bffd6d7 100644 --- a/backend/src/graphql/model/Contribution.ts +++ b/backend/src/graphql/model/Contribution.ts @@ -13,6 +13,8 @@ export class Contribution { this.memo = contribution.memo this.createdAt = contribution.createdAt this.deletedAt = contribution.deletedAt + this.confirmedAt = contribution.confirmedAt + this.confirmedBy = contribution.confirmedBy } @Field(() => Number) @@ -35,6 +37,12 @@ export class Contribution { @Field(() => Date, { nullable: true }) deletedAt: Date | null + + @Field(() => Date, { nullable: true }) + confirmedAt: Date | null + + @Field(() => Number, { nullable: true }) + confirmedBy: number | null } @ObjectType() From cbbe0ffad2cf8a528d5a3c5c5a336cfed7afe82b Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 16:45:19 +0200 Subject: [PATCH 35/49] Change the method name from listAllConfirmedContributions to listAllContributions. --- backend/src/auth/RIGHTS.ts | 2 +- backend/src/auth/ROLES.ts | 2 +- backend/src/graphql/resolver/ContributionResolver.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 975c2006a..5258c9e3b 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -27,7 +27,7 @@ export enum RIGHTS { GDT_BALANCE = 'GDT_BALANCE', CREATE_CONTRIBUTION = 'CREATE_CONTRIBUTION', LIST_CONTRIBUTIONS = 'LIST_CONTRIBUTIONS', - LIST_ALL_CONFIRMED_CONTRIBUTIONS = 'LIST_ALL_CONFIRMED_CONTRIBUTIONS', + LIST_ALL_CONTRIBUTIONS = 'LIST_ALL_CONTRIBUTIONS', // 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 e5628bb62..4a96d4813 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -25,7 +25,7 @@ export const ROLE_USER = new Role('user', [ RIGHTS.GDT_BALANCE, RIGHTS.CREATE_CONTRIBUTION, RIGHTS.LIST_CONTRIBUTIONS, - RIGHTS.LIST_ALL_CONFIRMED_CONTRIBUTIONS, + RIGHTS.LIST_ALL_CONTRIBUTIONS, ]) export const ROLE_ADMIN = new Role('admin', Object.values(RIGHTS)) // all rights diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 8a23b3150..c015496c1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -65,9 +65,9 @@ export class ContributionResolver { return contributions.map((contribution) => new Contribution(contribution, new User(user))) } - @Authorized([RIGHTS.LIST_ALL_CONFIRMED_CONTRIBUTIONS]) + @Authorized([RIGHTS.LIST_ALL_CONTRIBUTIONS]) @Query(() => ContributionListResult) - async listAllConfirmedContributions( + async listAllContributions( @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, ): Promise { From ab77a798f589c4907ae149f127e9e7c7c56219bb Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 16:57:25 +0200 Subject: [PATCH 36/49] Add query seed for listAllContributions. --- backend/src/seeds/graphql/queries.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index c27ecdd66..deae5f97b 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -191,6 +191,24 @@ export const listContributions = gql` } } ` + +export const listAllContributions = ` +query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC) { + listAllContributions(currentPage: $currentPage, pageSize: $pageSize, order: $order) { + linkCount + linkList { + id + firstName + lastName + amount + memo + createdAt + confirmedAt + confirmedBy + } + } +} +` // from admin interface export const listUnconfirmedContributions = gql` From ba90911aec704026ee6953e9db0f3731ef263116 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 16:57:57 +0200 Subject: [PATCH 37/49] Test that unauthenticated user can not query listAllContributions. --- .../resolver/ContributionResolver.test.ts | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 9b0f6a3bc..a4cb1d714 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -3,7 +3,7 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { createContribution } from '@/seeds/graphql/mutations' -import { listContributions, login } from '@/seeds/graphql/queries' +import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' import { userFactory } from '@/seeds/factory/user' @@ -168,6 +168,11 @@ describe('ContributionResolver', () => { }) }) + afterAll(async () => { + await cleanDB() + await con.close() + }) + describe('filter confirmed is false', () => { it('returns creations', async () => { await expect( @@ -230,4 +235,53 @@ describe('ContributionResolver', () => { }) }) }) + + describe('listAllContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + await mutate({ + mutation: createContribution, + variables: { + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), + }, + }) + }) + + afterAll(async () => { + await cleanDB() + await con.close() + }) + }) + }) }) From 246edadc1314e6092eb51daa7ffdf9eeff72e72f Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 17:22:22 +0200 Subject: [PATCH 38/49] Test that authenticated user gets allContributions. --- .../resolver/ContributionResolver.test.ts | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index a4cb1d714..9aa247d4e 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -261,9 +261,9 @@ describe('ContributionResolver', () => { beforeAll(async () => { await userFactory(testEnv, bibiBloxberg) await userFactory(testEnv, peterLustig) - const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await creationFactory(testEnv, bibisCreation!) + // await creationFactory(testEnv, creations!) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, @@ -282,6 +282,37 @@ describe('ContributionResolver', () => { await cleanDB() await con.close() }) + + it('returns allCreation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }), + ) + }) }) }) }) From 3733bf511483f09fae6d37c2e5926c75ae247487 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 17:30:21 +0200 Subject: [PATCH 39/49] Change con.close to resetToken method. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 9aa247d4e..124adff18 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -170,7 +170,7 @@ describe('ContributionResolver', () => { afterAll(async () => { await cleanDB() - await con.close() + resetToken() }) describe('filter confirmed is false', () => { @@ -280,7 +280,7 @@ describe('ContributionResolver', () => { afterAll(async () => { await cleanDB() - await con.close() + resetToken() }) it('returns allCreation', async () => { From 34de4bb9014497a0db5a2d0d5cdcf662ff954ee2 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 17:42:49 +0200 Subject: [PATCH 40/49] Merge conflict on contribution resolver test file --- .../resolver/ContributionResolver.test.ts | 142 ++++++++++-------- 1 file changed, 78 insertions(+), 64 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index fd9636439..4f57cc0a1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -2,17 +2,12 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' -<<<<<<< HEAD -import { createContribution } from '@/seeds/graphql/mutations' -import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries' -======= import { adminUpdateContribution, createContribution, updateContribution, } from '@/seeds/graphql/mutations' -import { listContributions, login } from '@/seeds/graphql/queries' ->>>>>>> master +import { listAllContributions, listContributions, login } from '@/seeds/graphql/queries' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { GraphQLError } from 'graphql' import { userFactory } from '@/seeds/factory/user' @@ -246,19 +241,6 @@ describe('ContributionResolver', () => { }) }) -<<<<<<< HEAD - describe('listAllContribution', () => { - describe('unauthenticated', () => { - it('returns an error', async () => { - await expect( - query({ - query: listAllContributions, - variables: { - currentPage: 1, - pageSize: 25, - order: 'DESC', - filterConfirmed: false, -======= describe('updateContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { @@ -270,7 +252,6 @@ describe('ContributionResolver', () => { amount: 100.0, memo: 'Test Contribution', creationDate: 'not-valid', ->>>>>>> master }, }), ).resolves.toEqual( @@ -283,25 +264,13 @@ describe('ContributionResolver', () => { describe('authenticated', () => { beforeAll(async () => { -<<<<<<< HEAD - await userFactory(testEnv, bibiBloxberg) - await userFactory(testEnv, peterLustig) - creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // await creationFactory(testEnv, creations!) -======= await userFactory(testEnv, peterLustig) await userFactory(testEnv, bibiBloxberg) ->>>>>>> master await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) -<<<<<<< HEAD - await mutate({ -======= result = await mutate({ ->>>>>>> master mutation: createContribution, variables: { amount: 100.0, @@ -316,37 +285,6 @@ describe('ContributionResolver', () => { resetToken() }) -<<<<<<< HEAD - it('returns allCreation', async () => { - await expect( - query({ - query: listAllContributions, - variables: { - currentPage: 1, - pageSize: 25, - order: 'DESC', - filterConfirmed: false, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listAllContributions: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - memo: 'Herzlich Willkommen bei Gradido!', - amount: '1000', - }), - expect.objectContaining({ - id: expect.any(Number), - memo: 'Test env contribution', - amount: '100', - }), - ]), - }, - }), - ) -======= describe('wrong contribution id', () => { it('throws an error', async () => { await expect( @@ -497,8 +435,84 @@ describe('ContributionResolver', () => { }), ) }) ->>>>>>> master + }) + + describe('listAllContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) }) }) + + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + // await creationFactory(testEnv, creations!) + await userFactory(testEnv, peterLustig) + await userFactory(testEnv, bibiBloxberg) + 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(), + }, + }) + }) + + it('returns allCreation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }), + ) + }) + }) }) From c242f977337a5e048dcd1cfecf818f3d92d0456d Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 17:52:31 +0200 Subject: [PATCH 41/49] Change the test for listAllContributions to have every seed user and create all creations, add Test suite that checks if every seeded data arrives as well. --- .../resolver/ContributionResolver.test.ts | 141 +++++++++--------- 1 file changed, 72 insertions(+), 69 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 4f57cc0a1..b294fb40d 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -14,6 +14,8 @@ 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 { bobBaumeister } from '@/seeds/users/bob-baumeister' +import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' let mutate: any, query: any, con: any let testEnv: any @@ -436,83 +438,84 @@ describe('ContributionResolver', () => { ) }) }) - - describe('listAllContribution', () => { - describe('unauthenticated', () => { - it('returns an error', async () => { - await expect( - query({ - query: listAllContributions, - variables: { - currentPage: 1, - pageSize: 25, - order: 'DESC', - filterConfirmed: false, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - errors: [new GraphQLError('401 Unauthorized')], - }), - ) - }) - }) - }) }) }) - describe('authenticated', () => { - beforeAll(async () => { - await userFactory(testEnv, bibiBloxberg) - await userFactory(testEnv, peterLustig) - creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - // await creationFactory(testEnv, creations!) - await userFactory(testEnv, peterLustig) - await userFactory(testEnv, bibiBloxberg) - 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(), - }, + describe('listAllContribution', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) }) }) - it('returns allCreation', async () => { - await expect( - query({ - query: listAllContributions, + describe('authenticated', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + await userFactory(testEnv, peterLustig) + await userFactory(testEnv, raeuberHotzenplotz) + await userFactory(testEnv, bobBaumeister) + creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + result = await mutate({ + mutation: createContribution, variables: { - currentPage: 1, - pageSize: 25, - order: 'DESC', - filterConfirmed: false, + amount: 100.0, + memo: 'Test env contribution', + creationDate: new Date().toString(), }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listAllContributions: expect.arrayContaining([ - expect.objectContaining({ - id: expect.any(Number), - memo: 'Herzlich Willkommen bei Gradido!', - amount: '1000', - }), - expect.objectContaining({ - id: expect.any(Number), - memo: 'Test env contribution', - amount: '100', - }), - ]), - }, - }), - ) + }) + }) + + it('returns allCreation', async () => { + await expect( + query({ + query: listAllContributions, + variables: { + currentPage: 1, + pageSize: 25, + order: 'DESC', + filterConfirmed: false, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listAllContributions: { + linkCount: 25, + linkList: expect.arrayContaining([ + expect.objectContaining({ + id: expect.any(Number), + memo: 'Herzlich Willkommen bei Gradido!', + amount: '1000', + }), + expect.objectContaining({ + id: expect.any(Number), + memo: 'Test env contribution', + amount: '100', + }), + ]), + }, + }, + }), + ) + }) }) }) }) From b86ddbf8b0be90856de0a3a3e2b990745e9a5804 Mon Sep 17 00:00:00 2001 From: elweyn Date: Thu, 14 Jul 2022 18:08:51 +0200 Subject: [PATCH 42/49] Fix linting. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 1 + backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index b294fb40d..247d1be0e 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -468,6 +468,7 @@ describe('ContributionResolver', () => { await userFactory(testEnv, peterLustig) await userFactory(testEnv, raeuberHotzenplotz) await userFactory(testEnv, bobBaumeister) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) await query({ query: login, diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 3faffb95d..cdaa5faa3 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -4,7 +4,7 @@ import { backendLogger as logger } from '@/server/logger' import { Contribution as dbContribution } from '@entity/Contribution' import { User as dbUser } from '@entity/User' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' -import { FindOperator, IsNull, Not } from '@dbTools/typeorm' +import { FindOperator, IsNull } from '@dbTools/typeorm' import ContributionArgs from '@arg/ContributionArgs' import Paginated from '@arg/Paginated' import { Order } from '@enum/Order' From 175bc514be7fea2f1737e4e6861c1fa7c5680e5b Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Fri, 15 Jul 2022 13:36:31 +0200 Subject: [PATCH 43/49] feat: Do not log IntrospectionQuery from Query Browser --- backend/src/server/plugins.ts | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/src/server/plugins.ts b/backend/src/server/plugins.ts index 1972bc1c8..24df45baa 100644 --- a/backend/src/server/plugins.ts +++ b/backend/src/server/plugins.ts @@ -31,20 +31,24 @@ const filterVariables = (variables: any) => { const logPlugin = { requestDidStart(requestContext: any) { const { logger } = requestContext - const { query, mutation, variables } = requestContext.request - logger.info(`Request: + const { query, mutation, variables, operationName } = requestContext.request + if (operationName !== 'IntrospectionQuery') { + logger.info(`Request: ${mutation || query}variables: ${JSON.stringify(filterVariables(variables), null, 2)}`) + } return { willSendResponse(requestContext: any) { - if (requestContext.context.user) logger.info(`User ID: ${requestContext.context.user.id}`) - if (requestContext.response.data) { - logger.info('Response Success!') - logger.trace(`Response-Data: + if (operationName !== 'IntrospectionQuery') { + if (requestContext.context.user) logger.info(`User ID: ${requestContext.context.user.id}`) + if (requestContext.response.data) { + logger.info('Response Success!') + logger.trace(`Response-Data: ${JSON.stringify(requestContext.response.data, null, 2)}`) - } - if (requestContext.response.errors) - logger.error(`Response-Errors: + } + if (requestContext.response.errors) + logger.error(`Response-Errors: ${JSON.stringify(requestContext.response.errors, null, 2)}`) + } return requestContext }, } From d0662270431f112710c23748c72c6577c99eef3e Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:13:33 +0200 Subject: [PATCH 44/49] Change the Contribution entity to join on user. --- .../0039-contributions_table/Contribution.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/database/entity/0039-contributions_table/Contribution.ts b/database/entity/0039-contributions_table/Contribution.ts index 6c7358f90..ee6e0f73f 100644 --- a/database/entity/0039-contributions_table/Contribution.ts +++ b/database/entity/0039-contributions_table/Contribution.ts @@ -1,6 +1,15 @@ import Decimal from 'decimal.js-light' -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn, DeleteDateColumn } from 'typeorm' +import { + BaseEntity, + Column, + Entity, + PrimaryGeneratedColumn, + DeleteDateColumn, + OneToOne, + JoinColumn, +} from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' +import { User } from '../User' @Entity('contributions') export class Contribution extends BaseEntity { @@ -10,6 +19,10 @@ export class Contribution extends BaseEntity { @Column({ unsigned: true, nullable: false, name: 'user_id' }) userId: number + @OneToOne(() => User) + @JoinColumn({ name: 'user_id' }) + user: User + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) createdAt: Date From 5f5c19e6ba364ee036284fb47a197a6da3683832 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:14:15 +0200 Subject: [PATCH 45/49] Change find to much more efficient code. --- .../graphql/resolver/ContributionResolver.ts | 28 ++++++------------- 1 file changed, 8 insertions(+), 20 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 812e81527..d71ecac59 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -2,7 +2,6 @@ import { RIGHTS } from '@/auth/RIGHTS' import { Context, getUser } from '@/server/context' import { backendLogger as logger } from '@/server/logger' import { Contribution as dbContribution } from '@entity/Contribution' -import { User as dbUser } from '@entity/User' import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql' import { FindOperator, IsNull } from '@dbTools/typeorm' import ContributionArgs from '@arg/ContributionArgs' @@ -72,31 +71,20 @@ export class ContributionResolver { @Args() { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, ): Promise { - const dbContributions = await dbContribution.find({ + const [dbContributions, count] = await dbContribution.findAndCount({ + relations: ['user'], order: { createdAt: order, }, skip: (currentPage - 1) * pageSize, take: pageSize, }) - const contributions: Contribution[] = [] - const userIds: number[] = [] - dbContributions.forEach(async (dbContribution) => { - userIds.push(dbContribution.userId) - }) - userIds.filter((elem, index, self) => { - return index === self.indexOf(elem) - }) - const users = new Map() - for (let i = 0; i < userIds.length; i++) { - const id = userIds[i] - const user = await dbUser.findOneOrFail({ id }) - users.set(id, user) - } - dbContributions.forEach((dbContribution) => { - contributions.push(new Contribution(dbContribution, users.get(dbContribution.userId))) - }) - return new ContributionListResult(contributions.length, contributions) + return new ContributionListResult( + count, + dbContributions.map( + (contribution) => new Contribution(contribution, new User(contribution.user)), + ), + ) } @Authorized([RIGHTS.UPDATE_CONTRIBUTION]) From 382211f3ca12fa9585ef853751d886672997876e Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:14:55 +0200 Subject: [PATCH 46/49] Adapt test so that we check findAllContributions. --- .../src/graphql/resolver/ContributionResolver.test.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 247d1be0e..fd5ff68a2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -466,15 +466,14 @@ describe('ContributionResolver', () => { beforeAll(async () => { await userFactory(testEnv, bibiBloxberg) await userFactory(testEnv, peterLustig) - await userFactory(testEnv, raeuberHotzenplotz) - await userFactory(testEnv, bobBaumeister) + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - creations.forEach(async (creation) => await creationFactory(testEnv, creation!)) + await creationFactory(testEnv, bibisCreation!) await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) - result = await mutate({ + await mutate({ mutation: createContribution, variables: { amount: 100.0, @@ -499,7 +498,7 @@ describe('ContributionResolver', () => { expect.objectContaining({ data: { listAllContributions: { - linkCount: 25, + linkCount: 2, linkList: expect.arrayContaining([ expect.objectContaining({ id: expect.any(Number), From cada4eca19cfa95b3ba7603838f2f163effb05ad Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:32:20 +0200 Subject: [PATCH 47/49] Change OneToOne relation to OneToMany / ManyToOne. --- .../0039-contributions_table/Contribution.ts | 3 ++- .../0040-add_contribution_link_id_to_user/User.ts | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/database/entity/0039-contributions_table/Contribution.ts b/database/entity/0039-contributions_table/Contribution.ts index ee6e0f73f..5df527530 100644 --- a/database/entity/0039-contributions_table/Contribution.ts +++ b/database/entity/0039-contributions_table/Contribution.ts @@ -7,6 +7,7 @@ import { DeleteDateColumn, OneToOne, JoinColumn, + ManyToOne, } from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' import { User } from '../User' @@ -19,7 +20,7 @@ export class Contribution extends BaseEntity { @Column({ unsigned: true, nullable: false, name: 'user_id' }) userId: number - @OneToOne(() => User) + @ManyToOne(() => User, (user) => user.contributions) @JoinColumn({ name: 'user_id' }) user: User diff --git a/database/entity/0040-add_contribution_link_id_to_user/User.ts b/database/entity/0040-add_contribution_link_id_to_user/User.ts index 9bf76e5f5..56047345a 100644 --- a/database/entity/0040-add_contribution_link_id_to_user/User.ts +++ b/database/entity/0040-add_contribution_link_id_to_user/User.ts @@ -1,4 +1,13 @@ -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + DeleteDateColumn, + OneToMany, + JoinColumn, +} from 'typeorm' +import { Contribution } from '../Contribution' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { @@ -76,4 +85,8 @@ export class User extends BaseEntity { default: null, }) passphrase: string + + @OneToMany(() => Contribution, (contribution) => contribution.user) + @JoinColumn({ name: 'user_id' }) + contributions?: Contribution[] } From 843993e34085397eb86b8e67477bf58e94debd6a Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:34:47 +0200 Subject: [PATCH 48/49] Fix linting. --- backend/src/graphql/resolver/ContributionResolver.test.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index fd5ff68a2..e6478ffc2 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -14,8 +14,6 @@ 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 { bobBaumeister } from '@/seeds/users/bob-baumeister' -import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' let mutate: any, query: any, con: any let testEnv: any From 5892f7b42947db8270fe4d26a4faed587abe68c3 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 15 Jul 2022 14:48:51 +0200 Subject: [PATCH 49/49] Remove OneToOne import from contribution entity. --- database/entity/0039-contributions_table/Contribution.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/database/entity/0039-contributions_table/Contribution.ts b/database/entity/0039-contributions_table/Contribution.ts index 5df527530..b5e6ac0e0 100644 --- a/database/entity/0039-contributions_table/Contribution.ts +++ b/database/entity/0039-contributions_table/Contribution.ts @@ -5,7 +5,6 @@ import { Entity, PrimaryGeneratedColumn, DeleteDateColumn, - OneToOne, JoinColumn, ManyToOne, } from 'typeorm'