Merge pull request #2724 from gradido/remove-adminCreateContributions

refactor(backend): remove admin create contributions
This commit is contained in:
Moriz Wahl 2023-02-24 18:10:59 +01:00 committed by GitHub
commit 5f54d048d3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 39 additions and 413 deletions

View File

@ -1,7 +1,6 @@
import { mount } from '@vue/test-utils'
import CreationFormular from './CreationFormular'
import { adminCreateContribution } from '../graphql/adminCreateContribution'
import { adminCreateContributions } from '../graphql/adminCreateContributions'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
const localVue = global.localVue
@ -328,122 +327,6 @@ describe('CreationFormular', () => {
})
})
})
describe('mass creation with success', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockResolvedValue({
data: {
adminCreateContributions: {
success: true,
successfulContribution: ['bob@baumeister.de', 'bibi@bloxberg.de'],
failedContribution: [],
},
},
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test mass create coins')
await wrapper.find('input[type="number"]').setValue(200)
await wrapper.find('.test-submit').trigger('click')
})
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
mutation: adminCreateContributions,
variables: {
pendingCreations: [
{
email: 'bob@baumeister.de',
creationDate: getCreationDate(1),
amount: 200,
memo: 'Test mass create coins',
},
{
email: 'bibi@bloxberg.de',
creationDate: getCreationDate(1),
amount: 200,
memo: 'Test mass create coins',
},
],
},
}),
)
})
it('updates open creations in store', () => {
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 2)
})
it('emits remove-all-bookmark', () => {
expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy()
})
})
describe('mass creation with success but all failed', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockResolvedValue({
data: {
adminCreateContributions: {
success: true,
successfulContribution: [],
failedContribution: ['bob@baumeister.de', 'bibi@bloxberg.de'],
},
},
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test mass create coins')
await wrapper.find('input[type="number"]').setValue(200)
await wrapper.find('.test-submit').trigger('click')
})
it('updates open creations in store', () => {
expect(stateCommitMock).toBeCalledWith('openCreationsPlus', 0)
})
it('emits remove all bookmarks', () => {
expect(wrapper.emitted('remove-all-bookmark')).toBeTruthy()
})
it('emits toast failed creations with two emails', () => {
expect(wrapper.emitted('toast-failed-creations')).toEqual([
[['bob@baumeister.de', 'bibi@bloxberg.de']],
])
})
})
describe('mass creation with error', () => {
beforeEach(async () => {
jest.clearAllMocks()
apolloMutateMock.mockRejectedValue({
message: 'Oh no!',
})
await wrapper.setProps({
type: 'massCreation',
creation: [200, 400, 600],
items: [{ email: 'bob@baumeister.de' }, { email: 'bibi@bloxberg.de' }],
})
await wrapper.findAll('input[type="radio"]').at(1).setChecked()
await wrapper.find('textarea').setValue('Test mass create coins')
await wrapper.find('input[type="number"]').setValue(200)
await wrapper.find('.test-submit').trigger('click')
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
})
})
})

View File

