From 7be2fb68b442400082885546b576797d9be57bce Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 12:27:47 +0100 Subject: [PATCH 01/11] refactor: Get User Creations --- backend/src/graphql/resolver/AdminResolver.ts | 98 +++++-------------- 1 file changed, 26 insertions(+), 72 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 4b9c72afb..61e58151e 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql' -import { getCustomRepository, ObjectLiteral, Raw } from '@dbTools/typeorm' +import { getCustomRepository, ObjectLiteral, getConnection } from '@dbTools/typeorm' import { UserAdmin, SearchUsersResult } from '../model/UserAdmin' import { PendingCreation } from '../model/PendingCreation' import { CreatePendingCreations } from '../model/CreatePendingCreations' @@ -293,81 +293,35 @@ export class AdminResolver { } async function getUserCreations(id: number): Promise { - const dateNextMonth = moment().add(1, 'month').format('YYYY-MM') + '-01' - const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM') + '-01' - const beforeLastMonthNumber = moment().subtract(2, 'month').format('M') - const lastMonthNumber = moment().subtract(1, 'month').format('M') - const currentMonthNumber = moment().format('M') + const now = new Date(Date.now()) + const months = [ + now.getMonth() + 1, + new Date(now.getFullYear(), now.getMonth() - 1, 1).getMonth() + 1, + new Date(now.getFullYear(), now.getMonth() - 2, 1).getMonth() + 1, + ].reverse() - const createdAmountsQuery = await Transaction.createQueryBuilder('transactions') - .select('MONTH(transactions.creation_date)', 'target_month') - .addSelect('SUM(transactions.amount)', 'sum') - .where('transactions.user_id = :id', { id }) - .andWhere('transactions.transaction_type_id = :type', { type: TransactionTypeId.CREATION }) - .andWhere({ - creationDate: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, { - date: dateBeforeLastMonth, - endDate: dateNextMonth, - }), - }) - .groupBy('target_month') - .orderBy('target_month', 'ASC') - .getRawMany() + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() - const pendingAmountsQuery = await AdminPendingCreation.createQueryBuilder( - 'admin_pending_creations', - ) - .select('MONTH(admin_pending_creations.date)', 'target_month') - .addSelect('SUM(admin_pending_creations.amount)', 'sum') - .where('admin_pending_creations.userId = :id', { id }) - .andWhere({ - date: Raw((alias) => `${alias} >= :date and ${alias} < :endDate`, { - date: dateBeforeLastMonth, - endDate: dateNextMonth, - }), - }) - .groupBy('target_month') - .orderBy('target_month', 'ASC') - .getRawMany() + const unionQuery = await queryRunner.manager.query(` + SELECT MONTH(date) AS month, sum(amount) AS sum FROM + (SELECT creation_date AS date, amount AS amount FROM transactions + WHERE user_id = ${id} AND transaction_type_id = 1 + AND creation_date >= last_day(curdate() - interval 3 month) + interval 1 day + UNION + SELECT date AS date, amount AS amount FROM admin_pending_creations + WHERE userId = ${id} + AND date >= last_day(curdate() - interval 3 month) + interval 1 day) AS result + GROUP BY month + ORDER BY date DESC + `) - const map = new Map() - if (Array.isArray(createdAmountsQuery) && createdAmountsQuery.length > 0) { - createdAmountsQuery.forEach((createdAmount) => { - if (!map.has(createdAmount.target_month)) { - map.set(createdAmount.target_month, createdAmount.sum) - } else { - const store = map.get(createdAmount.target_month) - map.set(createdAmount.target_month, Number(store) + Number(createdAmount.sum)) - } - }) - } + await queryRunner.release() - if (Array.isArray(pendingAmountsQuery) && pendingAmountsQuery.length > 0) { - pendingAmountsQuery.forEach((pendingAmount) => { - if (!map.has(pendingAmount.target_month)) { - map.set(pendingAmount.target_month, pendingAmount.sum) - } else { - const store = map.get(pendingAmount.target_month) - map.set(pendingAmount.target_month, Number(store) + Number(pendingAmount.sum)) - } - }) - } - const usedCreationBeforeLastMonth = map.get(Number(beforeLastMonthNumber)) - ? Number(map.get(Number(beforeLastMonthNumber))) / 10000 - : 0 - const usedCreationLastMonth = map.get(Number(lastMonthNumber)) - ? Number(map.get(Number(lastMonthNumber))) / 10000 - : 0 - - const usedCreationCurrentMonth = map.get(Number(currentMonthNumber)) - ? Number(map.get(Number(currentMonthNumber))) / 10000 - : 0 - - return [ - 1000 - usedCreationBeforeLastMonth, - 1000 - usedCreationLastMonth, - 1000 - usedCreationCurrentMonth, - ] + return months.map((month) => { + const creation = unionQuery.find((raw: any) => parseInt(raw.month) === month) + return 1000 - (creation ? Number(creation.sum) / 10000 : 0) + }) } function updateCreations(creations: number[], pendingCreation: AdminPendingCreation): number[] { From 2bdcea15f069a094d6ec31307d9f7dd64d534f35 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 13:25:26 +0100 Subject: [PATCH 02/11] use only one query to get the open creations --- backend/src/graphql/resolver/AdminResolver.ts | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 61e58151e..4eb06b493 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -40,9 +40,7 @@ export class AdminResolver { if (notActivated) { filterCriteria.push({ emailChecked: false }) } - // prevent overfetching data from db, select only needed columns - // prevent reading and transmitting data from db at least 300 Bytes - // one of my example dataset shrink down from 342 Bytes to 42 Bytes, that's ~88% saved db bandwith + const userFields = ['id', 'firstName', 'lastName', 'email', 'emailChecked'] const [users, count] = await userRepository.findBySearchCriteriaPagedFiltered( userFields.map((fieldName) => { @@ -54,6 +52,8 @@ export class AdminResolver { pageSize, ) + const creations = await getUserCreations(users.map((u) => u.id)) + const adminUsers = await Promise.all( users.map(async (user) => { const adminUser = new UserAdmin() @@ -61,7 +61,8 @@ export class AdminResolver { adminUser.firstName = user.firstName adminUser.lastName = user.lastName adminUser.email = user.email - adminUser.creation = await getUserCreations(user.id) + const userCreations = creations.find((c) => c.id === user.id) + adminUser.creation = userCreations ? userCreations.creations : [1000, 1000, 1000] adminUser.emailChecked = user.emailChecked adminUser.hasElopage = await hasElopageBuys(user.email) if (!user.emailChecked) { @@ -109,7 +110,7 @@ export class AdminResolver { if (!user.emailChecked) { throw new Error('Creation could not be saved, Email is not activated') } - const creations = await getUserCreations(user.id) + const creations = await getUserCreation(user.id) const creationDateObj = new Date(creationDate) if (isCreationValid(creations, amount, creationDateObj)) { const adminPendingCreation = AdminPendingCreation.create() @@ -122,7 +123,7 @@ export class AdminResolver { await AdminPendingCreation.save(adminPendingCreation) } - return getUserCreations(user.id) + return getUserCreation(user.id) } @Authorized([RIGHTS.CREATE_PENDING_CREATION]) @@ -171,7 +172,7 @@ export class AdminResolver { } const creationDateObj = new Date(creationDate) - let creations = await getUserCreations(user.id) + let creations = await getUserCreation(user.id) if (pendingCreationToUpdate.date.getMonth() === creationDateObj.getMonth()) { creations = updateCreations(creations, pendingCreationToUpdate) } @@ -190,7 +191,8 @@ export class AdminResolver { result.memo = pendingCreationToUpdate.memo result.date = pendingCreationToUpdate.date result.moderator = pendingCreationToUpdate.moderator - result.creation = await getUserCreations(user.id) + + result.creation = await getUserCreation(user.id) return result } @@ -213,7 +215,7 @@ export class AdminResolver { firstName: user.firstName, lastName: user.lastName, email: user.email, - creation: await getUserCreations(user.id), + creation: await getUserCreation(user.id), } return newPendingCreation @@ -292,7 +294,17 @@ export class AdminResolver { } } -async function getUserCreations(id: number): Promise { +interface CreationMap { + id: number + creations: number[] +} + +async function getUserCreation(id: number): Promise { + const creations = await getUserCreations([id]) + return creations[0] ? creations[0].creations : [1000, 1000, 1000] +} + +async function getUserCreations(ids: number[]): Promise { const now = new Date(Date.now()) const months = [ now.getMonth() + 1, @@ -304,23 +316,30 @@ async function getUserCreations(id: number): Promise { await queryRunner.connect() const unionQuery = await queryRunner.manager.query(` - SELECT MONTH(date) AS month, sum(amount) AS sum FROM - (SELECT creation_date AS date, amount AS amount FROM transactions - WHERE user_id = ${id} AND transaction_type_id = 1 + SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM + (SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions + WHERE user_id IN (${ids.toString()}) AND transaction_type_id = 1 AND creation_date >= last_day(curdate() - interval 3 month) + interval 1 day UNION - SELECT date AS date, amount AS amount FROM admin_pending_creations - WHERE userId = ${id} + SELECT date AS date, amount AS amount, userId AS userId FROM admin_pending_creations + WHERE userId IN (${ids.toString()}) AND date >= last_day(curdate() - interval 3 month) + interval 1 day) AS result - GROUP BY month + GROUP BY month, userId ORDER BY date DESC `) await queryRunner.release() - return months.map((month) => { - const creation = unionQuery.find((raw: any) => parseInt(raw.month) === month) - return 1000 - (creation ? Number(creation.sum) / 10000 : 0) + return ids.map((id) => { + return { + id, + creations: months.map((month) => { + const creation = unionQuery.find( + (raw: any) => parseInt(raw.month) === month && parseInt(raw.id) === id, + ) + return 1000 - (creation ? Number(creation.sum) / 10000 : 0) + }), + } }) } From 99f641f1f97c2c94b23c4597b4f113c8150a518a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 13:49:42 +0100 Subject: [PATCH 03/11] replace any --- backend/src/graphql/resolver/AdminResolver.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 4eb06b493..6befcf13b 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -335,7 +335,8 @@ async function getUserCreations(ids: number[]): Promise { id, creations: months.map((month) => { const creation = unionQuery.find( - (raw: any) => parseInt(raw.month) === month && parseInt(raw.id) === id, + (raw: { month: string; id: string; creation: number[] }) => + parseInt(raw.month) === month && parseInt(raw.id) === id, ) return 1000 - (creation ? Number(creation.sum) / 10000 : 0) }), From c0c5510903906c2531036f2ac1d6bf100bd875ce Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 14:24:07 +0100 Subject: [PATCH 04/11] less db calls for get pending creations --- backend/src/graphql/resolver/AdminResolver.ts | 36 +++++++++---------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 6befcf13b..251274fac 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx } from 'type-graphql' -import { getCustomRepository, ObjectLiteral, getConnection } from '@dbTools/typeorm' +import { getCustomRepository, ObjectLiteral, getConnection, In } from '@dbTools/typeorm' import { UserAdmin, SearchUsersResult } from '../model/UserAdmin' import { PendingCreation } from '../model/PendingCreation' import { CreatePendingCreations } from '../model/CreatePendingCreations' @@ -201,27 +201,23 @@ export class AdminResolver { @Query(() => [PendingCreation]) async getPendingCreations(): Promise { const pendingCreations = await AdminPendingCreation.find() + const userIds = pendingCreations.map((p) => p.userId) + const userCreations = await getUserCreations(userIds) + const users = await User.find({ id: In(userIds) }) - const pendingCreationsPromise = await Promise.all( - pendingCreations.map(async (pendingCreation) => { - const userRepository = getCustomRepository(UserRepository) - const user = await userRepository.findOneOrFail({ id: pendingCreation.userId }) + return pendingCreations.map((pendingCreation) => { + const user = users.find((u) => u.id === pendingCreation.userId) + const creation = userCreations.find((c) => c.id === pendingCreation.id) - const parsedAmount = Number(parseInt(pendingCreation.amount.toString()) / 10000) - // pendingCreation.amount = parsedAmount - const newPendingCreation = { - ...pendingCreation, - amount: parsedAmount, - firstName: user.firstName, - lastName: user.lastName, - email: user.email, - creation: await getUserCreation(user.id), - } - - return newPendingCreation - }), - ) - return pendingCreationsPromise.reverse() + return { + ...pendingCreation, + amount: Number(parseInt(pendingCreation.amount.toString()) / 10000), + firstName: user ? user.firstName : '', + lastName: user ? user.lastName : '', + email: user ? user.email : '', + creation: creation ? creation.creations : [1000, 1000, 1000], + } + }) } @Authorized([RIGHTS.DELETE_PENDING_CREATION]) From 55211f09402551c451587a9b313e4c560514d6b8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 14:43:03 +0100 Subject: [PATCH 05/11] use enum as creation type, date criteria as variable --- backend/src/graphql/resolver/AdminResolver.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 251274fac..199ef6d25 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -311,15 +311,18 @@ async function getUserCreations(ids: number[]): Promise { const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() + const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day' + const unionQuery = await queryRunner.manager.query(` SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM (SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions - WHERE user_id IN (${ids.toString()}) AND transaction_type_id = 1 - AND creation_date >= last_day(curdate() - interval 3 month) + interval 1 day - UNION - SELECT date AS date, amount AS amount, userId AS userId FROM admin_pending_creations - WHERE userId IN (${ids.toString()}) - AND date >= last_day(curdate() - interval 3 month) + interval 1 day) AS result + WHERE user_id IN (${ids.toString()}) + AND transaction_type_id = ${TransactionTypeId.CREATION} + AND creation_date >= ${dateFilter} + UNION + SELECT date AS date, amount AS amount, userId AS userId FROM admin_pending_creations + WHERE userId IN (${ids.toString()}) + AND date >= ${dateFilter}) AS result GROUP BY month, userId ORDER BY date DESC `) From cd7a9bf3617478e8f736e891e52edefe7e30ece7 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 15:49:58 +0100 Subject: [PATCH 06/11] do not use moment anymore --- backend/src/graphql/resolver/AdminResolver.ts | 72 +++++++------------ 1 file changed, 27 insertions(+), 45 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 199ef6d25..9b956549c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -12,7 +12,6 @@ import { UserRepository } from '../../typeorm/repository/User' import CreatePendingCreationArgs from '../arg/CreatePendingCreationArgs' import UpdatePendingCreationArgs from '../arg/UpdatePendingCreationArgs' import SearchUsersArgs from '../arg/SearchUsersArgs' -import moment from 'moment' import { Transaction } from '@entity/Transaction' import { UserTransaction } from '@entity/UserTransaction' import { UserTransactionRepository } from '../../typeorm/repository/UserTransaction' @@ -301,12 +300,7 @@ async function getUserCreation(id: number): Promise { } async function getUserCreations(ids: number[]): Promise { - const now = new Date(Date.now()) - const months = [ - now.getMonth() + 1, - new Date(now.getFullYear(), now.getMonth() - 1, 1).getMonth() + 1, - new Date(now.getFullYear(), now.getMonth() - 2, 1).getMonth() + 1, - ].reverse() + const months = getCreationMonths() const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() @@ -344,50 +338,38 @@ async function getUserCreations(ids: number[]): Promise { } function updateCreations(creations: number[], pendingCreation: AdminPendingCreation): number[] { - const dateMonth = moment().format('YYYY-MM') - const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM') - const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM') - const creationDateMonth = moment(pendingCreation.date).format('YYYY-MM') + const index = getCreationIndex(pendingCreation.date.getMonth()) - switch (creationDateMonth) { - case dateMonth: - creations[2] += parseInt(pendingCreation.amount.toString()) - break - case dateLastMonth: - creations[1] += parseInt(pendingCreation.amount.toString()) - break - case dateBeforeLastMonth: - creations[0] += parseInt(pendingCreation.amount.toString()) - break - default: - throw new Error('UpdatedCreationDate is not in the last three months') + if (index > -1) { + creations[index] += parseInt(pendingCreation.amount.toString()) + } else { + throw new Error('UpdatedCreationDate is not in the last three months') } + return creations } function isCreationValid(creations: number[], amount: number, creationDate: Date) { - const dateMonth = moment().format('YYYY-MM') - const dateLastMonth = moment().subtract(1, 'month').format('YYYY-MM') - const dateBeforeLastMonth = moment().subtract(2, 'month').format('YYYY-MM') - const creationDateMonth = moment(creationDate).format('YYYY-MM') + const index = getCreationIndex(creationDate.getMonth()) - let openCreation - switch (creationDateMonth) { - case dateMonth: - openCreation = creations[2] - break - case dateLastMonth: - openCreation = creations[1] - break - case dateBeforeLastMonth: - openCreation = creations[0] - break - default: - throw new Error('CreationDate is not in last three months') + if (index > -1) { + if (creations[index] < amount) { + throw new Error(`Open creation (${creations[index]}) is less than amount (${amount})`) + } else { + return true + } } - - if (openCreation < amount) { - throw new Error(`Open creation (${openCreation}) is less than amount (${amount})`) - } - return true +} + +const getCreationMonths = (): number[] => { + const now = new Date(Date.now()) + return [ + now.getMonth() + 1, + new Date(now.getFullYear(), now.getMonth() - 1, 1).getMonth() + 1, + new Date(now.getFullYear(), now.getMonth() - 2, 1).getMonth() + 1, + ].reverse() +} + +const getCreationIndex = (month: number): number => { + return getCreationMonths().findIndex((el) => el === month + 1) } From 03e67604ba4b7a6941191f95ca79278e57edb5bf Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 15:50:42 +0100 Subject: [PATCH 07/11] remove moment --- backend/package.json | 1 - backend/yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/backend/package.json b/backend/package.json index 93a954d3f..c3ffcca87 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,7 +31,6 @@ "jsonwebtoken": "^8.5.1", "lodash.clonedeep": "^4.5.0", "module-alias": "^2.2.2", - "moment": "^2.29.1", "mysql2": "^2.3.0", "nodemailer": "^6.6.5", "random-bigint": "^0.0.1", diff --git a/backend/yarn.lock b/backend/yarn.lock index 6ef0d5701..c76b5f00f 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -4092,11 +4092,6 @@ module-alias@^2.2.2: resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== -moment@^2.29.1: - version "2.29.1" - resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" - integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== - ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" From dcf2e1a19e36259374ddbb468e4c57a5b07b2469 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 16:38:57 +0100 Subject: [PATCH 08/11] fix wrong id in find statement --- backend/src/graphql/resolver/AdminResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 9b956549c..a5e69d0ba 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -206,7 +206,7 @@ export class AdminResolver { return pendingCreations.map((pendingCreation) => { const user = users.find((u) => u.id === pendingCreation.userId) - const creation = userCreations.find((c) => c.id === pendingCreation.id) + const creation = userCreations.find((c) => c.id === pendingCreation.userId) return { ...pendingCreation, From 2ce19c9c80293229015043e54163fe396bfe4a12 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 17:45:25 +0100 Subject: [PATCH 09/11] improve error messages, test for valid creation on confirmation --- backend/src/graphql/resolver/AdminResolver.ts | 48 ++++++++++++------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index a5e69d0ba..21783159d 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -236,6 +236,11 @@ export class AdminResolver { if (moderatorUser.id === pendingCreation.userId) throw new Error('Moderator can not confirm own pending creation') + const creations = await getUserCreation(pendingCreation.userId, false) + if (!isCreationValid(creations, Number(pendingCreation.amount) / 10000, pendingCreation.date)) { + throw new Error('Creation is not valid!!') + } + const receivedCallDate = new Date() let transaction = new Transaction() transaction.transactionTypeId = TransactionTypeId.CREATION @@ -294,12 +299,12 @@ interface CreationMap { creations: number[] } -async function getUserCreation(id: number): Promise { - const creations = await getUserCreations([id]) +async function getUserCreation(id: number, includePending = true): Promise { + const creations = await getUserCreations([id], includePending) return creations[0] ? creations[0].creations : [1000, 1000, 1000] } -async function getUserCreations(ids: number[]): Promise { +async function getUserCreations(ids: number[], includePending = true): Promise { const months = getCreationMonths() const queryRunner = getConnection().createQueryRunner() @@ -307,16 +312,21 @@ async function getUserCreations(ids: number[]): Promise { const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day' + const unionString = includePending + ? ` + UNION + SELECT date AS date, amount AS amount, userId AS userId FROM admin_pending_creations + WHERE userId IN (${ids.toString()}) + AND date >= ${dateFilter}` + : '' + const unionQuery = await queryRunner.manager.query(` SELECT MONTH(date) AS month, sum(amount) AS sum, userId AS id FROM (SELECT creation_date AS date, amount AS amount, user_id AS userId FROM transactions WHERE user_id IN (${ids.toString()}) AND transaction_type_id = ${TransactionTypeId.CREATION} AND creation_date >= ${dateFilter} - UNION - SELECT date AS date, amount AS amount, userId AS userId FROM admin_pending_creations - WHERE userId IN (${ids.toString()}) - AND date >= ${dateFilter}) AS result + ${unionString}) AS result GROUP BY month, userId ORDER BY date DESC `) @@ -340,25 +350,27 @@ async function getUserCreations(ids: number[]): Promise { function updateCreations(creations: number[], pendingCreation: AdminPendingCreation): number[] { const index = getCreationIndex(pendingCreation.date.getMonth()) - if (index > -1) { - creations[index] += parseInt(pendingCreation.amount.toString()) - } else { - throw new Error('UpdatedCreationDate is not in the last three months') + if (index < 0) { + throw new Error('You cannot create GDD for a month older than the last three months.') } - + creations[index] += parseInt(pendingCreation.amount.toString()) return creations } function isCreationValid(creations: number[], amount: number, creationDate: Date) { const index = getCreationIndex(creationDate.getMonth()) - if (index > -1) { - if (creations[index] < amount) { - throw new Error(`Open creation (${creations[index]}) is less than amount (${amount})`) - } else { - return true - } + if (index < 0) { + throw new Error(`No Creation found!`) } + + if (amount > creations[index]) { + throw new Error( + `The amount (${amount} GDD) to be created exceeds the available amount (${creations[index]} GDD) for this month.`, + ) + } + + return true } const getCreationMonths = (): number[] => { From a4b33a8c131707069410fa588b80e371ffb2ae81 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 22 Feb 2022 18:22:44 +0100 Subject: [PATCH 10/11] fix path of index.js when starting in production --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index c3ffcca87..95a41c616 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,7 +10,7 @@ "scripts": { "build": "tsc --build", "clean": "tsc --build --clean", - "start": "node build/index.js", + "start": "node build/src/index.js", "dev": "nodemon -w src --ext ts --exec ts-node src/index.ts", "lint": "eslint . --ext .js,.ts", "test": "TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles" From b8c3deac3e5dae58cd10a93f4ea6235e19986b01 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 22 Feb 2022 18:49:24 +0100 Subject: [PATCH 11/11] fix: Return Empty Array When No Pending Creations Are Present --- 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 21783159d..0b8e3f52b 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -200,6 +200,10 @@ export class AdminResolver { @Query(() => [PendingCreation]) async getPendingCreations(): Promise { const pendingCreations = await AdminPendingCreation.find() + if (pendingCreations.length === 0) { + return [] + } + const userIds = pendingCreations.map((p) => p.userId) const userCreations = await getUserCreations(userIds) const users = await User.find({ id: In(userIds) })