Merge branch 'master' into import-no-cycle-refactor-events

This commit is contained in:
Ulf Gebhardt 2023-03-31 12:08:15 +02:00
commit 959487c3bb
Signed by: ulfgebhardt
GPG Key ID: DA6B843E748679C9
152 changed files with 1168 additions and 894 deletions

View File

@ -28,6 +28,7 @@ const mocks = {
let propsData let propsData
let wrapper let wrapper
let spy
describe('ChangeUserRoleFormular', () => { describe('ChangeUserRoleFormular', () => {
const Wrapper = () => { const Wrapper = () => {
@ -70,12 +71,16 @@ describe('ChangeUserRoleFormular', () => {
expect(wrapper.text()).toContain('userRole.notChangeYourSelf') expect(wrapper.text()).toContain('userRole.notChangeYourSelf')
}) })
it('has role select disabled', () => { it('has no role select', () => {
expect(wrapper.find('select[disabled="disabled"]').exists()).toBe(true) expect(wrapper.find('select.role-select').exists()).toBe(false)
})
it('has no button', () => {
expect(wrapper.find('button.btn.btn-dange').exists()).toBe(false)
}) })
}) })
describe('change others role', () => { describe("change other user's role", () => {
let rolesToSelect let rolesToSelect
describe('general', () => { describe('general', () => {
@ -106,19 +111,12 @@ describe('ChangeUserRoleFormular', () => {
expect(wrapper.find('select.role-select[disabled="disabled"]').exists()).toBe(false) expect(wrapper.find('select.role-select[disabled="disabled"]').exists()).toBe(false)
}) })
describe('on API error', () => { it('has "change_user_role" button', () => {
beforeEach(() => { expect(wrapper.find('button.btn.btn-danger').text()).toBe('change_user_role')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
rolesToSelect.at(1).setSelected()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
}) })
}) })
describe('user is usual user', () => { describe('user has role "usual user"', () => {
beforeEach(() => { beforeEach(() => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
@ -141,6 +139,10 @@ describe('ChangeUserRoleFormular', () => {
describe('change select to', () => { describe('change select to', () => {
describe('same role', () => { describe('same role', () => {
it('has "change_user_role" button disabled', () => {
expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(true)
})
it('does not call the API', () => { it('does not call the API', () => {
rolesToSelect.at(0).setSelected() rolesToSelect.at(0).setSelected()
expect(apolloMutateMock).not.toHaveBeenCalled() expect(apolloMutateMock).not.toHaveBeenCalled()
@ -152,39 +154,75 @@ describe('ChangeUserRoleFormular', () => {
rolesToSelect.at(1).setSelected() rolesToSelect.at(1).setSelected()
}) })
it('calls the API', () => { it('has "change_user_role" button enabled', () => {
expect(apolloMutateMock).toBeCalledWith( expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true)
expect.objectContaining({ expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(
mutation: setUserRole, false,
variables: {
userId: 1,
isAdmin: true,
},
}),
) )
}) })
it('emits "updateIsAdmin"', () => { describe('clicking the "change_user_role" button', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual( beforeEach(async () => {
expect.arrayContaining([ spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
expect.arrayContaining([ spy.mockImplementation(() => Promise.resolve(true))
{ await wrapper.find('button').trigger('click')
userId: 1, await wrapper.vm.$nextTick()
isAdmin: expect.any(Date), })
},
]),
]),
)
})
it('toasts success message', () => { it('calls the modal', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') expect(wrapper.emitted('showModal'))
expect(spy).toHaveBeenCalled()
})
describe('confirm role change with success', () => {
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
mutation: setUserRole,
variables: {
userId: 1,
isAdmin: true,
},
}),
)
})
it('emits "updateIsAdmin"', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual(
expect.arrayContaining([
expect.arrayContaining([
{
userId: 1,
isAdmin: expect.any(Date),
},
]),
]),
)
})
it('toasts success message', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo')
})
})
describe('confirm role change with error', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
}) })
}) })
}) })
}) })
describe('user is admin', () => { describe('user has role "admin"', () => {
beforeEach(() => { beforeEach(() => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
@ -207,6 +245,10 @@ describe('ChangeUserRoleFormular', () => {
describe('change select to', () => { describe('change select to', () => {
describe('same role', () => { describe('same role', () => {
it('has "change_user_role" button disabled', () => {
expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(true)
})
it('does not call the API', () => { it('does not call the API', () => {
rolesToSelect.at(1).setSelected() rolesToSelect.at(1).setSelected()
expect(apolloMutateMock).not.toHaveBeenCalled() expect(apolloMutateMock).not.toHaveBeenCalled()
@ -218,33 +260,69 @@ describe('ChangeUserRoleFormular', () => {
rolesToSelect.at(0).setSelected() rolesToSelect.at(0).setSelected()
}) })
it('calls the API', () => { it('has "change_user_role" button enabled', () => {
expect(apolloMutateMock).toBeCalledWith( expect(wrapper.find('button.btn.btn-danger').exists()).toBe(true)
expect.objectContaining({ expect(wrapper.find('button.btn.btn-danger[disabled="disabled"]').exists()).toBe(
mutation: setUserRole, false,
variables: {
userId: 1,
isAdmin: false,
},
}),
) )
}) })
it('emits "updateIsAdmin"', () => { describe('clicking the "change_user_role" button', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual( beforeEach(async () => {
expect.arrayContaining([ spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
expect.arrayContaining([ spy.mockImplementation(() => Promise.resolve(true))
{ await wrapper.find('button').trigger('click')
userId: 1, await wrapper.vm.$nextTick()
isAdmin: null, })
},
]),
]),
)
})
it('toasts success message', () => { it('calls the modal', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo') expect(wrapper.emitted('showModal'))
expect(spy).toHaveBeenCalled()
})
describe('confirm role change with success', () => {
it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({
mutation: setUserRole,
variables: {
userId: 1,
isAdmin: false,
},
}),
)
})
it('emits "updateIsAdmin"', () => {
expect(wrapper.emitted('updateIsAdmin')).toEqual(
expect.arrayContaining([
expect.arrayContaining([
{
userId: 1,
isAdmin: null,
},
]),
]),
)
})
it('toasts success message', () => {
expect(toastSuccessSpy).toBeCalledWith('userRole.successfullyChangedTo')
})
})
describe('confirm role change with error', () => {
beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
})
it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!')
})
})
}) })
}) })
}) })

View File

@ -4,19 +4,23 @@
<div v-if="item.userId === $store.state.moderator.id" class="m-3 mb-4"> <div v-if="item.userId === $store.state.moderator.id" class="m-3 mb-4">
{{ $t('userRole.notChangeYourSelf') }} {{ $t('userRole.notChangeYourSelf') }}
</div> </div>
<div class="m-3"> <div v-else class="m-3">
<label for="role" class="mr-3">{{ $t('userRole.selectLabel') }}</label> <label for="role" class="mr-3">{{ $t('userRole.selectLabel') }}</label>
<b-form-select <b-form-select class="role-select" v-model="roleSelected" :options="roles" />
class="role-select" <div class="mt-3 mb-5">
v-model="roleSelected" <b-button
:options="roles" variant="danger"
:disabled="item.userId === $store.state.moderator.id" v-b-modal.user-role-modal
/> :disabled="currentRole === roleSelected"
@click="showModal()"
>
{{ $t('change_user_role') }}
</b-button>
</div>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { setUserRole } from '../graphql/setUserRole' import { setUserRole } from '../graphql/setUserRole'
@ -35,6 +39,7 @@ export default {
}, },
data() { data() {
return { return {
currentRole: this.item.isAdmin ? rolesValues.admin : rolesValues.user,
roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user, roleSelected: this.item.isAdmin ? rolesValues.admin : rolesValues.user,
roles: [ roles: [
{ value: rolesValues.user, text: this.$t('userRole.selectRoles.user') }, { value: rolesValues.user, text: this.$t('userRole.selectRoles.user') },
@ -42,14 +47,35 @@ export default {
], ],
} }
}, },
watch: {
roleSelected(newRole, oldRole) {
if (newRole !== oldRole) {
this.setUserRole(newRole, oldRole)
}
},
},
methods: { methods: {
showModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.changeUserRole.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
newRole:
this.roleSelected === 'admin'
? this.$t('userRole.selectRoles.admin')
: this.$t('userRole.selectRoles.user'),
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.changeUserRole.title'),
okTitle: this.$t('overlay.changeUserRole.yes'),
okVariant: 'danger',
},
)
.then((okClicked) => {
if (okClicked) {
this.setUserRole(this.roleSelected, this.currentRole)
}
})
.catch((error) => {
this.toastError(error.message)
})
},
setUserRole(newRole, oldRole) { setUserRole(newRole, oldRole) {
this.$apollo this.$apollo
.mutate({ .mutate({

View File

@ -2,14 +2,18 @@ import { mount } from '@vue/test-utils'
import CreationFormular from './CreationFormular' import CreationFormular from './CreationFormular'
import { adminCreateContribution } from '../graphql/adminCreateContribution' import { adminCreateContribution } from '../graphql/adminCreateContribution'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
import VueApollo from 'vue-apollo'
import { createMockClient } from 'mock-apollo-client'
import { adminOpenCreations } from '../graphql/adminOpenCreations'
const mockClient = createMockClient()
const apolloProvider = new VueApollo({
defaultClient: mockClient,
})
const localVue = global.localVue const localVue = global.localVue
localVue.use(VueApollo)
const apolloMutateMock = jest.fn().mockResolvedValue({
data: {
adminCreateContribution: [0, 0, 0],
},
})
const stateCommitMock = jest.fn() const stateCommitMock = jest.fn()
const mocks = { const mocks = {
@ -18,9 +22,6 @@ const mocks = {
const date = new Date(d) const date = new Date(d)
return date.toISOString().split('T')[0] return date.toISOString().split('T')[0]
}), }),
$apollo: {
mutate: apolloMutateMock,
},
$store: { $store: {
commit: stateCommitMock, commit: stateCommitMock,
}, },
@ -31,7 +32,8 @@ const propsData = {
creation: [], creation: [],
} }
const now = new Date(Date.now()) const now = new Date()
const getCreationDate = (sub) => { const getCreationDate = (sub) => {
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0) const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
return date.toISOString().split('T')[0] return date.toISOString().split('T')[0]
@ -40,8 +42,43 @@ const getCreationDate = (sub) => {
describe('CreationFormular', () => { describe('CreationFormular', () => {
let wrapper let wrapper
const adminOpenCreationsMock = jest.fn()
const adminCreateContributionMock = jest.fn()
mockClient.setRequestHandler(
adminOpenCreations,
adminOpenCreationsMock.mockResolvedValue({
data: {
adminOpenCreations: [
{
month: new Date(now.getFullYear(), now.getMonth() - 2).getMonth(),
year: new Date(now.getFullYear(), now.getMonth() - 2).getFullYear(),
amount: '200',
},
{
month: new Date(now.getFullYear(), now.getMonth() - 1).getMonth(),
year: new Date(now.getFullYear(), now.getMonth() - 1).getFullYear(),
amount: '400',
},
{
month: now.getMonth(),
year: now.getFullYear(),
amount: '600',
},
],
},
}),
)
mockClient.setRequestHandler(
adminCreateContribution,
adminCreateContributionMock.mockResolvedValue({
data: {
adminCreateContribution: [0, 0, 0],
},
}),
)
const Wrapper = () => { const Wrapper = () => {
return mount(CreationFormular, { localVue, mocks, propsData }) return mount(CreationFormular, { localVue, mocks, propsData, apolloProvider })
} }
describe('mount', () => { describe('mount', () => {
@ -107,17 +144,12 @@ describe('CreationFormular', () => {
}) })
it('sends ... to apollo', () => { it('sends ... to apollo', () => {
expect(apolloMutateMock).toBeCalledWith( expect(adminCreateContributionMock).toBeCalledWith({
expect.objectContaining({ email: 'benjamin@bluemchen.de',
mutation: adminCreateContribution, creationDate: getCreationDate(2),
variables: { amount: 90,
email: 'benjamin@bluemchen.de', memo: 'Test create coins',
creationDate: getCreationDate(2), })
amount: 90,
memo: 'Test create coins',
},
}),
)
}) })
it('emits update-user-data', () => { it('emits update-user-data', () => {
@ -144,7 +176,7 @@ describe('CreationFormular', () => {
describe('sendForm with server error', () => { describe('sendForm with server error', () => {
beforeEach(async () => { beforeEach(async () => {
apolloMutateMock.mockRejectedValueOnce({ message: 'Ouch!' }) adminCreateContributionMock.mockRejectedValueOnce({ message: 'Ouch!' })
await wrapper.find('.test-submit').trigger('click') await wrapper.find('.test-submit').trigger('click')
}) })
@ -212,7 +244,7 @@ describe('CreationFormular', () => {
}) })
it('sends ... to apollo', () => { it('sends ... to apollo', () => {
expect(apolloMutateMock).toBeCalled() expect(adminCreateContributionMock).toBeCalled()
}) })
}) })
@ -275,7 +307,7 @@ describe('CreationFormular', () => {
}) })
it('sends mutation to apollo', () => { it('sends mutation to apollo', () => {
expect(apolloMutateMock).toBeCalled() expect(adminCreateContributionMock).toBeCalled()
}) })
it('toast success message', () => { it('toast success message', () => {

View File

@ -117,10 +117,6 @@ export default {
return {} return {}
}, },
}, },
creation: {
type: Array,
required: true,
},
}, },
data() { data() {
return { return {
@ -129,6 +125,7 @@ export default {
rangeMin: 0, rangeMin: 0,
rangeMax: 1000, rangeMax: 1000,
selected: '', selected: '',
userId: this.item.userId,
} }
}, },
methods: { methods: {
@ -136,7 +133,7 @@ export default {
// do we want to reset the memo everytime the month changes? // do we want to reset the memo everytime the month changes?
this.text = this.$t('creation_form.creation_for') + ' ' + name.short + ' ' + name.year this.text = this.$t('creation_form.creation_for') + ' ' + name.short + ' ' + name.year
this.rangeMin = 0 this.rangeMin = 0
this.rangeMax = name.creation this.rangeMax = Number(name.creation)
}, },
submitCreation() { submitCreation() {
this.$apollo this.$apollo
@ -167,6 +164,10 @@ export default {
this.$refs.creationForm.reset() this.$refs.creationForm.reset()
this.value = 0 this.value = 0
}) })
.finally(() => {
this.$apollo.queries.OpenCreations.refetch()
this.selected = ''
})
}, },
}, },
watch: { watch: {

View File

@ -35,6 +35,7 @@ const propsData = {
describe('DeletedUserFormular', () => { describe('DeletedUserFormular', () => {
let wrapper let wrapper
let spy
const Wrapper = () => { const Wrapper = () => {
return mount(DeletedUserFormular, { localVue, mocks, propsData }) return mount(DeletedUserFormular, { localVue, mocks, propsData })
@ -62,6 +63,10 @@ describe('DeletedUserFormular', () => {
it('shows a text that you cannot delete yourself', () => { it('shows a text that you cannot delete yourself', () => {
expect(wrapper.text()).toBe('removeNotSelf') expect(wrapper.text()).toBe('removeNotSelf')
}) })
it('has no "delete_user" button', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
}) })
describe('delete other user', () => { describe('delete other user', () => {
@ -71,35 +76,32 @@ describe('DeletedUserFormular', () => {
userId: 1, userId: 1,
deletedAt: null, deletedAt: null,
}, },
static: true,
}) })
}) })
it('has a checkbox', () => {
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
})
it('shows the text "delete_user"', () => { it('shows the text "delete_user"', () => {
expect(wrapper.text()).toBe('delete_user') expect(wrapper.text()).toBe('delete_user')
}) })
describe('click on checkbox', () => { it('has a "delete_user" button', () => {
expect(wrapper.find('button').text()).toBe('delete_user')
})
describe('click on "delete_user" button', () => {
beforeEach(async () => { beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked() spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(true))
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('has a confirmation button', () => { it('calls the modal', () => {
expect(wrapper.find('button').exists()).toBe(true) expect(wrapper.emitted('showDeleteModal'))
}) expect(spy).toHaveBeenCalled()
it('has the button text "delete_user"', () => {
expect(wrapper.find('button').text()).toBe('delete_user')
}) })
describe('confirm delete with success', () => { describe('confirm delete with success', () => {
beforeEach(async () => {
await wrapper.find('button').trigger('click')
})
it('calls the API', () => { it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith( expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -123,32 +125,20 @@ describe('DeletedUserFormular', () => {
]), ]),
) )
}) })
it('unchecks the checkbox', () => {
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
})
}) })
describe('confirm delete with error', () => { describe('confirm delete with error', () => {
beforeEach(async () => { beforeEach(async () => {
spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) apolloMutateMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('button').trigger('click') await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('toasts an error message', () => { it('toasts an error message', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!') expect(toastErrorSpy).toBeCalledWith('Oh no!')
}) })
}) })
describe('click on checkbox again', () => {
beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked(false)
})
it('has no confirmation button anymore', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
})
}) })
}) })
@ -162,37 +152,33 @@ describe('DeletedUserFormular', () => {
}) })
}) })
it('has a checkbox', () => {
expect(wrapper.find('input[type="checkbox"]').exists()).toBe(true)
})
it('shows the text "undelete_user"', () => { it('shows the text "undelete_user"', () => {
expect(wrapper.text()).toBe('undelete_user') expect(wrapper.text()).toBe('undelete_user')
}) })
describe('click on checkbox', () => { it('has a "undelete_user" button', () => {
expect(wrapper.find('button').text()).toBe('undelete_user')
})
describe('click on "undelete_user" button', () => {
beforeEach(async () => { beforeEach(async () => {
apolloMutateMock.mockResolvedValue({ apolloMutateMock.mockResolvedValue({
data: { data: {
unDeleteUser: null, unDeleteUser: null,
}, },
}) })
await wrapper.find('input[type="checkbox"]').setChecked() spy = jest.spyOn(wrapper.vm.$bvModal, 'msgBoxConfirm')
spy.mockImplementation(() => Promise.resolve(true))
await wrapper.find('button').trigger('click')
await wrapper.vm.$nextTick()
}) })
it('has a confirmation button', () => { it('calls the modal', () => {
expect(wrapper.find('button').exists()).toBe(true) expect(wrapper.emitted('showUndeleteModal'))
}) expect(spy).toHaveBeenCalled()
it('has the button text "undelete_user"', () => {
expect(wrapper.find('button').text()).toBe('undelete_user')
}) })
describe('confirm recover with success', () => { describe('confirm recover with success', () => {
beforeEach(async () => {
await wrapper.find('button').trigger('click')
})
it('calls the API', () => { it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith( expect(apolloMutateMock).toBeCalledWith(
expect.objectContaining({ expect.objectContaining({
@ -205,7 +191,7 @@ describe('DeletedUserFormular', () => {
}) })
it('emits update deleted At', () => { it('emits update deleted At', () => {
expect(wrapper.emitted('updateDeletedAt')).toEqual( expect(wrapper.emitted('updateDeletedAt')).toMatchObject(
expect.arrayContaining([ expect.arrayContaining([
expect.arrayContaining([ expect.arrayContaining([
{ {
@ -216,10 +202,6 @@ describe('DeletedUserFormular', () => {
]), ]),
) )
}) })
it('unchecks the checkbox', () => {
expect(wrapper.find('input').attributes('checked')).toBe(undefined)
})
}) })
describe('confirm recover with error', () => { describe('confirm recover with error', () => {
@ -232,16 +214,6 @@ describe('DeletedUserFormular', () => {
expect(toastErrorSpy).toBeCalledWith('Oh no!') expect(toastErrorSpy).toBeCalledWith('Oh no!')
}) })
}) })
describe('click on checkbox again', () => {
beforeEach(async () => {
await wrapper.find('input[type="checkbox"]').setChecked(false)
})
it('has no confirmation button anymore', () => {
expect(wrapper.find('button').exists()).toBe(false)
})
})
}) })
}) })
}) })

View File

@ -4,15 +4,16 @@
{{ $t('removeNotSelf') }} {{ $t('removeNotSelf') }}
</div> </div>
<div v-else class="mt-5"> <div v-else class="mt-5">
<b-form-checkbox switch size="lg" v-model="checked">
<div>{{ item.deletedAt ? $t('undelete_user') : $t('delete_user') }}</div>
</b-form-checkbox>
<div class="mt-3 mb-5"> <div class="mt-3 mb-5">
<b-button v-if="checked && item.deletedAt === null" variant="danger" @click="deleteUser"> <b-button
v-if="!item.deletedAt"
variant="danger"
v-b-modal.delete-user-modal
@click="showDeleteModal()"
>
{{ $t('delete_user') }} {{ $t('delete_user') }}
</b-button> </b-button>
<b-button v-if="checked && item.deletedAt !== null" variant="success" @click="unDeleteUser"> <b-button v-else variant="success" v-b-modal.delete-user-modal @click="showUndeleteModal()">
{{ $t('undelete_user') }} {{ $t('undelete_user') }}
</b-button> </b-button>
</div> </div>
@ -31,12 +32,56 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
checked: false,
}
},
methods: { methods: {
showDeleteModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.deleteUser.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.deleteUser.title'),
okTitle: this.$t('overlay.deleteUser.yes'),
okVariant: 'danger',
static: true,
},
)
.then((okClicked) => {
if (okClicked) {
this.deleteUser()
}
})
.catch((error) => {
this.toastError(error.message)
})
},
showUndeleteModal() {
this.$bvModal
.msgBoxConfirm(
this.$t('overlay.undeleteUser.question', {
username: `${this.item.firstName} ${this.item.lastName}`,
}),
{
cancelTitle: this.$t('overlay.cancel'),
centered: true,
hideHeaderClose: true,
title: this.$t('overlay.undeleteUser.title'),
okTitle: this.$t('overlay.undeleteUser.yes'),
okVariant: 'success',
},
)
.then((okClicked) => {
if (okClicked) {
this.unDeleteUser()
}
})
.catch((error) => {
this.toastError(error.message)
})
},
deleteUser() { deleteUser() {
this.$apollo this.$apollo
.mutate({ .mutate({
@ -50,7 +95,6 @@ export default {
userId: this.item.userId, userId: this.item.userId,
deletedAt: result.data.deleteUser, deletedAt: result.data.deleteUser,
}) })
this.checked = false
}) })
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)
@ -69,7 +113,6 @@ export default {
userId: this.item.userId, userId: this.item.userId,
deletedAt: result.data.unDeleteUser, deletedAt: result.data.unDeleteUser,
}) })
this.checked = false
}) })
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)

View File

@ -1,19 +1,18 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import EditCreationFormular from './EditCreationFormular' import EditCreationFormular from './EditCreationFormular'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
import VueApollo from 'vue-apollo'
import { createMockClient } from 'mock-apollo-client'
import { adminOpenCreations } from '../graphql/adminOpenCreations'
import { adminUpdateContribution } from '../graphql/adminUpdateContribution'
const mockClient = createMockClient()
const apolloProvider = new VueApollo({
defaultClient: mockClient,
})
const localVue = global.localVue const localVue = global.localVue
localVue.use(VueApollo)
const apolloMutateMock = jest.fn().mockResolvedValue({
data: {
adminUpdateContribution: {
creation: [0, 0, 0],
amount: 500,
date: new Date(),
memo: 'Test Schöpfung 2',
},
},
})
const stateCommitMock = jest.fn() const stateCommitMock = jest.fn()
@ -23,22 +22,18 @@ const mocks = {
const date = new Date(d) const date = new Date(d)
return date.toISOString().split('T')[0] return date.toISOString().split('T')[0]
}), }),
$apollo: {
mutate: apolloMutateMock,
},
$store: { $store: {
commit: stateCommitMock, commit: stateCommitMock,
}, },
} }
const now = new Date(Date.now()) const now = new Date()
const getCreationDate = (sub) => { const getCreationDate = (sub) => {
const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0) const date = sub === 0 ? now : new Date(now.getFullYear(), now.getMonth() - sub, 1, 0)
return date.toISOString().split('T')[0] return date.toISOString().split('T')[0]
} }
const propsData = { const propsData = {
creation: [200, 400, 600],
creationUserData: { creationUserData: {
memo: 'Test schöpfung 1', memo: 'Test schöpfung 1',
amount: 100, amount: 100,
@ -46,20 +41,65 @@ const propsData = {
}, },
item: { item: {
id: 0, id: 0,
email: 'bob@baumeister.de', amount: '300',
contributionDate: `${now.getFullYear()}-${now.getMonth() + 1}-${now.getDate()}`,
}, },
} }
const data = () => {
return { creation: ['1000', '1000', '400'] }
}
describe('EditCreationFormular', () => { describe('EditCreationFormular', () => {
let wrapper let wrapper
const adminUpdateContributionMock = jest.fn()
const adminOpenCreationsMock = jest.fn()
mockClient.setRequestHandler(
adminOpenCreations,
adminOpenCreationsMock.mockResolvedValue({
data: {
adminOpenCreations: [
{
month: new Date(now.getFullYear(), now.getMonth() - 2).getMonth(),
year: new Date(now.getFullYear(), now.getMonth() - 2).getFullYear(),
amount: '1000',
},
{
month: new Date(now.getFullYear(), now.getMonth() - 1).getMonth(),
year: new Date(now.getFullYear(), now.getMonth() - 1).getFullYear(),
amount: '1000',
},
{
month: now.getMonth(),
year: now.getFullYear(),
amount: '400',
},
],
},
}),
)
mockClient.setRequestHandler(
adminUpdateContribution,
adminUpdateContributionMock.mockResolvedValue({
data: {
adminUpdateContribution: {
amount: '600',
date: new Date(),
memo: 'This is my memo',
},
},
}),
)
const Wrapper = () => { const Wrapper = () => {
return mount(EditCreationFormular, { localVue, mocks, propsData }) return mount(EditCreationFormular, { localVue, mocks, propsData, data, apolloProvider })
} }
describe('mount', () => { describe('mount', () => {
beforeEach(() => { beforeEach(async () => {
wrapper = Wrapper() wrapper = Wrapper()
await wrapper.vm.$nextTick()
}) })
it('has a DIV element with the class.component-edit-creation-formular', () => { it('has a DIV element with the class.component-edit-creation-formular', () => {
@ -89,42 +129,16 @@ describe('EditCreationFormular', () => {
}) })
it('calls the API', () => { it('calls the API', () => {
expect(apolloMutateMock).toBeCalledWith( expect(adminUpdateContributionMock).toBeCalledWith({
expect.objectContaining({ id: 0,
variables: { creationDate: getCreationDate(0),
id: 0, amount: 500,
email: 'bob@baumeister.de', memo: 'Test Schöpfung 2',
creationDate: getCreationDate(0), })
amount: 500,
memo: 'Test Schöpfung 2',
},
}),
)
})
it('emits update-user-data', () => {
expect(wrapper.emitted('update-user-data')).toEqual([
[
{
id: 0,
email: 'bob@baumeister.de',
},
[0, 0, 0],
],
])
}) })
it('emits update-creation-data', () => { it('emits update-creation-data', () => {
expect(wrapper.emitted('update-creation-data')).toEqual([ expect(wrapper.emitted('update-creation-data')).toBeTruthy()
[
{
amount: 500,
date: expect.any(Date),
memo: 'Test Schöpfung 2',
row: expect.any(Object),
},
],
])
}) })
it('toasts a success message', () => { it('toasts a success message', () => {
@ -134,7 +148,7 @@ describe('EditCreationFormular', () => {
describe('change and save memo and value with error', () => { describe('change and save memo and value with error', () => {
beforeEach(async () => { beforeEach(async () => {
apolloMutateMock.mockRejectedValue({ message: 'Oh no!' }) adminUpdateContributionMock.mockRejectedValue({ message: 'Oh no!' })
await wrapper.find('input[type="number"]').setValue(500) await wrapper.find('input[type="number"]').setValue(500)
await wrapper.find('textarea').setValue('Test Schöpfung 2') await wrapper.find('textarea').setValue('Test Schöpfung 2')
await wrapper.find('.test-submit').trigger('click') await wrapper.find('.test-submit').trigger('click')

View File

@ -96,18 +96,14 @@ export default {
type: Object, type: Object,
required: true, required: true,
}, },
creation: {
type: Array,
required: true,
},
}, },
data() { data() {
return { return {
text: !this.creationUserData.memo ? '' : this.creationUserData.memo, text: !this.creationUserData.memo ? '' : this.creationUserData.memo,
value: !this.creationUserData.amount ? 0 : Number(this.creationUserData.amount), value: !this.creationUserData.amount ? 0 : Number(this.creationUserData.amount),
rangeMin: 0, rangeMin: 0,
rangeMax: 1000, selected: this.selectedComputed,
selected: '', userId: this.item.userId,
} }
}, },
methods: { methods: {
@ -117,20 +113,13 @@ export default {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: this.item.id, id: this.item.id,
email: this.item.email,
creationDate: this.selected.date, creationDate: this.selected.date,
amount: Number(this.value), amount: Number(this.value),
memo: this.text, memo: this.text,
}, },
}) })
.then((result) => { .then((result) => {
this.$emit('update-user-data', this.item, result.data.adminUpdateContribution.creation) this.$emit('update-creation-data')
this.$emit('update-creation-data', {
amount: Number(result.data.adminUpdateContribution.amount),
date: result.data.adminUpdateContribution.date,
memo: result.data.adminUpdateContribution.memo,
row: this.row,
})
this.toastSuccess( this.toastSuccess(
this.$t('creation_form.toasted_update', { this.$t('creation_form.toasted_update', {
value: this.value, value: this.value,
@ -149,15 +138,29 @@ export default {
// Den geschöpften Wert auf o setzen // Den geschöpften Wert auf o setzen
this.value = 0 this.value = 0
}) })
.finally(() => {
this.$apollo.queries.OpenCreations.refetch()
})
}, },
}, },
created() { computed: {
if (this.creationUserData.date) { creationIndex() {
const month = this.$d(new Date(this.creationUserData.date), 'month') const month = this.$d(new Date(this.item.contributionDate), 'month')
const index = this.radioOptions.findIndex((obj) => obj.item.short === month) return this.radioOptions.findIndex((obj) => {
this.selected = this.radioOptions[index].item return obj.item.short === month
this.rangeMax = Number(this.creation[index]) + Number(this.creationUserData.amount) })
} },
selectedComputed() {
return this.radioOptions[this.creationIndex].item
},
rangeMax() {
return Number(this.creation[this.creationIndex]) + Number(this.item.amount)
},
},
watch: {
selectedComputed() {
this.selected = this.selectedComputed
},
}, },
} }
</script> </script>

View File

@ -5,7 +5,6 @@ const localVue = global.localVue
const apolloMutateMock = jest.fn().mockResolvedValue({}) const apolloMutateMock = jest.fn().mockResolvedValue({})
const apolloQueryMock = jest.fn().mockResolvedValue({}) const apolloQueryMock = jest.fn().mockResolvedValue({})
const toggleDetailsMock = jest.fn()
const propsData = { const propsData = {
items: [ items: [
@ -17,7 +16,7 @@ const propsData = {
amount: 300, amount: 300,
memo: 'Aktives Grundeinkommen für Januar 2022', memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z', date: '2022-01-01T00:00:00.000Z',
moderator: 1, moderatorId: 1,
creation: [700, 1000, 1000], creation: [700, 1000, 1000],
__typename: 'PendingCreation', __typename: 'PendingCreation',
}, },
@ -29,7 +28,7 @@ const propsData = {
amount: 210, amount: 210,
memo: 'Aktives Grundeinkommen für Januar 2022', memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z', date: '2022-01-01T00:00:00.000Z',
moderator: null, moderatorId: null,
creation: [790, 1000, 1000], creation: [790, 1000, 1000],
__typename: 'PendingCreation', __typename: 'PendingCreation',
}, },
@ -41,7 +40,7 @@ const propsData = {
amount: 330, amount: 330,
memo: 'Aktives Grundeinkommen für Januar 2022', memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z', date: '2022-01-01T00:00:00.000Z',
moderator: 1, moderatorId: 1,
creation: [670, 1000, 1000], creation: [670, 1000, 1000],
__typename: 'PendingCreation', __typename: 'PendingCreation',
}, },
@ -83,7 +82,7 @@ const mocks = {
$store: { $store: {
state: { state: {
moderator: { moderator: {
id: 0, id: 1,
name: 'test moderator', name: 'test moderator',
}, },
}, },
@ -132,14 +131,6 @@ describe('OpenCreationsTable', () => {
}) })
}) })
describe('call updateUserData', () => {
it('user creations has updated data', async () => {
wrapper.vm.updateUserData(propsData.items[0], [444, 555, 666])
await wrapper.vm.$nextTick()
expect(wrapper.vm.items[0].creation).toEqual([444, 555, 666])
})
})
describe('call updateState', () => { describe('call updateState', () => {
beforeEach(() => { beforeEach(() => {
wrapper.vm.updateState(4) wrapper.vm.updateState(4)
@ -149,40 +140,5 @@ describe('OpenCreationsTable', () => {
expect(wrapper.vm.$root.$emit('update-state', 4)).toBeTruthy() expect(wrapper.vm.$root.$emit('update-state', 4)).toBeTruthy()
}) })
}) })
describe('call updateCreationData', () => {
const date = new Date()
beforeEach(() => {
wrapper.vm.updateCreationData({
amount: Number(80.0),
date: date,
memo: 'Test memo',
row: {
item: {},
detailsShowing: false,
toggleDetails: toggleDetailsMock,
},
})
})
it('emits update-state', () => {
expect(
wrapper.vm.$emit('update-contributions', {
amount: Number(80.0),
date: date,
memo: 'Test memo',
row: {
item: {},
detailsShowing: false,
toggleDetails: toggleDetailsMock,
},
}),
).toBeTruthy()
})
it('calls toggleDetails', () => {
expect(toggleDetailsMock).toBeCalled()
})
})
}) })
}) })

View File

@ -27,9 +27,10 @@
<template #cell(editCreation)="row"> <template #cell(editCreation)="row">
<div v-if="!myself(row.item)"> <div v-if="!myself(row.item)">
<b-button <b-button
v-if="row.item.moderator" v-if="row.item.moderatorId"
variant="info" variant="info"
size="md" size="md"
:index="0"
@click="rowToggleDetails(row, 0)" @click="rowToggleDetails(row, 0)"
class="mr-2" class="mr-2"
> >
@ -89,14 +90,13 @@
@row-toggle-details="rowToggleDetails(row, 0)" @row-toggle-details="rowToggleDetails(row, 0)"
> >
<template #show-creation> <template #show-creation>
<div v-if="row.item.moderator"> <div v-if="row.item.moderatorId">
<edit-creation-formular <edit-creation-formular
type="singleCreation" type="singleCreation"
:creation="row.item.creation"
:item="row.item" :item="row.item"
:row="row" :row="row"
:creationUserData="creationUserData" :creationUserData="creationUserData"
@update-creation-data="updateCreationData" @update-creation-data="$emit('update-contributions')"
/> />
</div> </div>
<div v-else> <div v-else>
@ -104,7 +104,6 @@
:contributionId="row.item.id" :contributionId="row.item.id"
:contributionState="row.item.state" :contributionState="row.item.state"
@update-state="updateState" @update-state="updateState"
@update-user-data="updateUserData"
/> />
</div> </div>
</template> </template>
@ -146,22 +145,9 @@ export default {
required: true, required: true,
}, },
}, },
data() {
return {
creationUserData: {
amount: null,
date: null,
memo: null,
moderator: null,
},
}
},
methods: { methods: {
myself(item) { myself(item) {
return ( return item.userId === this.$store.state.moderator.id
`${item.firstName} ${item.lastName}` ===
`${this.$store.state.moderator.firstName} ${this.$store.state.moderator.lastName}`
)
}, },
getStatusIcon(status) { getStatusIcon(status) {
return iconMap[status] ? iconMap[status] : 'default-icon' return iconMap[status] ? iconMap[status] : 'default-icon'
@ -174,16 +160,6 @@ export default {
if (item.state === 'IN_PROGRESS') return 'table-primary' if (item.state === 'IN_PROGRESS') return 'table-primary'
if (item.state === 'PENDING') return 'table-primary' if (item.state === 'PENDING') return 'table-primary'
}, },
updateCreationData(data) {
const row = data.row
this.$emit('update-contributions', data)
delete data.row
this.creationUserData = { ...this.creationUserData, ...data }
row.toggleDetails()
},
updateUserData(rowItem, newCreation) {
rowItem.creation = newCreation
},
updateState(id) { updateState(id) {
this.$emit('update-state', id) this.$emit('update-state', id)
}, },

View File

@ -32,6 +32,8 @@ export const adminListContributions = gql`
deniedBy deniedBy
deletedAt deletedAt
deletedBy deletedBy
moderatorId
userId
} }
} }
} }

View File

@ -0,0 +1,11 @@
import gql from 'graphql-tag'
export const adminOpenCreations = gql`
query ($userId: Int!) {
adminOpenCreations(userId: $userId) {
year
month
amount
}
}
`

View File

@ -1,18 +1,11 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
export const adminUpdateContribution = gql` export const adminUpdateContribution = gql`
mutation ($id: Int!, $email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) { mutation ($id: Int!, $amount: Decimal!, $memo: String!, $creationDate: String!) {
adminUpdateContribution( adminUpdateContribution(id: $id, amount: $amount, memo: $memo, creationDate: $creationDate) {
id: $id
email: $email
amount: $amount
memo: $memo
creationDate: $creationDate
) {
amount amount
date date
memo memo
creation
} }
} }
` `

View File

@ -1,6 +1,7 @@
{ {
"all_emails": "Alle Nutzer", "all_emails": "Alle Nutzer",
"back": "zurück", "back": "zurück",
"change_user_role": "Nutzerrolle ändern",
"chat": "Chat", "chat": "Chat",
"contributionLink": { "contributionLink": {
"amount": "Betrag", "amount": "Betrag",
@ -114,6 +115,11 @@
"open_creations": "Offene Schöpfungen", "open_creations": "Offene Schöpfungen",
"overlay": { "overlay": {
"cancel": "Abbrechen", "cancel": "Abbrechen",
"changeUserRole": {
"question": "Willst du die Rolle von {username} wirklich zu {newRole} ändern?",
"title": "Nutzerrolle ändern",
"yes": "Ja, Nutzerrolle ändern"
},
"confirm": { "confirm": {
"question": "Willst du diesen Gemeinwohl-Beitrag wirklich bestätigen und gutschreiben?", "question": "Willst du diesen Gemeinwohl-Beitrag wirklich bestätigen und gutschreiben?",
"text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar. Bitte überprüfe genau, dass alles stimmt.", "text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar. Bitte überprüfe genau, dass alles stimmt.",
@ -126,11 +132,21 @@
"title": "Gemeinwohl-Beitrag löschen!", "title": "Gemeinwohl-Beitrag löschen!",
"yes": "Ja, Beitrag löschen!" "yes": "Ja, Beitrag löschen!"
}, },
"deleteUser": {
"question": "Willst du {username} wirklich löschen?",
"title": "Nutzer löschen",
"yes": "Ja, Nutzer löschen"
},
"deny": { "deny": {
"question": "Willst du diesen Gemeinwohl-Beitrag wirklich ablehnen?", "question": "Willst du diesen Gemeinwohl-Beitrag wirklich ablehnen?",
"text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.", "text": "Nach dem Speichern ist der Datensatz nicht mehr änderbar und kann auch nicht mehr gelöscht werden. Bitte überprüfe genau, dass alles stimmt.",
"title": "Gemeinwohl-Beitrag ablehnen!", "title": "Gemeinwohl-Beitrag ablehnen!",
"yes": "Ja, Beitrag ablehnen und speichern!" "yes": "Ja, Beitrag ablehnen und speichern!"
},
"undeleteUser": {
"question": "Willst du wirklich {username} wiederherstellen?",
"title": "Nutzer wiederherstellen",
"yes": "Ja, Nutzer wiederherstellen"
} }
}, },
"redeemed": "eingelöst", "redeemed": "eingelöst",

View File

@ -1,6 +1,7 @@
{ {
"all_emails": "All users", "all_emails": "All users",
"back": "back", "back": "back",
"change_user_role": "Change user role",
"chat": "Chat", "chat": "Chat",
"contributionLink": { "contributionLink": {
"amount": "Amount", "amount": "Amount",
@ -114,6 +115,11 @@
"open_creations": "Open creations", "open_creations": "Open creations",
"overlay": { "overlay": {
"cancel": "Cancel", "cancel": "Cancel",
"changeUserRole": {
"question": "Do you really want to change {username}'s role to {newRole}?",
"title": "Change user role",
"yes": "Yes, change user role"
},
"confirm": { "confirm": {
"question": "Do you really want to carry out and finally save this pre-stored creation?", "question": "Do you really want to carry out and finally save this pre-stored creation?",
"text": "After saving, the record can no longer be changed. Please check carefully that everything is correct.", "text": "After saving, the record can no longer be changed. Please check carefully that everything is correct.",
@ -126,11 +132,21 @@
"title": "Delete creation!", "title": "Delete creation!",
"yes": "Yes, delete and save creation!" "yes": "Yes, delete and save creation!"
}, },
"deleteUser": {
"question": "Do you really want to delete {username}?",
"title": "Delete user",
"yes": "Yes, delete user"
},
"deny": { "deny": {
"question": "Do you really want to carry out and finally save this pre-stored creation?", "question": "Do you really want to carry out and finally save this pre-stored creation?",
"text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.", "text": "After saving, the record can no longer be changed or deleted. Please check carefully that everything is correct.",
"title": "Reject creation!", "title": "Reject creation!",
"yes": "Yes, reject and save creation!" "yes": "Yes, reject and save creation!"
},
"undeleteUser": {
"question": "Do you really want to undelete {username}",
"title": "Undelete user",
"yes": "Yes,undelete user"
} }
}, },
"redeemed": "redeemed", "redeemed": "redeemed",

View File

@ -1,9 +1,11 @@
import { adminOpenCreations } from '../graphql/adminOpenCreations'
export const creationMonths = { export const creationMonths = {
props: { data() {
creation: { return {
type: Array, creation: [1000, 1000, 1000],
default: () => [1000, 1000, 1000], userId: 0,
}, }
}, },
computed: { computed: {
creationDates() { creationDates() {
@ -38,4 +40,23 @@ export const creationMonths = {
return this.creationDates.map((date) => this.$d(date, 'monthShort')).join(' | ') return this.creationDates.map((date) => this.$d(date, 'monthShort')).join(' | ')
}, },
}, },
apollo: {
OpenCreations: {
query() {
return adminOpenCreations
},
variables() {
return {
userId: this.userId,
}
},
fetchPolicy: 'no-cache',
update({ adminOpenCreations }) {
this.creation = adminOpenCreations.map((obj) => obj.amount)
},
error({ message }) {
this.toastError(message)
},
},
},
} }

View File

@ -44,7 +44,7 @@
:fields="fields" :fields="fields"
@show-overlay="showOverlay" @show-overlay="showOverlay"
@update-state="updateStatus" @update-state="updateStatus"
@update-contributions="$apollo.queries.AllContributions.refetch()" @update-contributions="$apollo.queries.ListAllContributions.refetch()"
/> />
<b-pagination <b-pagination
@ -212,7 +212,7 @@ export default {
return this.formatDateOrDash(value) return this.formatDateOrDash(value)
}, },
}, },
{ key: 'moderator', label: this.$t('moderator') }, { key: 'moderatorId', label: this.$t('moderator') },
{ key: 'editCreation', label: this.$t('chat') }, { key: 'editCreation', label: this.$t('chat') },
{ key: 'confirm', label: this.$t('save') }, { key: 'confirm', label: this.$t('save') },
], ],

View File

@ -42,7 +42,7 @@ const defaultData = () => {
amount: 500, amount: 500,
memo: 'Danke für alles', memo: 'Danke für alles',
date: new Date(), date: new Date(),
moderator: 1, moderatorId: 1,
state: 'PENDING', state: 'PENDING',
creation: [500, 500, 500], creation: [500, 500, 500],
messagesCount: 0, messagesCount: 0,
@ -64,7 +64,7 @@ const defaultData = () => {
amount: 1000000, amount: 1000000,
memo: 'Gut Ergattert', memo: 'Gut Ergattert',
date: new Date(), date: new Date(),
moderator: 1, moderatorId: 1,
state: 'PENDING', state: 'PENDING',
creation: [500, 500, 500], creation: [500, 500, 500],
messagesCount: 0, messagesCount: 0,

View File

@ -25,7 +25,7 @@ const apolloQueryMock = jest.fn().mockResolvedValue({
email: 'benjamin@bluemchen.de', email: 'benjamin@bluemchen.de',
creation: [1000, 1000, 1000], creation: [1000, 1000, 1000],
emailChecked: true, emailChecked: true,
deletedAt: null, deletedAt: new Date(),
}, },
{ {
userId: 3, userId: 3,
@ -243,6 +243,17 @@ describe('UserSearch', () => {
}) })
}) })
describe('recover user', () => {
const userId = 2
beforeEach(() => {
wrapper.findComponent({ name: 'SearchUserTable' }).vm.$emit('updateDeletedAt', userId, null)
})
it('toasts a success message', () => {
expect(toastSuccessSpy).toBeCalledWith('user_recovered')
})
})
describe('apollo returns error', () => { describe('apollo returns error', () => {
beforeEach(() => { beforeEach(() => {
apolloQueryMock.mockRejectedValue({ apolloQueryMock.mockRejectedValue({

View File

@ -76,7 +76,30 @@ module.exports = {
'import/no-named-default': 'error', 'import/no-named-default': 'error',
'import/no-namespace': 'error', 'import/no-namespace': 'error',
'import/no-unassigned-import': 'error', 'import/no-unassigned-import': 'error',
'import/order': 'error', 'import/order': [
'error',
{
groups: ['builtin', 'external', 'internal', 'parent', 'sibling', 'index', 'object', 'type'],
'newlines-between': 'always',
pathGroups: [
{
pattern: '@?*/**',
group: 'external',
position: 'after',
},
{
pattern: '@/**',
group: 'external',
position: 'after',
},
],
alphabetize: {
order: 'asc' /* sort in ascending order. Options: ['ignore', 'asc', 'desc'] */,
caseInsensitive: true /* ignore case. Options: [true, false] */,
},
distinctGroup: true,
},
],
'import/prefer-default-export': 'off', // TODO 'import/prefer-default-export': 'off', // TODO
}, },
overrides: [ overrides: [

View File

@ -2,8 +2,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import axios from 'axios' import axios from 'axios'
import { backendLogger as logger } from '@/server/logger'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export const apiPost = async (url: string, payload: unknown): Promise<any> => { export const apiPost = async (url: string, payload: unknown): Promise<any> => {

View File

@ -4,9 +4,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import CONFIG from '@/config'
// eslint-disable-next-line import/no-relative-parent-imports // eslint-disable-next-line import/no-relative-parent-imports
import KlicktippConnector from 'klicktipp-api' import KlicktippConnector from 'klicktipp-api'
import CONFIG from '@/config'
const klicktippConnector = new KlicktippConnector() const klicktippConnector = new KlicktippConnector()

View File

@ -1,8 +1,10 @@
import { verify, sign } from 'jsonwebtoken' import { verify, sign } from 'jsonwebtoken'
import { CustomJwtPayload } from './CustomJwtPayload'
import CONFIG from '@/config/' import CONFIG from '@/config/'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
import { CustomJwtPayload } from './CustomJwtPayload'
export const decode = (token: string): CustomJwtPayload | null => { export const decode = (token: string): CustomJwtPayload | null => {
if (!token) throw new LogError('401 Unauthorized') if (!token) throw new LogError('401 Unauthorized')
try { try {

View File

@ -1,7 +1,7 @@
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env) // ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
import dotenv from 'dotenv'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import dotenv from 'dotenv'
dotenv.config() dotenv.config()

View File

@ -1,10 +1,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/unbound-method */
import { createTransport } from 'nodemailer' import { createTransport } from 'nodemailer'
import { sendEmailTranslated } from './sendEmailTranslated'
import { logger, i18n } from '@test/testSetup' import { logger, i18n } from '@test/testSetup'
import CONFIG from '@/config' import CONFIG from '@/config'
import { sendEmailTranslated } from './sendEmailTranslated'
CONFIG.EMAIL = false CONFIG.EMAIL = false
CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL' CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
CONFIG.EMAIL_SMTP_PORT = '1234' CONFIG.EMAIL_SMTP_PORT = '1234'

View File

@ -1,11 +1,13 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import path from 'path' import path from 'path'
import { createTransport } from 'nodemailer'
import Email from 'email-templates' import Email from 'email-templates'
import i18n from 'i18n' import i18n from 'i18n'
import { backendLogger as logger } from '@/server/logger' import { createTransport } from 'nodemailer'
import CONFIG from '@/config' import CONFIG from '@/config'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
export const sendEmailTranslated = async (params: { export const sendEmailTranslated = async (params: {
receiver: { receiver: {

View File

@ -4,6 +4,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import CONFIG from '@/config'
import { sendEmailTranslated } from './sendEmailTranslated'
import { import {
sendAddedContributionMessageEmail, sendAddedContributionMessageEmail,
sendAccountActivationEmail, sendAccountActivationEmail,
@ -15,10 +22,6 @@ import {
sendTransactionLinkRedeemedEmail, sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail, sendTransactionReceivedEmail,
} from './sendEmailVariants' } from './sendEmailVariants'
import { sendEmailTranslated } from './sendEmailTranslated'
import { testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import CONFIG from '@/config'
let con: any let con: any
let testEnv: any let testEnv: any

View File

@ -1,8 +1,10 @@
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { sendEmailTranslated } from './sendEmailTranslated'
import CONFIG from '@/config' import CONFIG from '@/config'
import { decimalSeparatorByLanguage } from '@/util/utilities' import { decimalSeparatorByLanguage } from '@/util/utilities'
import { sendEmailTranslated } from './sendEmailTranslated'
export const sendAddedContributionMessageEmail = (data: { export const sendAddedContributionMessageEmail = (data: {
firstName: string firstName: string
lastName: string lastName: string

View File

@ -1,7 +1,10 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,9 +1,10 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,7 +1,8 @@
import { Decimal } from 'decimal.js-light'
import { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,5 +1,6 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event } from './Event' import { Event } from './Event'
import { EventType } from './EventType' import { EventType } from './EventType'

View File

@ -1,11 +1,12 @@
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction' import { Transaction as DbTransaction } from '@entity/Transaction'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink' import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Contribution as DbContribution } from '@entity/Contribution' import { User as DbUser } from '@entity/User'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { EventType } from './EventType' import { EventType } from './EventType'
export const Event = ( export const Event = (

View File

@ -1,11 +1,12 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { gql } from 'graphql-request'
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'
import { gql } from 'graphql-request'
import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient'
import { backendLogger as logger } from '@/server/logger'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string | undefined> { export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string | undefined> {
let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'

View File

@ -1,11 +1,12 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
import { gql } from 'graphql-request'
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'
import { gql } from 'graphql-request'
import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient'
import { backendLogger as logger } from '@/server/logger'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string | undefined> { export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string | undefined> {
let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/'

View File

@ -6,9 +6,11 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'
import { validateCommunities } from './validateCommunities'
import { logger } from '@test/testSetup'
import { testEnvironment, cleanDB } from '@test/helpers' import { testEnvironment, cleanDB } from '@test/helpers'
import { logger } from '@test/testSetup'
import { validateCommunities } from './validateCommunities'
let con: any let con: any
let testEnv: any let testEnv: any

View File

@ -1,12 +1,14 @@
import { Community as DbCommunity } from '@entity/Community'
import { IsNull } from '@dbTools/typeorm' import { IsNull } from '@dbTools/typeorm'
import { Community as DbCommunity } from '@entity/Community'
import LogError from '@/server/LogError'
import { backendLogger as logger } from '@/server/logger'
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
import { requestGetPublicKey as v1_0_requestGetPublicKey } from './client/1_0/FederationClient' import { requestGetPublicKey as v1_0_requestGetPublicKey } from './client/1_0/FederationClient'
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
import { requestGetPublicKey as v1_1_requestGetPublicKey } from './client/1_1/FederationClient' import { requestGetPublicKey as v1_1_requestGetPublicKey } from './client/1_1/FederationClient'
import { ApiVersionType } from './enum/apiVersionType' import { ApiVersionType } from './enum/apiVersionType'
import { backendLogger as logger } from '@/server/logger'
import LogError from '@/server/LogError'
export function startValidateCommunities(timerInterval: number): void { export function startValidateCommunities(timerInterval: number): void {
logger.info( logger.info(

View File

@ -1,5 +1,5 @@
import { ArgsType, Field, InputType } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field, InputType } from 'type-graphql'
@InputType() @InputType()
@ArgsType() @ArgsType()

View File

@ -1,14 +1,11 @@
import { ArgsType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field, Int } from 'type-graphql'
@ArgsType() @ArgsType()
export default class AdminUpdateContributionArgs { export default class AdminUpdateContributionArgs {
@Field(() => Int) @Field(() => Int)
id: number id: number
@Field(() => String)
email: string
@Field(() => Decimal) @Field(() => Decimal)
amount: Decimal amount: Decimal

View File

@ -1,5 +1,5 @@
import { ArgsType, Field, InputType } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field, InputType } from 'type-graphql'
@InputType() @InputType()
@ArgsType() @ArgsType()

View File

@ -1,5 +1,5 @@
import { ArgsType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field, Int } from 'type-graphql'
@ArgsType() @ArgsType()
export default class ContributionLinkArgs { export default class ContributionLinkArgs {

View File

@ -1,5 +1,6 @@
/* eslint-disable type-graphql/invalid-nullable-input-type */ /* eslint-disable type-graphql/invalid-nullable-input-type */
import { ArgsType, Field, Int } from 'type-graphql' import { ArgsType, Field, Int } from 'type-graphql'
import { Order } from '@enum/Order' import { Order } from '@enum/Order'
@ArgsType() @ArgsType()

View File

@ -1,4 +1,5 @@
import { ArgsType, Field, Int } from 'type-graphql' import { ArgsType, Field, Int } from 'type-graphql'
import SearchUsersFilters from '@arg/SearchUsersFilters' import SearchUsersFilters from '@arg/SearchUsersFilters'
@ArgsType() @ArgsType()

View File

@ -1,5 +1,5 @@
import { ArgsType, Field } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field } from 'type-graphql'
@ArgsType() @ArgsType()
export default class TransactionLinkArgs { export default class TransactionLinkArgs {

View File

@ -1,5 +1,5 @@
import { ArgsType, Field } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ArgsType, Field } from 'type-graphql'
@ArgsType() @ArgsType()
export default class TransactionSendArgs { export default class TransactionSendArgs {

View File

@ -2,13 +2,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { User } from '@entity/User'
import { AuthChecker } from 'type-graphql' import { AuthChecker } from 'type-graphql'
import { User } from '@entity/User'
import { decode, encode } from '@/auth/JWT'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '@/auth/ROLES'
import { RIGHTS } from '@/auth/RIGHTS'
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS' import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
import { decode, encode } from '@/auth/JWT'
import { RIGHTS } from '@/auth/RIGHTS'
import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '@/auth/ROLES'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => { const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field } from 'type-graphql'
@ObjectType() @ObjectType()
export class AdminUpdateContribution { export class AdminUpdateContribution {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int, Float } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int, Float } from 'type-graphql'
@ObjectType() @ObjectType()
export class Balance { export class Balance {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class Community { export class Community {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class DynamicStatisticsFields { export class DynamicStatisticsFields {

View File

@ -1,7 +1,7 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light'
import { Contribution as dbContribution } from '@entity/Contribution' import { Contribution as dbContribution } from '@entity/Contribution'
import { User } from '@entity/User' import { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class Contribution { export class Contribution {
@ -21,6 +21,8 @@ export class Contribution {
this.deniedBy = contribution.deniedBy this.deniedBy = contribution.deniedBy
this.deletedAt = contribution.deletedAt this.deletedAt = contribution.deletedAt
this.deletedBy = contribution.deletedBy this.deletedBy = contribution.deletedBy
this.moderatorId = contribution.moderatorId
this.userId = contribution.userId
} }
@Field(() => Int) @Field(() => Int)
@ -67,6 +69,12 @@ export class Contribution {
@Field(() => String) @Field(() => String)
state: string state: string
@Field(() => Int, { nullable: true })
moderatorId: number | null
@Field(() => Int, { nullable: true })
userId: number | null
} }
@ObjectType() @ObjectType()

View File

@ -1,6 +1,7 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light'
import { ContributionLink as dbContributionLink } from '@entity/ContributionLink' import { ContributionLink as dbContributionLink } from '@entity/ContributionLink'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import CONFIG from '@/config' import CONFIG from '@/config'
@ObjectType() @ObjectType()

View File

@ -1,4 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import { ContributionLink } from '@model/ContributionLink' import { ContributionLink } from '@model/ContributionLink'
@ObjectType() @ObjectType()

View File

@ -1,6 +1,6 @@
import { Field, Int, ObjectType } from 'type-graphql'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { User } from '@entity/User' import { User } from '@entity/User'
import { Field, Int, ObjectType } from 'type-graphql'
@ObjectType() @ObjectType()
export class ContributionMessage { export class ContributionMessage {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
interface DecayInterface { interface DecayInterface {
balance: Decimal balance: Decimal

View File

@ -3,6 +3,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field, Float, Int } from 'type-graphql' import { ObjectType, Field, Float, Int } from 'type-graphql'
import { GdtEntryType } from '@enum/GdtEntryType' import { GdtEntryType } from '@enum/GdtEntryType'
@ObjectType() @ObjectType()

View File

@ -4,6 +4,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field, Int, Float } from 'type-graphql' import { ObjectType, Field, Int, Float } from 'type-graphql'
import { GdtEntry } from './GdtEntry' import { GdtEntry } from './GdtEntry'
@ObjectType() @ObjectType()

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class OpenCreation { export class OpenCreation {

View File

@ -1,9 +1,11 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Transaction as dbTransaction } from '@entity/Transaction' import { Transaction as dbTransaction } from '@entity/Transaction'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import { TransactionTypeId } from '@enum/TransactionTypeId'
import { Decay } from './Decay' import { Decay } from './Decay'
import { User } from './User' import { User } from './User'
import { TransactionTypeId } from '@enum/TransactionTypeId'
@ObjectType() @ObjectType()
export class Transaction { export class Transaction {

View File

@ -1,9 +1,11 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { User } from './User' import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import CONFIG from '@/config' import CONFIG from '@/config'
import { User } from './User'
@ObjectType() @ObjectType()
export class TransactionLink { export class TransactionLink {
constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) { constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) {

View File

@ -1,6 +1,7 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field } from 'type-graphql'
import { Transaction } from './Transaction'
import { Balance } from './Balance' import { Balance } from './Balance'
import { Transaction } from './Transaction'
@ObjectType() @ObjectType()
export class TransactionList { export class TransactionList {

View File

@ -1,7 +1,7 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light'
import { Contribution } from '@entity/Contribution' import { Contribution } from '@entity/Contribution'
import { User } from '@entity/User' import { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class UnconfirmedContribution { export class UnconfirmedContribution {

View File

@ -1,5 +1,6 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { User as dbUser } from '@entity/User' import { User as dbUser } from '@entity/User'
import { ObjectType, Field, Int } from 'type-graphql'
import { KlickTipp } from './KlickTipp' import { KlickTipp } from './KlickTipp'
import { UserContact } from './UserContact' import { UserContact } from './UserContact'

View File

@ -1,6 +1,6 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decimal } from 'decimal.js-light'
import { User } from '@entity/User' import { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class UserAdmin { export class UserAdmin {

View File

@ -1,5 +1,5 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { UserContact as dbUserContact } from '@entity/UserContact' import { UserContact as dbUserContact } from '@entity/UserContact'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class UserContact { export class UserContact {

View File

@ -1,21 +1,20 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import { Decimal } from 'decimal.js-light'
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
import { getCustomRepository } from '@dbTools/typeorm' import { getCustomRepository } from '@dbTools/typeorm'
import { Transaction as dbTransaction } from '@entity/Transaction' import { Transaction as dbTransaction } from '@entity/Transaction'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink' import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { Decimal } from 'decimal.js-light'
import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
import { Balance } from '@model/Balance'
import { TransactionLinkRepository } from '@repository/TransactionLink'
import { RIGHTS } from '@/auth/RIGHTS'
import { Context, getUser } from '@/server/context'
import { backendLogger as logger } from '@/server/logger'
import { calculateDecay } from '@/util/decay'
import { GdtResolver } from './GdtResolver' import { GdtResolver } from './GdtResolver'
import { getLastTransaction } from './util/getLastTransaction' import { getLastTransaction } from './util/getLastTransaction'
import { TransactionLinkRepository } from '@repository/TransactionLink'
import { Balance } from '@model/Balance'
import { backendLogger as logger } from '@/server/logger'
import { Context, getUser } from '@/server/context'
import { calculateDecay } from '@/util/decay'
import { RIGHTS } from '@/auth/RIGHTS'
@Resolver() @Resolver()
export class BalanceResolver { export class BalanceResolver {

View File

@ -6,9 +6,11 @@
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'
import { getCommunities } from '@/seeds/graphql/queries'
import { testEnvironment } from '@test/helpers' import { testEnvironment } from '@test/helpers'
import { getCommunities } from '@/seeds/graphql/queries'
let query: any let query: any
// to do: We need a setup for the tests that closes the connection // to do: We need a setup for the tests that closes the connection

View File

@ -1,6 +1,6 @@
import { Community as DbCommunity } from '@entity/Community'
import { Resolver, Query, Authorized } from 'type-graphql' import { Resolver, Query, Authorized } from 'type-graphql'
import { Community as DbCommunity } from '@entity/Community'
import { Community } from '@model/Community' import { Community } from '@model/Community'
import { RIGHTS } from '@/auth/RIGHTS' import { RIGHTS } from '@/auth/RIGHTS'

View File

@ -4,11 +4,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { Decimal } from 'decimal.js-light'
import { GraphQLError } from 'graphql'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { Decimal } from 'decimal.js-light'
import { GraphQLError } from 'graphql'
import { cleanDB, testEnvironment, resetToken } from '@test/helpers'
import { logger } from '@test/testSetup' import { logger } from '@test/testSetup'
import { EventType } from '@/event/Event'
import { userFactory } from '@/seeds/factory/user'
import { import {
login, login,
createContributionLink, createContributionLink,
@ -16,11 +21,8 @@ import {
updateContributionLink, updateContributionLink,
} from '@/seeds/graphql/mutations' } from '@/seeds/graphql/mutations'
import { listContributionLinks } from '@/seeds/graphql/queries' import { listContributionLinks } from '@/seeds/graphql/queries'
import { cleanDB, testEnvironment, resetToken } from '@test/helpers'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig' import { peterLustig } from '@/seeds/users/peter-lustig'
import { userFactory } from '@/seeds/factory/user'
import { EventType } from '@/event/Events'
let mutate: any, query: any, con: any let mutate: any, query: any, con: any
let testEnv: any let testEnv: any

View File

@ -1,31 +1,32 @@
import { MoreThan, IsNull } from '@dbTools/typeorm'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Decimal } from 'decimal.js-light' import { Decimal } from 'decimal.js-light'
import { Resolver, Args, Arg, Authorized, Mutation, Query, Int, Ctx } from 'type-graphql' import { Resolver, Args, Arg, Authorized, Mutation, Query, Int, Ctx } from 'type-graphql'
import { MoreThan, IsNull } from '@dbTools/typeorm'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' // TODO: this is a strange construct
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
import Paginated from '@arg/Paginated'
import { Order } from '@enum/Order'
import { ContributionLink } from '@model/ContributionLink'
import { ContributionLinkList } from '@model/ContributionLinkList'
import { RIGHTS } from '@/auth/RIGHTS'
import {
EVENT_ADMIN_CONTRIBUTION_LINK_CREATE,
EVENT_ADMIN_CONTRIBUTION_LINK_DELETE,
EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE,
} from '@/event/Events'
import { Context, getUser } from '@/server/context'
import LogError from '@/server/LogError'
import { import {
CONTRIBUTIONLINK_NAME_MAX_CHARS, CONTRIBUTIONLINK_NAME_MAX_CHARS,
CONTRIBUTIONLINK_NAME_MIN_CHARS, CONTRIBUTIONLINK_NAME_MIN_CHARS,
MEMO_MAX_CHARS, MEMO_MAX_CHARS,
MEMO_MIN_CHARS, MEMO_MIN_CHARS,
} from './const/const' } from './const/const'
import { isStartEndDateValid } from './util/creations'
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver' import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
import { ContributionLinkList } from '@model/ContributionLinkList' import { isStartEndDateValid } from './util/creations'
import { ContributionLink } from '@model/ContributionLink'
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
import { RIGHTS } from '@/auth/RIGHTS'
import { Order } from '@enum/Order'
import Paginated from '@arg/Paginated'
// TODO: this is a strange construct
import LogError from '@/server/LogError'
import { Context, getUser } from '@/server/context'
import {
EVENT_ADMIN_CONTRIBUTION_LINK_CREATE,
EVENT_ADMIN_CONTRIBUTION_LINK_DELETE,
EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE,
} from '@/event/Events'
@Resolver() @Resolver()
export class ContributionLinkResolver { export class ContributionLinkResolver {

View File

@ -6,10 +6,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { GraphQLError } from 'graphql'
import { Event as DbEvent } from '@entity/Event' import { Event as DbEvent } from '@entity/Event'
import { GraphQLError } from 'graphql'
import { cleanDB, resetToken, testEnvironment } from '@test/helpers' import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup' import { logger, i18n as localization } from '@test/testSetup'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
import { EventType } from '@/event/Events'
import { userFactory } from '@/seeds/factory/user'
import { import {
adminCreateContributionMessage, adminCreateContributionMessage,
createContribution, createContribution,
@ -17,11 +22,8 @@ import {
login, login,
} from '@/seeds/graphql/mutations' } from '@/seeds/graphql/mutations'
import { listContributionMessages } from '@/seeds/graphql/queries' import { listContributionMessages } from '@/seeds/graphql/queries'
import { userFactory } from '@/seeds/factory/user'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig' import { peterLustig } from '@/seeds/users/peter-lustig'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
import { EventType } from '@/event/Events'
jest.mock('@/emails/sendEmailVariants', () => { jest.mock('@/emails/sendEmailVariants', () => {
const originalModule = jest.requireActual('@/emails/sendEmailVariants') const originalModule = jest.requireActual('@/emails/sendEmailVariants')

View File

@ -1,27 +1,26 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/restrict-template-expressions */
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
import { getConnection } from '@dbTools/typeorm' import { getConnection } from '@dbTools/typeorm'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Contribution as DbContribution } from '@entity/Contribution' import { Contribution as DbContribution } from '@entity/Contribution'
import { UserContact as DbUserContact } from '@entity/UserContact' import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { User as DbUser } from '@entity/User' import { User as DbUser } from '@entity/User'
import { UserContact as DbUserContact } from '@entity/UserContact'
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage'
import ContributionMessageArgs from '@arg/ContributionMessageArgs' import ContributionMessageArgs from '@arg/ContributionMessageArgs'
import { ContributionMessageType } from '@enum/MessageType'
import { ContributionStatus } from '@enum/ContributionStatus'
import { Order } from '@enum/Order'
import Paginated from '@arg/Paginated' import Paginated from '@arg/Paginated'
import { ContributionStatus } from '@enum/ContributionStatus'
import { ContributionMessageType } from '@enum/MessageType'
import { Order } from '@enum/Order'
import { ContributionMessage, ContributionMessageListResult } from '@model/ContributionMessage'
import { RIGHTS } from '@/auth/RIGHTS' import { RIGHTS } from '@/auth/RIGHTS'
import { Context, getUser } from '@/server/context'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants' import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
import LogError from '@/server/LogError'
import { import {
EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE, EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE,
EVENT_CONTRIBUTION_MESSAGE_CREATE, EVENT_CONTRIBUTION_MESSAGE_CREATE,
} from '@/event/Events' } from '@/event/Events'
import { Context, getUser } from '@/server/context'
import LogError from '@/server/LogError'
@Resolver() @Resolver()
export class ContributionMessageResolver { export class ContributionMessageResolver {

View File

@ -6,17 +6,34 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Decimal } from 'decimal.js-light'
import { GraphQLError } from 'graphql'
import { Contribution } from '@entity/Contribution' import { Contribution } from '@entity/Contribution'
import { Event as DbEvent } from '@entity/Event'
import { Transaction as DbTransaction } from '@entity/Transaction' import { Transaction as DbTransaction } from '@entity/Transaction'
import { User } from '@entity/User' import { User } from '@entity/User'
import { UserInputError } from 'apollo-server-express' import { UserInputError } from 'apollo-server-express'
import { Event as DbEvent } from '@entity/Event' import { Decimal } from 'decimal.js-light'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { GraphQLError } from 'graphql'
import { bobBaumeister } from '@/seeds/users/bob-baumeister'
import { stephenHawking } from '@/seeds/users/stephen-hawking' import { ContributionStatus } from '@enum/ContributionStatus'
import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { Order } from '@enum/Order'
import { ContributionListResult } from '@model/Contribution'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution'
import {
cleanDB,
resetToken,
testEnvironment,
contributionDateFormatter,
resetEntity,
} from '@test/helpers'
import {
sendContributionConfirmedEmail,
sendContributionDeletedEmail,
sendContributionDeniedEmail,
} from '@/emails/sendEmailVariants'
import { creations } from '@/seeds/creation/index'
import { creationFactory } from '@/seeds/factory/creation'
import { userFactory } from '@/seeds/factory/user'
import { import {
createContribution, createContribution,
updateContribution, updateContribution,
@ -35,29 +52,14 @@ import {
listContributions, listContributions,
adminListContributions, adminListContributions,
} from '@/seeds/graphql/queries' } from '@/seeds/graphql/queries'
import { import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
sendContributionConfirmedEmail, import { bobBaumeister } from '@/seeds/users/bob-baumeister'
sendContributionDeletedEmail, import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
sendContributionDeniedEmail,
} from '@/emails/sendEmailVariants'
import {
cleanDB,
resetToken,
testEnvironment,
contributionDateFormatter,
resetEntity,
} from '@test/helpers'
import { userFactory } from '@/seeds/factory/user'
import { creationFactory } from '@/seeds/factory/creation'
import { creations } from '@/seeds/creation/index'
import { peterLustig } from '@/seeds/users/peter-lustig' import { peterLustig } from '@/seeds/users/peter-lustig'
import { EventType } from '@/event/Events' import { EventType } from '@/event/Events'
import { logger, i18n as localization } from '@test/testSetup' import { logger, i18n as localization } from '@test/testSetup'
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz' import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import { stephenHawking } from '@/seeds/users/stephen-hawking'
import { ContributionListResult } from '@model/Contribution'
import { ContributionStatus } from '@enum/ContributionStatus'
import { Order } from '@enum/Order'
jest.mock('@/emails/sendEmailVariants') jest.mock('@/emails/sendEmailVariants')
@ -435,7 +437,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: pendingContribution.data.createContribution.id, id: pendingContribution.data.createContribution.id,
email: 'bibi@bloxberg.de',
amount: 10.0, amount: 10.0,
memo: 'Test env contribution', memo: 'Test env contribution',
creationDate: new Date().toString(), creationDate: new Date().toString(),
@ -1670,7 +1671,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: 1, id: 1,
email: 'bibi@bloxberg.de',
amount: new Decimal(300), amount: new Decimal(300),
memo: 'Danke Bibi!', memo: 'Danke Bibi!',
creationDate: contributionDateFormatter(new Date()), creationDate: contributionDateFormatter(new Date()),
@ -1749,7 +1749,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: 1, id: 1,
email: 'bibi@bloxberg.de',
amount: new Decimal(300), amount: new Decimal(300),
memo: 'Danke Bibi!', memo: 'Danke Bibi!',
creationDate: contributionDateFormatter(new Date()), creationDate: contributionDateFormatter(new Date()),
@ -2043,6 +2042,50 @@ describe('ContributionResolver', () => {
}), }),
) )
}) })
describe('user tries to update admin contribution', () => {
beforeAll(async () => {
await mutate({
mutation: login,
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
})
})
afterAll(async () => {
await mutate({
mutation: login,
variables: { email: 'peter@lustig.de', password: 'Aa12345_' },
})
})
it('logs and throws "Cannot update contribution of moderator" error', async () => {
jest.clearAllMocks()
const adminContribution = await Contribution.findOne({
where: {
moderatorId: admin.id,
userId: bibi.id,
},
})
await expect(
mutate({
mutation: updateContribution,
variables: {
contributionId: (adminContribution && adminContribution.id) || -1,
amount: 100.0,
memo: 'Test Test Test',
creationDate: new Date().toString(),
},
}),
).resolves.toMatchObject({
errors: [new GraphQLError('Cannot update contribution of moderator')],
})
expect(logger.error).toBeCalledWith(
'Cannot update contribution of moderator',
expect.any(Object),
bibi.id,
)
})
})
}) })
describe('second creation surpasses the available amount ', () => { describe('second creation surpasses the available amount ', () => {
@ -2080,58 +2123,6 @@ describe('ContributionResolver', () => {
// stephen@hawking.uk: [1000, 1000, 1000] - deleted // stephen@hawking.uk: [1000, 1000, 1000] - deleted
// garrick@ollivander.com: [1000, 1000, 1000] - not activated // garrick@ollivander.com: [1000, 1000, 1000] - not activated
describe('user for creation to update does not exist', () => {
it('throws an error', async () => {
jest.clearAllMocks()
await expect(
mutate({
mutation: adminUpdateContribution,
variables: {
id: 1,
email: 'bob@baumeister.de',
amount: new Decimal(300),
memo: 'Danke Bibi!',
creationDate: contributionDateFormatter(new Date()),
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('Could not find User')],
}),
)
})
it('logs the error "Could not find User"', () => {
expect(logger.error).toBeCalledWith('Could not find User', 'bob@baumeister.de')
})
})
describe('user for creation to update is deleted', () => {
it('throws an error', async () => {
jest.clearAllMocks()
await expect(
mutate({
mutation: adminUpdateContribution,
variables: {
id: 1,
email: 'stephen@hawking.uk',
amount: new Decimal(300),
memo: 'Danke Bibi!',
creationDate: contributionDateFormatter(new Date()),
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [new GraphQLError('User was deleted')],
}),
)
})
it('logs the error "User was deleted"', () => {
expect(logger.error).toBeCalledWith('User was deleted', 'stephen@hawking.uk')
})
})
describe('creation does not exist', () => { describe('creation does not exist', () => {
it('throws an error', async () => { it('throws an error', async () => {
jest.clearAllMocks() jest.clearAllMocks()
@ -2140,7 +2131,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: -1, id: -1,
email: 'bibi@bloxberg.de',
amount: new Decimal(300), amount: new Decimal(300),
memo: 'Danke Bibi!', memo: 'Danke Bibi!',
creationDate: contributionDateFormatter(new Date()), creationDate: contributionDateFormatter(new Date()),
@ -2158,40 +2148,6 @@ describe('ContributionResolver', () => {
}) })
}) })
describe('user email does not match creation user', () => {
it('throws an error', async () => {
jest.clearAllMocks()
await expect(
mutate({
mutation: adminUpdateContribution,
variables: {
id: creation ? creation.id : -1,
email: 'bibi@bloxberg.de',
amount: new Decimal(300),
memo: 'Danke Bibi!',
creationDate: creation
? contributionDateFormatter(creation.contributionDate)
: contributionDateFormatter(new Date()),
},
}),
).resolves.toEqual(
expect.objectContaining({
errors: [
new GraphQLError(
'User of the pending contribution and send user does not correspond',
),
],
}),
)
})
it('logs the error "User of the pending contribution and send user does not correspond"', () => {
expect(logger.error).toBeCalledWith(
'User of the pending contribution and send user does not correspond',
)
})
})
describe('creation update is not valid', () => { describe('creation update is not valid', () => {
// as this test has not clearly defined that date, it is a false positive // as this test has not clearly defined that date, it is a false positive
it('throws an error', async () => { it('throws an error', async () => {
@ -2201,7 +2157,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: creation ? creation.id : -1, id: creation ? creation.id : -1,
email: 'peter@lustig.de',
amount: new Decimal(1900), amount: new Decimal(1900),
memo: 'Danke Peter!', memo: 'Danke Peter!',
creationDate: creation creationDate: creation
@ -2238,7 +2193,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: creation?.id, id: creation?.id,
email: 'peter@lustig.de',
amount: new Decimal(300), amount: new Decimal(300),
memo: 'Danke Peter!', memo: 'Danke Peter!',
creationDate: creation creationDate: creation
@ -2253,7 +2207,6 @@ describe('ContributionResolver', () => {
date: expect.any(String), date: expect.any(String),
memo: 'Danke Peter!', memo: 'Danke Peter!',
amount: '300', amount: '300',
creation: ['1000', '700', '500'],
}, },
}, },
}), }),
@ -2280,7 +2233,6 @@ describe('ContributionResolver', () => {
mutation: adminUpdateContribution, mutation: adminUpdateContribution,
variables: { variables: {
id: creation?.id, id: creation?.id,
email: 'peter@lustig.de',
amount: new Decimal(200), amount: new Decimal(200),
memo: 'Das war leider zu Viel!', memo: 'Das war leider zu Viel!',
creationDate: creation creationDate: creation
@ -2295,7 +2247,6 @@ describe('ContributionResolver', () => {
date: expect.any(String), date: expect.any(String),
memo: 'Das war leider zu Viel!', memo: 'Das war leider zu Viel!',
amount: '200', amount: '200',
creation: ['1000', '800', '1000'],
}, },
}, },
}), }),

Some files were not shown because too many files have changed in this diff Show More