@ -86,16 +86,11 @@
</template>
<script>
import { adminCreateContribution } from '../graphql/adminCreateContribution'
import { adminCreateContributions } from '../graphql/adminCreateContributions'
import { creationMonths } from '../mixins/creationMonths'
export default {
name: 'CreationFormular',
mixins: [creationMonths],
props: {
type: {
type: String,
required: false,
},
pagetype: {
type: String,
required: false,
@ -140,78 +135,38 @@ export default {
updateRadioSelected(name) {
// do we want to reset the memo everytime the month changes?
this.text = this.$t('creation_form.creation_for') + ' ' + name.short + ' ' + name.year
if (this.type === 'singleCreation') {
this.rangeMin = 0
this.rangeMax = name.creation
}
this.rangeMin = 0
this.rangeMax = name.creation
},
submitCreation() {
let submitObj = []
if (this.type === 'massCreation') {
this.items.forEach((item) => {
submitObj.push({
email: item.email,
this.$apollo
.mutate({
mutation: adminCreateContribution,
variables: {
email: this.item.email,
creationDate: this.selected.date,
amount: Number(this.value),
memo: this.text,
})
},
})
.then((result) => {
this.$emit('update-user-data', this.item, result.data.adminCreateContribution)
this.$store.commit('openCreationsPlus', 1)
this.toastSuccess(
this.$t('creation_form.toasted', {
value: this.value,
email: this.item.email,
}),
)
// what is this? Tests says that this.text is not reseted
this.$refs.creationForm.reset()
this.value = 0
})
.catch((error) => {
this.toastError(error.message)
this.$refs.creationForm.reset()
this.value = 0
})
this.$apollo
.mutate({
mutation: adminCreateContributions,
variables: {
pendingCreations: submitObj,
},
fetchPolicy: 'no-cache',
})
.then((result) => {
const failedContributions = []
this.$store.commit(
'openCreationsPlus',
result.data.adminCreateContributions.successfulContribution.length,
)
if (result.data.adminCreateContributions.failedContribution.length > 0) {
result.data.adminCreateContributions.failedContribution.forEach((email) => {
failedContributions.push(email)
})
}
this.$emit('remove-all-bookmark')
this.$emit('toast-failed-creations', failedContributions)
})
.catch((error) => {
this.toastError(error.message)
})
} else if (this.type === 'singleCreation') {
submitObj = {
email: this.item.email,
creationDate: this.selected.date,
amount: Number(this.value),
memo: this.text,
}
this.$apollo
.mutate({
mutation: adminCreateContribution,
variables: submitObj,
})
.then((result) => {
this.$emit('update-user-data', this.item, result.data.adminCreateContribution)
this.$store.commit('openCreationsPlus', 1)
this.toastSuccess(
this.$t('creation_form.toasted', {
value: this.value,
email: this.item.email,
}),
)
// what is this? Tests says that this.text is not reseted
this.$refs.creationForm.reset()
this.value = 0
})
.catch((error) => {
this.toastError(error.message)
this.$refs.creationForm.reset()
this.value = 0
})
}
},
},
watch: {

View File

@ -68,14 +68,11 @@ describe('NavBar', () => {
})
describe('wallet', () => {
const windowLocationMock = jest.fn()
const windowLocation = window.location
beforeEach(async () => {
delete window.location
window.location = {
assign: windowLocationMock,
}
await wrapper.findAll('.nav-item').at(5).find('a').trigger('click')
window.location = ''
await wrapper.findAll('.nav-item').at(4).find('a').trigger('click')
})
afterEach(() => {
@ -83,8 +80,8 @@ describe('NavBar', () => {
window.location = windowLocation
})
it.skip('changes window location to wallet', () => {
expect(windowLocationMock()).toBe('valid-token')
it('changes window location to wallet', () => {
expect(window.location).toBe('http://localhost/authenticate?token=valid-token')
})
it('dispatches logout to store', () => {

View File

@ -53,7 +53,6 @@
<b-tab :title="$t('creation')" active :disabled="row.item.deletedAt !== null">
<creation-formular
v-if="!row.item.deletedAt"
type="singleCreation"
pagetype="singleCreation"
:creation="row.item.creation"
:item="row.item"

View File

@ -1,11 +0,0 @@
import gql from 'graphql-tag'
export const adminCreateContributions = gql`
mutation ($pendingCreations: [AdminCreateContributionArgs!]!) {
adminCreateContributions(pendingCreations: $pendingCreations) {
success
successfulContribution
failedContribution
}
}
`

View File

@ -72,7 +72,6 @@ export default {
return {
showArrays: false,
searchResult: [],
massCreation: [],
criteria: '',
filters: {
byActivated: null,

View File

@ -24,9 +24,6 @@ export const mutations = {
moderator: (state, moderator) => {
state.moderator = moderator
},
setUserSelectedInMassCreation: (state, userSelectedInMassCreation) => {
state.userSelectedInMassCreation = userSelectedInMassCreation
},
}
export const actions = {

View File

@ -10,7 +10,6 @@ const {
resetOpenCreations,
setOpenCreations,
moderator,
setUserSelectedInMassCreation,
} = mutations
const { logout } = actions
@ -65,14 +64,6 @@ describe('Vuex store', () => {
expect(state.openCreations).toEqual(12)
})
})
describe('setUserSelectedInMassCreation', () => {
it('sets userSelectedInMassCreation to given value', () => {
const state = { userSelectedInMassCreation: [] }
setUserSelectedInMassCreation(state, [0, 1, 2])
expect(state.userSelectedInMassCreation).toEqual([0, 1, 2])
})
})
})
describe('actions', () => {

View File

@ -42,7 +42,6 @@ export enum RIGHTS {
DELETE_USER = 'DELETE_USER',
UNDELETE_USER = 'UNDELETE_USER',
ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION',
ADMIN_CREATE_CONTRIBUTIONS = 'ADMIN_CREATE_CONTRIBUTIONS',
ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION',
ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION',
LIST_UNCONFIRMED_CONTRIBUTIONS = 'LIST_UNCONFIRMED_CONTRIBUTIONS',

View File

@ -1,19 +0,0 @@
import { ObjectType, Field } from 'type-graphql'
@ObjectType()
export class AdminCreateContributions {
constructor() {
this.success = false
this.successfulContribution = []
this.failedContribution = []
}
@Field(() => Boolean)
success: boolean
@Field(() => [String])
successfulContribution: string[]
@Field(() => [String])
failedContribution: string[]
}

View File

@ -13,7 +13,6 @@ import {
denyContribution,
confirmContribution,
adminCreateContribution,
adminCreateContributions,
adminUpdateContribution,
adminDeleteContribution,
login,
@ -1655,21 +1654,6 @@ describe('ContributionResolver', () => {
})
})
describe('adminCreateContributions', () => {
it('returns an error', async () => {
await expect(
mutate({
mutation: adminCreateContributions,
variables: { pendingCreations: [variables] },
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('401 Unauthorized')],
}),
)
})
})
describe('adminUpdateContribution', () => {
it('returns an error', async () => {
await expect(
@ -1749,21 +1733,6 @@ describe('ContributionResolver', () => {
})
})
describe('adminCreateContributions', () => {
it('returns an error', async () => {
await expect(
mutate({
mutation: adminCreateContributions,
variables: { pendingCreations: [variables] },
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('401 Unauthorized')],
}),
)
})
})
describe('adminUpdateContribution', () => {
it('returns an error', async () => {
await expect(
@ -2094,59 +2063,13 @@ describe('ContributionResolver', () => {
})
})
describe('adminCreateContributions', () => {
describe('adminUpdateContribution', () => {
// at this point we have this data in DB:
// bibi@bloxberg.de: [1000, 1000, 800]
// peter@lustig.de: [1000, 600, 1000]
// stephen@hawking.uk: [1000, 1000, 1000] - deleted
// garrick@ollivander.com: [1000, 1000, 1000] - not activated
const massCreationVariables = [
'bibi@bloxberg.de',
'peter@lustig.de',
'stephen@hawking.uk',
'garrick@ollivander.com',
'bob@baumeister.de',
].map((email) => {
return {
email,
amount: new Decimal(500),
memo: 'Grundeinkommen',
creationDate: contributionDateFormatter(new Date()),
}
})
it('returns success, two successful creation and three failed creations', async () => {
await expect(
mutate({
mutation: adminCreateContributions,
variables: { pendingCreations: massCreationVariables },
}),
).resolves.toEqual(
expect.objectContaining({
data: {
adminCreateContributions: {
success: true,
successfulContribution: ['bibi@bloxberg.de', 'peter@lustig.de'],
failedContribution: [
'stephen@hawking.uk',
'garrick@ollivander.com',
'bob@baumeister.de',
],
},
},
}),
)
})
})
describe('adminUpdateContribution', () => {
// at this I expect to have this data in DB:
// bibi@bloxberg.de: [1000, 1000, 300]
// peter@lustig.de: [1000, 600, 500]
// stephen@hawking.uk: [1000, 1000, 1000] - deleted
// garrick@ollivander.com: [1000, 1000, 1000] - not activated
describe('user for creation to update does not exist', () => {
it('throws an error', async () => {
jest.clearAllMocks()
@ -2360,7 +2283,7 @@ describe('ContributionResolver', () => {
date: expect.any(String),
memo: 'Das war leider zu Viel!',
amount: '200',
creation: ['1000', '800', '500'],
creation: ['1000', '800', '1000'],
},
},
}),
@ -2772,15 +2695,15 @@ describe('ContributionResolver', () => {
resetToken()
})
it('returns 19 creations in total', async () => {
it('returns 17 creations in total', async () => {
const {
data: { adminListAllContributions: contributionListObject },
}: { data: { adminListAllContributions: ContributionListResult } } = await query({
query: adminListAllContributions,
})
expect(contributionListObject.contributionList).toHaveLength(19)
expect(contributionListObject.contributionList).toHaveLength(17)
expect(contributionListObject).toMatchObject({
contributionCount: 19,
contributionCount: 17,
contributionList: expect.arrayContaining([
expect.objectContaining({
amount: expect.decimalEqual(50),
@ -2845,24 +2768,6 @@ describe('ContributionResolver', () => {
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(500),
firstName: 'Bibi',
id: expect.any(Number),
lastName: 'Bloxberg',
memo: 'Grundeinkommen',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(500),
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Grundeinkommen',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: expect.decimalEqual(10),
firstName: 'Bibi',
@ -2957,21 +2862,21 @@ describe('ContributionResolver', () => {
})
})
it('returns five pending creations with page size set to 5', async () => {
it('returns two pending creations with page size set to 2', async () => {
const {
data: { adminListAllContributions: contributionListObject },
}: { data: { adminListAllContributions: ContributionListResult } } = await query({
query: adminListAllContributions,
variables: {
currentPage: 1,
pageSize: 5,
pageSize: 2,
order: Order.DESC,
statusFilter: ['PENDING'],
},
})
expect(contributionListObject.contributionList).toHaveLength(5)
expect(contributionListObject.contributionList).toHaveLength(2)
expect(contributionListObject).toMatchObject({
contributionCount: 6,
contributionCount: 4,
contributionList: expect.arrayContaining([
expect.objectContaining({
amount: '400',
@ -2991,33 +2896,6 @@ describe('ContributionResolver', () => {
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: '500',
firstName: 'Bibi',
id: expect.any(Number),
lastName: 'Bloxberg',
memo: 'Grundeinkommen',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: '500',
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Grundeinkommen',
messagesCount: 0,
state: 'PENDING',
}),
expect.objectContaining({
amount: '100',
firstName: 'Peter',
id: expect.any(Number),
lastName: 'Lustig',
memo: 'Test env contribution',
messagesCount: 0,
state: 'PENDING',
}),
expect.not.objectContaining({
state: 'DENIED',
}),

View File

@ -8,7 +8,6 @@ import { UserContact } from '@entity/UserContact'
import { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { AdminCreateContributions } from '@model/AdminCreateContributions'
import { AdminUpdateContribution } from '@model/AdminUpdateContribution'
import { Contribution, ContributionListResult } from '@model/Contribution'
import { Decay } from '@model/Decay'
@ -318,33 +317,6 @@ export class ContributionResolver {
return getUserCreation(emailContact.userId, clientTimezoneOffset)
}
@Authorized([RIGHTS.ADMIN_CREATE_CONTRIBUTIONS])
@Mutation(() => AdminCreateContributions)
async adminCreateContributions(
@Arg('pendingCreations', () => [AdminCreateContributionArgs])
contributions: AdminCreateContributionArgs[],
@Ctx() context: Context,
): Promise<AdminCreateContributions> {
let success = false
const successfulContribution: string[] = []
const failedContribution: string[] = []
for (const contribution of contributions) {
await this.adminCreateContribution(contribution, context)
.then(() => {
successfulContribution.push(contribution.email)
success = true
})
.catch(() => {
failedContribution.push(contribution.email)
})
}
return {
success,
successfulContribution,
failedContribution,
}
}
@Authorized([RIGHTS.ADMIN_UPDATE_CONTRIBUTION])
@Mutation(() => AdminUpdateContribution)
async adminUpdateContribution(

View File

@ -126,16 +126,6 @@ export const unDeleteUser = gql`
}
`
export const adminCreateContributions = gql`
mutation ($pendingCreations: [AdminCreateContributionArgs!]!) {
adminCreateContributions(pendingCreations: $pendingCreations) {
success
successfulContribution
failedContribution
}
}
`
export const adminUpdateContribution = gql`
mutation ($id: Int!, $email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) {
adminUpdateContribution(

View File

@ -123,10 +123,6 @@ Zusätzlich wird als Parameter ein *creationDate* vom User mitgeliefert, das dem
nothing to do
#### + adminCreateContributions
Hier wird eine Liste von übergebenen Contributions über den internen Aufruf von *adminCreateContribution()* verarbeitet. Da dort eine Berücksichtigung des User-TimeOffsets notwendig ist, muss hier die UserTime entsprechen im Context weitergereicht werden.
#### - adminDeleteContribution
nothing to do