From b4841a760cf93243cb8d2835725a20fc48cfe686 Mon Sep 17 00:00:00 2001 From: ogerly Date: Tue, 31 Jan 2023 16:26:35 +0100 Subject: [PATCH 01/44] style refactor mobil auth area --- .../LinkInformations/RedeemLoggedOut.vue | 8 ++-- .../LinkInformations/RedeemValid.vue | 2 +- frontend/src/pages/Login.vue | 15 ++++--- frontend/src/pages/Register.vue | 43 +++++++++++-------- frontend/src/pages/TransactionLink.vue | 4 +- 5 files changed, 41 insertions(+), 31 deletions(-) diff --git a/frontend/src/components/LinkInformations/RedeemLoggedOut.vue b/frontend/src/components/LinkInformations/RedeemLoggedOut.vue index bbad3f9fb..34c31af5a 100644 --- a/frontend/src/components/LinkInformations/RedeemLoggedOut.vue +++ b/frontend/src/components/LinkInformations/RedeemLoggedOut.vue @@ -3,20 +3,20 @@ -
+

{{ $t('gdd_per_link.redeem') }}

- +

{{ $t('gdd_per_link.no-account') }}

{{ $t('gdd_per_link.to-register') }}
- +

{{ $t('gdd_per_link.has-account') }}

