From dc46f4e78c5d26af13871d4773e4c102c30238fe Mon Sep 17 00:00:00 2001 From: Moriz Wahl Date: Tue, 14 Jun 2022 15:29:34 +0200 Subject: [PATCH 1/4] 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 2/4] 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 3/4] 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 4/4] 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 }