From 84c0c6f10b7cc07083f6b4ea439035257a28f68f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 26 May 2022 02:56:38 +0200 Subject: [PATCH 01/69] start creating contributionLinks table --- .../ContributionLinks.ts | 108 ++++++++++++++++++ database/entity/ContributionLinks.ts | 1 + 2 files changed, 109 insertions(+) create mode 100644 database/entity/0038-add_contribution_links_table/ContributionLinks.ts create mode 100644 database/entity/ContributionLinks.ts diff --git a/database/entity/0038-add_contribution_links_table/ContributionLinks.ts b/database/entity/0038-add_contribution_links_table/ContributionLinks.ts new file mode 100644 index 000000000..5f78a7450 --- /dev/null +++ b/database/entity/0038-add_contribution_links_table/ContributionLinks.ts @@ -0,0 +1,108 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +export enum CycleTypes { + ONCE = 1, + HOUR = 2, + TWOHOURS = 3, + FOURHOURS = 4, + EIGHTHOURS = 5, + HALFDAY = 6, + DAY = 7, + TWODAYS = 8, + THREEDAYS = 9, + FOURDAYS = 10, + FIVEDAYS = 11, + SIXDAYS = 12, + WEEK = 13, + TWOWEEKS = 14, + MONTH = 15, + TWOMONTH = 16, + QUARTER = 17, + HALFYEAR = 18, + YEAR = 19, +} + +@Entity('contribution_links') +export class ContributionLinks extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ length: 100, nullable: false, collation: 'utf8mb4_unicode_ci' }) + name: string + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + description: string + + @Column({ name: 'valid_from', type: 'datetime', nullable: true, default: null }) + validFrom: Date | null + + @Column({ name: 'valid_to', type: 'datetime', nullable: true, default: null }) + validTo: Date | null + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ name: 'cycle', unsigned: true, nullable: false }) + cycle: number + + @Column({ name: 'max_per_cycle', unsigned: true, nullable: false, default: 1 }) + maxPerCycle: number + + @Column({ + name: 'max_amount_per_month', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + maxAmountPerMonth: Decimal + + @Column({ + name: 'total_max_count_of_contribution', + unsigned: true, + nullable: true, + default: null, + }) + totalMaxCountOfContribution: number | null + + @Column({ + name: 'max_account_balance', + type: 'decimal', + precision: 40, + scale: 20, + nullable: true, + default: null, + transformer: DecimalTransformer, + }) + maxAccountBalance: Decimal + + @Column({ + name: 'min_gap_hours', + unsigned: true, + nullable: true, + default: null, + }) + minGapHours: number | null + + @Column({ name: 'created_at', type: 'datetime', nullable: true, default: null }) + createdAt: Date | null + + @Column({ name: 'deleted_at', type: 'datetime', nullable: true, default: null }) + deletedAt: Date | null + + @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) + code: string + + @Column({ name: 'link_enabled', type: 'boolean', nullable: true, default: null }) + linkEnabled: boolean | null +} diff --git a/database/entity/ContributionLinks.ts b/database/entity/ContributionLinks.ts new file mode 100644 index 000000000..721dfd2b3 --- /dev/null +++ b/database/entity/ContributionLinks.ts @@ -0,0 +1 @@ +export { ContributionLinks } from './0038-add_contribution_links_table/ContributionLinks' From e6d9420d8e4307c9a55125eb5e06f4f849843090 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 10:40:55 +0200 Subject: [PATCH 02/69] migration to rename admin-pending-creations --- .../migrations/0037-contributions_table.ts | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 database/migrations/0037-contributions_table.ts diff --git a/database/migrations/0037-contributions_table.ts b/database/migrations/0037-contributions_table.ts new file mode 100644 index 000000000..983593f51 --- /dev/null +++ b/database/migrations/0037-contributions_table.ts @@ -0,0 +1,53 @@ +/* MIGRATION to rename ADMIN_PENDING_CREATION table and add columns + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('RENAME TABLE `admin_pending_creations` TO `contributions`;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `userId` `user_id` int(10);') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created` `contribution_date` datetime;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `date` `created_at` datetime;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `moderator` `moderator_id` int(10);') + + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `contribution_link_id` int(10) unsigned DEFAULT NULL AFTER `moderator_id`;', + ) + + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `confirmed_by` int(10) unsigned DEFAULT NULL AFTER `contribution_link_id`;', + ) + + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `confirmed_at` datetime DEFAULT NULL AFTER `confirmed_by`;', + ) + + await queryFn( + 'ALTER TABLE `contributions` ADD COLUMN `deleted_at` datetime DEFAULT NULL AFTER `confirmed_at`;', + ) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('ALTER TABLE `contributions` DROP COLUMN IF EXISTS `deleted_at`;') + + await queryFn('ALTER TABLE `contributions` DROP COLUMN IF EXISTS `confirmed_at`;') + + await queryFn('ALTER TABLE `contributions` DROP COLUMN IF EXISTS `confirmed_by`;') + + await queryFn('ALTER TABLE `contributions` DROP COLUMN IF EXISTS `contribution_link_id`;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `moderator_id` `moderator` int(10);') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created_at` `date` datetime;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `contribution_date` `created` datetime;') + + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `user_id` `userId` int(10);') + + await queryFn('RENAME TABLE `contributions` TO `admin_pending_creations`;') +} From 617ea9a2b0e43bd6d5207d31814e0d7ea622dcfe Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 10:56:54 +0200 Subject: [PATCH 03/69] contribution entity --- .../0037-contributions_table/Contribution.ts | 45 +++++++++++++++++++ database/entity/Contribution.ts | 1 + database/entity/index.ts | 4 +- .../migrations/0037-contributions_table.ts | 8 ++-- 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 database/entity/0037-contributions_table/Contribution.ts create mode 100644 database/entity/Contribution.ts diff --git a/database/entity/0037-contributions_table/Contribution.ts b/database/entity/0037-contributions_table/Contribution.ts new file mode 100644 index 000000000..997bef2c7 --- /dev/null +++ b/database/entity/0037-contributions_table/Contribution.ts @@ -0,0 +1,45 @@ +import Decimal from 'decimal.js-light' +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm' +import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' + +@Entity('contributions') +export class Contribution extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ unsigned: true, nullable: false, name: 'user_id' }) + userId: number + + @Column({ type: 'datetime', default: () => 'CURRENT_TIMESTAMP', name: 'created_at' }) + createdAt: Date + + @Column({ type: 'datetime', nullable: false, name: 'contribution_date' }) + contributionDate: Date + + @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) + memo: string + + @Column({ + type: 'decimal', + precision: 40, + scale: 20, + nullable: false, + transformer: DecimalTransformer, + }) + amount: Decimal + + @Column({ unsigned: true, nullable: true, name: 'moderator_id' }) + moderatorId: number + + @Column({ unsigned: true, nullable: true, name: 'contribution_link_id' }) + contributionLinkId: number + + @Column({ unsigned: true, nullable: true, name: 'confirmed_by' }) + confirmedBy: number + + @Column({ nullable: true, name: 'confirmed_at' }) + confirmedAt: Date + + @Column({ nullable: true, name: 'deleted_at' }) + deletedAt: Date +} diff --git a/database/entity/Contribution.ts b/database/entity/Contribution.ts new file mode 100644 index 000000000..ccb227359 --- /dev/null +++ b/database/entity/Contribution.ts @@ -0,0 +1 @@ +export { Contribution } from './0037-contributions_table/Contribution' diff --git a/database/entity/index.ts b/database/entity/index.ts index 542333755..54495975c 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -5,10 +5,10 @@ import { Transaction } from './Transaction' import { TransactionLink } from './TransactionLink' import { User } from './User' import { UserSetting } from './UserSetting' -import { AdminPendingCreation } from './AdminPendingCreation' +import { Contribution } from './Contribution' export const entities = [ - AdminPendingCreation, + Contribution, LoginElopageBuys, LoginEmailOptIn, Migration, diff --git a/database/migrations/0037-contributions_table.ts b/database/migrations/0037-contributions_table.ts index 983593f51..163959a3a 100644 --- a/database/migrations/0037-contributions_table.ts +++ b/database/migrations/0037-contributions_table.ts @@ -9,9 +9,9 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `userId` `user_id` int(10);') - await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created` `contribution_date` datetime;') + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created` `created_at` datetime;') - await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `date` `created_at` datetime;') + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `date` `contribution_date` datetime;') await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `moderator` `moderator_id` int(10);') @@ -43,9 +43,9 @@ export async function downgrade(queryFn: (query: string, values?: any[]) => Prom await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `moderator_id` `moderator` int(10);') - await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created_at` `date` datetime;') + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `created_at` `created` datetime;') - await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `contribution_date` `created` datetime;') + await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `contribution_date` `date` datetime;') await queryFn('ALTER TABLE `contributions` CHANGE COLUMN `user_id` `userId` int(10);') From 4c7fc10d8f13378759a30f51589282d1ccf338bb Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 10:57:25 +0200 Subject: [PATCH 04/69] change DB version --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 559b8e9c5..011fd8c3a 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0036-unique_previous_in_transactions', + DB_VERSION: '0037-contributions_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From 69a3cc0264f04c167d825603c50dbf0a6f26627a Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 14:15:34 +0200 Subject: [PATCH 05/69] use new Contribution Data Model --- ...dingCreation.ts => PendingContribution.ts} | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 127 +++++++++--------- 2 files changed, 68 insertions(+), 61 deletions(-) rename backend/src/graphql/model/{PendingCreation.ts => PendingContribution.ts} (93%) diff --git a/backend/src/graphql/model/PendingCreation.ts b/backend/src/graphql/model/PendingContribution.ts similarity index 93% rename from backend/src/graphql/model/PendingCreation.ts rename to backend/src/graphql/model/PendingContribution.ts index 500ba6f6b..e8991deb7 100644 --- a/backend/src/graphql/model/PendingCreation.ts +++ b/backend/src/graphql/model/PendingContribution.ts @@ -2,7 +2,7 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' @ObjectType() -export class PendingCreation { +export class PendingContribution { @Field(() => String) firstName: string diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 8c3d71b73..323df8a99 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -11,7 +11,7 @@ import { FindOperator, } from '@dbTools/typeorm' import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' -import { PendingCreation } from '@model/PendingCreation' +import { PendingContribution } from '@model/PendingContribution' import { CreatePendingCreations } from '@model/CreatePendingCreations' import { UpdatePendingCreation } from '@model/UpdatePendingCreation' import { RIGHTS } from '@/auth/RIGHTS' @@ -25,7 +25,7 @@ import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { TransactionRepository } from '@repository/Transaction' import { calculateDecay } from '@/util/decay' -import { AdminPendingCreation } from '@entity/AdminPendingCreation' +import { Contribution } from '@entity/Contribution' import { hasElopageBuys } from '@/util/hasElopageBuys' import { LoginEmailOptIn } from '@entity/LoginEmailOptIn' import { User as dbUser } from '@entity/User' @@ -183,15 +183,15 @@ export class AdminResolver { const creations = await getUserCreation(user.id) const creationDateObj = new Date(creationDate) if (isCreationValid(creations, amount, creationDateObj)) { - const adminPendingCreation = AdminPendingCreation.create() - adminPendingCreation.userId = user.id - adminPendingCreation.amount = amount - adminPendingCreation.created = new Date() - adminPendingCreation.date = creationDateObj - adminPendingCreation.memo = memo - adminPendingCreation.moderator = moderator.id + const contribution = Contribution.create() + contribution.userId = user.id + contribution.amount = amount + contribution.createdAt = new Date() + contribution.contributionDate = creationDateObj + contribution.memo = memo + contribution.moderatorId = moderator.id - await AdminPendingCreation.save(adminPendingCreation) + await Contribution.save(contribution) } return getUserCreation(user.id) } @@ -200,20 +200,20 @@ export class AdminResolver { @Mutation(() => CreatePendingCreations) async createPendingCreations( @Arg('pendingCreations', () => [CreatePendingCreationArgs]) - pendingCreations: CreatePendingCreationArgs[], + contributions: CreatePendingCreationArgs[], @Ctx() context: Context, ): Promise { let success = false const successfulCreation: string[] = [] const failedCreation: string[] = [] - for (const pendingCreation of pendingCreations) { - await this.createPendingCreation(pendingCreation, context) + for (const contribution of contributions) { + await this.createPendingCreation(contribution, context) .then(() => { - successfulCreation.push(pendingCreation.email) + successfulCreation.push(contribution.email) success = true }) .catch(() => { - failedCreation.push(pendingCreation.email) + failedCreation.push(contribution.email) }) } return { @@ -239,34 +239,34 @@ export class AdminResolver { const moderator = getUser(context) - const pendingCreationToUpdate = await AdminPendingCreation.findOne({ id }) + const contributionToUpdate = await Contribution.findOne({ id }) - if (!pendingCreationToUpdate) { - throw new Error('No creation found to given id.') + if (!contributionToUpdate) { + throw new Error('No contribution found to given id.') } - if (pendingCreationToUpdate.userId !== user.id) { - throw new Error('user of the pending creation and send user does not correspond') + 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 (pendingCreationToUpdate.date.getMonth() === creationDateObj.getMonth()) { - creations = updateCreations(creations, pendingCreationToUpdate) + if (contributionToUpdate.contributionDate.getMonth() === creationDateObj.getMonth()) { + creations = updateCreations(creations, contributionToUpdate) } // all possible cases not to be true are thrown in this function isCreationValid(creations, amount, creationDateObj) - pendingCreationToUpdate.amount = amount - pendingCreationToUpdate.memo = memo - pendingCreationToUpdate.date = new Date(creationDate) - pendingCreationToUpdate.moderator = moderator.id + contributionToUpdate.amount = amount + contributionToUpdate.memo = memo + contributionToUpdate.contributionDate = new Date(creationDate) + contributionToUpdate.moderatorId = moderator.id - await AdminPendingCreation.save(pendingCreationToUpdate) + await Contribution.save(contributionToUpdate) const result = new UpdatePendingCreation() result.amount = amount - result.memo = pendingCreationToUpdate.memo - result.date = pendingCreationToUpdate.date + result.memo = contributionToUpdate.memo + result.date = contributionToUpdate.contributionDate result.creation = await getUserCreation(user.id) @@ -274,24 +274,28 @@ export class AdminResolver { } @Authorized([RIGHTS.SEARCH_PENDING_CREATION]) - @Query(() => [PendingCreation]) - async getPendingCreations(): Promise { - const pendingCreations = await AdminPendingCreation.find() - if (pendingCreations.length === 0) { + @Query(() => [PendingContribution]) + async getPendingCreations(): Promise { + const contributions = await Contribution.find() + if (contributions.length === 0) { return [] } - const userIds = pendingCreations.map((p) => p.userId) + const userIds = contributions.map((p) => p.userId) const userCreations = await getUserCreations(userIds) const users = await dbUser.find({ where: { id: In(userIds) }, withDeleted: true }) - return pendingCreations.map((pendingCreation) => { - const user = users.find((u) => u.id === pendingCreation.userId) - const creation = userCreations.find((c) => c.id === pendingCreation.userId) + return contributions.map((contribution) => { + const user = users.find((u) => u.id === contribution.userId) + const creation = userCreations.find((c) => c.id === contribution.userId) return { - ...pendingCreation, - amount: pendingCreation.amount, + id: contribution.id, + userId: contribution.userId, + date: contribution.contributionDate, + memo: contribution.memo, + amount: contribution.amount, + moderator: contribution.moderatorId, firstName: user ? user.firstName : '', lastName: user ? user.lastName : '', email: user ? user.email : '', @@ -303,11 +307,11 @@ export class AdminResolver { @Authorized([RIGHTS.DELETE_PENDING_CREATION]) @Mutation(() => Boolean) async deletePendingCreation(@Arg('id', () => Int) id: number): Promise { - const pendingCreation = await AdminPendingCreation.findOne(id) - if (!pendingCreation) { + const contribution = await Contribution.findOne(id) + if (!contribution) { throw new Error('Creation not found for given id.') } - const res = await AdminPendingCreation.delete(pendingCreation) + const res = await contribution.softRemove() return !!res } @@ -317,26 +321,26 @@ export class AdminResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const pendingCreation = await AdminPendingCreation.findOne(id) - if (!pendingCreation) { + const contribution = await Contribution.findOne(id) + if (!contribution) { throw new Error('Creation not found to given id.') } const moderatorUser = getUser(context) - if (moderatorUser.id === pendingCreation.userId) + if (moderatorUser.id === contribution.userId) throw new Error('Moderator can not confirm own pending creation') - const user = await dbUser.findOneOrFail({ id: pendingCreation.userId }, { withDeleted: true }) + const user = await dbUser.findOneOrFail({ id: contribution.userId }, { withDeleted: true }) if (user.deletedAt) throw new Error('This user was deleted. Cannot confirm a creation.') - const creations = await getUserCreation(pendingCreation.userId, false) - if (!isCreationValid(creations, pendingCreation.amount, pendingCreation.date)) { + const creations = await getUserCreation(contribution.userId, false) + if (!isCreationValid(creations, contribution.amount, contribution.contributionDate)) { throw new Error('Creation is not valid!!') } const receivedCallDate = new Date() const transactionRepository = getCustomRepository(TransactionRepository) - const lastTransaction = await transactionRepository.findLastForUser(pendingCreation.userId) + const lastTransaction = await transactionRepository.findLastForUser(contribution.userId) let newBalance = new Decimal(0) let decay: Decay | null = null @@ -344,15 +348,15 @@ export class AdminResolver { decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, receivedCallDate) newBalance = decay.balance } - newBalance = newBalance.add(pendingCreation.amount.toString()) + newBalance = newBalance.add(contribution.amount.toString()) const transaction = new DbTransaction() transaction.typeId = TransactionTypeId.CREATION - transaction.memo = pendingCreation.memo - transaction.userId = pendingCreation.userId + transaction.memo = contribution.memo + transaction.userId = contribution.userId transaction.previous = lastTransaction ? lastTransaction.id : null - transaction.amount = pendingCreation.amount - transaction.creationDate = pendingCreation.date + transaction.amount = contribution.amount + transaction.creationDate = contribution.contributionDate transaction.balance = newBalance transaction.balanceDate = receivedCallDate transaction.decay = decay ? decay.decay : new Decimal(0) @@ -361,7 +365,10 @@ export class AdminResolver { throw new Error('Unable to confirm creation.') }) - await AdminPendingCreation.delete(pendingCreation) + contribution.confirmedAt = receivedCallDate + contribution.confirmedBy = moderatorUser.id + + await Contribution.save(contribution) return true } @@ -478,9 +485,9 @@ async function getUserCreations(ids: number[], includePending = true): Promise= ${dateFilter}` + AND contribution_date >= ${dateFilter}` : '' const unionQuery = await queryRunner.manager.query(` @@ -510,13 +517,13 @@ async function getUserCreations(ids: number[], includePending = true): Promise Date: Mon, 30 May 2022 14:50:58 +0200 Subject: [PATCH 06/69] add logger to admin resolver, log some tracing, fix seeds --- backend/src/graphql/resolver/AdminResolver.test.ts | 8 ++++---- backend/src/graphql/resolver/AdminResolver.ts | 12 +++++++++++- backend/src/seeds/factory/creation.ts | 8 ++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 4771232ea..8d4efd283 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -25,7 +25,7 @@ import { User } from '@entity/User' /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import Decimal from 'decimal.js-light' -import { AdminPendingCreation } from '@entity/AdminPendingCreation' +import { Contribution } from '@entity/Contribution' import { Transaction as DbTransaction } from '@entity/Transaction' // mock account activation email to avoid console spam @@ -54,7 +54,7 @@ afterAll(async () => { let admin: User let user: User -let creation: AdminPendingCreation | void +let creation: Contribution | void describe('AdminResolver', () => { describe('delete user', () => { @@ -1275,8 +1275,8 @@ describe('AdminResolver', () => { }) describe('confirm two creations one after the other quickly', () => { - let c1: AdminPendingCreation | void - let c2: AdminPendingCreation | void + let c1: Contribution | void + let c2: Contribution | void beforeAll(async () => { const now = new Date() diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 323df8a99..7247faa8d 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -40,6 +40,7 @@ import { communityUser } from '@/util/communityUser' import { checkOptInCode, activationLink, printTimeDuration } from './UserResolver' import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import CONFIG from '@/config' +import { backendLogger as logger } from '@/server/logger' // const EMAIL_OPT_IN_REGISTER = 1 // const EMAIL_OPT_UNKNOWN = 3 // elopage? @@ -169,6 +170,7 @@ export class AdminResolver { @Args() { email, amount, memo, creationDate }: CreatePendingCreationArgs, @Ctx() context: Context, ): Promise { + logger.trace('createPendingCreation...') const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { throw new Error(`Could not find user with email: ${email}`) @@ -180,7 +182,9 @@ export class AdminResolver { throw new Error('Creation could not be saved, Email is not activated') } const moderator = getUser(context) + logger.trace('moderator: ', moderator.id) const creations = await getUserCreation(user.id) + logger.trace('creations', creations) const creationDateObj = new Date(creationDate) if (isCreationValid(creations, amount, creationDateObj)) { const contribution = Contribution.create() @@ -191,6 +195,7 @@ export class AdminResolver { contribution.memo = memo contribution.moderatorId = moderator.id + logger.trace('contribution to save', contribution) await Contribution.save(contribution) } return getUserCreation(user.id) @@ -470,23 +475,27 @@ interface CreationMap { } async function getUserCreation(id: number, includePending = true): Promise { + logger.trace('getUserCreation', id, includePending) const creations = await getUserCreations([id], includePending) return creations[0] ? creations[0].creations : FULL_CREATION_AVAILABLE } async function getUserCreations(ids: number[], includePending = true): Promise { + logger.trace('getUserCreations:', ids, includePending) const months = getCreationMonths() + logger.trace('getUserCreations months', months) const queryRunner = getConnection().createQueryRunner() await queryRunner.connect() const dateFilter = 'last_day(curdate() - interval 3 month) + interval 1 day' + logger.trace('getUserCreations dateFilter', dateFilter) const unionString = includePending ? ` UNION SELECT contribution_date AS date, amount AS amount, user_id AS userId FROM contributions - WHERE userId IN (${ids.toString()}) + WHERE user_id IN (${ids.toString()}) AND contribution_date >= ${dateFilter}` : '' @@ -528,6 +537,7 @@ function updateCreations(creations: Decimal[], contribution: Contribution): Deci } function isCreationValid(creations: Decimal[], amount: Decimal, creationDate: Date) { + logger.trace('isCreationValid', creations, amount, creationDate) const index = getCreationIndex(creationDate.getMonth()) if (index < 0) { diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index e49be3758..dc17c4084 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -7,7 +7,7 @@ import { CreationInterface } from '@/seeds/creation/CreationInterface' import { ApolloServerTestClient } from 'apollo-server-testing' import { User } from '@entity/User' import { Transaction } from '@entity/Transaction' -import { AdminPendingCreation } from '@entity/AdminPendingCreation' +import { Contribution } from '@entity/Contribution' // import CONFIG from '@/config/index' export const nMonthsBefore = (date: Date, months = 1): string => { @@ -17,7 +17,7 @@ export const nMonthsBefore = (date: Date, months = 1): string => { export const creationFactory = async ( client: ApolloServerTestClient, creation: CreationInterface, -): Promise => { +): Promise => { const { mutate, query } = client await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) @@ -27,9 +27,9 @@ export const creationFactory = async ( const user = await User.findOneOrFail({ where: { email: creation.email } }) - const pendingCreation = await AdminPendingCreation.findOneOrFail({ + const pendingCreation = await Contribution.findOneOrFail({ where: { userId: user.id, amount: creation.amount }, - order: { created: 'DESC' }, + order: { createdAt: 'DESC' }, }) if (creation.confirmed) { From fa2a05c35ff5d4808b414155dd05e9e23e120405 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 14:59:51 +0200 Subject: [PATCH 07/69] enable soft delete --- database/entity/0037-contributions_table/Contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/entity/0037-contributions_table/Contribution.ts b/database/entity/0037-contributions_table/Contribution.ts index 997bef2c7..61a7550af 100644 --- a/database/entity/0037-contributions_table/Contribution.ts +++ b/database/entity/0037-contributions_table/Contribution.ts @@ -1,5 +1,5 @@ import Decimal from 'decimal.js-light' -import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm' +import { BaseEntity, Column, Entity, PrimaryGeneratedColumn, DeleteDateColumn } from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' @Entity('contributions') @@ -40,6 +40,6 @@ export class Contribution extends BaseEntity { @Column({ nullable: true, name: 'confirmed_at' }) confirmedAt: Date - @Column({ nullable: true, name: 'deleted_at' }) - deletedAt: Date + @DeleteDateColumn({ name: 'deleted_at' }) + deletedAt: Date | null } From 22944bce2871f4ae2b8b0a2649349325717e1c23 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 15:00:23 +0200 Subject: [PATCH 08/69] get tests running again --- backend/src/graphql/resolver/AdminResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 8d4efd283..61da03dfa 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -981,7 +981,7 @@ describe('AdminResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('No creation found to given id.')], + errors: [new GraphQLError('No contribution found to given id.')], }), ) }) @@ -1004,7 +1004,7 @@ describe('AdminResolver', () => { expect.objectContaining({ errors: [ new GraphQLError( - 'user of the pending creation and send user does not correspond', + 'user of the pending contribution and send user does not correspond', ), ], }), From 56ba42babdcda2bf3d59b21634be40c38267f93e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 30 May 2022 21:00:56 +0200 Subject: [PATCH 09/69] do not read confirmed and deleted contributions --- 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 7247faa8d..bf92597b3 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -496,7 +496,8 @@ async function getUserCreations(ids: number[], includePending = true): Promise= ${dateFilter}` + AND contribution_date >= ${dateFilter} + AND confirmed_at IS NULL AND deleted_at IS NULL` : '' const unionQuery = await queryRunner.manager.query(` From e44a41b22be204bebb3276102493433a163a9f42 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 31 May 2022 08:12:41 +0200 Subject: [PATCH 10/69] contribution to transaction as transaction --- .../graphql/resolver/AdminResolver.test.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 78 ++++++++++++------- 2 files changed, 50 insertions(+), 30 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 61da03dfa..c44757a00 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -1315,7 +1315,7 @@ describe('AdminResolver', () => { ) await expect(r2).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('Unable to confirm creation.')], + errors: [new GraphQLError('Creation was not successful.')], }), ) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index bf92597b3..909580ead 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -344,37 +344,57 @@ export class AdminResolver { const receivedCallDate = new Date() - const transactionRepository = getCustomRepository(TransactionRepository) - const lastTransaction = await transactionRepository.findLastForUser(contribution.userId) + const queryRunner = getConnection().createQueryRunner() + await queryRunner.connect() + await queryRunner.startTransaction('READ UNCOMMITTED') + try { + const lastTransaction = await queryRunner.manager + .createQueryBuilder() + .select('transaction') + .from(DbTransaction, 'transaction') + .where('transaction.userId = :id', { id: contribution.userId }) + .orderBy('transaction.balanceDate', 'DESC') + .getOne() + logger.info('lastTransaction ID', lastTransaction ? lastTransaction.id : 'undefined') - let newBalance = new Decimal(0) - let decay: Decay | null = null - if (lastTransaction) { - decay = calculateDecay(lastTransaction.balance, lastTransaction.balanceDate, receivedCallDate) - newBalance = decay.balance + let newBalance = new Decimal(0) + let decay: Decay | null = null + if (lastTransaction) { + decay = calculateDecay( + lastTransaction.balance, + lastTransaction.balanceDate, + receivedCallDate, + ) + newBalance = decay.balance + } + newBalance = newBalance.add(contribution.amount.toString()) + + const transaction = new DbTransaction() + transaction.typeId = TransactionTypeId.CREATION + transaction.memo = contribution.memo + transaction.userId = contribution.userId + transaction.previous = lastTransaction ? lastTransaction.id : null + transaction.amount = contribution.amount + transaction.creationDate = contribution.contributionDate + transaction.balance = newBalance + transaction.balanceDate = receivedCallDate + transaction.decay = decay ? decay.decay : new Decimal(0) + transaction.decayStart = decay ? decay.start : null + await queryRunner.manager.insert(DbTransaction, transaction) + + contribution.confirmedAt = receivedCallDate + contribution.confirmedBy = moderatorUser.id + await queryRunner.manager.update(Contribution, { id: contribution.id }, contribution) + + await queryRunner.commitTransaction() + logger.info('creation commited successfuly.') + } catch (e) { + await queryRunner.rollbackTransaction() + logger.error(`Creation was not successful: ${e}`) + throw new Error(`Creation was not successful.`) + } finally { + await queryRunner.release() } - newBalance = newBalance.add(contribution.amount.toString()) - - const transaction = new DbTransaction() - transaction.typeId = TransactionTypeId.CREATION - transaction.memo = contribution.memo - transaction.userId = contribution.userId - transaction.previous = lastTransaction ? lastTransaction.id : null - transaction.amount = contribution.amount - transaction.creationDate = contribution.contributionDate - transaction.balance = newBalance - transaction.balanceDate = receivedCallDate - transaction.decay = decay ? decay.decay : new Decimal(0) - transaction.decayStart = decay ? decay.start : null - await transaction.save().catch(() => { - throw new Error('Unable to confirm creation.') - }) - - contribution.confirmedAt = receivedCallDate - contribution.confirmedBy = moderatorUser.id - - await Contribution.save(contribution) - return true } From 2444c94af9f6d3869e65721317ffd58a29a408a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Wed, 1 Jun 2022 01:19:44 +0200 Subject: [PATCH 11/69] create migration and entity for new contribution_links table --- .../ContributionLinks.ts | 2 +- database/entity/ContributionLinks.ts | 2 +- .../0037-add_contribution_links_table.ts | 35 +++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) rename database/entity/{0038-add_contribution_links_table => 0037-add_contribution_links_table}/ContributionLinks.ts (97%) create mode 100644 database/migrations/0037-add_contribution_links_table.ts diff --git a/database/entity/0038-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts similarity index 97% rename from database/entity/0038-add_contribution_links_table/ContributionLinks.ts rename to database/entity/0037-add_contribution_links_table/ContributionLinks.ts index 5f78a7450..2fb4e2801 100644 --- a/database/entity/0038-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -100,7 +100,7 @@ export class ContributionLinks extends BaseEntity { @Column({ name: 'deleted_at', type: 'datetime', nullable: true, default: null }) deletedAt: Date | null - @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) + @Column({ length: 24, nullable: true, collation: 'utf8mb4_unicode_ci' }) code: string @Column({ name: 'link_enabled', type: 'boolean', nullable: true, default: null }) diff --git a/database/entity/ContributionLinks.ts b/database/entity/ContributionLinks.ts index 721dfd2b3..de26c1c4a 100644 --- a/database/entity/ContributionLinks.ts +++ b/database/entity/ContributionLinks.ts @@ -1 +1 @@ -export { ContributionLinks } from './0038-add_contribution_links_table/ContributionLinks' +export { ContributionLinks } from './0037-add_contribution_links_table/ContributionLinks' diff --git a/database/migrations/0037-add_contribution_links_table.ts b/database/migrations/0037-add_contribution_links_table.ts new file mode 100644 index 000000000..79bcdf5ce --- /dev/null +++ b/database/migrations/0037-add_contribution_links_table.ts @@ -0,0 +1,35 @@ +/* MIGRATION TO ADD CONTRIBUTION_LINKS + * + * This migration adds the table `contribution_links` in order to store all sorts of contribution_links data + */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE IF NOT EXISTS \`contribution_links\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`name\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + \`decsription\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + \`valid_from\` datetime NULL, + \`valid_to\` datetime NULL, + \`amount\` bigint(20) NOT NULL, + \`cycle\` int(10) unsigned NOT NULL DEFAULT '1', + \`max_per_cycle\` int(10) unsigned NOT NULL DEFAULT '1', + \`max_amount_per_month\` bigint(20) NULL DEFAULT NULL, + \`total_max_count_of_contribution\` int(10) unsigned NULL DEFAULT NULL, + \`max_account_balance\` bigint(20) NULL DEFAULT NULL, + \`min_gap_hours\` int(10) unsigned NULL DEFAULT NULL, + \`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, + \`deleted_at\` datetime NULL DEFAULT NULL, + \`code\` varchar(24) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + \`link_enabled\` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // write downgrade logic as parameter of queryFn + await queryFn(`DROP TABLE IF EXISTS \`contribution_links\`;`) +} From 772bf82000630bb9d9ac22fa3436e9197c0ecad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 1 Jun 2022 11:09:21 +0200 Subject: [PATCH 12/69] Fix linting in database README.md --- database/README.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/database/README.md b/database/README.md index d6cf84518..e951f4530 100644 --- a/database/README.md +++ b/database/README.md @@ -1,32 +1,39 @@ # database ## Project setup -``` + +```bash yarn install ``` ## Upgrade migrations production -``` + +```bash yarn up ``` ## Upgrade migrations development -``` + +```bash yarn dev_up ``` ## Downgrade migrations production -``` + +```bash yarn down ``` ## Downgrade migrations development -``` + +```bash yarn dev_down ``` ## Reset database -``` + +```bash yarn dev_reset ``` + Runs all down migrations and after this all up migrations. From dbc98e8490daf9fc11c38cb8e931912e0fe028b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 1 Jun 2022 12:03:42 +0200 Subject: [PATCH 13/69] Remove 'user_setting' table from db - Add migration 0037-drop_server_user_table.ts --- .../migrations/0037-drop_server_user_table.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 database/migrations/0037-drop_server_user_table.ts diff --git a/database/migrations/0037-drop_server_user_table.ts b/database/migrations/0037-drop_server_user_table.ts new file mode 100644 index 000000000..50a78e104 --- /dev/null +++ b/database/migrations/0037-drop_server_user_table.ts @@ -0,0 +1,19 @@ +/* MIGRATION DROP user_setting TABLE */ + +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export async function upgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn('DROP TABLE `user_setting`;') +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + await queryFn(` + CREATE TABLE IF NOT EXISTS \`user_setting\` ( + \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, + \`userId\` int(11) NOT NULL, + \`key\` varchar(255) NOT NULL, + \`value\` varchar(255) NOT NULL, + PRIMARY KEY (\`id\`) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`) +} From 71595428b3567f6e71fbfccfa343113efb8d2400 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Wed, 1 Jun 2022 12:39:57 +0200 Subject: [PATCH 14/69] Set DB_VERSION to '0037-drop_server_user_table.ts' --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 28318ed6b..2015eb6cc 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0036-unique_previous_in_transactions', + DB_VERSION: '0037-drop_server_user_table.ts', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From d048a001437e8eec311471efd8d7049d94acc548 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 2 Jun 2022 08:43:30 +0200 Subject: [PATCH 15/69] getPendingCreations excludes confirmed contributions --- 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 909580ead..b75222e5c 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -281,7 +281,7 @@ export class AdminResolver { @Authorized([RIGHTS.SEARCH_PENDING_CREATION]) @Query(() => [PendingContribution]) async getPendingCreations(): Promise { - const contributions = await Contribution.find() + const contributions = await Contribution.find({ where: { confirmedAt: IsNull() } }) if (contributions.length === 0) { return [] } From 9f9c6aed79e931d58a0e4097982f354618b6e754 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Thu, 2 Jun 2022 14:28:27 +0200 Subject: [PATCH 16/69] devops: Release Version 1.9.0 --- CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++ admin/package.json | 2 +- backend/package.json | 2 +- database/package.json | 2 +- frontend/package.json | 2 +- package.json | 2 +- 6 files changed, 45 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 48eeff9a9..53aa4a9e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,48 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [1.9.0](https://github.com/gradido/gradido/compare/1.8.3...1.9.0) + +- refactor: 🍰 Refactor To `filters` Object And Rename Filters Properties [`#1914`](https://github.com/gradido/gradido/pull/1914) +- refactor register button position [`#1964`](https://github.com/gradido/gradido/pull/1964) +- fixed redeem link is mobile start false [`#1958`](https://github.com/gradido/gradido/pull/1958) +- 1951 remove back link and remove gray box [`#1959`](https://github.com/gradido/gradido/pull/1959) +- 1952 change footer icons color an remove save login [`#1955`](https://github.com/gradido/gradido/pull/1955) +- fix: License should be a valid SPDX license expression [`#1954`](https://github.com/gradido/gradido/pull/1954) +- refactor: 🍰 Refactor THX Page – 2. Step [`#1858`](https://github.com/gradido/gradido/pull/1858) +- fix: Add Timezone to Decay Start Block [`#1931`](https://github.com/gradido/gradido/pull/1931) +- devops: Update License in all package.json [`#1925`](https://github.com/gradido/gradido/pull/1925) +- docu: Creation Flowchart [`#1918`](https://github.com/gradido/gradido/pull/1918) +- refactor: Use Logger Categories [`#1912`](https://github.com/gradido/gradido/pull/1912) +- 1883 remove the animated coins in the profile settings [`#1946`](https://github.com/gradido/gradido/pull/1946) +- 1942 replace pictures for carousel [`#1943`](https://github.com/gradido/gradido/pull/1943) +- 1933 auth footer is not on one level [`#1941`](https://github.com/gradido/gradido/pull/1941) +- 1929 styling new template for password component [`#1935`](https://github.com/gradido/gradido/pull/1935) +- 1926 button concept for gradido template [`#1927`](https://github.com/gradido/gradido/pull/1927) +- 1916 remove select language from register form [`#1930`](https://github.com/gradido/gradido/pull/1930) +- rename files from auth folder, rule vue name = name files [`#1937`](https://github.com/gradido/gradido/pull/1937) +- Add files Bild_1_2400.jpg [`#1945`](https://github.com/gradido/gradido/pull/1945) +- Bilder für Slider [`#1940`](https://github.com/gradido/gradido/pull/1940) +- contribution analysis of elopage and concept proposal [`#1917`](https://github.com/gradido/gradido/pull/1917) +- 1676 feature federation technical concept [`#1711`](https://github.com/gradido/gradido/pull/1711) +- more details about Windows installation [`#1842`](https://github.com/gradido/gradido/pull/1842) +- Concept to Introduce Gradido ID [`#1797`](https://github.com/gradido/gradido/pull/1797) +- first draft of concept event protocol [`#1796`](https://github.com/gradido/gradido/pull/1796) +- 1682 new design for the login and registration area [`#1693`](https://github.com/gradido/gradido/pull/1693) +- fix: Database Connection Charset to utf8mb4_unicode_ci [`#1915`](https://github.com/gradido/gradido/pull/1915) +- refactor: 🍰 Create Filter Object in GQL And Rename Args [`#1860`](https://github.com/gradido/gradido/pull/1860) +- feat: 🍰 Improve Apollo Logging [`#1859`](https://github.com/gradido/gradido/pull/1859) +- Add files via upload [`#1903`](https://github.com/gradido/gradido/pull/1903) +- 🍰 Hide Pagenation On Short Transactionlist [`#1875`](https://github.com/gradido/gradido/pull/1875) +- 🍰 Ignore macOS .DS_Store Files [`#1902`](https://github.com/gradido/gradido/pull/1902) +- pre I from #1682, add images, svg for new styling [`#1900`](https://github.com/gradido/gradido/pull/1900) +- add browserstack logo image [`#1888`](https://github.com/gradido/gradido/pull/1888) + #### [1.8.3](https://github.com/gradido/gradido/compare/1.8.2...1.8.3) +> 13 May 2022 + +- Release 1.8.3 [`#1899`](https://github.com/gradido/gradido/pull/1899) - Checkbox [`#1894`](https://github.com/gradido/gradido/pull/1894) - fix: Count Deprecated Links as Well [`#1892`](https://github.com/gradido/gradido/pull/1892) diff --git a/admin/package.json b/admin/package.json index c649ca752..e36308fd9 100644 --- a/admin/package.json +++ b/admin/package.json @@ -3,7 +3,7 @@ "description": "Administraion Interface for Gradido", "main": "index.js", "author": "Moriz Wahl", - "version": "1.8.3", + "version": "1.9.0", "license": "Apache-2.0", "private": false, "scripts": { diff --git a/backend/package.json b/backend/package.json index ff483a0c6..bd5388632 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "gradido-backend", - "version": "1.8.3", + "version": "1.9.0", "description": "Gradido unified backend providing an API-Service for Gradido Transactions", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/backend", diff --git a/database/package.json b/database/package.json index 7a960994c..50e3bdd78 100644 --- a/database/package.json +++ b/database/package.json @@ -1,6 +1,6 @@ { "name": "gradido-database", - "version": "1.8.3", + "version": "1.9.0", "description": "Gradido Database Tool to execute database migrations", "main": "src/index.ts", "repository": "https://github.com/gradido/gradido/database", diff --git a/frontend/package.json b/frontend/package.json index ae5dca33c..e59ec8140 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "bootstrap-vue-gradido-wallet", - "version": "1.8.3", + "version": "1.9.0", "private": true, "scripts": { "start": "node run/server.js", diff --git a/package.json b/package.json index b607c476d..3d5bc51b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "gradido", - "version": "1.8.3", + "version": "1.9.0", "description": "Gradido", "main": "index.js", "repository": "git@github.com:gradido/gradido.git", From 1e0e6e24b97c7929b52d40496b13d2458aa79a3e Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 2 Jun 2022 21:46:18 +0200 Subject: [PATCH 17/69] locales link german, english navbar --- frontend/src/components/Auth/AuthNavbar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Auth/AuthNavbar.vue b/frontend/src/components/Auth/AuthNavbar.vue index 7b3eed0e3..8063dd330 100644 --- a/frontend/src/components/Auth/AuthNavbar.vue +++ b/frontend/src/components/Auth/AuthNavbar.vue @@ -20,7 +20,7 @@ - + {{ $t('auth.navbar.aboutGradido') }} {{ $t('signup') }} From 74bcdaf8c4f11e3f2746c8d2f33e0b1bbd1d0a00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Fri, 3 Jun 2022 00:06:14 +0200 Subject: [PATCH 18/69] add .project and correct .gitignore --- .gitignore | 2 -- .project | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .project diff --git a/.gitignore b/.gitignore index 08ccd2b30..6ac3a32c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,3 @@ -.dbeaver -.project *.log /node_modules/* messages.pot diff --git a/.project b/.project new file mode 100644 index 000000000..5d9663f32 --- /dev/null +++ b/.project @@ -0,0 +1,18 @@ + + + Gradido + + + + + + org.eclipse.wst.validation.validationbuilder + + + + + + org.jkiss.dbeaver.DBeaverNature + org.eclipse.wst.jsdt.core.jsNature + + From d958cfdfde0d045e1d8e92b1753c8e70455bcfd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 7 Jun 2022 10:33:46 +0200 Subject: [PATCH 19/69] Fix DB_VERSION --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 2015eb6cc..1adc68981 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0037-drop_server_user_table.ts', + DB_VERSION: '0037-drop_server_user_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From 6abb399ab9398e441b196d97de499701ee41e674 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 7 Jun 2022 11:33:47 +0200 Subject: [PATCH 20/69] Removed UserSetting from backend --- .../repository/UserSettingRepository.ts | 25 ------- backend/src/util/communityUser.ts | 1 - database/entity/0002-add_settings/User.ts | 2 +- .../entity/0002-add_settings/UserSetting.ts | 2 +- .../entity/0017-combine_user_tables/User.ts | 2 +- .../User.ts | 2 +- .../User.ts | 2 +- .../0020-rename_and_clean_state_users/User.ts | 2 +- .../0023-users_disabled_soft_delete/User.ts | 2 +- database/entity/0033-add_referrer_id/User.ts | 2 +- .../0034-drop_server_user_table/User.ts | 2 +- .../0037-drop_server_user_table/User.ts | 70 +++++++++++++++++++ database/entity/User.ts | 2 +- database/entity/UserSetting.ts | 1 - database/entity/index.ts | 2 - 15 files changed, 80 insertions(+), 39 deletions(-) delete mode 100644 backend/src/typeorm/repository/UserSettingRepository.ts create mode 100644 database/entity/0037-drop_server_user_table/User.ts delete mode 100644 database/entity/UserSetting.ts diff --git a/backend/src/typeorm/repository/UserSettingRepository.ts b/backend/src/typeorm/repository/UserSettingRepository.ts deleted file mode 100644 index f911cfd1a..000000000 --- a/backend/src/typeorm/repository/UserSettingRepository.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { EntityRepository, Repository } from '@dbTools/typeorm' -import { UserSetting } from '@entity/UserSetting' -import { isStringBoolean } from '@/util/validate' - -@EntityRepository(UserSetting) -export class UserSettingRepository extends Repository { - async setOrUpdate(userId: number, value: string): Promise { - let entity = await this.findOne({ userId: userId }) - - if (!entity) { - entity = new UserSetting() - entity.userId = userId - } - entity.value = value - return this.save(entity) - } - - async readBoolean(userId: number): Promise { - const entity = await this.findOne({ userId: userId }) - if (!entity || !isStringBoolean(entity.value)) { - return true - } - return entity.value.toLowerCase() === 'true' - } -} diff --git a/backend/src/util/communityUser.ts b/backend/src/util/communityUser.ts index 0d0d12f6c..1a84c2cdf 100644 --- a/backend/src/util/communityUser.ts +++ b/backend/src/util/communityUser.ts @@ -20,7 +20,6 @@ const communityDbUser: dbUser = { isAdmin: null, publisherId: 0, passphrase: '', - settings: [], hasId: function (): boolean { throw new Error('Function not implemented.') }, diff --git a/database/entity/0002-add_settings/User.ts b/database/entity/0002-add_settings/User.ts index a756cbbd5..4f333fd50 100644 --- a/database/entity/0002-add_settings/User.ts +++ b/database/entity/0002-add_settings/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' // Moriz: I do not like the idea of having two user tables @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) diff --git a/database/entity/0002-add_settings/UserSetting.ts b/database/entity/0002-add_settings/UserSetting.ts index 006d63e3e..6103f67b5 100644 --- a/database/entity/0002-add_settings/UserSetting.ts +++ b/database/entity/0002-add_settings/UserSetting.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm' -import { User } from '../User' +import { User } from '../0034-drop_server_user_table/User' // Wolle: last fitting one or old was correct? @Entity() export class UserSetting extends BaseEntity { diff --git a/database/entity/0017-combine_user_tables/User.ts b/database/entity/0017-combine_user_tables/User.ts index a9bf29d24..7281c4773 100644 --- a/database/entity/0017-combine_user_tables/User.ts +++ b/database/entity/0017-combine_user_tables/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0018-combine_login_user_backups_and_user_table/User.ts b/database/entity/0018-combine_login_user_backups_and_user_table/User.ts index 2ae351e47..347f51a8d 100644 --- a/database/entity/0018-combine_login_user_backups_and_user_table/User.ts +++ b/database/entity/0018-combine_login_user_backups_and_user_table/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts b/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts index b469a55a7..8c00ec5c3 100644 --- a/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts +++ b/database/entity/0019-replace_login_user_id_with_state_user_id/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('state_users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0020-rename_and_clean_state_users/User.ts b/database/entity/0020-rename_and_clean_state_users/User.ts index 3d3b4d7b1..5501bccba 100644 --- a/database/entity/0020-rename_and_clean_state_users/User.ts +++ b/database/entity/0020-rename_and_clean_state_users/User.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0023-users_disabled_soft_delete/User.ts b/database/entity/0023-users_disabled_soft_delete/User.ts index 95fe1b3f7..950e74aeb 100644 --- a/database/entity/0023-users_disabled_soft_delete/User.ts +++ b/database/entity/0023-users_disabled_soft_delete/User.ts @@ -6,7 +6,7 @@ import { OneToMany, DeleteDateColumn, } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0033-add_referrer_id/User.ts b/database/entity/0033-add_referrer_id/User.ts index 48c804b63..353c6a830 100644 --- a/database/entity/0033-add_referrer_id/User.ts +++ b/database/entity/0033-add_referrer_id/User.ts @@ -6,7 +6,7 @@ import { OneToMany, DeleteDateColumn, } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0034-drop_server_user_table/User.ts b/database/entity/0034-drop_server_user_table/User.ts index 1f56d13d2..82fd72009 100644 --- a/database/entity/0034-drop_server_user_table/User.ts +++ b/database/entity/0034-drop_server_user_table/User.ts @@ -6,7 +6,7 @@ import { OneToMany, DeleteDateColumn, } from 'typeorm' -import { UserSetting } from '../UserSetting' +import { UserSetting } from '../0002-add_settings/UserSetting' @Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) export class User extends BaseEntity { diff --git a/database/entity/0037-drop_server_user_table/User.ts b/database/entity/0037-drop_server_user_table/User.ts new file mode 100644 index 000000000..528cef32b --- /dev/null +++ b/database/entity/0037-drop_server_user_table/User.ts @@ -0,0 +1,70 @@ +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' + +@Entity('users', { engine: 'InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci' }) +export class User extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'public_key', type: 'binary', length: 32, default: null, nullable: true }) + pubKey: Buffer + + @Column({ name: 'privkey', type: 'binary', length: 80, default: null, nullable: true }) + privKey: Buffer + + @Column({ length: 255, unique: true, nullable: false, collation: 'utf8mb4_unicode_ci' }) + email: string + + @Column({ + name: 'first_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + firstName: string + + @Column({ + name: 'last_name', + length: 255, + nullable: true, + default: null, + collation: 'utf8mb4_unicode_ci', + }) + lastName: string + + @DeleteDateColumn() + deletedAt: Date | null + + @Column({ type: 'bigint', default: 0, unsigned: true }) + password: BigInt + + @Column({ name: 'email_hash', type: 'binary', length: 32, default: null, nullable: true }) + emailHash: Buffer + + @Column({ name: 'created', default: () => 'CURRENT_TIMESTAMP', nullable: false }) + createdAt: Date + + @Column({ name: 'email_checked', type: 'bool', nullable: false, default: false }) + emailChecked: boolean + + @Column({ length: 4, default: 'de', collation: 'utf8mb4_unicode_ci', nullable: false }) + language: string + + @Column({ name: 'is_admin', type: 'datetime', nullable: true, default: null }) + isAdmin: Date | null + + @Column({ name: 'referrer_id', type: 'int', unsigned: true, nullable: true, default: null }) + referrerId?: number | null + + @Column({ name: 'publisher_id', default: 0 }) + publisherId: number + + @Column({ + type: 'text', + name: 'passphrase', + collation: 'utf8mb4_unicode_ci', + nullable: true, + default: null, + }) + passphrase: string +} diff --git a/database/entity/User.ts b/database/entity/User.ts index 4cd68174c..6983f293b 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0034-drop_server_user_table/User' +export { User } from './0037-drop_server_user_table/User' diff --git a/database/entity/UserSetting.ts b/database/entity/UserSetting.ts deleted file mode 100644 index 38da380f9..000000000 --- a/database/entity/UserSetting.ts +++ /dev/null @@ -1 +0,0 @@ -export { UserSetting } from './0002-add_settings/UserSetting' diff --git a/database/entity/index.ts b/database/entity/index.ts index 542333755..46d9ef31a 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -4,7 +4,6 @@ import { Migration } from './Migration' import { Transaction } from './Transaction' import { TransactionLink } from './TransactionLink' import { User } from './User' -import { UserSetting } from './UserSetting' import { AdminPendingCreation } from './AdminPendingCreation' export const entities = [ @@ -15,5 +14,4 @@ export const entities = [ Transaction, TransactionLink, User, - UserSetting, ] From 338c1591cb1cd5954e5665ac45d25fd8384dbb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Tue, 7 Jun 2022 11:45:07 +0200 Subject: [PATCH 21/69] Cleanup --- database/entity/0002-add_settings/UserSetting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/entity/0002-add_settings/UserSetting.ts b/database/entity/0002-add_settings/UserSetting.ts index 6103f67b5..9da036954 100644 --- a/database/entity/0002-add_settings/UserSetting.ts +++ b/database/entity/0002-add_settings/UserSetting.ts @@ -1,5 +1,5 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm' -import { User } from '../0034-drop_server_user_table/User' // Wolle: last fitting one or old was correct? +import { User } from '../0034-drop_server_user_table/User' @Entity() export class UserSetting extends BaseEntity { From bb35332e2ed6c0bf66b3cd4ec29b82a679c97db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Tue, 7 Jun 2022 22:13:27 +0200 Subject: [PATCH 22/69] change Cycle Type in tabel ContributionLinks --- .dbeaver/.credentials-config.json.bak | 2 ++ .dbeaver/.data-sources.json.bak | 33 +++++++++++++++++++ .dbeaver/credentials-config.json | 3 ++ .dbeaver/data-sources.json | 32 ++++++++++++++++++ .../ContributionLinks.ts | 26 ++------------- .../0037-add_contribution_links_table.ts | 2 +- 6 files changed, 73 insertions(+), 25 deletions(-) create mode 100644 .dbeaver/.credentials-config.json.bak create mode 100644 .dbeaver/.data-sources.json.bak create mode 100644 .dbeaver/credentials-config.json create mode 100644 .dbeaver/data-sources.json diff --git a/.dbeaver/.credentials-config.json.bak b/.dbeaver/.credentials-config.json.bak new file mode 100644 index 000000000..2eca1988d --- /dev/null +++ b/.dbeaver/.credentials-config.json.bak @@ -0,0 +1,2 @@ +'U[V$8%kQN, +`W-T3NvU{ɆHK~,DXpX.g/:?B9|GZn>/ \ No newline at end of file diff --git a/.dbeaver/.data-sources.json.bak b/.dbeaver/.data-sources.json.bak new file mode 100644 index 000000000..7d9528f01 --- /dev/null +++ b/.dbeaver/.data-sources.json.bak @@ -0,0 +1,33 @@ +{ + "folders": {}, + "connections": { + "mariaDB-1813fbbc7bc-107c0b3aeaeb91ab": { + "provider": "mysql", + "driver": "mariaDB", + "name": "gradido", + "save-password": true, + "read-only": false, + "configuration": { + "host": "localhost", + "port": "3306", + "database": "gradido", + "url": "jdbc:mariadb://localhost:3306/gradido", + "home": "mysql_client", + "type": "dev", + "auth-model": "native", + "handlers": {} + } + } + }, + "connection-types": { + "dev": { + "name": "Development", + "color": "255,255,255", + "description": "Regular development database", + "auto-commit": true, + "confirm-execute": false, + "confirm-data-change": false, + "auto-close-transactions": false + } + } +} \ No newline at end of file diff --git a/.dbeaver/credentials-config.json b/.dbeaver/credentials-config.json new file mode 100644 index 000000000..d814bdfcf --- /dev/null +++ b/.dbeaver/credentials-config.json @@ -0,0 +1,3 @@ +4k1,fbAqĬc##s8-1&;"7djM?bljfBq= +my +vV \ No newline at end of file diff --git a/.dbeaver/data-sources.json b/.dbeaver/data-sources.json new file mode 100644 index 000000000..096f0d57e --- /dev/null +++ b/.dbeaver/data-sources.json @@ -0,0 +1,32 @@ +{ + "folders": {}, + "connections": { + "mariaDB-1813fbbc7bc-107c0b3aeaeb91ab": { + "provider": "mysql", + "driver": "mariaDB", + "name": "gradido", + "save-password": true, + "read-only": false, + "configuration": { + "host": "localhost", + "port": "3306", + "url": "jdbc:mariadb://localhost:3306/", + "home": "mysql_client", + "type": "dev", + "auth-model": "native", + "handlers": {} + } + } + }, + "connection-types": { + "dev": { + "name": "Development", + "color": "255,255,255", + "description": "Regular development database", + "auto-commit": true, + "confirm-execute": false, + "confirm-data-change": false, + "auto-close-transactions": false + } + } +} \ No newline at end of file diff --git a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts index 2fb4e2801..0626a5067 100644 --- a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -2,28 +2,6 @@ import Decimal from 'decimal.js-light' import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' -export enum CycleTypes { - ONCE = 1, - HOUR = 2, - TWOHOURS = 3, - FOURHOURS = 4, - EIGHTHOURS = 5, - HALFDAY = 6, - DAY = 7, - TWODAYS = 8, - THREEDAYS = 9, - FOURDAYS = 10, - FIVEDAYS = 11, - SIXDAYS = 12, - WEEK = 13, - TWOWEEKS = 14, - MONTH = 15, - TWOMONTH = 16, - QUARTER = 17, - HALFYEAR = 18, - YEAR = 19, -} - @Entity('contribution_links') export class ContributionLinks extends BaseEntity { @PrimaryGeneratedColumn('increment', { unsigned: true }) @@ -50,8 +28,8 @@ export class ContributionLinks extends BaseEntity { }) amount: Decimal - @Column({ name: 'cycle', unsigned: true, nullable: false }) - cycle: number + @Column({ length: 12, nullable: false, collation: 'utf8mb4_unicode_ci' }) + cycle: string @Column({ name: 'max_per_cycle', unsigned: true, nullable: false, default: 1 }) maxPerCycle: number diff --git a/database/migrations/0037-add_contribution_links_table.ts b/database/migrations/0037-add_contribution_links_table.ts index 79bcdf5ce..42f03184b 100644 --- a/database/migrations/0037-add_contribution_links_table.ts +++ b/database/migrations/0037-add_contribution_links_table.ts @@ -15,7 +15,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`valid_from\` datetime NULL, \`valid_to\` datetime NULL, \`amount\` bigint(20) NOT NULL, - \`cycle\` int(10) unsigned NOT NULL DEFAULT '1', + \`cycle\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE', \`max_per_cycle\` int(10) unsigned NOT NULL DEFAULT '1', \`max_amount_per_month\` bigint(20) NULL DEFAULT NULL, \`total_max_count_of_contribution\` int(10) unsigned NULL DEFAULT NULL, From 698f13078e73472186f15651094674e3fef4ff58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Tue, 7 Jun 2022 22:14:02 +0200 Subject: [PATCH 23/69] define enum for ContributionLinks cycles --- .../src/graphql/enum/ContributionCycleType.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 backend/src/graphql/enum/ContributionCycleType.ts diff --git a/backend/src/graphql/enum/ContributionCycleType.ts b/backend/src/graphql/enum/ContributionCycleType.ts new file mode 100644 index 000000000..05084a669 --- /dev/null +++ b/backend/src/graphql/enum/ContributionCycleType.ts @@ -0,0 +1,29 @@ +import { registerEnumType } from 'type-graphql' + +export enum ContributionCycleType { + NONE = 'none', + ONCE = 'once', + HOUR = 'hour', + TWO_HOURS = 'two_hours', + FOUR_HOURS = 'four_hours', + EIGHT_HOURS = 'eight_hours', + HALF_DAY = 'half_day', + DAY = 'day', + TWO_DAYS = 'two_days', + THREE_DAYS = 'three_days', + FOUR_DAYS = 'four_days', + FIVE_DAYS = 'five_days', + SIX_DAYS = 'six_days', + WEEK = 'week', + TWO_WEEKS = 'two_weeks', + MONTH = 'month', + TWO_MONTH = 'two_month', + QUARTER = 'quarter', + HALF_YEAR = 'half_year', + YEAR = 'year', +} + +registerEnumType(ContributionCycleType, { + name: 'ContributionCycleType', // this one is mandatory + description: 'Name of the Type of the ContributionCycle', // this one is optional +}) From e71725686c03c2e459081ef90ecd90db5205ffb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Tue, 7 Jun 2022 22:21:40 +0200 Subject: [PATCH 24/69] change backend config for db-version 0037 --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 28318ed6b..ec469c183 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0036-unique_previous_in_transactions', + DB_VERSION: '0037-add_contribution_links_table/ContributionLinks', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From a881103c5f70f0e67c394e447436085808309f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Tue, 7 Jun 2022 22:52:08 +0200 Subject: [PATCH 25/69] add softdelete feature to contributionlinks --- .../0037-add_contribution_links_table/ContributionLinks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts index 0626a5067..6539716c6 100644 --- a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -1,5 +1,5 @@ import Decimal from 'decimal.js-light' -import { BaseEntity, Entity, PrimaryGeneratedColumn, Column } from 'typeorm' +import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } from 'typeorm' import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' @Entity('contribution_links') @@ -75,7 +75,7 @@ export class ContributionLinks extends BaseEntity { @Column({ name: 'created_at', type: 'datetime', nullable: true, default: null }) createdAt: Date | null - @Column({ name: 'deleted_at', type: 'datetime', nullable: true, default: null }) + @DeleteDateColumn() deletedAt: Date | null @Column({ length: 24, nullable: true, collation: 'utf8mb4_unicode_ci' }) From 605a6757a35f8600c7df8b311e075ea4a4b750ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Wed, 8 Jun 2022 21:32:42 +0200 Subject: [PATCH 26/69] change default cycle-value to ONCE --- database/migrations/0037-add_contribution_links_table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/0037-add_contribution_links_table.ts b/database/migrations/0037-add_contribution_links_table.ts index 42f03184b..5e55f4ff1 100644 --- a/database/migrations/0037-add_contribution_links_table.ts +++ b/database/migrations/0037-add_contribution_links_table.ts @@ -15,7 +15,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`valid_from\` datetime NULL, \`valid_to\` datetime NULL, \`amount\` bigint(20) NOT NULL, - \`cycle\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'NONE', + \`cycle\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ONCE', \`max_per_cycle\` int(10) unsigned NOT NULL DEFAULT '1', \`max_amount_per_month\` bigint(20) NULL DEFAULT NULL, \`total_max_count_of_contribution\` int(10) unsigned NULL DEFAULT NULL, From fd352602ec3ecbed5dcb15bbe9811a519df669fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Wed, 8 Jun 2022 21:33:26 +0200 Subject: [PATCH 27/69] delete value NONE from enum --- backend/src/graphql/enum/ContributionCycleType.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/graphql/enum/ContributionCycleType.ts b/backend/src/graphql/enum/ContributionCycleType.ts index 05084a669..5fe494a02 100644 --- a/backend/src/graphql/enum/ContributionCycleType.ts +++ b/backend/src/graphql/enum/ContributionCycleType.ts @@ -1,7 +1,6 @@ import { registerEnumType } from 'type-graphql' export enum ContributionCycleType { - NONE = 'none', ONCE = 'once', HOUR = 'hour', TWO_HOURS = 'two_hours', From 114465c23490b985848f359d0f86e4f4935b8cc3 Mon Sep 17 00:00:00 2001 From: clauspeterhuebner <86960882+clauspeterhuebner@users.noreply.github.com> Date: Thu, 9 Jun 2022 22:24:22 +0200 Subject: [PATCH 28/69] Update backend/src/config/index.ts Co-authored-by: Moriz Wahl --- backend/src/config/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index ec469c183..f4999c4a8 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0037-add_contribution_links_table/ContributionLinks', + DB_VERSION: '0037-add_contribution_links_table', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info From b65aa5cefccbf315844c4820fd03f7b869362933 Mon Sep 17 00:00:00 2001 From: clauspeterhuebner <86960882+clauspeterhuebner@users.noreply.github.com> Date: Thu, 9 Jun 2022 22:26:30 +0200 Subject: [PATCH 29/69] Update database/entity/0037-add_contribution_links_table/ContributionLinks.ts Co-authored-by: Moriz Wahl --- .../0037-add_contribution_links_table/ContributionLinks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts index 6539716c6..fde67eef3 100644 --- a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -72,8 +72,8 @@ export class ContributionLinks extends BaseEntity { }) minGapHours: number | null - @Column({ name: 'created_at', type: 'datetime', nullable: true, default: null }) - createdAt: Date | null + @Column({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) + createdAt: Date @DeleteDateColumn() deletedAt: Date | null From 83c66d052a969cab0c1af0a7ecc2823adf8ab8ec Mon Sep 17 00:00:00 2001 From: clauspeterhuebner <86960882+clauspeterhuebner@users.noreply.github.com> Date: Thu, 9 Jun 2022 22:27:54 +0200 Subject: [PATCH 30/69] Update database/entity/0037-add_contribution_links_table/ContributionLinks.ts Co-authored-by: Moriz Wahl --- .../0037-add_contribution_links_table/ContributionLinks.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts index fde67eef3..16500b06b 100644 --- a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -81,6 +81,6 @@ export class ContributionLinks extends BaseEntity { @Column({ length: 24, nullable: true, collation: 'utf8mb4_unicode_ci' }) code: string - @Column({ name: 'link_enabled', type: 'boolean', nullable: true, default: null }) - linkEnabled: boolean | null + @Column({ name: 'link_enabled', type: 'boolean', default: true }) + linkEnabled: boolean } From 3328c200d8d067d2e727495e6a727f65a6c791ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 9 Jun 2022 22:28:56 +0200 Subject: [PATCH 31/69] rework PR comments --- .gitignore | 1 + database/migrations/0037-add_contribution_links_table.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6ac3a32c2..ced6746aa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.log +*.bak /node_modules/* messages.pot nbproject diff --git a/database/migrations/0037-add_contribution_links_table.ts b/database/migrations/0037-add_contribution_links_table.ts index 5e55f4ff1..4d3527ef5 100644 --- a/database/migrations/0037-add_contribution_links_table.ts +++ b/database/migrations/0037-add_contribution_links_table.ts @@ -24,7 +24,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, \`deleted_at\` datetime NULL DEFAULT NULL, \`code\` varchar(24) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, - \`link_enabled\` tinyint(4) NOT NULL DEFAULT '0', + \`link_enabled\` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (\`id\`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`) } From e76817ebdbde036eaad068743d1edff60d704819 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 9 Jun 2022 22:55:52 +0200 Subject: [PATCH 32/69] column validFrom NOT NULL --- database/migrations/0037-add_contribution_links_table.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/migrations/0037-add_contribution_links_table.ts b/database/migrations/0037-add_contribution_links_table.ts index 4d3527ef5..e05bfd5e7 100644 --- a/database/migrations/0037-add_contribution_links_table.ts +++ b/database/migrations/0037-add_contribution_links_table.ts @@ -12,7 +12,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`id\` int(10) unsigned NOT NULL AUTO_INCREMENT, \`name\` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, \`decsription\` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, - \`valid_from\` datetime NULL, + \`valid_from\` datetime NOT NULL, \`valid_to\` datetime NULL, \`amount\` bigint(20) NOT NULL, \`cycle\` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ONCE', From 94a48173e0f023b5b1f371678572ef293e5ae52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 9 Jun 2022 23:20:05 +0200 Subject: [PATCH 33/69] remove *.bak files from repo Merge remote-tracking branch 'origin/1920-feature-create-contribution-link-table' into 1920-feature-create-contribution-link-table --- .dbeaver/.credentials-config.json.bak | 2 -- .dbeaver/.data-sources.json.bak | 33 --------------------------- 2 files changed, 35 deletions(-) delete mode 100644 .dbeaver/.credentials-config.json.bak delete mode 100644 .dbeaver/.data-sources.json.bak diff --git a/.dbeaver/.credentials-config.json.bak b/.dbeaver/.credentials-config.json.bak deleted file mode 100644 index 2eca1988d..000000000 --- a/.dbeaver/.credentials-config.json.bak +++ /dev/null @@ -1,2 +0,0 @@ -'U[V$8%kQN, -`W-T3NvU{ɆHK~,DXpX.g/:?B9|GZn>/ \ No newline at end of file diff --git a/.dbeaver/.data-sources.json.bak b/.dbeaver/.data-sources.json.bak deleted file mode 100644 index 7d9528f01..000000000 --- a/.dbeaver/.data-sources.json.bak +++ /dev/null @@ -1,33 +0,0 @@ -{ - "folders": {}, - "connections": { - "mariaDB-1813fbbc7bc-107c0b3aeaeb91ab": { - "provider": "mysql", - "driver": "mariaDB", - "name": "gradido", - "save-password": true, - "read-only": false, - "configuration": { - "host": "localhost", - "port": "3306", - "database": "gradido", - "url": "jdbc:mariadb://localhost:3306/gradido", - "home": "mysql_client", - "type": "dev", - "auth-model": "native", - "handlers": {} - } - } - }, - "connection-types": { - "dev": { - "name": "Development", - "color": "255,255,255", - "description": "Regular development database", - "auto-commit": true, - "confirm-execute": false, - "confirm-data-change": false, - "auto-close-transactions": false - } - } -} \ No newline at end of file From d7ff6446d994735e1468b18979943099fd4ecc00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 9 Jun 2022 23:52:40 +0200 Subject: [PATCH 34/69] evaluate nullable columns vs entity --- .../ContributionLinks.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts index 16500b06b..7b391606f 100644 --- a/database/entity/0037-add_contribution_links_table/ContributionLinks.ts +++ b/database/entity/0037-add_contribution_links_table/ContributionLinks.ts @@ -11,10 +11,10 @@ export class ContributionLinks extends BaseEntity { name: string @Column({ length: 255, nullable: false, collation: 'utf8mb4_unicode_ci' }) - description: string + memo: string - @Column({ name: 'valid_from', type: 'datetime', nullable: true, default: null }) - validFrom: Date | null + @Column({ name: 'valid_from', type: 'datetime', nullable: false }) + validFrom: Date @Column({ name: 'valid_to', type: 'datetime', nullable: true, default: null }) validTo: Date | null @@ -43,7 +43,7 @@ export class ContributionLinks extends BaseEntity { default: null, transformer: DecimalTransformer, }) - maxAmountPerMonth: Decimal + maxAmountPerMonth: Decimal | null @Column({ name: 'total_max_count_of_contribution', @@ -62,7 +62,7 @@ export class ContributionLinks extends BaseEntity { default: null, transformer: DecimalTransformer, }) - maxAccountBalance: Decimal + maxAccountBalance: Decimal | null @Column({ name: 'min_gap_hours', @@ -79,7 +79,7 @@ export class ContributionLinks extends BaseEntity { deletedAt: Date | null @Column({ length: 24, nullable: true, collation: 'utf8mb4_unicode_ci' }) - code: string + code: string | null @Column({ name: 'link_enabled', type: 'boolean', default: true }) linkEnabled: boolean From 8a0147c8117f38d23f3c02b29ff0bf81858370b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Claus-Peter=20H=C3=BCbner?= Date: Thu, 9 Jun 2022 23:54:06 +0200 Subject: [PATCH 35/69] shift enum in ticket #1921 --- .../src/graphql/enum/ContributionCycleType.ts | 28 ------------------- 1 file changed, 28 deletions(-) delete mode 100644 backend/src/graphql/enum/ContributionCycleType.ts diff --git a/backend/src/graphql/enum/ContributionCycleType.ts b/backend/src/graphql/enum/ContributionCycleType.ts deleted file mode 100644 index 5fe494a02..000000000 --- a/backend/src/graphql/enum/ContributionCycleType.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { registerEnumType } from 'type-graphql' - -export enum ContributionCycleType { - ONCE = 'once', - HOUR = 'hour', - TWO_HOURS = 'two_hours', - FOUR_HOURS = 'four_hours', - EIGHT_HOURS = 'eight_hours', - HALF_DAY = 'half_day', - DAY = 'day', - TWO_DAYS = 'two_days', - THREE_DAYS = 'three_days', - FOUR_DAYS = 'four_days', - FIVE_DAYS = 'five_days', - SIX_DAYS = 'six_days', - WEEK = 'week', - TWO_WEEKS = 'two_weeks', - MONTH = 'month', - TWO_MONTH = 'two_month', - QUARTER = 'quarter', - HALF_YEAR = 'half_year', - YEAR = 'year', -} - -registerEnumType(ContributionCycleType, { - name: 'ContributionCycleType', // this one is mandatory - description: 'Name of the Type of the ContributionCycle', // this one is optional -}) From 5121910c836dfe975a93487d1eaf181a43401245 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Jun 2022 12:03:15 +0200 Subject: [PATCH 36/69] rename ContributionLinks to ContributionLink, add CL entity to index --- .../0038-add_contribution_links_table/ContributionLink.ts | 2 +- database/entity/ContributionLink.ts | 2 +- database/entity/ContributionLinks.ts | 1 + database/entity/index.ts | 2 ++ 4 files changed, 5 insertions(+), 2 deletions(-) create mode 100644 database/entity/ContributionLinks.ts diff --git a/database/entity/0038-add_contribution_links_table/ContributionLink.ts b/database/entity/0038-add_contribution_links_table/ContributionLink.ts index 7b391606f..490ca3868 100644 --- a/database/entity/0038-add_contribution_links_table/ContributionLink.ts +++ b/database/entity/0038-add_contribution_links_table/ContributionLink.ts @@ -3,7 +3,7 @@ import { BaseEntity, Entity, PrimaryGeneratedColumn, Column, DeleteDateColumn } import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer' @Entity('contribution_links') -export class ContributionLinks extends BaseEntity { +export class ContributionLink extends BaseEntity { @PrimaryGeneratedColumn('increment', { unsigned: true }) id: number diff --git a/database/entity/ContributionLink.ts b/database/entity/ContributionLink.ts index de26c1c4a..721dfd2b3 100644 --- a/database/entity/ContributionLink.ts +++ b/database/entity/ContributionLink.ts @@ -1 +1 @@ -export { ContributionLinks } from './0037-add_contribution_links_table/ContributionLinks' +export { ContributionLinks } from './0038-add_contribution_links_table/ContributionLinks' diff --git a/database/entity/ContributionLinks.ts b/database/entity/ContributionLinks.ts new file mode 100644 index 000000000..c7e4e2b7e --- /dev/null +++ b/database/entity/ContributionLinks.ts @@ -0,0 +1 @@ +export { ContributionLink } from './0038-add_contribution_links_table/ContributionLink' diff --git a/database/entity/index.ts b/database/entity/index.ts index 46d9ef31a..991e482e9 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -1,3 +1,4 @@ +import { ContributionLink } from './ContributionLink' import { LoginElopageBuys } from './LoginElopageBuys' import { LoginEmailOptIn } from './LoginEmailOptIn' import { Migration } from './Migration' @@ -8,6 +9,7 @@ import { AdminPendingCreation } from './AdminPendingCreation' export const entities = [ AdminPendingCreation, + ContributionLink, LoginElopageBuys, LoginEmailOptIn, Migration, From 83c8f9731e87749de795968905b45287f3bb2981 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Jun 2022 12:05:13 +0200 Subject: [PATCH 37/69] remove duplicate file --- database/entity/ContributionLink.ts | 2 +- database/entity/ContributionLinks.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) delete mode 100644 database/entity/ContributionLinks.ts diff --git a/database/entity/ContributionLink.ts b/database/entity/ContributionLink.ts index 721dfd2b3..c7e4e2b7e 100644 --- a/database/entity/ContributionLink.ts +++ b/database/entity/ContributionLink.ts @@ -1 +1 @@ -export { ContributionLinks } from './0038-add_contribution_links_table/ContributionLinks' +export { ContributionLink } from './0038-add_contribution_links_table/ContributionLink' diff --git a/database/entity/ContributionLinks.ts b/database/entity/ContributionLinks.ts deleted file mode 100644 index c7e4e2b7e..000000000 --- a/database/entity/ContributionLinks.ts +++ /dev/null @@ -1 +0,0 @@ -export { ContributionLink } from './0038-add_contribution_links_table/ContributionLink' From 795d3393981ccead3ab0b4645f0b6266e5e4dbee Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Jun 2022 12:23:57 +0200 Subject: [PATCH 38/69] add type definitions, make code required (not null) --- .../0038-add_contribution_links_table/ContributionLink.ts | 8 +++++--- database/migrations/0038-add_contribution_links_table.ts | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/database/entity/0038-add_contribution_links_table/ContributionLink.ts b/database/entity/0038-add_contribution_links_table/ContributionLink.ts index 490ca3868..496f57d71 100644 --- a/database/entity/0038-add_contribution_links_table/ContributionLink.ts +++ b/database/entity/0038-add_contribution_links_table/ContributionLink.ts @@ -47,6 +47,7 @@ export class ContributionLink extends BaseEntity { @Column({ name: 'total_max_count_of_contribution', + type: 'int', unsigned: true, nullable: true, default: null, @@ -66,6 +67,7 @@ export class ContributionLink extends BaseEntity { @Column({ name: 'min_gap_hours', + type: 'int', unsigned: true, nullable: true, default: null, @@ -75,11 +77,11 @@ export class ContributionLink extends BaseEntity { @Column({ name: 'created_at', type: 'datetime', default: () => 'CURRENT_TIMESTAMP' }) createdAt: Date - @DeleteDateColumn() + @DeleteDateColumn({ name: 'deleted_at' }) deletedAt: Date | null - @Column({ length: 24, nullable: true, collation: 'utf8mb4_unicode_ci' }) - code: string | null + @Column({ length: 24, nullable: false, collation: 'utf8mb4_unicode_ci' }) + code: string @Column({ name: 'link_enabled', type: 'boolean', default: true }) linkEnabled: boolean diff --git a/database/migrations/0038-add_contribution_links_table.ts b/database/migrations/0038-add_contribution_links_table.ts index 3ac6821d6..f983644cc 100644 --- a/database/migrations/0038-add_contribution_links_table.ts +++ b/database/migrations/0038-add_contribution_links_table.ts @@ -23,7 +23,7 @@ export async function upgrade(queryFn: (query: string, values?: any[]) => Promis \`min_gap_hours\` int(10) unsigned NULL DEFAULT NULL, \`created_at\` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, \`deleted_at\` datetime NULL DEFAULT NULL, - \`code\` varchar(24) COLLATE utf8mb4_unicode_ci NULL DEFAULT NULL, + \`code\` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL, \`link_enabled\` tinyint(4) NOT NULL DEFAULT '1', PRIMARY KEY (\`id\`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;`) From a84f11880f3108d5cb2496a44b5576d78e51cdee Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Jun 2022 12:31:40 +0200 Subject: [PATCH 39/69] rename migration 0037 to what it acutally does --- .../User.ts | 0 database/entity/User.ts | 2 +- ...rop_server_user_table.ts => 0037-drop_user_setting_table.ts} | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename database/entity/{0037-drop_server_user_table => 0037-drop_user_setting_table}/User.ts (100%) rename database/migrations/{0037-drop_server_user_table.ts => 0037-drop_user_setting_table.ts} (100%) diff --git a/database/entity/0037-drop_server_user_table/User.ts b/database/entity/0037-drop_user_setting_table/User.ts similarity index 100% rename from database/entity/0037-drop_server_user_table/User.ts rename to database/entity/0037-drop_user_setting_table/User.ts diff --git a/database/entity/User.ts b/database/entity/User.ts index 6983f293b..2d434799e 100644 --- a/database/entity/User.ts +++ b/database/entity/User.ts @@ -1 +1 @@ -export { User } from './0037-drop_server_user_table/User' +export { User } from './0037-drop_user_setting_table/User' diff --git a/database/migrations/0037-drop_server_user_table.ts b/database/migrations/0037-drop_user_setting_table.ts similarity index 100% rename from database/migrations/0037-drop_server_user_table.ts rename to database/migrations/0037-drop_user_setting_table.ts From 5456e6dae4e3eaf33aa6290b46419588bd3fb90c Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Mon, 13 Jun 2022 13:19:03 +0200 Subject: [PATCH 40/69] add ContributionLink graphQL model --- backend/src/graphql/model/ContributionLink.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 backend/src/graphql/model/ContributionLink.ts diff --git a/backend/src/graphql/model/ContributionLink.ts b/backend/src/graphql/model/ContributionLink.ts new file mode 100644 index 000000000..b60df37e2 --- /dev/null +++ b/backend/src/graphql/model/ContributionLink.ts @@ -0,0 +1,56 @@ +import { ObjectType, Field, Int } from 'type-graphql' +import Decimal from 'decimal.js-light' +import { ContributionLink as dbContributionLink } from '@entity/ContributionLink' + +@ObjectType() +export class ContributionLink { + constructor(contributionLink: dbContributionLink) { + this.id = contributionLink.id + this.amount = contributionLink.amount + this.name = contributionLink.name + this.memo = contributionLink.memo + this.createdAt = contributionLink.createdAt + this.deletedAt = contributionLink.deletedAt + this.validFrom = contributionLink.validFrom + this.validTo = contributionLink.validTo + this.maxAmountPerMonth = contributionLink.maxAmountPerMonth + this.cycle = contributionLink.cycle + this.maxPerCycle = contributionLink.maxPerCycle + } + + @Field(() => Number) + id: number + + @Field(() => Decimal) + amount: Decimal + + @Field(() => String) + name: string + + @Field(() => String) + memo: string + + @Field(() => String) + code: string + + @Field(() => Date) + createdAt: Date + + @Field(() => Date, { nullable: true }) + deletedAt: Date | null + + @Field(() => Date, { nullable: true }) + validFrom: Date | null + + @Field(() => Date, { nullable: true }) + validTo: Date | null + + @Field(() => Decimal) + maxAmountPerMonth: Decimal | null + + @Field(() => string) + cycle: string + + @Field(() => Int) + maxPerCycle: number +} From bd593e96ae3d4acb6ff3285d40b91e0ad607d373 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 09:23:51 +0200 Subject: [PATCH 41/69] roles and args for contribution links --- backend/src/auth/RIGHTS.ts | 1 + .../src/graphql/arg/ContributionLinkArgs.ts | 29 +++++++++++++++++++ backend/src/graphql/model/ContributionLink.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 11 +++++++ 4 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 backend/src/graphql/arg/ContributionLinkArgs.ts diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index 8188b3daa..dcd0e44d6 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -37,4 +37,5 @@ export enum RIGHTS { UNDELETE_USER = 'UNDELETE_USER', CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST', LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN', + CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', } diff --git a/backend/src/graphql/arg/ContributionLinkArgs.ts b/backend/src/graphql/arg/ContributionLinkArgs.ts new file mode 100644 index 000000000..f3d3f91df --- /dev/null +++ b/backend/src/graphql/arg/ContributionLinkArgs.ts @@ -0,0 +1,29 @@ +import { ArgsType, Field, Int } from 'type-graphql' +import Decimal from 'decimal.js-light' + +@ArgsType() +export default class ContributionLinkArgs { + @Field(() => Decimal) + amount: Decimal + + @Field(() => String) + name: string + + @Field(() => String) + memo: string + + @Field(() => String) + cycle: string + + @Field(() => Date, { nullable: true }) + validFrom?: Date | null + + @Field(() => Date, { nullable: true }) + validTo?: Date | null + + @Field(() => Decimal, { nullable: true }) + maxAmountPerMonth: Decimal | null + + @Field(() => Int, { default: 1 }) + maxPerCycle: number +} diff --git a/backend/src/graphql/model/ContributionLink.ts b/backend/src/graphql/model/ContributionLink.ts index b60df37e2..df28bb0e5 100644 --- a/backend/src/graphql/model/ContributionLink.ts +++ b/backend/src/graphql/model/ContributionLink.ts @@ -45,7 +45,7 @@ export class ContributionLink { @Field(() => Date, { nullable: true }) validTo: Date | null - @Field(() => Decimal) + @Field(() => Decimal, { nullable: true }) maxAmountPerMonth: Decimal | null @Field(() => string) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 4c94e48c8..d9945a617 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -14,12 +14,15 @@ import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' import { PendingCreation } from '@model/PendingCreation' import { CreatePendingCreations } from '@model/CreatePendingCreations' import { UpdatePendingCreation } from '@model/UpdatePendingCreation' +import { ContributionLink } from '@model/ContributionLink' import { RIGHTS } from '@/auth/RIGHTS' import { UserRepository } from '@repository/User' import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs' import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs' import SearchUsersArgs from '@arg/SearchUsersArgs' +import ContributionLinkArgs from '@arg/ContributionLinkArgs' import { Transaction as DbTransaction } from '@entity/Transaction' +import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { Transaction } from '@model/Transaction' import { TransactionLink, TransactionLinkResult } from '@model/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' @@ -460,6 +463,14 @@ export class AdminResolver { linkList: transactionLinks.map((tl) => new TransactionLink(tl, new User(user))), } } + + @Authorized([RIGHTS.CREATE_CONTRIBUTION_LINK]) + @Mutation(() => ContributionLink) + async createContributionLink( + @Args() + { amount, name, memo, cycle, validFrom, validTo, maxAmountPerMonth, maxPerCycle }: ContributionLinkArgs, + ) + } interface CreationMap { From 1e0b246252b426db52d4ff19c7b0a7e5ee9b26d1 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 09:44:37 +0200 Subject: [PATCH 42/69] simple createContributionLink mutation --- .../src/graphql/arg/ContributionLinkArgs.ts | 2 +- backend/src/graphql/model/ContributionLink.ts | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 27 ++++++++++++++++--- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/backend/src/graphql/arg/ContributionLinkArgs.ts b/backend/src/graphql/arg/ContributionLinkArgs.ts index f3d3f91df..809ce89c4 100644 --- a/backend/src/graphql/arg/ContributionLinkArgs.ts +++ b/backend/src/graphql/arg/ContributionLinkArgs.ts @@ -24,6 +24,6 @@ export default class ContributionLinkArgs { @Field(() => Decimal, { nullable: true }) maxAmountPerMonth: Decimal | null - @Field(() => Int, { default: 1 }) + @Field(() => Int) maxPerCycle: number } diff --git a/backend/src/graphql/model/ContributionLink.ts b/backend/src/graphql/model/ContributionLink.ts index df28bb0e5..9fb60e4aa 100644 --- a/backend/src/graphql/model/ContributionLink.ts +++ b/backend/src/graphql/model/ContributionLink.ts @@ -48,7 +48,7 @@ export class ContributionLink { @Field(() => Decimal, { nullable: true }) maxAmountPerMonth: Decimal | null - @Field(() => string) + @Field(() => String) cycle: string @Field(() => Int) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index d9945a617..d99bf6c6f 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -468,9 +468,30 @@ export class AdminResolver { @Mutation(() => ContributionLink) async createContributionLink( @Args() - { amount, name, memo, cycle, validFrom, validTo, maxAmountPerMonth, maxPerCycle }: ContributionLinkArgs, - ) - + { + amount, + name, + memo, + cycle, + validFrom, + validTo, + maxAmountPerMonth, + maxPerCycle, + }: ContributionLinkArgs, + ): Promise { + const dbContributionLink = new DbContributionLink() + dbContributionLink.amount = amount + dbContributionLink.name = name + dbContributionLink.memo = memo + dbContributionLink.createdAt = new Date() + dbContributionLink.cycle = cycle + if (validFrom) dbContributionLink.validFrom = validFrom + if (validTo) dbContributionLink.validTo = validTo + dbContributionLink.maxAmountPerMonth = maxAmountPerMonth + dbContributionLink.maxPerCycle = maxPerCycle + dbContributionLink.save() + return new ContributionLink(dbContributionLink) + } } interface CreationMap { From f258f63ee74f3535a661f1e5e8c57e890eb89f08 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 10:43:03 +0200 Subject: [PATCH 43/69] test createContributionLink --- .../src/graphql/arg/ContributionLinkArgs.ts | 8 +- backend/src/graphql/model/ContributionLink.ts | 6 + .../graphql/resolver/AdminResolver.test.ts | 118 ++++++++++++++++++ backend/src/graphql/resolver/AdminResolver.ts | 6 +- backend/src/seeds/graphql/mutations.ts | 36 ++++++ 5 files changed, 168 insertions(+), 6 deletions(-) diff --git a/backend/src/graphql/arg/ContributionLinkArgs.ts b/backend/src/graphql/arg/ContributionLinkArgs.ts index 809ce89c4..7344a28ff 100644 --- a/backend/src/graphql/arg/ContributionLinkArgs.ts +++ b/backend/src/graphql/arg/ContributionLinkArgs.ts @@ -15,11 +15,11 @@ export default class ContributionLinkArgs { @Field(() => String) cycle: string - @Field(() => Date, { nullable: true }) - validFrom?: Date | null + @Field(() => String, { nullable: true }) + validFrom?: string | null - @Field(() => Date, { nullable: true }) - validTo?: Date | null + @Field(() => String, { nullable: true }) + validTo?: string | null @Field(() => Decimal, { nullable: true }) maxAmountPerMonth: Decimal | null diff --git a/backend/src/graphql/model/ContributionLink.ts b/backend/src/graphql/model/ContributionLink.ts index 9fb60e4aa..e4b37ad59 100644 --- a/backend/src/graphql/model/ContributionLink.ts +++ b/backend/src/graphql/model/ContributionLink.ts @@ -1,6 +1,7 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' import { ContributionLink as dbContributionLink } from '@entity/ContributionLink' +import CONFIG from '@/config' @ObjectType() export class ContributionLink { @@ -16,6 +17,8 @@ export class ContributionLink { this.maxAmountPerMonth = contributionLink.maxAmountPerMonth this.cycle = contributionLink.cycle this.maxPerCycle = contributionLink.maxPerCycle + this.code = 'CL-' + contributionLink.code + this.link = CONFIG.COMMUNITY_REDEEM_URL.replace(/{code}/g, this.code) } @Field(() => Number) @@ -33,6 +36,9 @@ export class ContributionLink { @Field(() => String) code: string + @Field(() => String) + link: string + @Field(() => Date) createdAt: Date diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index acf880efb..6b0f295c7 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -20,6 +20,7 @@ import { updatePendingCreation, deletePendingCreation, confirmPendingCreation, + createContributionLink, } from '@/seeds/graphql/mutations' import { getPendingCreations, @@ -34,6 +35,7 @@ import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' import Decimal from 'decimal.js-light' import { AdminPendingCreation } from '@entity/AdminPendingCreation' import { Transaction as DbTransaction } from '@entity/Transaction' +import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' // mock account activation email to avoid console spam jest.mock('@/mailer/sendAccountActivationEmail', () => { @@ -1593,4 +1595,120 @@ describe('AdminResolver', () => { }) }) }) + + describe('Contribution Links', () => { + const variables = { + amount: new Decimal(200), + name: 'Dokumenta 2022', + memo: 'Danke für deine Teilnahme an der Dokumenta 2022', + cycle: 'once', + validFrom: new Date(2022, 5, 18).toISOString(), + validTo: new Date(2022, 7, 14).toISOString(), + maxAmountPerMonth: new Decimal(200), + maxPerCycle: 1, + } + + describe('unauthenticated', () => { + describe('createContributionLink', () => { + it.only('returns an error', async () => { + await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) + + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await query({ + query: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('createContributionLink', () => { + it.only('returns an error', async () => { + await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, peterLustig) + await query({ + query: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('createContributionLink', () => { + it.only('returns a contribution link object', async () => { + await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( + expect.objectContaining({ + data: { + createContributionLink: expect.objectContaining({ + amount: '200', + code: expect.stringMatching(/^CL-[0-9a-f]{24,24}$/), + link: expect.any(String), + createdAt: expect.any(String), + name: 'Dokumenta 2022', + memo: 'Danke für deine Teilnahme an der Dokumenta 2022', + validFrom: expect.any(String), + validTo: expect.any(String), + maxAmountPerMonth: '200', + cycle: 'once', + maxPerCycle: 1, + }), + }, + }), + ) + }) + + it.only('has a contribution link stored in db', async () => { + const cls = await DbContributionLink.find() + expect(cls).toHaveLength(1) + expect(cls[0]).toEqual( + expect.objectContaining({ + id: expect.any(Number), + name: 'Dokumenta 2022', + memo: 'Danke für deine Teilnahme an der Dokumenta 2022', + validFrom: new Date('2022-06-18T00:00:00.000Z'), + validTo: new Date('2022-08-14T00:00:00.000Z'), + cycle: 'once', + maxPerCycle: 1, + totalMaxCountOfContribution: null, + maxAccountBalance: null, + minGapHours: null, + createdAt: expect.any(Date), + deletedAt: null, + code: expect.stringMatching(/^[0-9a-f]{24,24}$/), + linkEnabled: true, + // amount: '200', + // maxAmountPerMonth: '200', + }), + ) + }) + }) + }) + }) + }) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index d99bf6c6f..d7c843611 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -42,6 +42,7 @@ import { Order } from '@enum/Order' import { communityUser } from '@/util/communityUser' import { checkOptInCode, activationLink, printTimeDuration } from './UserResolver' import { sendAccountActivationEmail } from '@/mailer/sendAccountActivationEmail' +import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver' import CONFIG from '@/config' // const EMAIL_OPT_IN_REGISTER = 1 @@ -484,9 +485,10 @@ export class AdminResolver { dbContributionLink.name = name dbContributionLink.memo = memo dbContributionLink.createdAt = new Date() + dbContributionLink.code = contributionLinkCode(dbContributionLink.createdAt) dbContributionLink.cycle = cycle - if (validFrom) dbContributionLink.validFrom = validFrom - if (validTo) dbContributionLink.validTo = validTo + if (validFrom) dbContributionLink.validFrom = new Date(validFrom) + if (validTo) dbContributionLink.validTo = new Date(validTo) dbContributionLink.maxAmountPerMonth = maxAmountPerMonth dbContributionLink.maxPerCycle = maxPerCycle dbContributionLink.save() diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index e66827566..355b5aa79 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -137,3 +137,39 @@ export const deletePendingCreation = gql` deletePendingCreation(id: $id) } ` + +export const createContributionLink = gql` + mutation ( + $amount: Decimal! + $name: String! + $memo: String! + $cycle: String! + $validFrom: String + $validTo: String + $maxAmountPerMonth: Decimal + $maxPerCycle: Int! = 1 + ) { + createContributionLink( + amount: $amount + name: $name + memo: $memo + cycle: $cycle + validFrom: $validFrom + validTo: $validTo + maxAmountPerMonth: $maxAmountPerMonth + maxPerCycle: $maxPerCycle + ) { + amount + name + memo + code + link + createdAt + validFrom + validTo + maxAmountPerMonth + cycle + maxPerCycle + } + } +` From e1a84eb52e0313247b8185a7a69b5da23b11b090 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 10:45:10 +0200 Subject: [PATCH 44/69] add enum cycle type for contribution links --- .../src/graphql/enum/ContributionCycleType.ts | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 backend/src/graphql/enum/ContributionCycleType.ts diff --git a/backend/src/graphql/enum/ContributionCycleType.ts b/backend/src/graphql/enum/ContributionCycleType.ts new file mode 100644 index 000000000..5fe494a02 --- /dev/null +++ b/backend/src/graphql/enum/ContributionCycleType.ts @@ -0,0 +1,28 @@ +import { registerEnumType } from 'type-graphql' + +export enum ContributionCycleType { + ONCE = 'once', + HOUR = 'hour', + TWO_HOURS = 'two_hours', + FOUR_HOURS = 'four_hours', + EIGHT_HOURS = 'eight_hours', + HALF_DAY = 'half_day', + DAY = 'day', + TWO_DAYS = 'two_days', + THREE_DAYS = 'three_days', + FOUR_DAYS = 'four_days', + FIVE_DAYS = 'five_days', + SIX_DAYS = 'six_days', + WEEK = 'week', + TWO_WEEKS = 'two_weeks', + MONTH = 'month', + TWO_MONTH = 'two_month', + QUARTER = 'quarter', + HALF_YEAR = 'half_year', + YEAR = 'year', +} + +registerEnumType(ContributionCycleType, { + name: 'ContributionCycleType', // this one is mandatory + description: 'Name of the Type of the ContributionCycle', // this one is optional +}) From fd2f6a16b7b22cc61d8a7778728bc61f155443c5 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 12:10:16 +0200 Subject: [PATCH 45/69] lsit contribution links query --- backend/src/auth/RIGHTS.ts | 1 + .../src/graphql/model/ContributionLinkList.ts | 11 ++++ .../graphql/resolver/AdminResolver.test.ts | 52 +++++++++++++++++++ backend/src/graphql/resolver/AdminResolver.ts | 18 +++++++ backend/src/seeds/graphql/queries.ts | 22 ++++++++ 5 files changed, 104 insertions(+) create mode 100644 backend/src/graphql/model/ContributionLinkList.ts diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index dcd0e44d6..a9270cd36 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -38,4 +38,5 @@ export enum RIGHTS { CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST', LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN', CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', + LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', } diff --git a/backend/src/graphql/model/ContributionLinkList.ts b/backend/src/graphql/model/ContributionLinkList.ts new file mode 100644 index 000000000..412d0bf7b --- /dev/null +++ b/backend/src/graphql/model/ContributionLinkList.ts @@ -0,0 +1,11 @@ +import { ObjectType, Field } from 'type-graphql' +import { ContributionLink } from '@model/ContributionLink' + +@ObjectType() +export class ContributionLinkList { + @Field(() => [ContributionLink]) + links: ContributionLink[] + + @Field(() => Number) + count: number +} diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 6b0f295c7..8e700ce9b 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -27,6 +27,7 @@ import { login, searchUsers, listTransactionLinksAdmin, + listContributionLinks, } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' import { User } from '@entity/User' @@ -1618,6 +1619,16 @@ describe('AdminResolver', () => { ) }) }) + + describe('listContributionLinks', () => { + it.only('returns an error', async () => { + await expect(query({ query: listContributionLinks })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('authenticated', () => { @@ -1644,6 +1655,16 @@ describe('AdminResolver', () => { ) }) }) + + describe('listContributionLinks', () => { + it.only('returns an error', async () => { + await expect(query({ query: listContributionLinks })).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('with admin rights', () => { @@ -1708,6 +1729,37 @@ describe('AdminResolver', () => { ) }) }) + + describe('listContributionLinks', () => { + describe('one link in DB', () => { + it.only('returns the link and count 1', async () => { + await expect(query({ query: listContributionLinks })).resolves.toEqual( + expect.objectContaining({ + data: { + listContributionLinks: { + links: expect.arrayContaining([ + expect.objectContaining({ + amount: '200', + code: expect.stringMatching(/^CL-[0-9a-f]{24,24}$/), + link: expect.any(String), + createdAt: expect.any(String), + name: 'Dokumenta 2022', + memo: 'Danke für deine Teilnahme an der Dokumenta 2022', + validFrom: expect.any(String), + validTo: expect.any(String), + maxAmountPerMonth: '200', + cycle: 'once', + maxPerCycle: 1, + }), + ]), + count: 1, + }, + }, + }), + ) + }) + }) + }) }) }) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index d7c843611..e13a013b6 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -15,6 +15,7 @@ import { PendingCreation } from '@model/PendingCreation' import { CreatePendingCreations } from '@model/CreatePendingCreations' import { UpdatePendingCreation } from '@model/UpdatePendingCreation' import { ContributionLink } from '@model/ContributionLink' +import { ContributionLinkList } from '@model/ContributionLinkList' import { RIGHTS } from '@/auth/RIGHTS' import { UserRepository } from '@repository/User' import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs' @@ -494,6 +495,23 @@ export class AdminResolver { dbContributionLink.save() return new ContributionLink(dbContributionLink) } + + @Authorized([RIGHTS.LIST_CONTRIBUTION_LINKS]) + @Query(() => ContributionLinkList) + async listContributionLinks( + @Args() + { currentPage = 1, pageSize = 5, order = Order.DESC }: Paginated, + ): Promise { + const [links, count] = await DbContributionLink.findAndCount({ + order: { createdAt: order }, + skip: (currentPage - 1) * pageSize, + take: pageSize, + }) + return { + links: links.map((link: DbContributionLink) => new ContributionLink(link)), + count, + } + } } interface CreationMap { diff --git a/backend/src/seeds/graphql/queries.ts b/backend/src/seeds/graphql/queries.ts index 03ee3b53e..9a0e00be3 100644 --- a/backend/src/seeds/graphql/queries.ts +++ b/backend/src/seeds/graphql/queries.ts @@ -217,3 +217,25 @@ export const listTransactionLinksAdmin = gql` } } ` + +export const listContributionLinks = gql` + query ($pageSize: Int = 25, $currentPage: Int = 1, $order: Order) { + listContributionLinks(pageSize: $pageSize, currentPage: $currentPage, order: $order) { + links { + id + amount + name + memo + code + link + createdAt + validFrom + validTo + maxAmountPerMonth + cycle + maxPerCycle + } + count + } + } +` From 8ba0a4af7c27c3d4fef0c54b452fa58bef9943e9 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 12:11:43 +0200 Subject: [PATCH 46/69] no only in admin resolver tests --- backend/src/graphql/resolver/AdminResolver.test.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 8e700ce9b..362a5b90b 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -1611,7 +1611,7 @@ describe('AdminResolver', () => { describe('unauthenticated', () => { describe('createContributionLink', () => { - it.only('returns an error', async () => { + it('returns an error', async () => { await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], @@ -1621,7 +1621,7 @@ describe('AdminResolver', () => { }) describe('listContributionLinks', () => { - it.only('returns an error', async () => { + it('returns an error', async () => { await expect(query({ query: listContributionLinks })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], @@ -1647,7 +1647,7 @@ describe('AdminResolver', () => { }) describe('createContributionLink', () => { - it.only('returns an error', async () => { + it('returns an error', async () => { await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], @@ -1657,7 +1657,7 @@ describe('AdminResolver', () => { }) describe('listContributionLinks', () => { - it.only('returns an error', async () => { + it('returns an error', async () => { await expect(query({ query: listContributionLinks })).resolves.toEqual( expect.objectContaining({ errors: [new GraphQLError('401 Unauthorized')], @@ -1682,7 +1682,7 @@ describe('AdminResolver', () => { }) describe('createContributionLink', () => { - it.only('returns a contribution link object', async () => { + it('returns a contribution link object', async () => { await expect(mutate({ mutation: createContributionLink, variables })).resolves.toEqual( expect.objectContaining({ data: { @@ -1704,7 +1704,7 @@ describe('AdminResolver', () => { ) }) - it.only('has a contribution link stored in db', async () => { + it('has a contribution link stored in db', async () => { const cls = await DbContributionLink.find() expect(cls).toHaveLength(1) expect(cls[0]).toEqual( @@ -1732,7 +1732,7 @@ describe('AdminResolver', () => { describe('listContributionLinks', () => { describe('one link in DB', () => { - it.only('returns the link and count 1', async () => { + it('returns the link and count 1', async () => { await expect(query({ query: listContributionLinks })).resolves.toEqual( expect.objectContaining({ data: { From fa30b871b71e848c624226e1477a5d6e2b27b02e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 13:12:41 +0200 Subject: [PATCH 47/69] delete contribution link mutation --- backend/src/auth/RIGHTS.ts | 1 + .../graphql/resolver/AdminResolver.test.ts | 72 +++++++++++++++++++ backend/src/graphql/resolver/AdminResolver.ts | 16 ++++- backend/src/seeds/graphql/mutations.ts | 6 ++ 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index a9270cd36..f8fa38616 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -39,4 +39,5 @@ export enum RIGHTS { LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN', CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', + DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', } diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 362a5b90b..32227f257 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -21,6 +21,7 @@ import { deletePendingCreation, confirmPendingCreation, createContributionLink, + deleteContributionLink, } from '@/seeds/graphql/mutations' import { getPendingCreations, @@ -1629,6 +1630,18 @@ describe('AdminResolver', () => { ) }) }) + + describe('deleteContributionLink', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: deleteContributionLink, variables: { id: -1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('authenticated', () => { @@ -1665,6 +1678,18 @@ describe('AdminResolver', () => { ) }) }) + + describe('deleteContributionLink', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: deleteContributionLink, variables: { id: -1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) }) describe('with admin rights', () => { @@ -1760,6 +1785,53 @@ describe('AdminResolver', () => { }) }) }) + + describe('deleteContributionLink', () => { + describe('no valid id', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: deleteContributionLink, variables: { id: -1 } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Contribution Link not found to given id.')], + }), + ) + }) + }) + + describe('valid id', () => { + let linkId: number + beforeAll(async () => { + const links = await query({ query: listContributionLinks }) + linkId = links.data.listContributionLinks.links[0].id + }) + + it('returns a date string', async () => { + await expect( + mutate({ mutation: deleteContributionLink, variables: { id: linkId } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + deleteContributionLink: expect.any(String), + }, + }), + ) + }) + + it('does not list this contribution link anymore', async () => { + await expect(query({ query: listContributionLinks })).resolves.toEqual( + expect.objectContaining({ + data: { + listContributionLinks: { + links: [], + count: 0, + }, + }, + }), + ) + }) + }) + }) }) }) }) diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index e13a013b6..715d09d35 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -1,4 +1,5 @@ import { Context, getUser } from '@/server/context' +import { backendLogger as logger } from '@/server/logger' import { Resolver, Query, Arg, Args, Authorized, Mutation, Ctx, Int } from 'type-graphql' import { getCustomRepository, @@ -492,7 +493,7 @@ export class AdminResolver { if (validTo) dbContributionLink.validTo = new Date(validTo) dbContributionLink.maxAmountPerMonth = maxAmountPerMonth dbContributionLink.maxPerCycle = maxPerCycle - dbContributionLink.save() + await dbContributionLink.save() return new ContributionLink(dbContributionLink) } @@ -512,6 +513,19 @@ export class AdminResolver { count, } } + + @Authorized([RIGHTS.DELETE_CONTRIBUTION_LINK]) + @Mutation(() => Date, { nullable: true }) + async deleteContributionLink(@Arg('id', () => Int) id: number): Promise { + const contributionLink = await DbContributionLink.findOne(id) + if (!contributionLink) { + logger.error(`Contribution Link not found to given id: ${id}`) + throw new Error('Contribution Link not found to given id.') + } + await contributionLink.softRemove() + const newContributionLink = await DbContributionLink.findOne({ id }, { withDeleted: true }) + return newContributionLink ? newContributionLink.deletedAt : null + } } interface CreationMap { diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 355b5aa79..7c334796e 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -173,3 +173,9 @@ export const createContributionLink = gql` } } ` + +export const deleteContributionLink = gql` + mutation ($id: Int!) { + deleteContributionLink(id: $id) + } +` From dc46f4e78c5d26af13871d4773e4c102c30238fe Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 15:29:34 +0200 Subject: [PATCH 48/69] add update contribution link mutation --- backend/src/auth/RIGHTS.ts | 1 + .../graphql/resolver/AdminResolver.test.ts | 118 ++++++++++++++++++ backend/src/graphql/resolver/AdminResolver.ts | 33 +++++ backend/src/seeds/graphql/mutations.ts | 39 ++++++ 4 files changed, 191 insertions(+) diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index f8fa38616..8ac2c78cc 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -40,4 +40,5 @@ export enum RIGHTS { CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', LIST_CONTRIBUTION_LINKS = 'LIST_CONTRIBUTION_LINKS', DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', + UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', } diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index 32227f257..d08c52320 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -22,6 +22,7 @@ import { confirmPendingCreation, createContributionLink, deleteContributionLink, + updateContributionLink, } from '@/seeds/graphql/mutations' import { getPendingCreations, @@ -1631,6 +1632,27 @@ describe('AdminResolver', () => { }) }) + describe('updateContributionLink', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updateContributionLink, + variables: { + ...variables, + id: -1, + amount: new Decimal(400), + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + describe('deleteContributionLink', () => { it('returns an error', async () => { await expect( @@ -1679,6 +1701,27 @@ describe('AdminResolver', () => { }) }) + describe('updateContributionLink', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updateContributionLink, + variables: { + ...variables, + id: -1, + amount: new Decimal(400), + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + describe('deleteContributionLink', () => { it('returns an error', async () => { await expect( @@ -1786,6 +1829,81 @@ describe('AdminResolver', () => { }) }) + describe('updateContributionLink', () => { + describe('no valid id', () => { + it('returns an error', async () => { + await expect( + mutate({ + mutation: updateContributionLink, + variables: { + ...variables, + id: -1, + amount: new Decimal(400), + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Contribution Link not found to given id.')], + }), + ) + }) + }) + + describe('valid id', () => { + let linkId: number + beforeAll(async () => { + const links = await query({ query: listContributionLinks }) + linkId = links.data.listContributionLinks.links[0].id + }) + + it('returns updated contribution link object', async () => { + await expect( + mutate({ + mutation: updateContributionLink, + variables: { + ...variables, + id: linkId, + amount: new Decimal(400), + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + updateContributionLink: { + id: linkId, + amount: '400', + code: expect.stringMatching(/^CL-[0-9a-f]{24,24}$/), + link: expect.any(String), + createdAt: expect.any(String), + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + validFrom: expect.any(String), + validTo: expect.any(String), + maxAmountPerMonth: '200', + cycle: 'once', + maxPerCycle: 1, + }, + }, + }), + ) + }) + + it('updated the DB record', async () => { + await expect(DbContributionLink.findOne(linkId)).resolves.toEqual( + expect.objectContaining({ + id: linkId, + name: 'Dokumenta 2023', + memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + }), + ) + }) + }) + }) + describe('deleteContributionLink', () => { describe('no valid id', () => { it('returns an error', async () => { diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index 715d09d35..785a7de02 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -526,6 +526,39 @@ export class AdminResolver { const newContributionLink = await DbContributionLink.findOne({ id }, { withDeleted: true }) return newContributionLink ? newContributionLink.deletedAt : null } + + @Authorized([RIGHTS.UPDATE_CONTRIBUTION_LINK]) + @Mutation(() => ContributionLink) + async updateContributionLink( + @Args() + { + amount, + name, + memo, + cycle, + validFrom, + validTo, + maxAmountPerMonth, + maxPerCycle, + }: ContributionLinkArgs, + @Arg('id', () => Int) id: number, + ): Promise { + const dbContributionLink = await DbContributionLink.findOne(id) + if (!dbContributionLink) { + logger.error(`Contribution Link not found to given id: ${id}`) + throw new Error('Contribution Link not found to given id.') + } + dbContributionLink.amount = amount + dbContributionLink.name = name + dbContributionLink.memo = memo + dbContributionLink.cycle = cycle + if (validFrom) dbContributionLink.validFrom = new Date(validFrom) + if (validTo) dbContributionLink.validTo = new Date(validTo) + dbContributionLink.maxAmountPerMonth = maxAmountPerMonth + dbContributionLink.maxPerCycle = maxPerCycle + await dbContributionLink.save() + return new ContributionLink(dbContributionLink) + } } interface CreationMap { diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 7c334796e..c0f0fa6e4 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -174,6 +174,45 @@ export const createContributionLink = gql` } ` +export const updateContributionLink = gql` + mutation ( + $amount: Decimal! + $name: String! + $memo: String! + $cycle: String! + $validFrom: String + $validTo: String + $maxAmountPerMonth: Decimal + $maxPerCycle: Int! = 1 + $id: Int! + ) { + updateContributionLink( + amount: $amount + name: $name + memo: $memo + cycle: $cycle + validFrom: $validFrom + validTo: $validTo + maxAmountPerMonth: $maxAmountPerMonth + maxPerCycle: $maxPerCycle + id: $id + ) { + id + amount + name + memo + code + link + createdAt + validFrom + validTo + maxAmountPerMonth + cycle + maxPerCycle + } + } +` + export const deleteContributionLink = gql` mutation ($id: Int!) { deleteContributionLink(id: $id) From 26a653fd7db50ce239e3d457ef4dc38e246d96c8 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 17:14:09 +0200 Subject: [PATCH 49/69] coverage backend to 70% --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b7000100e..b935ef8f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -528,7 +528,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 68 + min_coverage: 70 token: ${{ github.token }} ########################################################################## From 7640680c421b1205e195723abe06fe45e5c43fe2 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 18:04:20 +0200 Subject: [PATCH 50/69] seed two ContributionLinks --- .../graphql/resolver/AdminResolver.test.ts | 1 + .../ContributionLinkInterface.ts | 7 +++++ backend/src/seeds/contributionLink/index.ts | 18 +++++++++++++ backend/src/seeds/factory/contributionLink.ts | 27 +++++++++++++++++++ backend/src/seeds/index.ts | 7 +++++ 5 files changed, 60 insertions(+) create mode 100644 backend/src/seeds/contributionLink/ContributionLinkInterface.ts create mode 100644 backend/src/seeds/contributionLink/index.ts create mode 100644 backend/src/seeds/factory/contributionLink.ts diff --git a/backend/src/graphql/resolver/AdminResolver.test.ts b/backend/src/graphql/resolver/AdminResolver.test.ts index d08c52320..a1a92dace 100644 --- a/backend/src/graphql/resolver/AdminResolver.test.ts +++ b/backend/src/graphql/resolver/AdminResolver.test.ts @@ -1898,6 +1898,7 @@ describe('AdminResolver', () => { id: linkId, name: 'Dokumenta 2023', memo: 'Danke für deine Teilnahme an der Dokumenta 2023', + // amount: '400', }), ) }) diff --git a/backend/src/seeds/contributionLink/ContributionLinkInterface.ts b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts new file mode 100644 index 000000000..d213bff23 --- /dev/null +++ b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts @@ -0,0 +1,7 @@ +export interface ContributionLinkInterface { + amount: number + name: string + memo: string + validFrom?: date + validTo?: date +} diff --git a/backend/src/seeds/contributionLink/index.ts b/backend/src/seeds/contributionLink/index.ts new file mode 100644 index 000000000..41d28eb60 --- /dev/null +++ b/backend/src/seeds/contributionLink/index.ts @@ -0,0 +1,18 @@ +import { ContributionLinkInterface } from './ContributionLinkInterface' + +export const contributionLinks: ContributionLinkInterface[] = [ + { + name: 'Dokumenta 2017', + memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2017', + amount: 200, + validFrom: new Date(2017, 3, 8), + validTo: new Date(2017, 6, 16), + }, + { + name: 'Dokumenta 2022', + memo: 'Vielen Dank für deinen Besuch bei der Dokumenta 2022', + amount: 200, + validFrom: new Date(2022, 5, 18), + validTo: new Date(2022, 8, 25), + }, +] diff --git a/backend/src/seeds/factory/contributionLink.ts b/backend/src/seeds/factory/contributionLink.ts new file mode 100644 index 000000000..7e34b9d20 --- /dev/null +++ b/backend/src/seeds/factory/contributionLink.ts @@ -0,0 +1,27 @@ +import { ApolloServerTestClient } from 'apollo-server-testing' +import { createContributionLink } from '@/seeds/graphql/mutations' +import { login } from '@/seeds/graphql/queries' +import { ContributionLinkInterface } from '@/seeds/contributionLink/ContributionLinkInterface' + +export const contributionLinkFactory = async ( + client: ApolloServerTestClient, + contributionLink: ContributionLinkInterface, +): Promise => { + const { mutate, query } = client + + // login as admin + await query({ query: login, variables: { email: 'peter@lustig.de', password: 'Aa12345_' } }) + + const variables = { + amount: contributionLink.amount, + memo: contributionLink.memo, + name: contributionLink.name, + cycle: 'ONCE', + maxPerCycle: 1, + maxAmountPerMonth: 200, + validFrom: contributionLink.validFrom ? contributionLink.validFrom.toISOString() : undefined, + validTo: contributionLink.validTo ? contributionLink.validTo.toISOString() : undefined, + } + + await mutate({ mutation: createContributionLink, variables }) +} diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 710f255ee..8e9a4e2d8 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -9,9 +9,11 @@ import { name, internet, datatype } from 'faker' import { users } from './users/index' import { creations } from './creation/index' import { transactionLinks } from './transactionLink/index' +import { contributionLinks } from './contributionLink/index' import { userFactory } from './factory/user' import { creationFactory } from './factory/creation' import { transactionLinkFactory } from './factory/transactionLink' +import { contributionLinkFactory } from './factory/contributionLink' import { entities } from '@entity/index' import CONFIG from '@/config' @@ -77,6 +79,11 @@ const run = async () => { await transactionLinkFactory(seedClient, transactionLinks[i]) } + // create Contribution Links + for (let i = 0; i < contributionLinks.length; i++) { + await contributionLinkFactory(seedClient, contributionLinks[i]) + } + await con.close() } From dcc1e9c2e7f9efe7d3f9ee98c845cd5d402bee2d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 19:19:12 +0200 Subject: [PATCH 51/69] proper type in seed --- .../src/seeds/contributionLink/ContributionLinkInterface.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/seeds/contributionLink/ContributionLinkInterface.ts b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts index d213bff23..15ba4b72d 100644 --- a/backend/src/seeds/contributionLink/ContributionLinkInterface.ts +++ b/backend/src/seeds/contributionLink/ContributionLinkInterface.ts @@ -2,6 +2,6 @@ export interface ContributionLinkInterface { amount: number name: string memo: string - validFrom?: date - validTo?: date + validFrom?: Date + validTo?: Date } From 5845f708f67fb04d965947f3570c2968117a4b4d Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 19:47:18 +0200 Subject: [PATCH 52/69] rename from pending creation to unconfirmed contribution --- ...Args.ts => AdminCreateContributionArgs.ts} | 2 +- ...AdminUpdateUnconfirmedContributionArgs.ts} | 2 +- ...reations.ts => AdminCreateContribution.ts} | 2 +- ... => AdminUpdateUnconfirmedContribution.ts} | 2 +- ...ribution.ts => UnconfirmedContribution.ts} | 2 +- backend/src/graphql/resolver/AdminResolver.ts | 32 +++++++++---------- 6 files changed, 21 insertions(+), 21 deletions(-) rename backend/src/graphql/arg/{CreatePendingCreationArgs.ts => AdminCreateContributionArgs.ts} (85%) rename backend/src/graphql/arg/{UpdatePendingCreationArgs.ts => AdminUpdateUnconfirmedContributionArgs.ts} (84%) rename backend/src/graphql/model/{CreatePendingCreations.ts => AdminCreateContribution.ts} (89%) rename backend/src/graphql/model/{UpdatePendingCreation.ts => AdminUpdateUnconfirmedContribution.ts} (84%) rename backend/src/graphql/model/{PendingContribution.ts => UnconfirmedContribution.ts} (93%) diff --git a/backend/src/graphql/arg/CreatePendingCreationArgs.ts b/backend/src/graphql/arg/AdminCreateContributionArgs.ts similarity index 85% rename from backend/src/graphql/arg/CreatePendingCreationArgs.ts rename to backend/src/graphql/arg/AdminCreateContributionArgs.ts index 11c345465..b09edea32 100644 --- a/backend/src/graphql/arg/CreatePendingCreationArgs.ts +++ b/backend/src/graphql/arg/AdminCreateContributionArgs.ts @@ -3,7 +3,7 @@ import Decimal from 'decimal.js-light' @InputType() @ArgsType() -export default class CreatePendingCreationArgs { +export default class AdminCreateContributionArgs { @Field(() => String) email: string diff --git a/backend/src/graphql/arg/UpdatePendingCreationArgs.ts b/backend/src/graphql/arg/AdminUpdateUnconfirmedContributionArgs.ts similarity index 84% rename from backend/src/graphql/arg/UpdatePendingCreationArgs.ts rename to backend/src/graphql/arg/AdminUpdateUnconfirmedContributionArgs.ts index 691d73154..228207707 100644 --- a/backend/src/graphql/arg/UpdatePendingCreationArgs.ts +++ b/backend/src/graphql/arg/AdminUpdateUnconfirmedContributionArgs.ts @@ -2,7 +2,7 @@ import { ArgsType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' @ArgsType() -export default class UpdatePendingCreationArgs { +export default class AdminUpdatePendingContributionArgs { @Field(() => Int) id: number diff --git a/backend/src/graphql/model/CreatePendingCreations.ts b/backend/src/graphql/model/AdminCreateContribution.ts similarity index 89% rename from backend/src/graphql/model/CreatePendingCreations.ts rename to backend/src/graphql/model/AdminCreateContribution.ts index 8d5bcef2c..c2df3d73e 100644 --- a/backend/src/graphql/model/CreatePendingCreations.ts +++ b/backend/src/graphql/model/AdminCreateContribution.ts @@ -1,7 +1,7 @@ import { ObjectType, Field } from 'type-graphql' @ObjectType() -export class CreatePendingCreations { +export class AdminCreateContribution { constructor() { this.success = false this.successfulCreation = [] diff --git a/backend/src/graphql/model/UpdatePendingCreation.ts b/backend/src/graphql/model/AdminUpdateUnconfirmedContribution.ts similarity index 84% rename from backend/src/graphql/model/UpdatePendingCreation.ts rename to backend/src/graphql/model/AdminUpdateUnconfirmedContribution.ts index e19e1e064..5f905cd6c 100644 --- a/backend/src/graphql/model/UpdatePendingCreation.ts +++ b/backend/src/graphql/model/AdminUpdateUnconfirmedContribution.ts @@ -2,7 +2,7 @@ import { ObjectType, Field } from 'type-graphql' import Decimal from 'decimal.js-light' @ObjectType() -export class UpdatePendingCreation { +export class AdminUpdateUnconfirmedContribution { @Field(() => Date) date: Date diff --git a/backend/src/graphql/model/PendingContribution.ts b/backend/src/graphql/model/UnconfirmedContribution.ts similarity index 93% rename from backend/src/graphql/model/PendingContribution.ts rename to backend/src/graphql/model/UnconfirmedContribution.ts index e8991deb7..69001c19b 100644 --- a/backend/src/graphql/model/PendingContribution.ts +++ b/backend/src/graphql/model/UnconfirmedContribution.ts @@ -2,7 +2,7 @@ import { ObjectType, Field, Int } from 'type-graphql' import Decimal from 'decimal.js-light' @ObjectType() -export class PendingContribution { +export class UnconfirmedContribution { @Field(() => String) firstName: string diff --git a/backend/src/graphql/resolver/AdminResolver.ts b/backend/src/graphql/resolver/AdminResolver.ts index cf179263d..b0e2fcbec 100644 --- a/backend/src/graphql/resolver/AdminResolver.ts +++ b/backend/src/graphql/resolver/AdminResolver.ts @@ -11,13 +11,13 @@ import { FindOperator, } from '@dbTools/typeorm' import { UserAdmin, SearchUsersResult } from '@model/UserAdmin' -import { PendingContribution } from '@model/PendingContribution' -import { CreatePendingCreations } from '@model/CreatePendingCreations' -import { UpdatePendingCreation } from '@model/UpdatePendingCreation' +import { UnconfirmedContribution } from '@model/UnconfirmedContribution' +import { AdminCreateContribution } from '@model/AdminCreateContribution' +import { AdminUpdateUnconfirmedContribution } from '@model/AdminUpdateUnconfirmedContribution' import { RIGHTS } from '@/auth/RIGHTS' import { UserRepository } from '@repository/User' -import CreatePendingCreationArgs from '@arg/CreatePendingCreationArgs' -import UpdatePendingCreationArgs from '@arg/UpdatePendingCreationArgs' +import AdminCreateContributionArgs from '@arg/AdminCreateContributionArgs' +import AdminUpdateUnconfirmedContributionArgs from '@arg/AdminUpdateUnconfirmedContributionArgs' import SearchUsersArgs from '@arg/SearchUsersArgs' import { Transaction as DbTransaction } from '@entity/Transaction' import { Transaction } from '@model/Transaction' @@ -167,7 +167,7 @@ export class AdminResolver { @Authorized([RIGHTS.CREATE_PENDING_CREATION]) @Mutation(() => [Number]) async createPendingCreation( - @Args() { email, amount, memo, creationDate }: CreatePendingCreationArgs, + @Args() { email, amount, memo, creationDate }: AdminCreateContributionArgs, @Ctx() context: Context, ): Promise { logger.trace('createPendingCreation...') @@ -202,12 +202,12 @@ export class AdminResolver { } @Authorized([RIGHTS.CREATE_PENDING_CREATION]) - @Mutation(() => CreatePendingCreations) + @Mutation(() => AdminCreateContribution) async createPendingCreations( - @Arg('pendingCreations', () => [CreatePendingCreationArgs]) - contributions: CreatePendingCreationArgs[], + @Arg('pendingCreations', () => [AdminCreateContributionArgs]) + contributions: AdminCreateContributionArgs[], @Ctx() context: Context, - ): Promise { + ): Promise { let success = false const successfulCreation: string[] = [] const failedCreation: string[] = [] @@ -229,11 +229,11 @@ export class AdminResolver { } @Authorized([RIGHTS.UPDATE_PENDING_CREATION]) - @Mutation(() => UpdatePendingCreation) + @Mutation(() => AdminUpdateUnconfirmedContribution) async updatePendingCreation( - @Args() { id, email, amount, memo, creationDate }: UpdatePendingCreationArgs, + @Args() { id, email, amount, memo, creationDate }: AdminUpdateUnconfirmedContributionArgs, @Ctx() context: Context, - ): Promise { + ): Promise { const user = await dbUser.findOne({ email }, { withDeleted: true }) if (!user) { throw new Error(`Could not find user with email: ${email}`) @@ -268,7 +268,7 @@ export class AdminResolver { contributionToUpdate.moderatorId = moderator.id await Contribution.save(contributionToUpdate) - const result = new UpdatePendingCreation() + const result = new AdminUpdateUnconfirmedContribution() result.amount = amount result.memo = contributionToUpdate.memo result.date = contributionToUpdate.contributionDate @@ -279,8 +279,8 @@ export class AdminResolver { } @Authorized([RIGHTS.SEARCH_PENDING_CREATION]) - @Query(() => [PendingContribution]) - async getPendingCreations(): Promise { + @Query(() => [UnconfirmedContribution]) + async getPendingCreations(): Promise { const contributions = await Contribution.find({ where: { confirmedAt: IsNull() } }) if (contributions.length === 0) { return [] From 916706a9736939b228ac8c9539ec433be7779d67 Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 15 Jun 2022 08:21:53 +0200 Subject: [PATCH 53/69] fix mutation for admin mass creation --- admin/src/graphql/createPendingCreations.js | 2 +- backend/src/seeds/graphql/mutations.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/admin/src/graphql/createPendingCreations.js b/admin/src/graphql/createPendingCreations.js index 95d60bc9a..225adb71a 100644 --- a/admin/src/graphql/createPendingCreations.js +++ b/admin/src/graphql/createPendingCreations.js @@ -1,7 +1,7 @@ import gql from 'graphql-tag' export const createPendingCreations = gql` - mutation ($pendingCreations: [CreatePendingCreationArgs!]!) { + mutation ($pendingCreations: [AdminCreateContributionArgs!]!) { createPendingCreations(pendingCreations: $pendingCreations) { success successfulCreation diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index e66827566..55c20614b 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -106,7 +106,7 @@ export const unDeleteUser = gql` ` export const createPendingCreations = gql` - mutation ($pendingCreations: [CreatePendingCreationArgs!]!) { + mutation ($pendingCreations: [AdminCreateContributionArgs!]!) { createPendingCreations(pendingCreations: $pendingCreations) { success successfulCreation From e60f06bb9b969c37d8d519d24dd401a501f1003e Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Wed, 15 Jun 2022 08:32:30 +0200 Subject: [PATCH 54/69] rename mutations to create admin contributions --- admin/src/components/CreationFormular.spec.js | 14 ++--- admin/src/components/CreationFormular.vue | 16 +++--- admin/src/graphql/adminCreateContribution.js | 12 +++++ ...eations.js => adminCreateContributions.js} | 4 +- admin/src/graphql/createPendingCreation.js | 7 --- .../graphql/resolver/AdminResolver.test.ts | 54 ++++++++++--------- backend/src/graphql/resolver/AdminResolver.ts | 8 +-- backend/src/seeds/factory/creation.ts | 4 +- backend/src/seeds/graphql/mutations.ts | 13 +++-- 9 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 admin/src/graphql/adminCreateContribution.js rename admin/src/graphql/{createPendingCreations.js => adminCreateContributions.js} (59%) delete mode 100644 admin/src/graphql/createPendingCreation.js diff --git a/admin/src/components/CreationFormular.spec.js b/admin/src/components/CreationFormular.spec.js index 08ec71bdc..6c89ec727 100644 --- a/admin/src/components/CreationFormular.spec.js +++ b/admin/src/components/CreationFormular.spec.js @@ -1,14 +1,14 @@ import { mount } from '@vue/test-utils' import CreationFormular from './CreationFormular.vue' -import { createPendingCreation } from '../graphql/createPendingCreation' -import { createPendingCreations } from '../graphql/createPendingCreations' +import { adminCreateContribution } from '../graphql/adminCreateContribution' +import { adminCreateContributions } from '../graphql/adminCreateContributions' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' const localVue = global.localVue const apolloMutateMock = jest.fn().mockResolvedValue({ data: { - createPendingCreation: [0, 0, 0], + adminCreateContribution: [0, 0, 0], }, }) const stateCommitMock = jest.fn() @@ -110,7 +110,7 @@ describe('CreationFormular', () => { it('sends ... to apollo', () => { expect(apolloMutateMock).toBeCalledWith( expect.objectContaining({ - mutation: createPendingCreation, + mutation: adminCreateContribution, variables: { email: 'benjamin@bluemchen.de', creationDate: getCreationDate(2), @@ -334,7 +334,7 @@ describe('CreationFormular', () => { jest.clearAllMocks() apolloMutateMock.mockResolvedValue({ data: { - createPendingCreations: { + adminCreateContributions: { success: true, successfulCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'], failedCreation: [], @@ -355,7 +355,7 @@ describe('CreationFormular', () => { it('calls the API', () => { expect(apolloMutateMock).toBeCalledWith( expect.objectContaining({ - mutation: createPendingCreations, + mutation: adminCreateContributions, variables: { pendingCreations: [ { @@ -390,7 +390,7 @@ describe('CreationFormular', () => { jest.clearAllMocks() apolloMutateMock.mockResolvedValue({ data: { - createPendingCreations: { + adminCreateContributions: { success: true, successfulCreation: [], failedCreation: ['bob@baumeister.de', 'bibi@bloxberg.de'], diff --git a/admin/src/components/CreationFormular.vue b/admin/src/components/CreationFormular.vue index cdcd6ef1d..8531eaa16 100644 --- a/admin/src/components/CreationFormular.vue +++ b/admin/src/components/CreationFormular.vue @@ -85,8 +85,8 @@