- {{ $t('gdd_per_link.to-login') }} + {{ $t('gdd_per_link.to-login') }}
diff --git a/frontend/src/components/LinkInformations/RedeemValid.vue b/frontend/src/components/LinkInformations/RedeemValid.vue index c468a396a..39485b10e 100644 --- a/frontend/src/components/LinkInformations/RedeemValid.vue +++ b/frontend/src/components/LinkInformations/RedeemValid.vue @@ -3,7 +3,7 @@
- + {{ $t('gdd_per_link.redeem') }}
diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue index bd07af3ef..13e5db110 100755 --- a/frontend/src/pages/Login.vue +++ b/frontend/src/pages/Login.vue @@ -23,15 +23,20 @@ - - + + {{ $t('settings.password.forgot_pwd') }} -
- {{ $t('login') }} -
+ + + {{ $t('login') }} + + + diff --git a/frontend/src/pages/Register.vue b/frontend/src/pages/Register.vue index eb68ee7c5..eaebb9ed1 100755 --- a/frontend/src/pages/Register.vue +++ b/frontend/src/pages/Register.vue @@ -68,25 +68,30 @@ > -
- - - - -
-
- - {{ $t('signup') }} - -
+ + + + + + + + + + + + {{ $t('signup') }} + + + diff --git a/frontend/src/pages/TransactionLink.vue b/frontend/src/pages/TransactionLink.vue index c3875d20e..764f48494 100644 --- a/frontend/src/pages/TransactionLink.vue +++ b/frontend/src/pages/TransactionLink.vue @@ -1,6 +1,6 @@ diff --git a/frontend/src/components/DecayInformations/DecayInformation.vue b/frontend/src/components/DecayInformations/DecayInformation.vue index a1379a9b4..3329fd021 100644 --- a/frontend/src/components/DecayInformations/DecayInformation.vue +++ b/frontend/src/components/DecayInformations/DecayInformation.vue @@ -1,7 +1,6 @@ - diff --git a/admin/src/components/Tables/SelectedUsersTable.vue b/admin/src/components/Tables/SelectedUsersTable.vue deleted file mode 100644 index 810f8dac8..000000000 --- a/admin/src/components/Tables/SelectedUsersTable.vue +++ /dev/null @@ -1,26 +0,0 @@ - - diff --git a/admin/src/pages/Creation.spec.js b/admin/src/pages/Creation.spec.js deleted file mode 100644 index 9524fc5d6..000000000 --- a/admin/src/pages/Creation.spec.js +++ /dev/null @@ -1,337 +0,0 @@ -import { mount } from '@vue/test-utils' -import Creation from './Creation.vue' -import { toastErrorSpy } from '../../test/testSetup' - -const localVue = global.localVue - -const apolloQueryMock = jest.fn().mockResolvedValue({ - data: { - searchUsers: { - userCount: 2, - userList: [ - { - userId: 1, - firstName: 'Bibi', - lastName: 'Bloxberg', - email: 'bibi@bloxberg.de', - creation: [200, 400, 600], - emailChecked: true, - }, - { - userId: 2, - firstName: 'Benjamin', - lastName: 'Blümchen', - email: 'benjamin@bluemchen.de', - creation: [800, 600, 400], - emailChecked: true, - }, - ], - }, - }, -}) - -const storeCommitMock = jest.fn() - -const mocks = { - $t: jest.fn((t, options) => (options ? [t, options] : t)), - $d: jest.fn((d) => d), - $apollo: { - query: apolloQueryMock, - }, - $store: { - commit: storeCommitMock, - state: { - userSelectedInMassCreation: [], - }, - }, -} - -describe('Creation', () => { - let wrapper - - const Wrapper = () => { - return mount(Creation, { localVue, mocks }) - } - - describe('mount', () => { - beforeEach(() => { - jest.clearAllMocks() - wrapper = Wrapper() - }) - - it('has a DIV element with the class.creation', () => { - expect(wrapper.find('div.creation').exists()).toBeTruthy() - }) - - describe('apollo returns user array', () => { - it('calls the searchUser query', () => { - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ - variables: { - searchText: '', - currentPage: 1, - pageSize: 25, - filters: { - byActivated: true, - byDeleted: false, - }, - }, - }), - ) - }) - - it('has two rows in the left table', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2) - }) - - it('has nwo rows in the right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0) - }) - - it('has correct data in first row ', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain('Bibi') - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( - 'Bloxberg', - ) - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( - '200 | 400 | 600', - ) - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( - 'bibi@bloxberg.de', - ) - }) - - it('has correct data in second row ', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain( - 'Benjamin', - ) - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain( - 'Blümchen', - ) - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain( - '800 | 600 | 400', - ) - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).text()).toContain( - 'benjamin@bluemchen.de', - ) - }) - }) - - describe('push item', () => { - beforeEach(() => { - wrapper.findAll('table').at(0).findAll('tbody > tr').at(1).find('button').trigger('click') - }) - - it('has one item in left table', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1) - }) - - it('has one item in right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1) - }) - - it('has the correct user in left table', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( - 'bibi@bloxberg.de', - ) - }) - - it('has the correct user in right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain( - 'benjamin@bluemchen.de', - ) - }) - - it('updates userSelectedInMassCreation in store', () => { - expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', [ - { - userId: 2, - firstName: 'Benjamin', - lastName: 'Blümchen', - email: 'benjamin@bluemchen.de', - creation: [800, 600, 400], - showDetails: false, - emailChecked: true, - }, - ]) - }) - - describe('remove item', () => { - beforeEach(async () => { - await wrapper - .findAll('table') - .at(1) - .findAll('tbody > tr') - .at(0) - .find('button') - .trigger('click') - }) - - it('has two items in left table', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(2) - }) - - it('has the removed user in first row', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr').at(0).text()).toContain( - 'benjamin@bluemchen.de', - ) - }) - - it('has no items in right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0) - }) - - it('commits empty array as userSelectedInMassCreation', () => { - expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', []) - }) - }) - - describe('remove all bookmarks', () => { - beforeEach(async () => { - jest.clearAllMocks() - await wrapper.find('button.btn-light').trigger('click') - }) - - it('has no items in right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(0) - }) - - it('commits empty array to userSelectedInMassCreation', () => { - expect(storeCommitMock).toBeCalledWith('setUserSelectedInMassCreation', []) - }) - - it('calls searchUsers', () => { - expect(apolloQueryMock).toBeCalled() - }) - }) - }) - - describe('store has items in userSelectedInMassCreation', () => { - beforeEach(() => { - mocks.$store.state.userSelectedInMassCreation = [ - { - userId: 2, - firstName: 'Benjamin', - lastName: 'Blümchen', - email: 'benjamin@bluemchen.de', - creation: [800, 600, 400], - showDetails: false, - emailChecked: true, - }, - ] - wrapper = Wrapper() - }) - - it('has one item in left table', () => { - expect(wrapper.findAll('table').at(0).findAll('tbody > tr')).toHaveLength(1) - }) - - it('has one item in right table', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr')).toHaveLength(1) - }) - - it('has the stored user in second row', () => { - expect(wrapper.findAll('table').at(1).findAll('tbody > tr').at(0).text()).toContain( - 'benjamin@bluemchen.de', - ) - }) - }) - - describe('failed creations', () => { - beforeEach(async () => { - await wrapper - .findComponent({ name: 'CreationFormular' }) - .vm.$emit('toast-failed-creations', ['bibi@bloxberg.de', 'benjamin@bluemchen.de']) - }) - - it('toasts two error messages', () => { - expect(toastErrorSpy).toBeCalledWith([ - 'creation_form.creation_failed', - { email: 'bibi@bloxberg.de' }, - ]) - expect(toastErrorSpy).toBeCalledWith([ - 'creation_form.creation_failed', - { email: 'benjamin@bluemchen.de' }, - ]) - }) - }) - - describe('watchers', () => { - beforeEach(() => { - jest.clearAllMocks() - }) - - describe('search criteria', () => { - beforeEach(async () => { - await wrapper.setData({ criteria: 'XX' }) - }) - - it('calls API when criteria changes', async () => { - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ - variables: { - searchText: 'XX', - currentPage: 1, - pageSize: 25, - filters: { - byActivated: true, - byDeleted: false, - }, - }, - }), - ) - }) - - describe('reset search criteria', () => { - it('calls the API', async () => { - jest.clearAllMocks() - await wrapper.find('.test-click-clear-criteria').trigger('click') - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ - variables: { - searchText: '', - currentPage: 1, - pageSize: 25, - filters: { - byActivated: true, - byDeleted: false, - }, - }, - }), - ) - }) - }) - }) - - it('calls API when currentPage changes', async () => { - await wrapper.setData({ currentPage: 2 }) - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ - variables: { - searchText: '', - currentPage: 2, - pageSize: 25, - filters: { - byActivated: true, - byDeleted: false, - }, - }, - }), - ) - }) - }) - - describe('apollo returns error', () => { - beforeEach(() => { - apolloQueryMock.mockRejectedValue({ - message: 'Ouch', - }) - wrapper = Wrapper() - }) - - it('toasts an error message', () => { - expect(toastErrorSpy).toBeCalledWith('Ouch') - }) - }) - }) -}) diff --git a/admin/src/pages/Creation.vue b/admin/src/pages/Creation.vue deleted file mode 100644 index 26d44fd3e..000000000 --- a/admin/src/pages/Creation.vue +++ /dev/null @@ -1,200 +0,0 @@ - - diff --git a/admin/src/router/routes.js b/admin/src/router/routes.js index e365a6e40..b01466cfc 100644 --- a/admin/src/router/routes.js +++ b/admin/src/router/routes.js @@ -19,10 +19,6 @@ const routes = [ path: '/user', component: () => import('@/pages/UserSearch.vue'), }, - { - path: '/creation', - component: () => import('@/pages/Creation.vue'), - }, { path: '/creation-confirm', component: () => import('@/pages/CreationConfirm.vue'), From 484f03a53a539708eee82ce3d1f25e4a18737967 Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 2 Feb 2023 18:22:35 +0100 Subject: [PATCH 06/44] remove unused keys from locals --- admin/src/locales/de.json | 7 ------- admin/src/locales/en.json | 7 ------- 2 files changed, 14 deletions(-) diff --git a/admin/src/locales/de.json b/admin/src/locales/de.json index 9612e3247..4f4a0c5bc 100644 --- a/admin/src/locales/de.json +++ b/admin/src/locales/de.json @@ -32,7 +32,6 @@ "creation": "Schöpfung", "creationList": "Schöpfungsliste", "creation_form": { - "creation_failed": "Ausstehende Schöpfung für {email} konnte nicht erzeugt werden.", "creation_for": "Aktives Grundeinkommen für", "enter_text": "Text eintragen", "form": "Schöpfungsformular", @@ -87,7 +86,6 @@ "lastname": "Nachname", "math": { "equals": "=", - "exclaim": "!", "pipe": "|", "plus": "+" }, @@ -95,15 +93,12 @@ "request": "Die Anfrage wurde gesendet." }, "moderator": "Moderator", - "multiple_creation_text": "Bitte wähle ein oder mehrere Mitglieder aus für die du Schöpfen möchtest.", "name": "Name", "navbar": { "automaticContributions": "Automatische Beiträge", "logout": "Abmelden", - "multi_creation": "Mehrfachschöpfung", "my-account": "Mein Konto", "open_creation": "Offene Schöpfungen", - "overview": "Übersicht", "statistic": "Statistik", "user_search": "Nutzersuche" }, @@ -132,9 +127,7 @@ } }, "redeemed": "eingelöst", - "remove": "Entfernen", "removeNotSelf": "Als Admin/Moderator kannst du dich nicht selber löschen.", - "remove_all": "alle Nutzer entfernen", "save": "Speichern", "statistic": { "activeUsers": "Aktive Mitglieder", diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index f9598d006..566273415 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -32,7 +32,6 @@ "creation": "Creation", "creationList": "Creation list", "creation_form": { - "creation_failed": "Could not create pending creation for {email}", "creation_for": "Active Basic Income for", "enter_text": "Enter text", "form": "Creation form", @@ -87,7 +86,6 @@ "lastname": "Lastname", "math": { "equals": "=", - "exclaim": "!", "pipe": "|", "plus": "+" }, @@ -95,15 +93,12 @@ "request": "Request has been sent." }, "moderator": "Moderator", - "multiple_creation_text": "Please select one or more members for which you would like to perform creations.", "name": "Name", "navbar": { "automaticContributions": "Automatic Contributions", "logout": "Logout", - "multi_creation": "Multiple creation", "my-account": "My Account", "open_creation": "Open creations", - "overview": "Overview", "statistic": "Statistic", "user_search": "User search" }, @@ -132,9 +127,7 @@ } }, "redeemed": "redeemed", - "remove": "Remove", "removeNotSelf": "As an admin/moderator, you cannot delete yourself.", - "remove_all": "Remove all users", "save": "Speichern", "statistic": { "activeUsers": "Active members", From 19475750340d7ef853bb226f457614e06422bc6c Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 2 Feb 2023 18:34:31 +0100 Subject: [PATCH 07/44] fix test --- admin/src/components/NavBar.spec.js | 20 ++++++-------------- admin/src/router/router.test.js | 9 +-------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/admin/src/components/NavBar.spec.js b/admin/src/components/NavBar.spec.js index 139172c30..96b7cba9c 100644 --- a/admin/src/components/NavBar.spec.js +++ b/admin/src/components/NavBar.spec.js @@ -46,39 +46,31 @@ describe('NavBar', () => { }) describe('Navbar Menu', () => { - it('has a link to overview', () => { - expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/') - }) - it('has a link to /user', () => { - expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe('/user') - }) - - it('has a link to /creation', () => { - expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe('/creation') + expect(wrapper.findAll('.nav-item').at(0).find('a').attributes('href')).toBe('/user') }) it('has a link to /creation-confirm', () => { - expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe( + expect(wrapper.findAll('.nav-item').at(1).find('a').attributes('href')).toBe( '/creation-confirm', ) }) it('has a link to /contribution-links', () => { - expect(wrapper.findAll('.nav-item').at(4).find('a').attributes('href')).toBe( + expect(wrapper.findAll('.nav-item').at(2).find('a').attributes('href')).toBe( '/contribution-links', ) }) it('has a link to /statistic', () => { - expect(wrapper.findAll('.nav-item').at(5).find('a').attributes('href')).toBe('/statistic') + expect(wrapper.findAll('.nav-item').at(3).find('a').attributes('href')).toBe('/statistic') }) }) describe('wallet', () => { const assignLocationSpy = jest.fn() beforeEach(async () => { - await wrapper.findAll('.nav-item').at(6).find('a').trigger('click') + await wrapper.findAll('.nav-item').at(5).find('a').trigger('click') }) it.skip('changes window location to wallet', () => { @@ -97,7 +89,7 @@ describe('NavBar', () => { window.location = { assign: windowLocationMock, } - await wrapper.findAll('.nav-item').at(7).find('a').trigger('click') + await wrapper.findAll('.nav-item').at(5).find('a').trigger('click') }) it('redirects to /logout', () => { diff --git a/admin/src/router/router.test.js b/admin/src/router/router.test.js index fdc4b0b83..ad1ad1245 100644 --- a/admin/src/router/router.test.js +++ b/admin/src/router/router.test.js @@ -45,7 +45,7 @@ describe('router', () => { describe('routes', () => { it('has nine routes defined', () => { - expect(routes).toHaveLength(9) + expect(routes).toHaveLength(8) }) it('has "/overview" as default', async () => { @@ -67,13 +67,6 @@ describe('router', () => { }) }) - describe('creation', () => { - it('loads the "Creation" component', async () => { - const component = await routes.find((r) => r.path === '/creation').component() - expect(component.default.name).toBe('Creation') - }) - }) - describe('creation-confirm', () => { it('loads the "CreationConfirm" component', async () => { const component = await routes.find((r) => r.path === '/creation-confirm').component() From a918735630982175ba5684d1a7ff4132b9ff43b9 Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 3 Feb 2023 11:31:16 +0100 Subject: [PATCH 08/44] fix test over 96% --- .../ContributionLink/ContributionLink.spec.js | 28 +++++++++++++---- .../CreationTransactionList.spec.js | 11 +++++++ admin/src/locales/index.test.js | 18 +++++++++++ admin/src/pages/ContributionLinks.spec.js | 31 +++++++++++++++---- 4 files changed, 76 insertions(+), 12 deletions(-) create mode 100644 admin/src/locales/index.test.js diff --git a/admin/src/components/ContributionLink/ContributionLink.spec.js b/admin/src/components/ContributionLink/ContributionLink.spec.js index b72a0347c..e0f09f9fd 100644 --- a/admin/src/components/ContributionLink/ContributionLink.spec.js +++ b/admin/src/components/ContributionLink/ContributionLink.spec.js @@ -42,14 +42,30 @@ describe('ContributionLink', () => { expect(wrapper.find('div.contribution-link').exists()).toBe(true) }) - it('emits toggle::collapse new Contribution', async () => { - wrapper.vm.editContributionLinkData() - expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy() + describe('function editContributionLinkData', () => { + beforeEach(() => { + wrapper.vm.editContributionLinkData() + }) + it('emits toggle::collapse new Contribution', async () => { + await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy() + }) }) - it('emits toggle::collapse close Contribution-Form ', async () => { - wrapper.vm.closeContributionForm() - expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy() + describe('function closeContributionForm', () => { + beforeEach(async () => { + await wrapper.setData({ visible: true }) + wrapper.vm.closeContributionForm() + }) + + it('emits toggle::collapse close Contribution-Form ', async () => { + await expect(wrapper.vm.$root.$emit('bv::toggle::collapse', 'newContribution')).toBeTruthy() + }) + it('editContributionLink is false', async () => { + await expect(wrapper.vm.editContributionLink).toBe(false) + }) + it('contributionLinkData is empty', async () => { + await expect(wrapper.vm.contributionLinkData).toEqual({}) + }) }) }) }) diff --git a/admin/src/components/CreationTransactionList.spec.js b/admin/src/components/CreationTransactionList.spec.js index ff9607424..9613942f8 100644 --- a/admin/src/components/CreationTransactionList.spec.js +++ b/admin/src/components/CreationTransactionList.spec.js @@ -88,5 +88,16 @@ describe('CreationTransactionList', () => { expect(toastErrorSpy).toBeCalledWith('OUCH!') }) }) + + describe('watch currentPage', () => { + beforeEach(async () => { + jest.clearAllMocks() + await wrapper.setData({ currentPage: 2 }) + }) + + it('returns the string in normal order if reversed property is not true', () => { + expect(wrapper.vm.currentPage).toBe(2) + }) + }) }) }) diff --git a/admin/src/locales/index.test.js b/admin/src/locales/index.test.js new file mode 100644 index 000000000..1abcadbec --- /dev/null +++ b/admin/src/locales/index.test.js @@ -0,0 +1,18 @@ +import locales from './index.js' + +describe('locales', () => { + it('should contain 2 locales', () => { + expect(locales).toHaveLength(2) + }) + + it('should contain a German locale', () => { + expect(locales).toContainEqual( + expect.objectContaining({ + name: 'Deutsch', + code: 'de', + iso: 'de-DE', + enabled: true, + }), + ) + }) +}) diff --git a/admin/src/pages/ContributionLinks.spec.js b/admin/src/pages/ContributionLinks.spec.js index fb60a99cf..3d91fdbe9 100644 --- a/admin/src/pages/ContributionLinks.spec.js +++ b/admin/src/pages/ContributionLinks.spec.js @@ -1,6 +1,7 @@ import { mount } from '@vue/test-utils' import ContributionLinks from './ContributionLinks.vue' import { listContributionLinks } from '@/graphql/listContributionLinks.js' +import { toastErrorSpy } from '../../test/testSetup' const localVue = global.localVue @@ -46,13 +47,31 @@ describe('ContributionLinks', () => { beforeEach(() => { wrapper = Wrapper() }) + describe('apollo returns', () => { + it('calls listContributionLinks', () => { + expect(apolloQueryMock).toBeCalledWith( + expect.objectContaining({ + query: listContributionLinks, + }), + ) + }) + }) - it('calls listContributionLinks', () => { - expect(apolloQueryMock).toBeCalledWith( - expect.objectContaining({ - query: listContributionLinks, - }), - ) + describe.skip('query transaction with error', () => { + beforeEach(() => { + apolloQueryMock.mockRejectedValue({ message: 'OUCH!' }) + wrapper = Wrapper() + }) + + it('calls the API', () => { + expect(apolloQueryMock).toBeCalled() + }) + + it('toast error', () => { + expect(toastErrorSpy).toBeCalledWith( + 'listContributionLinks has no result, use default data', + ) + }) }) }) }) From add7d9b448da00d38cc09bf8c669dc28b7319f45 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 6 Feb 2023 23:10:36 +0100 Subject: [PATCH 09/44] implement LogError on TransactionLinkResolver --- .../resolver/TransactionLinkResolver.ts | 34 +++++-------------- 1 file changed, 8 insertions(+), 26 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index df70b4bc9..50e6d6146 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -32,6 +32,7 @@ import { getUserCreation, validateContribution } from './util/creations' import { executeTransaction } from './TransactionResolver' import QueryLinkResult from '@union/QueryLinkResult' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import LogError from '@/server/LogError' // TODO: do not export, test it inside the resolver export const transactionLinkCode = (date: Date): string => { @@ -184,24 +185,15 @@ export class TransactionLinkResolver { .where('contributionLink.code = :code', { code: code.replace('CL-', '') }) .getOne() if (!contributionLink) { - logger.error('no contribution link found to given code:', code) - throw new Error(`No contribution link found to given code: ${code}`) + throw new LogError('No contribution link found to given code', code) } logger.info('...contribution link found with id', contributionLink.id) if (new Date(contributionLink.validFrom).getTime() > now.getTime()) { - logger.error( - 'contribution link is not valid yet. Valid from: ', - contributionLink.validFrom, - ) - throw new Error('Contribution link not valid yet') + throw new LogError('Contribution link is not valid yet', contributionLink.validFrom) } if (contributionLink.validTo) { if (new Date(contributionLink.validTo).setHours(23, 59, 59) < now.getTime()) { - logger.error( - 'contribution link is no longer valid. Valid to: ', - contributionLink.validTo, - ) - throw new Error('Contribution link is no longer valid') + throw new LogError('Contribution link is no longer valid', contributionLink.validTo) } } let alreadyRedeemed: DbContribution | undefined @@ -217,11 +209,7 @@ export class TransactionLinkResolver { }) .getOne() if (alreadyRedeemed) { - logger.error( - 'contribution link with rule ONCE already redeemed by user with id', - user.id, - ) - throw new Error('Contribution link already redeemed') + throw new LogError('Contribution link already redeemed', user.id) } break } @@ -246,17 +234,12 @@ export class TransactionLinkResolver { ) .getOne() if (alreadyRedeemed) { - logger.error( - 'contribution link with rule DAILY already redeemed by user with id', - user.id, - ) - throw new Error('Contribution link already redeemed today') + throw new LogError('Contribution link already redeemed today', user.id) } break } default: { - logger.error('contribution link has unknown cycle', contributionLink.cycle) - throw new Error('Contribution link has unknown cycle') + throw new LogError('Contribution link has unknown cycle', contributionLink.cycle) } } @@ -312,8 +295,7 @@ export class TransactionLinkResolver { logger.info('creation from contribution link commited successfuly.') } catch (e) { await queryRunner.rollbackTransaction() - logger.error(`Creation from contribution link was not successful: ${e}`) - throw new Error(`Creation from contribution link was not successful. ${e}`) + throw new LogError('Creation from contribution link was not successful', e) } finally { await queryRunner.release() } From f38f93141833d21da2338147504dc7f10355aae4 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 6 Feb 2023 23:36:48 +0100 Subject: [PATCH 10/44] fix TransactionLinkResolver tests --- .../resolver/TransactionLinkResolver.test.ts | 118 ++++++++++++------ 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index 50f9b0414..0eca609de 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -24,6 +24,7 @@ import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import Decimal from 'decimal.js-light' import { GraphQLError } from 'graphql' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import { logger } from '@test/testSetup' // mock semaphore to allow use fake timers jest.mock('@/util/TRANSACTIONS_LOCK') @@ -61,6 +62,7 @@ describe('TransactionLinkResolver', () => { }) it('throws error when link does not exists', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: redeemTransactionLink, @@ -69,16 +71,26 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: No contribution link found to given code: CL-123456', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'No contribution link found to given code', + 'CL-123456', + ) + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('No contribution link found to given code'), + ) + }) + + const now = new Date() + const validFrom = new Date(now.getFullYear() + 1, 0, 1) + it('throws error when link is not valid yet', async () => { - const now = new Date() + jest.clearAllMocks() const { data: { createContributionLink: contributionLink }, } = await mutate({ @@ -88,7 +100,7 @@ describe('TransactionLinkResolver', () => { name: 'Daily Contribution Link', memo: 'Thank you for contribute daily to the community', cycle: 'DAILY', - validFrom: new Date(now.getFullYear() + 1, 0, 1).toISOString(), + validFrom: validFrom.toISOString(), validTo: new Date(now.getFullYear() + 1, 11, 31, 23, 59, 59, 999).toISOString(), maxAmountPerMonth: new Decimal(200), maxPerCycle: 1, @@ -102,16 +114,21 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link not valid yet', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) await resetEntity(DbContributionLink) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Contribution link is not valid yet', validFrom) + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('Contribution link is not valid yet'), + ) + }) + it('throws error when contributionLink cycle is invalid', async () => { + jest.clearAllMocks() const now = new Date() const { data: { createContributionLink: contributionLink }, @@ -136,17 +153,22 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link has unknown cycle', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) await resetEntity(DbContributionLink) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Contribution link has unknown cycle', 'INVALID') + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('Contribution link has unknown cycle'), + ) + }) + + const validTo = new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 0) it('throws error when link is no longer valid', async () => { - const now = new Date() + jest.clearAllMocks() const { data: { createContributionLink: contributionLink }, } = await mutate({ @@ -157,7 +179,7 @@ describe('TransactionLinkResolver', () => { memo: 'Thank you for contribute daily to the community', cycle: 'DAILY', validFrom: new Date(now.getFullYear() - 1, 0, 1).toISOString(), - validTo: new Date(now.getFullYear() - 1, 11, 31, 23, 59, 59, 999).toISOString(), + validTo: validTo.toISOString(), maxAmountPerMonth: new Decimal(200), maxPerCycle: 1, }, @@ -170,14 +192,18 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link is no longer valid', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) await resetEntity(DbContributionLink) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Contribution link is no longer valid', validTo) + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('Contribution link is no longer valid'), + ) + }) }) // TODO: have this test separated into a transactionLink and a contributionLink part @@ -250,6 +276,7 @@ describe('TransactionLinkResolver', () => { }) it('does not allow the user to redeem the contribution link', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: redeemTransactionLink, @@ -258,13 +285,18 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error( + 'The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.', + ), + ) + }) }) describe('user has no pending contributions that would not allow to redeem the link', () => { @@ -301,6 +333,7 @@ describe('TransactionLinkResolver', () => { }) it('does not allow the user to redeem the contribution link a second time on the same day', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: redeemTransactionLink, @@ -309,14 +342,17 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('Contribution link already redeemed today'), + ) + }) + describe('after one day', () => { beforeAll(async () => { jest.useFakeTimers() @@ -349,6 +385,7 @@ describe('TransactionLinkResolver', () => { }) it('does not allow the user to redeem the contribution link a second time on the same day', async () => { + jest.clearAllMocks() await expect( mutate({ mutation: redeemTransactionLink, @@ -357,13 +394,16 @@ describe('TransactionLinkResolver', () => { }, }), ).resolves.toMatchObject({ - errors: [ - new GraphQLError( - 'Creation from contribution link was not successful. Error: Contribution link already redeemed today', - ), - ], + errors: [new GraphQLError('Creation from contribution link was not successful')], }) }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'Creation from contribution link was not successful', + new Error('Contribution link already redeemed today'), + ) + }) }) }) }) From 283e09ea3db3eeec9bd68345efcf2784d773f277 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 11:43:04 +0100 Subject: [PATCH 11/44] separate error cases, have some basic testing for transaction links, reorder tests --- .../resolver/TransactionLinkResolver.test.ts | 514 ++++++++++-------- .../resolver/TransactionLinkResolver.ts | 6 +- 2 files changed, 293 insertions(+), 227 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index 0eca609de..af2e4fd59 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -16,6 +16,7 @@ import { redeemTransactionLink, createContribution, updateContribution, + createTransactionLink, } from '@/seeds/graphql/mutations' import { listTransactionLinksAdmin } from '@/seeds/graphql/queries' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' @@ -51,6 +52,69 @@ afterAll(async () => { }) describe('TransactionLinkResolver', () => { + describe('createTransactionLink', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + it('throws error when amount is zero', async () => { + jest.clearAllMocks() + await expect( + mutate({ + mutation: createTransactionLink, + variables: { + amount: 0, + memo: 'Test', + }, + }), + ).resolves.toMatchObject({ + errors: [new GraphQLError('Amount must be a positive number')], + }) + }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(0)) + }) + + it('throws error when amount is negative', async () => { + jest.clearAllMocks() + await expect( + mutate({ + mutation: createTransactionLink, + variables: { + amount: -10, + memo: 'Test', + }, + }), + ).resolves.toMatchObject({ + errors: [new GraphQLError('Amount must be a positive number')], + }) + }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Amount must be a positive number', new Decimal(-10)) + }) + + it('throws error when user has not enough GDD', async () => { + jest.clearAllMocks() + await expect( + mutate({ + mutation: createTransactionLink, + variables: { + amount: 1001, + memo: 'Test', + }, + }), + ).resolves.toMatchObject({ + errors: [new GraphQLError('User has not enough GDD')], + }) + }) + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('User has not enough GDD', expect.any(Number)) + }) + }) + describe('redeemTransactionLink', () => { describe('contributionLink', () => { describe('input not valid', () => { @@ -408,22 +472,52 @@ describe('TransactionLinkResolver', () => { }) }) }) + }) - describe('transaction links list', () => { - const variables = { - userId: 1, // dummy, may be replaced - filters: null, - currentPage: 1, - pageSize: 5, - } + describe('listTransactionLinksAdmin', () => { + const variables = { + userId: 1, // dummy, may be replaced + filters: null, + currentPage: 1, + pageSize: 5, + } - // TODO: there is a test not cleaning up after itself! Fix it! - beforeAll(async () => { - await cleanDB() - resetToken() + // TODO: there is a test not cleaning up after itself! Fix it! + beforeAll(async () => { + await cleanDB() + resetToken() + }) + + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables, + }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) }) + }) + + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) - describe('unauthenticated', () => { it('returns an error', async () => { await expect( query({ @@ -438,22 +532,40 @@ describe('TransactionLinkResolver', () => { }) }) - describe('authenticated', () => { - describe('without admin rights', () => { - beforeAll(async () => { - user = await userFactory(testEnv, bibiBloxberg) - await mutate({ - mutation: login, - variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, - }) - }) + describe('with admin rights', () => { + beforeAll(async () => { + // admin 'peter@lustig.de' has to exists for 'creationFactory' + await userFactory(testEnv, peterLustig) - afterAll(async () => { - await cleanDB() - resetToken() - }) + user = await userFactory(testEnv, bibiBloxberg) + variables.userId = user.id + variables.pageSize = 25 + // bibi needs GDDs + const bibisCreation = creations.find((creation) => creation.email === 'bibi@bloxberg.de') + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await creationFactory(testEnv, bibisCreation!) + // bibis transaktion links + const bibisTransaktionLinks = transactionLinks.filter( + (transactionLink) => transactionLink.email === 'bibi@bloxberg.de', + ) + for (let i = 0; i < bibisTransaktionLinks.length; i++) { + await transactionLinkFactory(testEnv, bibisTransaktionLinks[i]) + } - it('returns an error', async () => { + // admin: only now log in + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('without any filters', () => { + it('finds 6 open transaction links and no deleted or redeemed', async () => { await expect( query({ query: listTransactionLinksAdmin, @@ -461,219 +573,169 @@ describe('TransactionLinkResolver', () => { }), ).resolves.toEqual( expect.objectContaining({ - errors: [new GraphQLError('401 Unauthorized')], + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.not.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, }), ) }) }) - describe('with admin rights', () => { - beforeAll(async () => { - // admin 'peter@lustig.de' has to exists for 'creationFactory' - await userFactory(testEnv, peterLustig) - - user = await userFactory(testEnv, bibiBloxberg) - variables.userId = user.id - variables.pageSize = 25 - // bibi needs GDDs - const bibisCreation = creations.find( - (creation) => creation.email === 'bibi@bloxberg.de', + describe('all filters are null', () => { + it('finds 6 open transaction links and no deleted or redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: null, + withExpired: null, + withRedeemed: null, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.not.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), ) - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - await creationFactory(testEnv, bibisCreation!) - // bibis transaktion links - const bibisTransaktionLinks = transactionLinks.filter( - (transactionLink) => transactionLink.email === 'bibi@bloxberg.de', + }) + }) + + describe('filter with deleted', () => { + it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: true, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 7, + linkList: expect.arrayContaining([ + expect.not.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), ) - for (let i = 0; i < bibisTransaktionLinks.length; i++) { - await transactionLinkFactory(testEnv, bibisTransaktionLinks[i]) - } - - // admin: only now log in - await mutate({ - mutation: login, - variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, - }) }) + }) - afterAll(async () => { - await cleanDB() - resetToken() + describe('filter by expired', () => { + it('finds 5 open transaction links, 1 expired, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withExpired: true, + }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 7, + linkList: expect.arrayContaining([ + expect.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.not.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), + }, + }, + }), + ) }) + }) - describe('without any filters', () => { - it('finds 6 open transaction links and no deleted or redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.not.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, + // TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory + describe.skip('filter by redeemed', () => { + it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { + await expect( + query({ + query: listTransactionLinksAdmin, + variables: { + ...variables, + filters: { + withDeleted: null, + withExpired: null, + withRedeemed: true, }, - }), - ) - }) - }) - - describe('all filters are null', () => { - it('finds 6 open transaction links and no deleted or redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: null, - withExpired: null, - withRedeemed: null, - }, + }, + }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + listTransactionLinksAdmin: { + linkCount: 6, + linkList: expect.arrayContaining([ + expect.not.objectContaining({ + memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', + createdAt: expect.any(String), + }), + expect.objectContaining({ + memo: 'Yeah, eingelöst!', + redeemedAt: expect.any(String), + redeemedBy: expect.any(Number), + }), + expect.not.objectContaining({ + memo: 'Da habe ich mich wohl etwas übernommen.', + deletedAt: expect.any(String), + }), + ]), }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.not.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) - }) - }) - - describe('filter with deleted', () => { - it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: true, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 7, - linkList: expect.arrayContaining([ - expect.not.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) - }) - }) - - describe('filter by expired', () => { - it('finds 5 open transaction links, 1 expired, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withExpired: true, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 7, - linkList: expect.arrayContaining([ - expect.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.not.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) - }) - }) - - // TODO: works not as expected, because 'redeemedAt' and 'redeemedBy' have to be added to the transaktion link factory - describe.skip('filter by redeemed', () => { - it('finds 6 open transaction links, 1 deleted, and no redeemed', async () => { - await expect( - query({ - query: listTransactionLinksAdmin, - variables: { - ...variables, - filters: { - withDeleted: null, - withExpired: null, - withRedeemed: true, - }, - }, - }), - ).resolves.toEqual( - expect.objectContaining({ - data: { - listTransactionLinksAdmin: { - linkCount: 6, - linkList: expect.arrayContaining([ - expect.not.objectContaining({ - memo: 'Leider wollte niemand meine Gradidos zum Neujahr haben :(', - createdAt: expect.any(String), - }), - expect.objectContaining({ - memo: 'Yeah, eingelöst!', - redeemedAt: expect.any(String), - redeemedBy: expect.any(Number), - }), - expect.not.objectContaining({ - memo: 'Da habe ich mich wohl etwas übernommen.', - deletedAt: expect.any(String), - }), - ]), - }, - }, - }), - ) - }) + }, + }), + ) }) }) }) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.ts b/backend/src/graphql/resolver/TransactionLinkResolver.ts index 50e6d6146..696c51d97 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.ts @@ -64,12 +64,16 @@ export class TransactionLinkResolver { const createdDate = new Date() const validUntil = transactionLinkExpireDate(createdDate) + if (amount.lessThanOrEqualTo(0)) { + throw new LogError('Amount must be a positive number', amount) + } + const holdAvailableAmount = amount.minus(calculateDecay(amount, createdDate, validUntil).decay) // validate amount const sendBalance = await calculateBalance(user.id, holdAvailableAmount.mul(-1), createdDate) if (!sendBalance) { - throw new Error("user hasn't enough GDD or amount is < 0") + throw new LogError('User has not enough GDD', user.id) } const transactionLink = DbTransactionLink.create() From 7059ba1ee1bb301fa8703ab4daae5ff7df0e33c1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 7 Feb 2023 12:08:11 +0100 Subject: [PATCH 12/44] use LogError Class and correct tests --- .../resolver/TransactionResolver.test.ts | 80 +++++++++---------- .../graphql/resolver/TransactionResolver.ts | 31 +++---- 2 files changed, 50 insertions(+), 61 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 50b2b3690..404a76094 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -1,6 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import Decimal from 'decimal.js-light' import { EventProtocolType } from '@/event/EventProtocolType' import { userFactory } from '@/seeds/factory/user' import { @@ -118,10 +119,8 @@ describe('send coins', () => { it('logs the error thrown', async () => { // find peter to check the log - const user = await findUserByEmail(peterData.email) - expect(logger.error).toBeCalledWith( - `The recipient account was deleted: recipientUser=${user}`, - ) + const user = await findUserByEmail('stephen@hawking.uk') + expect(logger.error).toBeCalledWith('The recipient account was deleted', user) }) }) @@ -151,10 +150,8 @@ describe('send coins', () => { it('logs the error thrown', async () => { // find peter to check the log - const user = await findUserByEmail(peterData.email) - expect(logger.error).toBeCalledWith( - `The recipient account is not activated: recipientUser=${user}`, - ) + const user = await findUserByEmail('garrick@ollivander.com') + expect(logger.error).toBeCalledWith('The recipient account is not activated', user) }) }) }) @@ -181,37 +178,13 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('Sender and Recipient are the same.')], + errors: [new GraphQLError('Sender and Recipient are the same')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('Sender and Recipient are the same.') - }) - }) - - describe('memo text is too long', () => { - it('throws an error', async () => { - jest.clearAllMocks() - expect( - await mutate({ - mutation: sendCoins, - variables: { - email: 'peter@lustig.de', - amount: 100, - memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t', - }, - }), - ).toEqual( - expect.objectContaining({ - errors: [new GraphQLError('memo text is too long (255 characters maximum)')], - }), - ) - }) - - it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('memo text is too long: memo.length=256 > 255') + expect(logger.error).toBeCalledWith('Sender and Recipient are the same', expect.any(Number)) }) }) @@ -229,13 +202,37 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError('memo text is too short (5 characters minimum)')], + errors: [new GraphQLError('Memo text is too short')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith('memo text is too short: memo.length=4 < 5') + expect(logger.error).toBeCalledWith('Memo text is too short', 4) + }) + }) + + describe('memo text is too long', () => { + it('throws an error', async () => { + jest.clearAllMocks() + expect( + await mutate({ + mutation: sendCoins, + variables: { + email: 'peter@lustig.de', + amount: 100, + memo: 'test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test test t', + }, + }), + ).toEqual( + expect.objectContaining({ + errors: [new GraphQLError('Memo text is too long')], + }), + ) + }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('Memo text is too long', 256) }) }) @@ -253,15 +250,13 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError(`user hasn't enough GDD or amount is < 0`)], + errors: [new GraphQLError('User has not enough GDD or amount is < 0')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith( - `user hasn't enough GDD or amount is < 0 : balance=null`, - ) + expect(logger.error).toBeCalledWith('User has not enough GDD or amount is < 0', null) }) }) }) @@ -293,6 +288,7 @@ describe('send coins', () => { describe('trying to send negative amount', () => { it('throws an error', async () => { + jest.clearAllMocks() expect( await mutate({ mutation: sendCoins, @@ -304,13 +300,13 @@ describe('send coins', () => { }), ).toEqual( expect.objectContaining({ - errors: [new GraphQLError(`Amount to send must be positive`)], + errors: [new GraphQLError('Amount to send must be positive')], }), ) }) it('logs the error thrown', () => { - expect(logger.error).toBeCalledWith(`Amount to send must be positive`) + expect(logger.error).toBeCalledWith('Amount to send must be positive', new Decimal(-50)) }) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 2f97596b2..c4fbe8c10 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -37,6 +37,7 @@ import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' import { findUserByEmail } from './UserResolver' import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK' +import LogError from '@/server/LogError' export const executeTransaction = async ( amount: Decimal, @@ -53,18 +54,15 @@ export const executeTransaction = async ( ) if (sender.id === recipient.id) { - logger.error(`Sender and Recipient are the same.`) - throw new Error('Sender and Recipient are the same.') - } - - if (memo.length > MEMO_MAX_CHARS) { - logger.error(`memo text is too long: memo.length=${memo.length} > ${MEMO_MAX_CHARS}`) - throw new Error(`memo text is too long (${MEMO_MAX_CHARS} characters maximum)`) + throw new LogError('Sender and Recipient are the same', sender.id) } if (memo.length < MEMO_MIN_CHARS) { - logger.error(`memo text is too short: memo.length=${memo.length} < ${MEMO_MIN_CHARS}`) - throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`) + throw new LogError('Memo text is too short', memo.length) + } + + if (memo.length > MEMO_MAX_CHARS) { + throw new LogError('Memo text is too long', memo.length) } // validate amount @@ -77,8 +75,7 @@ export const executeTransaction = async ( ) logger.debug(`calculated Balance=${sendBalance}`) if (!sendBalance) { - logger.error(`user hasn't enough GDD or amount is < 0 : balance=${sendBalance}`) - throw new Error("user hasn't enough GDD or amount is < 0") + throw new LogError('User has not enough GDD or amount is < 0', sendBalance) } const queryRunner = getConnection().createQueryRunner() @@ -156,8 +153,7 @@ export const executeTransaction = async ( ) } catch (e) { await queryRunner.rollbackTransaction() - logger.error(`Transaction was not successful: ${e}`) - throw new Error(`Transaction was not successful: ${e}`) + throw new LogError('Transaction was not successful', e) } finally { await queryRunner.release() } @@ -319,8 +315,7 @@ export class TransactionResolver { ): Promise { logger.info(`sendCoins(email=${email}, amount=${amount}, memo=${memo})`) if (amount.lte(0)) { - logger.error(`Amount to send must be positive`) - throw new Error('Amount to send must be positive') + throw new LogError('Amount to send must be positive', amount) } // TODO this is subject to replay attacks @@ -329,13 +324,11 @@ export class TransactionResolver { // validate recipient user const recipientUser = await findUserByEmail(email) if (recipientUser.deletedAt) { - logger.error(`The recipient account was deleted: recipientUser=${recipientUser}`) - throw new Error('The recipient account was deleted') + throw new LogError('The recipient account was deleted', recipientUser) } const emailContact = recipientUser.emailContact if (!emailContact.emailChecked) { - logger.error(`The recipient account is not activated: recipientUser=${recipientUser}`) - throw new Error('The recipient account is not activated') + throw new LogError('The recipient account is not activated', recipientUser) } await executeTransaction(amount, memo, senderUser, recipientUser) From 03b27bdf9560121f3104716f0be4953187780baa Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 9 Feb 2023 17:18:40 +0100 Subject: [PATCH 13/44] remove comments in code --- frontend/src/pages/Login.vue | 3 --- 1 file changed, 3 deletions(-) diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue index 13e5db110..c615d4d16 100755 --- a/frontend/src/pages/Login.vue +++ b/frontend/src/pages/Login.vue @@ -34,9 +34,6 @@ {{ $t('login') }} - From 2ac133c41abee6e1c670b20a48110623139b5b23 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 10 Feb 2023 02:06:39 +0100 Subject: [PATCH 14/44] rework - initial work --- backend/src/event/Event.ts | 765 ++++++------------ backend/src/event/EventProtocolEmitter.ts | 17 - .../graphql/resolver/ContributionResolver.ts | 81 +- .../graphql/resolver/TransactionResolver.ts | 27 +- backend/src/graphql/resolver/UserResolver.ts | 60 +- 5 files changed, 325 insertions(+), 625 deletions(-) delete mode 100644 backend/src/event/EventProtocolEmitter.ts diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 77e86ad46..2545bdc63 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -1,509 +1,278 @@ -import decimal from 'decimal.js-light' +import { EventProtocol as dbEvent } from '@entity/EventProtocol' +import Decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' -export class EventBasic { - type: string - createdAt: Date -} -export class EventBasicUserId extends EventBasic { - userId: number -} - -export class EventBasicTx extends EventBasicUserId { - transactionId: number - amount: decimal -} - -export class EventBasicTxX extends EventBasicTx { - xUserId: number - xCommunityId: number -} - -export class EventBasicCt extends EventBasicUserId { - contributionId: number - amount: decimal -} - -export class EventBasicCtX extends EventBasicCt { - xUserId: number - xCommunityId: number -} - -export class EventBasicRedeem extends EventBasicUserId { - transactionId?: number - contributionId?: number -} - -export class EventBasicCtMsg extends EventBasicCt { - messageId: number -} - -export class EventVisitGradido extends EventBasic {} -export class EventRegister extends EventBasicUserId {} -export class EventRedeemRegister extends EventBasicRedeem {} -export class EventVerifyRedeem extends EventBasicRedeem {} -export class EventInactiveAccount extends EventBasicUserId {} -export class EventSendConfirmationEmail extends EventBasicUserId {} -export class EventSendAccountMultiRegistrationEmail extends EventBasicUserId {} -export class EventSendForgotPasswordEmail extends EventBasicUserId {} -export class EventSendTransactionSendEmail extends EventBasicTxX {} -export class EventSendTransactionReceiveEmail extends EventBasicTxX {} -export class EventSendTransactionLinkRedeemEmail extends EventBasicTxX {} -export class EventSendAddedContributionEmail extends EventBasicCt {} -export class EventSendContributionConfirmEmail extends EventBasicCt {} -export class EventConfirmationEmail extends EventBasicUserId {} -export class EventRegisterEmailKlicktipp extends EventBasicUserId {} -export class EventLogin extends EventBasicUserId {} -export class EventLogout extends EventBasicUserId {} -export class EventRedeemLogin extends EventBasicRedeem {} -export class EventActivateAccount extends EventBasicUserId {} -export class EventPasswordChange extends EventBasicUserId {} -export class EventTransactionSend extends EventBasicTxX {} -export class EventTransactionSendRedeem extends EventBasicTxX {} -export class EventTransactionRepeateRedeem extends EventBasicTxX {} -export class EventTransactionCreation extends EventBasicTx {} -export class EventTransactionReceive extends EventBasicTxX {} -export class EventTransactionReceiveRedeem extends EventBasicTxX {} -export class EventContributionCreate extends EventBasicCt {} -export class EventAdminContributionCreate extends EventBasicCt {} -export class EventAdminContributionDelete extends EventBasicCt {} -export class EventAdminContributionDeny extends EventBasicCt {} -export class EventAdminContributionUpdate extends EventBasicCt {} -export class EventUserCreateContributionMessage extends EventBasicCtMsg {} -export class EventAdminCreateContributionMessage extends EventBasicCtMsg {} -export class EventContributionDelete extends EventBasicCt {} -export class EventContributionUpdate extends EventBasicCt {} -export class EventContributionConfirm extends EventBasicCtX {} -export class EventContributionDeny extends EventBasicCtX {} -export class EventContributionLinkDefine extends EventBasicCt {} -export class EventContributionLinkActivateRedeem extends EventBasicCt {} -export class EventDeleteUser extends EventBasicUserId {} -export class EventUndeleteUser extends EventBasicUserId {} -export class EventChangeUserRole extends EventBasicUserId {} -export class EventAdminUpdateContribution extends EventBasicCt {} -export class EventAdminDeleteContribution extends EventBasicCt {} -export class EventCreateContributionLink extends EventBasicCt {} -export class EventDeleteContributionLink extends EventBasicCt {} -export class EventUpdateContributionLink extends EventBasicCt {} - -export class Event { - public setEventBasic(): Event { - this.type = EventProtocolType.BASIC - this.createdAt = new Date() - - return this - } - - public setEventVisitGradido(): Event { - this.setEventBasic() - this.type = EventProtocolType.VISIT_GRADIDO - - return this - } - - public setEventRegister(ev: EventRegister): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.REGISTER - - return this - } - - public setEventRedeemRegister(ev: EventRedeemRegister): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.REDEEM_REGISTER - - return this - } - - public setEventVerifyRedeem(ev: EventVerifyRedeem): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.VERIFY_REDEEM - - return this - } - - public setEventInactiveAccount(ev: EventInactiveAccount): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.INACTIVE_ACCOUNT - - return this - } - - public setEventSendConfirmationEmail(ev: EventSendConfirmationEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_CONFIRMATION_EMAIL - - return this - } - - public setEventSendAccountMultiRegistrationEmail( - ev: EventSendAccountMultiRegistrationEmail, - ): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL - - return this - } - - public setEventSendForgotPasswordEmail(ev: EventSendForgotPasswordEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.SEND_FORGOT_PASSWORD_EMAIL - - return this - } - - public setEventSendTransactionSendEmail(ev: EventSendTransactionSendEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_SEND_EMAIL - - return this - } - - public setEventSendTransactionReceiveEmail(ev: EventSendTransactionReceiveEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_RECEIVE_EMAIL - - return this - } - - public setEventSendTransactionLinkRedeemEmail(ev: EventSendTransactionLinkRedeemEmail): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.SEND_TRANSACTION_LINK_REDEEM_EMAIL - - return this - } - - public setEventSendAddedContributionEmail(ev: EventSendAddedContributionEmail): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.SEND_ADDED_CONTRIBUTION_EMAIL - - return this - } - - public setEventSendContributionConfirmEmail(ev: EventSendContributionConfirmEmail): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.SEND_CONTRIBUTION_CONFIRM_EMAIL - - return this - } - - public setEventConfirmationEmail(ev: EventConfirmationEmail): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.CONFIRM_EMAIL - - return this - } - - public setEventRegisterEmailKlicktipp(ev: EventRegisterEmailKlicktipp): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.REGISTER_EMAIL_KLICKTIPP - - return this - } - - public setEventLogin(ev: EventLogin): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.LOGIN - - return this - } - - public setEventLogout(ev: EventLogout): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.LOGOUT - - return this - } - - public setEventRedeemLogin(ev: EventRedeemLogin): Event { - this.setByBasicRedeem(ev.userId, ev.transactionId, ev.contributionId) - this.type = EventProtocolType.REDEEM_LOGIN - - return this - } - - public setEventActivateAccount(ev: EventActivateAccount): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.ACTIVATE_ACCOUNT - - return this - } - - public setEventPasswordChange(ev: EventPasswordChange): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.PASSWORD_CHANGE - - return this - } - - public setEventTransactionSend(ev: EventTransactionSend): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_SEND - - return this - } - - public setEventTransactionSendRedeem(ev: EventTransactionSendRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_SEND_REDEEM - - return this - } - - public setEventTransactionRepeateRedeem(ev: EventTransactionRepeateRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_REPEATE_REDEEM - - return this - } - - public setEventTransactionCreation(ev: EventTransactionCreation): Event { - this.setByBasicTx(ev.userId, ev.transactionId, ev.amount) - this.type = EventProtocolType.TRANSACTION_CREATION - - return this - } - - public setEventTransactionReceive(ev: EventTransactionReceive): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_RECEIVE - - return this - } - - public setEventTransactionReceiveRedeem(ev: EventTransactionReceiveRedeem): Event { - this.setByBasicTxX(ev.userId, ev.transactionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.TRANSACTION_RECEIVE_REDEEM - - return this - } - - public setEventContributionCreate(ev: EventContributionCreate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_CREATE - - return this - } - - public setEventAdminContributionCreate(ev: EventAdminContributionCreate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_CREATE - - return this - } - - public setEventAdminContributionDelete(ev: EventAdminContributionDelete): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_DELETE - - return this - } - - public setEventAdminContributionDeny(ev: EventAdminContributionDeny): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_DENY - - return this - } - - public setEventAdminContributionUpdate(ev: EventAdminContributionUpdate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_CONTRIBUTION_UPDATE - - return this - } - - public setEventUserCreateContributionMessage(ev: EventUserCreateContributionMessage): Event { - this.setByBasicCtMsg(ev.userId, ev.contributionId, ev.amount, ev.messageId) - this.type = EventProtocolType.USER_CREATE_CONTRIBUTION_MESSAGE - - return this - } - - public setEventAdminCreateContributionMessage(ev: EventAdminCreateContributionMessage): Event { - this.setByBasicCtMsg(ev.userId, ev.contributionId, ev.amount, ev.messageId) - this.type = EventProtocolType.ADMIN_CREATE_CONTRIBUTION_MESSAGE - - return this - } - - public setEventContributionDelete(ev: EventContributionDelete): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_DELETE - - return this - } - - public setEventContributionUpdate(ev: EventContributionUpdate): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_UPDATE - - return this - } - - public setEventContributionConfirm(ev: EventContributionConfirm): Event { - this.setByBasicCtX(ev.userId, ev.contributionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.CONTRIBUTION_CONFIRM - - return this - } - - public setEventContributionDeny(ev: EventContributionDeny): Event { - this.setByBasicCtX(ev.userId, ev.contributionId, ev.amount, ev.xUserId, ev.xCommunityId) - this.type = EventProtocolType.CONTRIBUTION_DENY - - return this - } - - public setEventContributionLinkDefine(ev: EventContributionLinkDefine): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_LINK_DEFINE - - return this - } - - public setEventContributionLinkActivateRedeem(ev: EventContributionLinkActivateRedeem): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CONTRIBUTION_LINK_ACTIVATE_REDEEM - - return this - } - - public setEventDeleteUser(ev: EventDeleteUser): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.DELETE_USER - - return this - } - - public setEventUndeleteUser(ev: EventUndeleteUser): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.UNDELETE_USER - - return this - } - - public setEventChangeUserRole(ev: EventChangeUserRole): Event { - this.setByBasicUser(ev.userId) - this.type = EventProtocolType.CHANGE_USER_ROLE - - return this - } - - public setEventAdminUpdateContribution(ev: EventAdminUpdateContribution): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_UPDATE_CONTRIBUTION - - return this - } - - public setEventAdminDeleteContribution(ev: EventAdminDeleteContribution): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.ADMIN_DELETE_CONTRIBUTION - - return this - } - - public setEventCreateContributionLink(ev: EventCreateContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.CREATE_CONTRIBUTION_LINK - - return this - } - - public setEventDeleteContributionLink(ev: EventDeleteContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.DELETE_CONTRIBUTION_LINK - - return this - } - - public setEventUpdateContributionLink(ev: EventUpdateContributionLink): Event { - this.setByBasicCt(ev.userId, ev.contributionId, ev.amount) - this.type = EventProtocolType.UPDATE_CONTRIBUTION_LINK - - return this - } - - setByBasicUser(userId: number): Event { - this.setEventBasic() - this.userId = userId - - return this - } - - setByBasicTx(userId: number, transactionId: number, amount: decimal): Event { - this.setByBasicUser(userId) - this.transactionId = transactionId - this.amount = amount - - return this - } - - setByBasicTxX( +export class Event extends dbEvent { + constructor( + type: EventProtocolType, userId: number, - transactionId: number, - amount: decimal, - xUserId: number, - xCommunityId: number, - ): Event { - this.setByBasicTx(userId, transactionId, amount) + xUserId: number | null = null, + xCommunityId: number | null = null, + transactionId: number | null = null, + contributionId: number | null = null, + amount: Decimal | null = null, + messageId: number | null = null, + autosave = true, + ) { + super() + this.type = type + this.userId = userId this.xUserId = xUserId this.xCommunityId = xCommunityId - - return this - } - - setByBasicCt(userId: number, contributionId: number, amount: decimal): Event { - this.setByBasicUser(userId) + this.transactionId = transactionId this.contributionId = contributionId this.amount = amount - - return this - } - - setByBasicCtMsg( - userId: number, - contributionId: number, - amount: decimal, - messageId: number, - ): Event { - this.setByBasicCt(userId, contributionId, amount) this.messageId = messageId - return this + if (autosave) { + // This is unsafe, since we cannot wait for this in the constructor - the saving process is async therefore + this.save() + } } - - setByBasicCtX( - userId: number, - contributionId: number, - amount: decimal, - xUserId: number, - xCommunityId: number, - ): Event { - this.setByBasicCt(userId, contributionId, amount) - this.xUserId = xUserId - this.xCommunityId = xCommunityId - - return this - } - - setByBasicRedeem(userId: number, transactionId?: number, contributionId?: number): Event { - this.setByBasicUser(userId) - if (transactionId) this.transactionId = transactionId - if (contributionId) this.contributionId = contributionId - - return this - } - - id: number - type: string - createdAt: Date - userId: number - xUserId?: number - xCommunityId?: number - transactionId?: number - contributionId?: number - amount?: decimal - messageId?: number } + +export const EVENT_CONTRIBUTION_CREATE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.CONTRIBUTION_CREATE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) +export const EVENT_CONTRIBUTION_DELETE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.CONTRIBUTION_DELETE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) +export const EVENT_CONTRIBUTION_UPDATE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.CONTRIBUTION_UPDATE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_ADMIN_CONTRIBUTION_CREATE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.ADMIN_CONTRIBUTION_CREATE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_ADMIN_CONTRIBUTION_UPDATE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_ADMIN_CONTRIBUTION_DELETE = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.ADMIN_CONTRIBUTION_DELETE, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_CONTRIBUTION_CONFIRM = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.CONTRIBUTION_CONFIRM, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_ADMIN_CONTRIBUTION_DENY = ( + userId: number, + contributionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.ADMIN_CONTRIBUTION_DENY, + userId, + null, + null, + null, + contributionId, + amount, + null, + autosave, + ) + +export const EVENT_TRANSACTION_SEND = ( + userId: number, + xUserId: number, + transactionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.TRANSACTION_SEND, + userId, + xUserId, + null, + transactionId, + null, + amount, + null, + autosave, + ) + +export const EVENT_TRANSACTION_RECEIVE = ( + userId: number, + xUserId: number, + transactionId: number, + amount: Decimal, + autosave = true, +): Event => + new Event( + EventProtocolType.TRANSACTION_RECEIVE, + userId, + xUserId, + null, + transactionId, + null, + amount, + null, + autosave, + ) + +export const EVENT_LOGIN = (userId: number, autosave = true): Event => + new Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null, autosave) + +export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = ( + userId: number, + autosave = true, +): Event => + new Event( + EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + userId, + null, + null, + null, + null, + null, + null, + autosave, + ) + +export const EVENT_SEND_CONFIRMATION_EMAIL = (userId: number, autosave = true): Event => + new Event( + EventProtocolType.SEND_CONFIRMATION_EMAIL, + userId, + null, + null, + null, + null, + null, + null, + autosave, + ) + +export const EVENT_REDEEM_REGISTER = ( + userId: number, + transactionId: number | null = null, + contributionId: number | null = null, + autosave = true, +): Event => + new Event( + EventProtocolType.REDEEM_REGISTER, + userId, + null, + null, + transactionId, + contributionId, + null, + null, + autosave, + ) + +export const EVENT_REGISTER = (userId: number, autosave = true): Event => + new Event(EventProtocolType.REGISTER, userId, null, null, null, null, null, null, autosave) + +export const EVENT_ACTIVATE_ACCOUNT = (userId: number, autosave = true): Event => + new Event( + EventProtocolType.ACTIVATE_ACCOUNT, + userId, + null, + null, + null, + null, + null, + null, + autosave, + ) diff --git a/backend/src/event/EventProtocolEmitter.ts b/backend/src/event/EventProtocolEmitter.ts deleted file mode 100644 index a87e8a256..000000000 --- a/backend/src/event/EventProtocolEmitter.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Event } from '@/event/Event' -import { backendLogger as logger } from '@/server/logger' -import { EventProtocol } from '@entity/EventProtocol' - -export const writeEvent = async (event: Event): Promise => { - logger.info('writeEvent', event) - const dbEvent = new EventProtocol() - dbEvent.type = event.type - dbEvent.createdAt = event.createdAt - dbEvent.userId = event.userId - dbEvent.xUserId = event.xUserId || null - dbEvent.xCommunityId = event.xCommunityId || null - dbEvent.contributionId = event.contributionId || null - dbEvent.transactionId = event.transactionId || null - dbEvent.amount = event.amount || null - return dbEvent.save() -} diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index c46a49555..b374275c4 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -37,17 +37,15 @@ import { } from './util/creations' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS, FULL_CREATION_AVAILABLE } from './const/const' import { - Event, - EventContributionCreate, - EventContributionDelete, - EventContributionUpdate, - EventContributionConfirm, - EventAdminContributionCreate, - EventAdminContributionDelete, - EventAdminContributionDeny, - EventAdminContributionUpdate, + EVENT_CONTRIBUTION_CREATE, + EVENT_CONTRIBUTION_DELETE, + EVENT_CONTRIBUTION_UPDATE, + EVENT_ADMIN_CONTRIBUTION_CREATE, + EVENT_ADMIN_CONTRIBUTION_UPDATE, + EVENT_ADMIN_CONTRIBUTION_DELETE, + EVENT_CONTRIBUTION_CONFIRM, + EVENT_ADMIN_CONTRIBUTION_DENY, } from '@/event/Event' -import { writeEvent } from '@/event/EventProtocolEmitter' import { calculateDecay } from '@/util/decay' import { sendContributionConfirmedEmail, @@ -77,8 +75,6 @@ export class ContributionResolver { throw new Error(`memo text is too short (${MEMO_MIN_CHARS} characters minimum)`) } - const event = new Event() - const user = getUser(context) const creations = await getUserCreation(user.id, clientTimezoneOffset) logger.trace('creations', creations) @@ -97,11 +93,7 @@ export class ContributionResolver { logger.trace('contribution to save', contribution) await DbContribution.save(contribution) - const eventCreateContribution = new EventContributionCreate() - eventCreateContribution.userId = user.id - eventCreateContribution.amount = amount - eventCreateContribution.contributionId = contribution.id - await writeEvent(event.setEventContributionCreate(eventCreateContribution)) + EVENT_CONTRIBUTION_CREATE(user.id, contribution.id, amount) return new UnconfirmedContribution(contribution, user, creations) } @@ -112,7 +104,6 @@ export class ContributionResolver { @Arg('id', () => Int) id: number, @Ctx() context: Context, ): Promise { - const event = new Event() const user = getUser(context) const contribution = await DbContribution.findOne(id) if (!contribution) { @@ -133,11 +124,7 @@ export class ContributionResolver { contribution.deletedAt = new Date() await contribution.save() - const eventDeleteContribution = new EventContributionDelete() - eventDeleteContribution.userId = user.id - eventDeleteContribution.contributionId = contribution.id - eventDeleteContribution.amount = contribution.amount - await writeEvent(event.setEventContributionDelete(eventDeleteContribution)) + EVENT_CONTRIBUTION_DELETE(user.id, contribution.id, contribution.amount) const res = await contribution.softRemove() return !!res @@ -288,13 +275,7 @@ export class ContributionResolver { contributionToUpdate.updatedAt = new Date() DbContribution.save(contributionToUpdate) - const event = new Event() - - const eventUpdateContribution = new EventContributionUpdate() - eventUpdateContribution.userId = user.id - eventUpdateContribution.contributionId = contributionId - eventUpdateContribution.amount = amount - await writeEvent(event.setEventContributionUpdate(eventUpdateContribution)) + EVENT_CONTRIBUTION_UPDATE(user.id, contributionId, amount) return new UnconfirmedContribution(contributionToUpdate, user, creations) } @@ -335,7 +316,6 @@ export class ContributionResolver { throw new Error('Contribution could not be saved, Email is not activated') } - const event = new Event() const moderator = getUser(context) logger.trace('moderator: ', moderator.id) const creations = await getUserCreation(emailContact.userId, clientTimezoneOffset) @@ -357,11 +337,7 @@ export class ContributionResolver { await DbContribution.save(contribution) - const eventAdminCreateContribution = new EventAdminContributionCreate() - eventAdminCreateContribution.userId = moderator.id - eventAdminCreateContribution.amount = amount - eventAdminCreateContribution.contributionId = contribution.id - await writeEvent(event.setEventAdminContributionCreate(eventAdminCreateContribution)) + EVENT_ADMIN_CONTRIBUTION_CREATE(moderator.id, contribution.id, amount) return getUserCreation(emailContact.userId, clientTimezoneOffset) } @@ -466,12 +442,7 @@ export class ContributionResolver { result.creation = await getUserCreation(user.id, clientTimezoneOffset) - const event = new Event() - const eventAdminContributionUpdate = new EventAdminContributionUpdate() - eventAdminContributionUpdate.userId = user.id - eventAdminContributionUpdate.amount = amount - eventAdminContributionUpdate.contributionId = contributionToUpdate.id - await writeEvent(event.setEventAdminContributionUpdate(eventAdminContributionUpdate)) + EVENT_ADMIN_CONTRIBUTION_UPDATE(user.id, contributionToUpdate.id, amount) return result } @@ -544,12 +515,8 @@ export class ContributionResolver { await contribution.save() const res = await contribution.softRemove() - const event = new Event() - const eventAdminContributionDelete = new EventAdminContributionDelete() - eventAdminContributionDelete.userId = contribution.userId - eventAdminContributionDelete.amount = contribution.amount - eventAdminContributionDelete.contributionId = contribution.id - await writeEvent(event.setEventAdminContributionDelete(eventAdminContributionDelete)) + EVENT_ADMIN_CONTRIBUTION_DELETE(contribution.userId, contribution.id, contribution.amount) + sendContributionDeletedEmail({ firstName: user.firstName, lastName: user.lastName, @@ -667,12 +634,7 @@ export class ContributionResolver { await queryRunner.release() } - const event = new Event() - const eventContributionConfirm = new EventContributionConfirm() - eventContributionConfirm.userId = user.id - eventContributionConfirm.amount = contribution.amount - eventContributionConfirm.contributionId = contribution.id - await writeEvent(event.setEventContributionConfirm(eventContributionConfirm)) + EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount) } finally { releaseLock() } @@ -766,12 +728,11 @@ export class ContributionResolver { contributionToUpdate.deniedAt = new Date() const res = await contributionToUpdate.save() - const event = new Event() - const eventAdminContributionDeny = new EventAdminContributionDeny() - eventAdminContributionDeny.userId = contributionToUpdate.userId - eventAdminContributionDeny.amount = contributionToUpdate.amount - eventAdminContributionDeny.contributionId = contributionToUpdate.id - await writeEvent(event.setEventAdminContributionDeny(eventAdminContributionDeny)) + EVENT_ADMIN_CONTRIBUTION_DENY( + contributionToUpdate.userId, + contributionToUpdate.id, + contributionToUpdate.amount, + ) sendContributionDeniedEmail({ firstName: user.firstName, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index bedf8c533..1d3864f27 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -29,8 +29,7 @@ import { sendTransactionLinkRedeemedEmail, sendTransactionReceivedEmail, } from '@/emails/sendEmailVariants' -import { Event, EventTransactionReceive, EventTransactionSend } from '@/event/Event' -import { writeEvent } from '@/event/EventProtocolEmitter' +import { EVENT_TRANSACTION_RECEIVE, EVENT_TRANSACTION_SEND } from '@/event/Event' import { BalanceResolver } from './BalanceResolver' import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const' @@ -141,19 +140,19 @@ export const executeTransaction = async ( await queryRunner.commitTransaction() logger.info(`commit Transaction successful...`) - const eventTransactionSend = new EventTransactionSend() - eventTransactionSend.userId = transactionSend.userId - eventTransactionSend.xUserId = transactionSend.linkedUserId - eventTransactionSend.transactionId = transactionSend.id - eventTransactionSend.amount = transactionSend.amount.mul(-1) - await writeEvent(new Event().setEventTransactionSend(eventTransactionSend)) + EVENT_TRANSACTION_SEND( + transactionSend.userId, + transactionSend.linkedUserId, + transactionSend.id, + transactionSend.amount.mul(-1), + ) - const eventTransactionReceive = new EventTransactionReceive() - eventTransactionReceive.userId = transactionReceive.userId - eventTransactionReceive.xUserId = transactionReceive.linkedUserId - eventTransactionReceive.transactionId = transactionReceive.id - eventTransactionReceive.amount = transactionReceive.amount - await writeEvent(new Event().setEventTransactionReceive(eventTransactionReceive)) + EVENT_TRANSACTION_RECEIVE( + transactionReceive.userId, + transactionReceive.linkedUserId, + transactionReceive.id, + transactionReceive.amount, + ) } catch (e) { await queryRunner.rollbackTransaction() logger.error(`Transaction was not successful: ${e}`) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 5ebf5b445..7a9eebe65 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -48,15 +48,13 @@ import { klicktippNewsletterStateMiddleware } from '@/middleware/klicktippMiddle import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' -import { writeEvent } from '@/event/EventProtocolEmitter' import { - Event, - EventLogin, - EventRedeemRegister, - EventRegister, - EventSendAccountMultiRegistrationEmail, - EventSendConfirmationEmail, - EventActivateAccount, + EVENT_LOGIN, + EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + EVENT_SEND_CONFIRMATION_EMAIL, + EVENT_REDEEM_REGISTER, + EVENT_REGISTER, + EVENT_ACTIVATE_ACCOUNT, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -177,9 +175,9 @@ export class UserResolver { key: 'token', value: encode(dbUser.gradidoID), }) - const ev = new EventLogin() - ev.userId = user.id - writeEvent(new Event().setEventLogin(ev)) + + EVENT_LOGIN(user.id) + logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -211,7 +209,6 @@ export class UserResolver { ) // TODO: wrong default value (should be null), how does graphql work here? Is it an required field? // default int publisher_id = 0; - const event = new Event() // Validate Language (no throw) if (!language || !isLanguage(language)) { @@ -249,9 +246,9 @@ export class UserResolver { email, language: foundUser.language, // use language of the emails owner for sending }) - const eventSendAccountMultiRegistrationEmail = new EventSendAccountMultiRegistrationEmail() - eventSendAccountMultiRegistrationEmail.userId = foundUser.id - writeEvent(event.setEventSendConfirmationEmail(eventSendAccountMultiRegistrationEmail)) + + EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser.id) + logger.info( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, ) @@ -268,10 +265,7 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegister = new EventRegister() - const eventRedeemRegister = new EventRedeemRegister() - const eventSendConfirmEmail = new EventSendConfirmationEmail() - + const eventRegisterRedeem = EVENT_REDEEM_REGISTER(0, null, null, false) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName @@ -288,14 +282,14 @@ export class UserResolver { logger.info('redeemCode found contributionLink=' + contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id - eventRedeemRegister.contributionId = contributionLink.id + eventRegisterRedeem.contributionId = contributionLink.id } } else { const transactionLink = await DbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink=' + transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId - eventRedeemRegister.transactionId = transactionLink.id + eventRegisterRedeem.transactionId = transactionLink.id } } } @@ -333,8 +327,9 @@ export class UserResolver { timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) - eventSendConfirmEmail.userId = dbUser.id - writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmEmail)) + + // TODO: this event is used twice, why? + EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -351,11 +346,10 @@ export class UserResolver { logger.info('createUser() successful...') if (redeemCode) { - eventRedeemRegister.userId = dbUser.id - await writeEvent(event.setEventRedeemRegister(eventRedeemRegister)) + eventRegisterRedeem.userId = dbUser.id + eventRegisterRedeem.save() } else { - eventRegister.userId = dbUser.id - await writeEvent(event.setEventRegister(eventRegister)) + EVENT_REGISTER(dbUser.id) } return new User(dbUser) @@ -458,8 +452,6 @@ export class UserResolver { await queryRunner.connect() await queryRunner.startTransaction('REPEATABLE READ') - const event = new Event() - try { // Save user await queryRunner.manager.save(user).catch((error) => { @@ -473,9 +465,7 @@ export class UserResolver { await queryRunner.commitTransaction() logger.info('User and UserContact data written successfully...') - const eventActivateAccount = new EventActivateAccount() - eventActivateAccount.userId = user.id - writeEvent(event.setEventActivateAccount(eventActivateAccount)) + EVENT_ACTIVATE_ACCOUNT(user.id) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing User and User Contact data', e) @@ -819,10 +809,8 @@ export class UserResolver { if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) } else { - const event = new Event() - const eventSendConfirmationEmail = new EventSendConfirmationEmail() - eventSendConfirmationEmail.userId = user.id - await writeEvent(event.setEventSendConfirmationEmail(eventSendConfirmationEmail)) + // TODO: this event is used twice, why? + EVENT_SEND_CONFIRMATION_EMAIL(user.id) } return true From f9b6b887e0e0aae32e0f88072dc38f529c4357f5 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 10 Feb 2023 03:37:48 +0100 Subject: [PATCH 15/44] things are working properly --- backend/src/event/Event.ts | 157 +++++++----------- backend/src/event/EventProtocolType.ts | 64 +++---- .../graphql/resolver/ContributionResolver.ts | 16 +- .../graphql/resolver/TransactionResolver.ts | 4 +- backend/src/graphql/resolver/UserResolver.ts | 26 +-- 5 files changed, 117 insertions(+), 150 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 2545bdc63..310bf6601 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -1,8 +1,9 @@ -import { EventProtocol as dbEvent } from '@entity/EventProtocol' +import { EventProtocol as DbEvent } from '@entity/EventProtocol' import Decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' -export class Event extends dbEvent { +export class Event { + event: DbEvent constructor( type: EventProtocolType, userId: number, @@ -12,31 +13,28 @@ export class Event extends dbEvent { contributionId: number | null = null, amount: Decimal | null = null, messageId: number | null = null, - autosave = true, ) { - super() - this.type = type - this.userId = userId - this.xUserId = xUserId - this.xCommunityId = xCommunityId - this.transactionId = transactionId - this.contributionId = contributionId - this.amount = amount - this.messageId = messageId + this.event = new DbEvent() + this.event.type = type + this.event.userId = userId + this.event.xUserId = xUserId + this.event.xCommunityId = xCommunityId + this.event.transactionId = transactionId + this.event.contributionId = contributionId + this.event.amount = amount + this.event.messageId = messageId + } - if (autosave) { - // This is unsafe, since we cannot wait for this in the constructor - the saving process is async therefore - this.save() - } + save(): Promise { + return this.event.save() } } -export const EVENT_CONTRIBUTION_CREATE = ( +export const EVENT_CONTRIBUTION_CREATE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.CONTRIBUTION_CREATE, userId, @@ -46,14 +44,13 @@ export const EVENT_CONTRIBUTION_CREATE = ( contributionId, amount, null, - autosave, - ) -export const EVENT_CONTRIBUTION_DELETE = ( + ).save() + +export const EVENT_CONTRIBUTION_DELETE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.CONTRIBUTION_DELETE, userId, @@ -63,14 +60,13 @@ export const EVENT_CONTRIBUTION_DELETE = ( contributionId, amount, null, - autosave, - ) -export const EVENT_CONTRIBUTION_UPDATE = ( + ).save() + +export const EVENT_CONTRIBUTION_UPDATE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.CONTRIBUTION_UPDATE, userId, @@ -80,15 +76,13 @@ export const EVENT_CONTRIBUTION_UPDATE = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_ADMIN_CONTRIBUTION_CREATE = ( +export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.ADMIN_CONTRIBUTION_CREATE, userId, @@ -98,15 +92,13 @@ export const EVENT_ADMIN_CONTRIBUTION_CREATE = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_ADMIN_CONTRIBUTION_UPDATE = ( +export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, userId, @@ -116,15 +108,13 @@ export const EVENT_ADMIN_CONTRIBUTION_UPDATE = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_ADMIN_CONTRIBUTION_DELETE = ( +export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.ADMIN_CONTRIBUTION_DELETE, userId, @@ -134,15 +124,13 @@ export const EVENT_ADMIN_CONTRIBUTION_DELETE = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_CONTRIBUTION_CONFIRM = ( +export const EVENT_CONTRIBUTION_CONFIRM = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.CONTRIBUTION_CONFIRM, userId, @@ -152,15 +140,13 @@ export const EVENT_CONTRIBUTION_CONFIRM = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_ADMIN_CONTRIBUTION_DENY = ( +export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( userId: number, contributionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.ADMIN_CONTRIBUTION_DENY, userId, @@ -170,16 +156,14 @@ export const EVENT_ADMIN_CONTRIBUTION_DENY = ( contributionId, amount, null, - autosave, - ) + ).save() -export const EVENT_TRANSACTION_SEND = ( +export const EVENT_TRANSACTION_SEND = async ( userId: number, xUserId: number, transactionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.TRANSACTION_SEND, userId, @@ -189,16 +173,14 @@ export const EVENT_TRANSACTION_SEND = ( null, amount, null, - autosave, - ) + ).save() -export const EVENT_TRANSACTION_RECEIVE = ( +export const EVENT_TRANSACTION_RECEIVE = async ( userId: number, xUserId: number, transactionId: number, amount: Decimal, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.TRANSACTION_RECEIVE, userId, @@ -208,16 +190,14 @@ export const EVENT_TRANSACTION_RECEIVE = ( null, amount, null, - autosave, - ) + ).save() -export const EVENT_LOGIN = (userId: number, autosave = true): Event => - new Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null, autosave) +export const EVENT_LOGIN = async (userId: number): Promise => + new Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() -export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = ( +export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( userId: number, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId, @@ -227,10 +207,9 @@ export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = ( null, null, null, - autosave, - ) + ).save() -export const EVENT_SEND_CONFIRMATION_EMAIL = (userId: number, autosave = true): Event => +export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => new Event( EventProtocolType.SEND_CONFIRMATION_EMAIL, userId, @@ -240,15 +219,13 @@ export const EVENT_SEND_CONFIRMATION_EMAIL = (userId: number, autosave = true): null, null, null, - autosave, - ) + ).save() -export const EVENT_REDEEM_REGISTER = ( +/* export const EVENT_REDEEM_REGISTER = async ( userId: number, transactionId: number | null = null, contributionId: number | null = null, - autosave = true, -): Event => +): Promise => new Event( EventProtocolType.REDEEM_REGISTER, userId, @@ -258,21 +235,11 @@ export const EVENT_REDEEM_REGISTER = ( contributionId, null, null, - autosave, - ) + ).save() +*/ -export const EVENT_REGISTER = (userId: number, autosave = true): Event => - new Event(EventProtocolType.REGISTER, userId, null, null, null, null, null, null, autosave) +export const EVENT_REGISTER = async (userId: number): Promise => + new Event(EventProtocolType.REGISTER, userId, null, null, null, null, null, null).save() -export const EVENT_ACTIVATE_ACCOUNT = (userId: number, autosave = true): Event => - new Event( - EventProtocolType.ACTIVATE_ACCOUNT, - userId, - null, - null, - null, - null, - null, - null, - autosave, - ) +export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => + new Event(EventProtocolType.ACTIVATE_ACCOUNT, userId, null, null, null, null, null, null).save() diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts index ccd15d238..b3ade4c5c 100644 --- a/backend/src/event/EventProtocolType.ts +++ b/backend/src/event/EventProtocolType.ts @@ -1,50 +1,50 @@ export enum EventProtocolType { - BASIC = 'BASIC', - VISIT_GRADIDO = 'VISIT_GRADIDO', + // BASIC = 'BASIC', + // VISIT_GRADIDO = 'VISIT_GRADIDO', REGISTER = 'REGISTER', REDEEM_REGISTER = 'REDEEM_REGISTER', - VERIFY_REDEEM = 'VERIFY_REDEEM', - INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', + // VERIFY_REDEEM = 'VERIFY_REDEEM', + // INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', - CONFIRM_EMAIL = 'CONFIRM_EMAIL', - REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', + // CONFIRM_EMAIL = 'CONFIRM_EMAIL', + // REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', LOGIN = 'LOGIN', - LOGOUT = 'LOGOUT', - REDEEM_LOGIN = 'REDEEM_LOGIN', + // LOGOUT = 'LOGOUT', + // REDEEM_LOGIN = 'REDEEM_LOGIN', ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT', - SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL', - PASSWORD_CHANGE = 'PASSWORD_CHANGE', - SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL', - SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL', + // SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL', + // PASSWORD_CHANGE = 'PASSWORD_CHANGE', + // SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL', + // SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL', TRANSACTION_SEND = 'TRANSACTION_SEND', - TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM', - TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM', - TRANSACTION_CREATION = 'TRANSACTION_CREATION', + // TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM', + // TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM', + // TRANSACTION_CREATION = 'TRANSACTION_CREATION', TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE', - TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM', - SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL', - SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL', - SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL', + // TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM', + // SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL', + // SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL', + // SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL', CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE', CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM', - CONTRIBUTION_DENY = 'CONTRIBUTION_DENY', - CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE', - CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM', + // CONTRIBUTION_DENY = 'CONTRIBUTION_DENY', + // CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE', + // CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM', CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE', CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE', ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE', ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE', ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY', ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE', - USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE', - ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', - DELETE_USER = 'DELETE_USER', - UNDELETE_USER = 'UNDELETE_USER', - CHANGE_USER_ROLE = 'CHANGE_USER_ROLE', - ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', - ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', - CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', - DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', - UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', + // USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE', + // ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', + // DELETE_USER = 'DELETE_USER', + // UNDELETE_USER = 'UNDELETE_USER', + // CHANGE_USER_ROLE = 'CHANGE_USER_ROLE', + // ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', + // ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', + // CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK', + // DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK', + // UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', } diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index b374275c4..62001c6e8 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -93,7 +93,7 @@ export class ContributionResolver { logger.trace('contribution to save', contribution) await DbContribution.save(contribution) - EVENT_CONTRIBUTION_CREATE(user.id, contribution.id, amount) + await EVENT_CONTRIBUTION_CREATE(user.id, contribution.id, amount) return new UnconfirmedContribution(contribution, user, creations) } @@ -124,7 +124,7 @@ export class ContributionResolver { contribution.deletedAt = new Date() await contribution.save() - EVENT_CONTRIBUTION_DELETE(user.id, contribution.id, contribution.amount) + await EVENT_CONTRIBUTION_DELETE(user.id, contribution.id, contribution.amount) const res = await contribution.softRemove() return !!res @@ -275,7 +275,7 @@ export class ContributionResolver { contributionToUpdate.updatedAt = new Date() DbContribution.save(contributionToUpdate) - EVENT_CONTRIBUTION_UPDATE(user.id, contributionId, amount) + await EVENT_CONTRIBUTION_UPDATE(user.id, contributionId, amount) return new UnconfirmedContribution(contributionToUpdate, user, creations) } @@ -337,7 +337,7 @@ export class ContributionResolver { await DbContribution.save(contribution) - EVENT_ADMIN_CONTRIBUTION_CREATE(moderator.id, contribution.id, amount) + await EVENT_ADMIN_CONTRIBUTION_CREATE(moderator.id, contribution.id, amount) return getUserCreation(emailContact.userId, clientTimezoneOffset) } @@ -442,7 +442,7 @@ export class ContributionResolver { result.creation = await getUserCreation(user.id, clientTimezoneOffset) - EVENT_ADMIN_CONTRIBUTION_UPDATE(user.id, contributionToUpdate.id, amount) + await EVENT_ADMIN_CONTRIBUTION_UPDATE(user.id, contributionToUpdate.id, amount) return result } @@ -515,7 +515,7 @@ export class ContributionResolver { await contribution.save() const res = await contribution.softRemove() - EVENT_ADMIN_CONTRIBUTION_DELETE(contribution.userId, contribution.id, contribution.amount) + await EVENT_ADMIN_CONTRIBUTION_DELETE(contribution.userId, contribution.id, contribution.amount) sendContributionDeletedEmail({ firstName: user.firstName, @@ -634,7 +634,7 @@ export class ContributionResolver { await queryRunner.release() } - EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount) + await EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount) } finally { releaseLock() } @@ -728,7 +728,7 @@ export class ContributionResolver { contributionToUpdate.deniedAt = new Date() const res = await contributionToUpdate.save() - EVENT_ADMIN_CONTRIBUTION_DENY( + await EVENT_ADMIN_CONTRIBUTION_DENY( contributionToUpdate.userId, contributionToUpdate.id, contributionToUpdate.amount, diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index 1d3864f27..46fb812f7 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -140,14 +140,14 @@ export const executeTransaction = async ( await queryRunner.commitTransaction() logger.info(`commit Transaction successful...`) - EVENT_TRANSACTION_SEND( + await EVENT_TRANSACTION_SEND( transactionSend.userId, transactionSend.linkedUserId, transactionSend.id, transactionSend.amount.mul(-1), ) - EVENT_TRANSACTION_RECEIVE( + await EVENT_TRANSACTION_RECEIVE( transactionReceive.userId, transactionReceive.linkedUserId, transactionReceive.id, diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 7a9eebe65..96bb62aef 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -49,10 +49,10 @@ import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' import { + Event, EVENT_LOGIN, EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, EVENT_SEND_CONFIRMATION_EMAIL, - EVENT_REDEEM_REGISTER, EVENT_REGISTER, EVENT_ACTIVATE_ACCOUNT, } from '@/event/Event' @@ -62,6 +62,7 @@ import { FULL_CREATION_AVAILABLE } from './const/const' import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor' import { PasswordEncryptionType } from '../enum/PasswordEncryptionType' import LogError from '@/server/LogError' +import { EventProtocolType } from '@/event/EventProtocolType' // eslint-disable-next-line @typescript-eslint/no-var-requires const sodium = require('sodium-native') @@ -176,8 +177,7 @@ export class UserResolver { value: encode(dbUser.gradidoID), }) - EVENT_LOGIN(user.id) - + await EVENT_LOGIN(user.id) logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -247,7 +247,7 @@ export class UserResolver { language: foundUser.language, // use language of the emails owner for sending }) - EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser.id) + await EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser.id) logger.info( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, @@ -265,7 +265,7 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegisterRedeem = EVENT_REDEEM_REGISTER(0, null, null, false) + const eventRegisterRedeem = new Event(EventProtocolType.REDEEM_REGISTER, 0) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName @@ -282,14 +282,14 @@ export class UserResolver { logger.info('redeemCode found contributionLink=' + contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id - eventRegisterRedeem.contributionId = contributionLink.id + eventRegisterRedeem.event.contributionId = contributionLink.id } } else { const transactionLink = await DbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink=' + transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId - eventRegisterRedeem.transactionId = transactionLink.id + eventRegisterRedeem.event.transactionId = transactionLink.id } } } @@ -329,7 +329,7 @@ export class UserResolver { logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) // TODO: this event is used twice, why? - EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) + await EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -346,10 +346,10 @@ export class UserResolver { logger.info('createUser() successful...') if (redeemCode) { - eventRegisterRedeem.userId = dbUser.id - eventRegisterRedeem.save() + eventRegisterRedeem.event.userId = dbUser.id + await eventRegisterRedeem.save() } else { - EVENT_REGISTER(dbUser.id) + await EVENT_REGISTER(dbUser.id) } return new User(dbUser) @@ -465,7 +465,7 @@ export class UserResolver { await queryRunner.commitTransaction() logger.info('User and UserContact data written successfully...') - EVENT_ACTIVATE_ACCOUNT(user.id) + await EVENT_ACTIVATE_ACCOUNT(user.id) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing User and User Contact data', e) @@ -810,7 +810,7 @@ export class UserResolver { logger.info(`Account confirmation link: ${activationLink}`) } else { // TODO: this event is used twice, why? - EVENT_SEND_CONFIRMATION_EMAIL(user.id) + await EVENT_SEND_CONFIRMATION_EMAIL(user.id) } return true From 362226a94fcf938976c86c7816eca9b377dc22d3 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 10 Feb 2023 03:44:34 +0100 Subject: [PATCH 16/44] completely dismiss class based approach, use EVENT function --- backend/src/event/Event.ts | 78 +++++++++----------- backend/src/graphql/resolver/UserResolver.ts | 10 +-- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 310bf6601..09f2f7102 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -2,32 +2,26 @@ import { EventProtocol as DbEvent } from '@entity/EventProtocol' import Decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' -export class Event { - event: DbEvent - constructor( - type: EventProtocolType, - userId: number, - xUserId: number | null = null, - xCommunityId: number | null = null, - transactionId: number | null = null, - contributionId: number | null = null, - amount: Decimal | null = null, - messageId: number | null = null, - ) { - this.event = new DbEvent() - this.event.type = type - this.event.userId = userId - this.event.xUserId = xUserId - this.event.xCommunityId = xCommunityId - this.event.transactionId = transactionId - this.event.contributionId = contributionId - this.event.amount = amount - this.event.messageId = messageId - } - - save(): Promise { - return this.event.save() - } +export const EVENT = ( + type: EventProtocolType, + userId: number, + xUserId: number | null = null, + xCommunityId: number | null = null, + transactionId: number | null = null, + contributionId: number | null = null, + amount: Decimal | null = null, + messageId: number | null = null, +): DbEvent => { + const event = new DbEvent() + event.type = type + event.userId = userId + event.xUserId = xUserId + event.xCommunityId = xCommunityId + event.transactionId = transactionId + event.contributionId = contributionId + event.amount = amount + event.messageId = messageId + return event } export const EVENT_CONTRIBUTION_CREATE = async ( @@ -35,7 +29,7 @@ export const EVENT_CONTRIBUTION_CREATE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.CONTRIBUTION_CREATE, userId, null, @@ -51,7 +45,7 @@ export const EVENT_CONTRIBUTION_DELETE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.CONTRIBUTION_DELETE, userId, null, @@ -67,7 +61,7 @@ export const EVENT_CONTRIBUTION_UPDATE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.CONTRIBUTION_UPDATE, userId, null, @@ -83,7 +77,7 @@ export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.ADMIN_CONTRIBUTION_CREATE, userId, null, @@ -99,7 +93,7 @@ export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, userId, null, @@ -115,7 +109,7 @@ export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.ADMIN_CONTRIBUTION_DELETE, userId, null, @@ -131,7 +125,7 @@ export const EVENT_CONTRIBUTION_CONFIRM = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.CONTRIBUTION_CONFIRM, userId, null, @@ -147,7 +141,7 @@ export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( contributionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.ADMIN_CONTRIBUTION_DENY, userId, null, @@ -164,7 +158,7 @@ export const EVENT_TRANSACTION_SEND = async ( transactionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.TRANSACTION_SEND, userId, xUserId, @@ -181,7 +175,7 @@ export const EVENT_TRANSACTION_RECEIVE = async ( transactionId: number, amount: Decimal, ): Promise => - new Event( + EVENT( EventProtocolType.TRANSACTION_RECEIVE, userId, xUserId, @@ -193,12 +187,12 @@ export const EVENT_TRANSACTION_RECEIVE = async ( ).save() export const EVENT_LOGIN = async (userId: number): Promise => - new Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() + EVENT(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( userId: number, ): Promise => - new Event( + EVENT( EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId, null, @@ -210,7 +204,7 @@ export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( ).save() export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - new Event( + EVENT( EventProtocolType.SEND_CONFIRMATION_EMAIL, userId, null, @@ -226,7 +220,7 @@ export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - new Event( + EVENT( EventProtocolType.REDEEM_REGISTER, userId, null, @@ -239,7 +233,7 @@ export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - new Event(EventProtocolType.REGISTER, userId, null, null, null, null, null, null).save() + EVENT(EventProtocolType.REGISTER, userId, null, null, null, null, null, null).save() export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => - new Event(EventProtocolType.ACTIVATE_ACCOUNT, userId, null, null, null, null, null, null).save() + EVENT(EventProtocolType.ACTIVATE_ACCOUNT, userId, null, null, null, null, null, null).save() diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 96bb62aef..2aba1c538 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -49,7 +49,7 @@ import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' import { - Event, + EVENT, EVENT_LOGIN, EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, EVENT_SEND_CONFIRMATION_EMAIL, @@ -265,7 +265,7 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegisterRedeem = new Event(EventProtocolType.REDEEM_REGISTER, 0) + const eventRegisterRedeem = EVENT(EventProtocolType.REDEEM_REGISTER, 0) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName @@ -282,14 +282,14 @@ export class UserResolver { logger.info('redeemCode found contributionLink=' + contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id - eventRegisterRedeem.event.contributionId = contributionLink.id + eventRegisterRedeem.contributionId = contributionLink.id } } else { const transactionLink = await DbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink=' + transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId - eventRegisterRedeem.event.transactionId = transactionLink.id + eventRegisterRedeem.transactionId = transactionLink.id } } } @@ -346,7 +346,7 @@ export class UserResolver { logger.info('createUser() successful...') if (redeemCode) { - eventRegisterRedeem.event.userId = dbUser.id + eventRegisterRedeem.userId = dbUser.id await eventRegisterRedeem.save() } else { await EVENT_REGISTER(dbUser.id) From dac694e46f1f79c8434563fc2780f805a84a400a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 10 Feb 2023 03:49:45 +0100 Subject: [PATCH 17/44] require 79% backend coverage --- .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 da8521a76..41ea77400 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -526,7 +526,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 78 + min_coverage: 79 token: ${{ github.token }} ########################################################################## From fb84abeb8353485e70db00c5095ecb3d2309634d Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Fri, 10 Feb 2023 03:57:11 +0100 Subject: [PATCH 18/44] save some space --- backend/src/event/Event.ts | 39 ++++---------------------------------- 1 file changed, 4 insertions(+), 35 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 09f2f7102..196e997e2 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -37,7 +37,6 @@ export const EVENT_CONTRIBUTION_CREATE = async ( null, contributionId, amount, - null, ).save() export const EVENT_CONTRIBUTION_DELETE = async ( @@ -53,7 +52,6 @@ export const EVENT_CONTRIBUTION_DELETE = async ( null, contributionId, amount, - null, ).save() export const EVENT_CONTRIBUTION_UPDATE = async ( @@ -69,7 +67,6 @@ export const EVENT_CONTRIBUTION_UPDATE = async ( null, contributionId, amount, - null, ).save() export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( @@ -85,7 +82,6 @@ export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( null, contributionId, amount, - null, ).save() export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( @@ -101,7 +97,6 @@ export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( null, contributionId, amount, - null, ).save() export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( @@ -117,7 +112,6 @@ export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( null, contributionId, amount, - null, ).save() export const EVENT_CONTRIBUTION_CONFIRM = async ( @@ -133,7 +127,6 @@ export const EVENT_CONTRIBUTION_CONFIRM = async ( null, contributionId, amount, - null, ).save() export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( @@ -149,7 +142,6 @@ export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( null, contributionId, amount, - null, ).save() export const EVENT_TRANSACTION_SEND = async ( @@ -166,7 +158,6 @@ export const EVENT_TRANSACTION_SEND = async ( transactionId, null, amount, - null, ).save() export const EVENT_TRANSACTION_RECEIVE = async ( @@ -183,7 +174,6 @@ export const EVENT_TRANSACTION_RECEIVE = async ( transactionId, null, amount, - null, ).save() export const EVENT_LOGIN = async (userId: number): Promise => @@ -191,29 +181,10 @@ export const EVENT_LOGIN = async (userId: number): Promise => export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( userId: number, -): Promise => - EVENT( - EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, - userId, - null, - null, - null, - null, - null, - null, - ).save() +): Promise => EVENT(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save() export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - EVENT( - EventProtocolType.SEND_CONFIRMATION_EMAIL, - userId, - null, - null, - null, - null, - null, - null, - ).save() + EVENT(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() /* export const EVENT_REDEEM_REGISTER = async ( userId: number, @@ -227,13 +198,11 @@ export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - EVENT(EventProtocolType.REGISTER, userId, null, null, null, null, null, null).save() + EVENT(EventProtocolType.REGISTER, userId).save() export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => - EVENT(EventProtocolType.ACTIVATE_ACCOUNT, userId, null, null, null, null, null, null).save() + EVENT(EventProtocolType.ACTIVATE_ACCOUNT, userId).save() From 5a987b5d09a494d0be96178e57e3ef5397e9df80 Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 08:16:55 +0100 Subject: [PATCH 19/44] add yarn locales functionality to backend --- backend/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index f58a1ddd3..413ddeb4c 100644 --- a/backend/package.json +++ b/backend/package.json @@ -15,7 +15,8 @@ "lint": "eslint --max-warnings=0 --ext .js,.ts .", "test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles", "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts", - "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts" + "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts", + "locales": "scripts/sort.sh" }, "dependencies": { "@hyperswarm/dht": "^6.2.0", From fa72f9666a0038071c575034cc6092be8932a7c1 Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 08:21:39 +0100 Subject: [PATCH 20/44] sort backend locales using yarn lint --fix --- backend/src/locales/de.json | 10 +++++----- backend/src/locales/en.json | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/backend/src/locales/de.json b/backend/src/locales/de.json index 304ae2adc..530e8db10 100644 --- a/backend/src/locales/de.json +++ b/backend/src/locales/de.json @@ -1,10 +1,5 @@ { "emails": { - "addedContributionMessage": { - "commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.", - "subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag", - "toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!" - }, "accountActivation": { "duration": "Der Link hat eine Gültigkeit von {hours} Stunden und {minutes} Minuten. Sollte die Gültigkeit des Links bereits abgelaufen sein, kannst du dir hier einen neuen Link schicken lassen:", "emailRegistered": "deine E-Mail-Adresse wurde soeben bei Gradido registriert.", @@ -19,6 +14,11 @@ "onForgottenPasswordCopyLink": "oder kopiere den obigen Link in dein Browserfenster.", "subject": "Gradido: Erneuter Registrierungsversuch mit deiner E-Mail" }, + "addedContributionMessage": { + "commonGoodContributionMessage": "du hast zu deinem Gemeinwohl-Beitrag „{contributionMemo}“ eine Nachricht von {senderFirstName} {senderLastName} erhalten.", + "subject": "Gradido: Nachricht zu deinem Gemeinwohl-Beitrag", + "toSeeAndAnswerMessage": "Um die Nachricht zu sehen und darauf zu antworten, gehe in deinem Gradido-Konto ins Menü „Gemeinschaft“ auf den Tab „Meine Beiträge zum Gemeinwohl“!" + }, "contributionConfirmed": { "commonGoodContributionConfirmed": "dein Gemeinwohl-Beitrag „{contributionMemo}“ wurde soeben von {senderFirstName} {senderLastName} bestätigt und in deinem Gradido-Konto gutgeschrieben.", "subject": "Gradido: Dein Gemeinwohl-Beitrag wurde bestätigt" diff --git a/backend/src/locales/en.json b/backend/src/locales/en.json index bdc92b2cf..269c38629 100644 --- a/backend/src/locales/en.json +++ b/backend/src/locales/en.json @@ -1,10 +1,5 @@ { "emails": { - "addedContributionMessage": { - "commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", - "subject": "Gradido: Message about your common good contribution", - "toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!" - }, "accountActivation": { "duration": "The link has a validity of {hours} hours and {minutes} minutes. If the validity of the link has already expired, you can have a new link sent to you here:", "emailRegistered": "Your email address has just been registered with Gradido.", @@ -19,6 +14,11 @@ "onForgottenPasswordCopyLink": "or copy the link above into your browser window.", "subject": "Gradido: Try To Register Again With Your Email" }, + "addedContributionMessage": { + "commonGoodContributionMessage": "you have received a message from {senderFirstName} {senderLastName} regarding your common good contribution “{contributionMemo}”.", + "subject": "Gradido: Message about your common good contribution", + "toSeeAndAnswerMessage": "To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!" + }, "contributionConfirmed": { "commonGoodContributionConfirmed": "Your public good contribution “{contributionMemo}” has just been confirmed by {senderFirstName} {senderLastName} and credited to your Gradido account.", "subject": "Gradido: Your contribution to the common good was confirmed" From a3e2b34ff33f778d0bae754a5a7ff63aec27dc62 Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 08:30:58 +0100 Subject: [PATCH 21/44] add backend linting to github test workflow --- .github/workflows/test.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index da8521a76..695011c11 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -360,6 +360,35 @@ jobs: - name: backend | Lint run: docker run --rm gradido/backend:test yarn run lint + ############################################################################## + # JOB: LOCALES BACKEND ####################################################### + ############################################################################## + locales_frontend: + name: Locales - Backend + runs-on: ubuntu-latest + needs: [build_test_backend] + steps: + ########################################################################## + # CHECKOUT CODE ########################################################## + ########################################################################## + - name: Checkout code + uses: actions/checkout@v3 + ########################################################################## + # DOWNLOAD DOCKER IMAGE ################################################## + ########################################################################## + - name: Download Docker Image (Backend) + uses: actions/download-artifact@v3 + with: + name: docker-backendend-test + path: /tmp + - name: Load Docker Image + run: docker load < /tmp/backend.tar + ########################################################################## + # LOCALES FRONTEND ####################################################### + ########################################################################## + - name: Backendend | Locales + run: docker run --rm gradido/backend:test yarn run locales + ############################################################################## # JOB: LINT DATABASE UP ###################################################### ############################################################################## From ee5aea4c85dc85b6b42ac794c2edf1b52708379d Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 08:47:28 +0100 Subject: [PATCH 22/44] fix typo in github test workflow --- .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 695011c11..7b9bf09f2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -363,7 +363,7 @@ jobs: ############################################################################## # JOB: LOCALES BACKEND ####################################################### ############################################################################## - locales_frontend: + locales_backend: name: Locales - Backend runs-on: ubuntu-latest needs: [build_test_backend] From 592e1c11c51515ff93af70d1b4590db5848d102e Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 08:47:28 +0100 Subject: [PATCH 23/44] fix typo in github test workflow --- .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 7b9bf09f2..c1ff5b97c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -379,7 +379,7 @@ jobs: - name: Download Docker Image (Backend) uses: actions/download-artifact@v3 with: - name: docker-backendend-test + name: docker-backend-test path: /tmp - name: Load Docker Image run: docker load < /tmp/backend.tar From cf0e7f18ff4a5d7e37c6b847107d5fcff72644f5 Mon Sep 17 00:00:00 2001 From: mahula Date: Fri, 10 Feb 2023 09:04:36 +0100 Subject: [PATCH 24/44] add scripts for locales check to backend --- backend/scripts/sort.sh | 25 +++++++++++++++++++++++++ backend/scripts/sort_filter.jq | 13 +++++++++++++ 2 files changed, 38 insertions(+) create mode 100755 backend/scripts/sort.sh create mode 100644 backend/scripts/sort_filter.jq diff --git a/backend/scripts/sort.sh b/backend/scripts/sort.sh new file mode 100755 index 000000000..e5c5c41c6 --- /dev/null +++ b/backend/scripts/sort.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +ROOT_DIR=$(dirname "$0")/.. + +tmp=$(mktemp) +exit_code=0 + +for locale_file in $ROOT_DIR/src/locales/*.json +do + jq -f $(dirname "$0")/sort_filter.jq $locale_file > "$tmp" + if [ "$*" == "--fix" ] + then + mv "$tmp" $locale_file + else + if diff -q "$tmp" $locale_file > /dev/null ; + then + : # all good + else + exit_code=$? + echo "$(basename -- $locale_file) is not sorted by keys" + fi + fi +done + +exit $exit_code diff --git a/backend/scripts/sort_filter.jq b/backend/scripts/sort_filter.jq new file mode 100644 index 000000000..9d108f8f0 --- /dev/null +++ b/backend/scripts/sort_filter.jq @@ -0,0 +1,13 @@ +def walk(f): + . as $in + | if type == "object" then + reduce keys_unsorted[] as $key + ( {}; . + { ($key): ($in[$key] | walk(f)) } ) | f + elif type == "array" then map( walk(f) ) | f + else f + end; + +def keys_sort_by(f): + to_entries | sort_by(.key|f ) | from_entries; + +walk(if type == "object" then keys_sort_by(ascii_upcase) else . end) \ No newline at end of file From 7be46f5e681a49d2a5d8f3dd1ead93666d4da385 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 10 Feb 2023 12:25:42 +0100 Subject: [PATCH 25/44] Add pwd before yarn run locales --- .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 c1ff5b97c..83a3f5cd6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -358,7 +358,7 @@ jobs: # LINT BACKEND ########################################################### ########################################################################## - name: backend | Lint - run: docker run --rm gradido/backend:test yarn run lint + run: docker run --rm gradido/backend:test pwd && yarn run lint ############################################################################## # JOB: LOCALES BACKEND ####################################################### From 73c38d7ecbfe1fafa9f4172d56dc4104869c30cb Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Fri, 10 Feb 2023 13:56:55 +0100 Subject: [PATCH 26/44] Update .github/workflows/test.yml --- .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 83a3f5cd6..c1ff5b97c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -358,7 +358,7 @@ jobs: # LINT BACKEND ########################################################### ########################################################################## - name: backend | Lint - run: docker run --rm gradido/backend:test pwd && yarn run lint + run: docker run --rm gradido/backend:test yarn run lint ############################################################################## # JOB: LOCALES BACKEND ####################################################### From 5fdd056623974b7aa035fe7bef3c468488f8aa17 Mon Sep 17 00:00:00 2001 From: Hannes Heine Date: Fri, 10 Feb 2023 13:57:02 +0100 Subject: [PATCH 27/44] Update .github/workflows/test.yml --- .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 c1ff5b97c..db1fc6099 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -387,7 +387,7 @@ jobs: # LOCALES FRONTEND ####################################################### ########################################################################## - name: Backendend | Locales - run: docker run --rm gradido/backend:test yarn run locales + run: docker run --rm gradido/backend:test pwd && yarn run locales ############################################################################## # JOB: LINT DATABASE UP ###################################################### From da8f371823a434b95edd4b79e9ca9c2b8df7b06c Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 10 Feb 2023 14:11:49 +0100 Subject: [PATCH 28/44] Debugging docker... --- .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 db1fc6099..548a858a6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -387,7 +387,7 @@ jobs: # LOCALES FRONTEND ####################################################### ########################################################################## - name: Backendend | Locales - run: docker run --rm gradido/backend:test pwd && yarn run locales + run: docker run --rm gradido/backend:test pwd && ls -halt && yarn run locales ############################################################################## # JOB: LINT DATABASE UP ###################################################### From 786876a46deac1bc886103dace18720d8f6822de Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 10 Feb 2023 14:24:50 +0100 Subject: [PATCH 29/44] Yarn locales is in backend not under gradido. --- .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 548a858a6..b9d9edd5f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -387,7 +387,7 @@ jobs: # LOCALES FRONTEND ####################################################### ########################################################################## - name: Backendend | Locales - run: docker run --rm gradido/backend:test pwd && ls -halt && yarn run locales + run: docker run --rm gradido/backend:test cd backend && yarn run locales ############################################################################## # JOB: LINT DATABASE UP ###################################################### From a88968d3da3f1996500382b1ca297b2bd5c66ad8 Mon Sep 17 00:00:00 2001 From: elweyn Date: Fri, 10 Feb 2023 15:14:13 +0100 Subject: [PATCH 30/44] Change structure to locales check --- .github/workflows/test.yml | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b9d9edd5f..c204eb321 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -374,20 +374,10 @@ jobs: - name: Checkout code uses: actions/checkout@v3 ########################################################################## - # DOWNLOAD DOCKER IMAGE ################################################## + # LOCALES BACKEND ##################################################### ########################################################################## - - name: Download Docker Image (Backend) - uses: actions/download-artifact@v3 - with: - name: docker-backend-test - path: /tmp - - name: Load Docker Image - run: docker load < /tmp/backend.tar - ########################################################################## - # LOCALES FRONTEND ####################################################### - ########################################################################## - - name: Backendend | Locales - run: docker run --rm gradido/backend:test cd backend && yarn run locales + - name: Backend | Locales + run: cd backend && yarn && yarn locales ############################################################################## # JOB: LINT DATABASE UP ###################################################### From 8424417cdeb62abd7f184412d26a678b78846a08 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 12:14:00 +0100 Subject: [PATCH 31/44] removed todos, separated events --- backend/src/event/Event.ts | 3 +++ backend/src/event/EventProtocolType.ts | 1 + backend/src/graphql/resolver/UserResolver.ts | 5 ++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 196e997e2..e2ae5863a 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -186,6 +186,9 @@ export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => EVENT(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() +export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => + EVENT(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save() + /* export const EVENT_REDEEM_REGISTER = async ( userId: number, transactionId: number | null = null, diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts index b3ade4c5c..4df32aa53 100644 --- a/backend/src/event/EventProtocolType.ts +++ b/backend/src/event/EventProtocolType.ts @@ -6,6 +6,7 @@ export enum EventProtocolType { // VERIFY_REDEEM = 'VERIFY_REDEEM', // INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT', SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL', + ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL', SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL', // CONFIRM_EMAIL = 'CONFIRM_EMAIL', // REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP', diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 2aba1c538..d3397c720 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -55,6 +55,7 @@ import { EVENT_SEND_CONFIRMATION_EMAIL, EVENT_REGISTER, EVENT_ACTIVATE_ACCOUNT, + EVENT_ADMIN_SEND_CONFIRMATION_EMAIL, } from '@/event/Event' import { getUserCreations } from './util/creations' import { isValidPassword } from '@/password/EncryptorUtils' @@ -328,7 +329,6 @@ export class UserResolver { }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) - // TODO: this event is used twice, why? await EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) if (!emailSent) { @@ -809,8 +809,7 @@ export class UserResolver { if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) } else { - // TODO: this event is used twice, why? - await EVENT_SEND_CONFIRMATION_EMAIL(user.id) + await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user.id) } return true From 38c5f6ff5d8b28d8daa4aa48abb18a752209dca8 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 12:33:46 +0100 Subject: [PATCH 32/44] test login event --- backend/src/graphql/resolver/UserResolver.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 17eddca94..e3a1bacd8 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -647,6 +647,19 @@ describe('UserResolver', () => { it('sets the token in the header', () => { expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) }) }) + + it('stores the login event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.LOGIN, + userId: userConatct.user.id, + }), + ) + }) }) describe('user is in database and wrong password', () => { From 1ac2e033bf51c41606b1d64c0f217c2c182ffa6c Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 12:37:16 +0100 Subject: [PATCH 33/44] test multi registration email event --- backend/src/graphql/resolver/UserResolver.test.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index e3a1bacd8..167cd2b4f 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -206,7 +206,7 @@ describe('UserResolver', () => { }) }) - describe('email already exists', () => { + describe('user already exists', () => { let mutation: User beforeAll(async () => { mutation = await mutate({ mutation: createUser, variables }) @@ -236,6 +236,19 @@ describe('UserResolver', () => { }), ) }) + + it('stores the send account multi registration email event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, + userId: userConatct.user.id, + }), + ) + }) }) describe('unknown language', () => { From b12865abf8312bc012277dca70bcab4277672d9a Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 12:41:59 +0100 Subject: [PATCH 34/44] test for event register --- backend/src/graphql/resolver/UserResolver.test.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 167cd2b4f..6e9a21b86 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -175,6 +175,19 @@ describe('UserResolver', () => { }) }) }) + + it('stores the register event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'peter@lustig.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.REGISTER, + userId: userConatct.user.id, + }), + ) + }) }) describe('account activation email', () => { From a88585086c99d3d37291b4810ed1e6465a88d989 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:12:58 +0100 Subject: [PATCH 35/44] test sendActivationEmail --- .../src/graphql/resolver/UserResolver.test.ts | 146 +++++++++++++++++- backend/src/graphql/resolver/UserResolver.ts | 15 +- backend/src/seeds/graphql/mutations.ts | 6 + 3 files changed, 148 insertions(+), 19 deletions(-) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 6e9a21b86..36a22c386 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -19,6 +19,7 @@ import { setUserRole, deleteUser, unDeleteUser, + sendActivationEmail, } from '@/seeds/graphql/mutations' import { verifyLogin, queryOptIn, searchAdminUsers, searchUsers } from '@/seeds/graphql/queries' import { GraphQLError } from 'graphql' @@ -176,7 +177,7 @@ describe('UserResolver', () => { }) }) - it('stores the register event in the database', async () => { + it('stores the REGISTER event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'peter@lustig.de' }, { relations: ['user'] }, @@ -209,7 +210,7 @@ describe('UserResolver', () => { }) }) - it('stores the send confirmation event in the database', () => { + it('stores the SEND_CONFIRMATION_EMAIL event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, @@ -250,7 +251,7 @@ describe('UserResolver', () => { ) }) - it('stores the send account multi registration email event in the database', async () => { + it('stores the SEND_ACCOUNT_MULTIREGISTRATION_EMAIL event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'peter@lustig.de' }, { relations: ['user'] }, @@ -354,7 +355,7 @@ describe('UserResolver', () => { ) }) - it('stores the account activated event in the database', () => { + it('stores the ACTIVATE_ACCOUNT event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ACTIVATE_ACCOUNT, @@ -363,7 +364,7 @@ describe('UserResolver', () => { ) }) - it('stores the redeem register event in the database', () => { + it('stores the REDEEM_REGISTER event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, @@ -447,7 +448,7 @@ describe('UserResolver', () => { ) }) - it('stores the redeem register event in the database', async () => { + it('stores the REDEEM_REGISTER event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, @@ -674,7 +675,7 @@ describe('UserResolver', () => { expect(headerPushMock).toBeCalledWith({ key: 'token', value: expect.any(String) }) }) - it('stores the login event in the database', async () => { + it('stores the LOGIN event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, @@ -926,7 +927,7 @@ describe('UserResolver', () => { ) }) - it('stores the login event in the database', () => { + it('stores the LOGIN event in the database', () => { expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, @@ -1707,6 +1708,135 @@ describe('UserResolver', () => { }) }) + /// + + describe('sendActivationEmail', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'bibi@bloxberg.de' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('authenticated', () => { + describe('without admin rights', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + it('returns an error', async () => { + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'bibi@bloxberg.de' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('401 Unauthorized')], + }), + ) + }) + }) + + describe('with admin rights', () => { + beforeAll(async () => { + admin = await userFactory(testEnv, peterLustig) + await mutate({ + mutation: login, + variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, + }) + }) + + afterAll(async () => { + await cleanDB() + resetToken() + }) + + describe('user does not exist', () => { + it('throws an error', async () => { + jest.clearAllMocks() + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'INVALID' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('No user with this credentials')], + }), + ) + }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith('No user with this credentials', 'invalid') + }) + }) + + describe('user is deleted', () => { + it('throws an error', async () => { + jest.clearAllMocks() + await userFactory(testEnv, stephenHawking) + await expect( + mutate({ mutation: sendActivationEmail, variables: { email: 'stephen@hawking.uk' } }), + ).resolves.toEqual( + expect.objectContaining({ + errors: [new GraphQLError('User with given email contact is deleted')], + }), + ) + }) + + it('logs the error thrown', () => { + expect(logger.error).toBeCalledWith( + 'User with given email contact is deleted', + 'stephen@hawking.uk', + ) + }) + }) + + describe('sendActivationEmail with success', () => { + beforeAll(async () => { + user = await userFactory(testEnv, bibiBloxberg) + }) + + it('returns true', async () => { + const result = await mutate({ + mutation: sendActivationEmail, + variables: { email: 'bibi@bloxberg.de' }, + }) + expect(result).toEqual( + expect.objectContaining({ + data: { + sendActivationEmail: true, + }, + }), + ) + }) + + it('stores the ADMIN_SEND_CONFIRMATION_EMAIL event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + expect(EventProtocol.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, + userId: userConatct.user.id, + }), + ) + }) + }) + }) + }) + }) + describe('unDelete user', () => { describe('unauthenticated', () => { it('returns an error', async () => { diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index d3397c720..aa7a94f9a 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -781,19 +781,12 @@ export class UserResolver { email = email.trim().toLowerCase() // const user = await dbUser.findOne({ id: emailContact.userId }) const user = await findUserByEmail(email) - if (!user) { - throw new LogError('Could not find user to given email contact', email) - } - if (user.deletedAt) { + if (user.deletedAt || user.emailContact.deletedAt) { throw new LogError('User with given email contact is deleted', email) } - const emailContact = user.emailContact - if (emailContact.deletedAt) { - throw new LogError('The given email contact for this user is deleted', email) - } - emailContact.emailResendCount++ - await emailContact.save() + user.emailContact.emailResendCount++ + await user.emailContact.save() // eslint-disable-next-line @typescript-eslint/no-unused-vars const emailSent = await sendAccountActivationEmail({ @@ -801,7 +794,7 @@ export class UserResolver { lastName: user.lastName, email, language: user.language, - activationLink: activationLink(emailContact.emailVerificationCode), + activationLink: activationLink(user.emailContact.emailVerificationCode), timeDurationObject: getTimeDurationObject(CONFIG.EMAIL_CODE_VALID_TIME), }) diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index 2b4ed6656..5c05a4de9 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -68,6 +68,12 @@ export const createUser = gql` } ` +export const sendActivationEmail = gql` + mutation ($email: String!) { + sendActivationEmail(email: $email) + } +` + export const sendCoins = gql` mutation ($email: String!, $amount: Decimal!, $memo: String!) { sendCoins(email: $email, amount: $amount, memo: $memo) From 973630e5fbd952fd0e172b160c1f44efb3ba6407 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:20:31 +0100 Subject: [PATCH 36/44] also test the emeil going out --- .../src/graphql/resolver/UserResolver.test.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 36a22c386..19eb04b34 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -1820,6 +1820,28 @@ describe('UserResolver', () => { ) }) + it('sends an account activation email', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + const activationLink = CONFIG.EMAIL_LINK_VERIFICATION.replace( + /{optin}/g, + userConatct.emailVerificationCode.toString(), + ).replace(/{code}/g, '') + expect(sendAccountActivationEmail).toBeCalledWith({ + firstName: 'Bibi', + lastName: 'Bloxberg', + email: 'bibi@bloxberg.de', + language: 'de', + activationLink, + timeDurationObject: expect.objectContaining({ + hours: expect.any(Number), + minutes: expect.any(Number), + }), + }) + }) + it('stores the ADMIN_SEND_CONFIRMATION_EMAIL event in the database', async () => { const userConatct = await UserContact.findOneOrFail( { email: 'bibi@bloxberg.de' }, From dd69330e39253d5b8204ecc1f72f4f616a583f5e Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:23:13 +0100 Subject: [PATCH 37/44] 80% coverage backend --- .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 41ea77400..bb812c7f4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -526,7 +526,7 @@ jobs: report_name: Coverage Backend type: lcov result_path: ./backend/coverage/lcov.info - min_coverage: 79 + min_coverage: 80 token: ${{ github.token }} ########################################################################## From 20509b8a112a6e95a1744ad84bd419212e7b01fe Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:34:21 +0100 Subject: [PATCH 38/44] remove basic event --- backend/src/event/EventProtocolType.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/event/EventProtocolType.ts b/backend/src/event/EventProtocolType.ts index 4df32aa53..3a4c914c1 100644 --- a/backend/src/event/EventProtocolType.ts +++ b/backend/src/event/EventProtocolType.ts @@ -1,5 +1,4 @@ export enum EventProtocolType { - // BASIC = 'BASIC', // VISIT_GRADIDO = 'VISIT_GRADIDO', REGISTER = 'REGISTER', REDEEM_REGISTER = 'REDEEM_REGISTER', From 5ffa2a4d8e85a71732da236beb3abb773541ddd9 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:34:34 +0100 Subject: [PATCH 39/44] refactor message for test event --- .../resolver/ContributionResolver.test.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 4a74029ad..085fb74a1 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -244,7 +244,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the create contribution event in the database', async () => { + it('stores the CONTRIBUTION_CREATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CREATE, @@ -696,7 +696,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the update contribution event in the database', async () => { + it('stores the CONTRIBUTION_UPDATE event in the database', async () => { bibi = await query({ query: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, @@ -1263,7 +1263,7 @@ describe('ContributionResolver', () => { ).resolves.toBeTruthy() }) - it('stores the delete contribution event in the database', async () => { + it('stores the CONTRIBUTION_DELETE event in the database', async () => { const contribution = await mutate({ mutation: createContribution, variables: { @@ -1780,7 +1780,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin create contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_CREATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_CREATE, @@ -2045,7 +2045,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin update contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, @@ -2085,7 +2085,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin update contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, @@ -2229,7 +2229,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the admin delete contribution event in the database', async () => { + it('stores the ADMIN_CONTRIBUTION_DELETE event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_DELETE, @@ -2371,7 +2371,7 @@ describe('ContributionResolver', () => { ) }) - it('stores the contribution confirm event in the database', async () => { + it('stores the CONTRIBUTION_CONFIRM event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CONFIRM, @@ -2403,7 +2403,7 @@ describe('ContributionResolver', () => { }) }) - it('stores the send confirmation email event in the database', async () => { + it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => { await expect(EventProtocol.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, From 31168c3aff74f07b8d8f4b70466f51a21f29b1b1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:36:06 +0100 Subject: [PATCH 40/44] refactor event names --- backend/src/graphql/resolver/TransactionResolver.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index 50b2b3690..b0e9f34c2 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -334,7 +334,7 @@ describe('send coins', () => { ) }) - it('stores the send transaction event in the database', async () => { + it('stores the TRANSACTION_SEND event in the database', async () => { // Find the exact transaction (sent one is the one with user[1] as user) const transaction = await Transaction.find({ userId: user[1].id, @@ -351,7 +351,7 @@ describe('send coins', () => { ) }) - it('stores the receive event in the database', async () => { + it('stores the TRANSACTION_RECEIVE event in the database', async () => { // Find the exact transaction (received one is the one with user[0] as user) const transaction = await Transaction.find({ userId: user[0].id, From a21c717b101c8a4901e1d5b638fabf899883b62b Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Mon, 13 Feb 2023 13:50:45 +0100 Subject: [PATCH 41/44] fix test setup --- .../src/graphql/resolver/TransactionLinkResolver.test.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index af2e4fd59..f60ab45d0 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -116,6 +116,11 @@ describe('TransactionLinkResolver', () => { }) describe('redeemTransactionLink', () => { + afterAll(async () => { + await cleanDB() + resetToken() + }) + describe('contributionLink', () => { describe('input not valid', () => { beforeAll(async () => { @@ -482,8 +487,7 @@ describe('TransactionLinkResolver', () => { pageSize: 5, } - // TODO: there is a test not cleaning up after itself! Fix it! - beforeAll(async () => { + afterAll(async () => { await cleanDB() resetToken() }) From 2a1cb037c73a90c5d5fe338cb30f498f7c910246 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 14 Feb 2023 01:47:07 +0100 Subject: [PATCH 42/44] log moderator who denies contribution instead of user --- backend/src/graphql/resolver/ContributionResolver.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index fa39ad60d..53026abe7 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -693,7 +693,7 @@ export class ContributionResolver { const res = await contributionToUpdate.save() await EVENT_ADMIN_CONTRIBUTION_DENY( - contributionToUpdate.userId, + moderator.id, contributionToUpdate.id, contributionToUpdate.amount, ) From 9101c3f74d9864ac8592664ecf128160c1789771 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 14 Feb 2023 15:39:41 +0100 Subject: [PATCH 43/44] save moderator & user_id for EVENT_ADMIN_CONTRIBUTION_DENY --- backend/src/event/Event.ts | 3 ++- backend/src/graphql/resolver/ContributionResolver.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index e2ae5863a..90e28fbb4 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -131,13 +131,14 @@ export const EVENT_CONTRIBUTION_CONFIRM = async ( export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( userId: number, + xUserId: number, contributionId: number, amount: Decimal, ): Promise => EVENT( EventProtocolType.ADMIN_CONTRIBUTION_DENY, userId, - null, + xUserId, null, null, contributionId, diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 53026abe7..f070fade5 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -693,6 +693,7 @@ export class ContributionResolver { const res = await contributionToUpdate.save() await EVENT_ADMIN_CONTRIBUTION_DENY( + contributionToUpdate.userId, moderator.id, contributionToUpdate.id, contributionToUpdate.amount, From d0ff8ffb2c2b1d477a4f607f8d46fd8a652da9f1 Mon Sep 17 00:00:00 2001 From: Ulf Gebhardt Date: Tue, 14 Feb 2023 15:50:25 +0100 Subject: [PATCH 44/44] diferentiate between saving and not saving event --- backend/src/event/Event.ts | 36 ++++++++++---------- backend/src/graphql/resolver/UserResolver.ts | 4 +-- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 90e28fbb4..8e65d85f2 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -2,7 +2,7 @@ import { EventProtocol as DbEvent } from '@entity/EventProtocol' import Decimal from 'decimal.js-light' import { EventProtocolType } from './EventProtocolType' -export const EVENT = ( +export const Event = ( type: EventProtocolType, userId: number, xUserId: number | null = null, @@ -29,7 +29,7 @@ export const EVENT_CONTRIBUTION_CREATE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.CONTRIBUTION_CREATE, userId, null, @@ -44,7 +44,7 @@ export const EVENT_CONTRIBUTION_DELETE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.CONTRIBUTION_DELETE, userId, null, @@ -59,7 +59,7 @@ export const EVENT_CONTRIBUTION_UPDATE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.CONTRIBUTION_UPDATE, userId, null, @@ -74,7 +74,7 @@ export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.ADMIN_CONTRIBUTION_CREATE, userId, null, @@ -89,7 +89,7 @@ export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, userId, null, @@ -104,7 +104,7 @@ export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.ADMIN_CONTRIBUTION_DELETE, userId, null, @@ -119,7 +119,7 @@ export const EVENT_CONTRIBUTION_CONFIRM = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.CONTRIBUTION_CONFIRM, userId, null, @@ -135,7 +135,7 @@ export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( contributionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.ADMIN_CONTRIBUTION_DENY, userId, xUserId, @@ -151,7 +151,7 @@ export const EVENT_TRANSACTION_SEND = async ( transactionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.TRANSACTION_SEND, userId, xUserId, @@ -167,7 +167,7 @@ export const EVENT_TRANSACTION_RECEIVE = async ( transactionId: number, amount: Decimal, ): Promise => - EVENT( + Event( EventProtocolType.TRANSACTION_RECEIVE, userId, xUserId, @@ -178,24 +178,24 @@ export const EVENT_TRANSACTION_RECEIVE = async ( ).save() export const EVENT_LOGIN = async (userId: number): Promise => - EVENT(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() + Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( userId: number, -): Promise => EVENT(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save() +): Promise => Event(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save() export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - EVENT(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() + Event(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - EVENT(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save() + Event(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save() /* export const EVENT_REDEEM_REGISTER = async ( userId: number, transactionId: number | null = null, contributionId: number | null = null, ): Promise => - EVENT( + Event( EventProtocolType.REDEEM_REGISTER, userId, null, @@ -206,7 +206,7 @@ export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promi */ export const EVENT_REGISTER = async (userId: number): Promise => - EVENT(EventProtocolType.REGISTER, userId).save() + Event(EventProtocolType.REGISTER, userId).save() export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => - EVENT(EventProtocolType.ACTIVATE_ACCOUNT, userId).save() + Event(EventProtocolType.ACTIVATE_ACCOUNT, userId).save() diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index aa7a94f9a..f9617b0df 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -49,7 +49,7 @@ import { klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' import { hasElopageBuys } from '@/util/hasElopageBuys' import { - EVENT, + Event, EVENT_LOGIN, EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, EVENT_SEND_CONFIRMATION_EMAIL, @@ -266,7 +266,7 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegisterRedeem = EVENT(EventProtocolType.REDEEM_REGISTER, 0) + const eventRegisterRedeem = Event(EventProtocolType.REDEEM_REGISTER, 0) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName