Merge branch 'master' into 2481-fix-admin-no-confirmation-when-changing-user-role

This commit is contained in:
elweyn 2023-03-31 10:00:58 +02:00
commit 594fb8fe60
166 changed files with 2533 additions and 1912 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -5,7 +5,6 @@ const localVue = global.localVue
const apolloMutateMock = jest.fn().mockResolvedValue({})
const apolloQueryMock = jest.fn().mockResolvedValue({})
const toggleDetailsMock = jest.fn()
const propsData = {
items: [
@ -17,7 +16,7 @@ const propsData = {
amount: 300,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: 1,
moderatorId: 1,
creation: [700, 1000, 1000],
__typename: 'PendingCreation',
},
@ -29,7 +28,7 @@ const propsData = {
amount: 210,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: null,
moderatorId: null,
creation: [790, 1000, 1000],
__typename: 'PendingCreation',
},
@ -41,7 +40,7 @@ const propsData = {
amount: 330,
memo: 'Aktives Grundeinkommen für Januar 2022',
date: '2022-01-01T00:00:00.000Z',
moderator: 1,
moderatorId: 1,
creation: [670, 1000, 1000],
__typename: 'PendingCreation',
},
@ -83,7 +82,7 @@ const mocks = {
$store: {
state: {
moderator: {
id: 0,
id: 1,
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', () => {
beforeEach(() => {
wrapper.vm.updateState(4)
@ -149,40 +140,5 @@ describe('OpenCreationsTable', () => {
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">
<div v-if="!myself(row.item)">
<b-button
v-if="row.item.moderator"
v-if="row.item.moderatorId"
variant="info"
size="md"
:index="0"
@click="rowToggleDetails(row, 0)"
class="mr-2"
>
@ -89,14 +90,13 @@
@row-toggle-details="rowToggleDetails(row, 0)"
>
<template #show-creation>
<div v-if="row.item.moderator">
<div v-if="row.item.moderatorId">
<edit-creation-formular
type="singleCreation"
:creation="row.item.creation"
:item="row.item"
:row="row"
:creationUserData="creationUserData"
@update-creation-data="updateCreationData"
@update-creation-data="$emit('update-contributions')"
/>
</div>
<div v-else>
@ -104,7 +104,6 @@
:contributionId="row.item.id"
:contributionState="row.item.state"
@update-state="updateState"
@update-user-data="updateUserData"
/>
</div>
</template>
@ -146,22 +145,9 @@ export default {
required: true,
},
},
data() {
return {
creationUserData: {
amount: null,
date: null,
memo: null,
moderator: null,
},
}
},
methods: {
myself(item) {
return (
`${item.firstName} ${item.lastName}` ===
`${this.$store.state.moderator.firstName} ${this.$store.state.moderator.lastName}`
)
return item.userId === this.$store.state.moderator.id
},
getStatusIcon(status) {
return iconMap[status] ? iconMap[status] : 'default-icon'
@ -174,16 +160,6 @@ export default {
if (item.state === 'IN_PROGRESS') 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) {
this.$emit('update-state', id)
},

View File

@ -32,6 +32,8 @@ export const adminListContributions = gql`
deniedBy
deletedAt
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'
export const adminUpdateContribution = gql`
mutation ($id: Int!, $email: String!, $amount: Decimal!, $memo: String!, $creationDate: String!) {
adminUpdateContribution(
id: $id
email: $email
amount: $amount
memo: $memo
creationDate: $creationDate
) {
mutation ($id: Int!, $amount: Decimal!, $memo: String!, $creationDate: String!) {
adminUpdateContribution(id: $id, amount: $amount, memo: $memo, creationDate: $creationDate) {
amount
date
memo
creation
}
}
`

View File

@ -1,9 +1,11 @@
import { adminOpenCreations } from '../graphql/adminOpenCreations'
export const creationMonths = {
props: {
creation: {
type: Array,
default: () => [1000, 1000, 1000],
},
data() {
return {
creation: [1000, 1000, 1000],
userId: 0,
}
},
computed: {
creationDates() {
@ -38,4 +40,23 @@ export const creationMonths = {
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"
@show-overlay="showOverlay"
@update-state="updateStatus"
@update-contributions="$apollo.queries.AllContributions.refetch()"
@update-contributions="$apollo.queries.ListAllContributions.refetch()"
/>
<b-pagination
@ -212,7 +212,7 @@ export default {
return this.formatDateOrDash(value)
},
},
{ key: 'moderator', label: this.$t('moderator') },
{ key: 'moderatorId', label: this.$t('moderator') },
{ key: 'editCreation', label: this.$t('chat') },
{ key: 'confirm', label: this.$t('save') },
],

View File

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

View File

@ -1,12 +1,27 @@
// eslint-disable-next-line import/no-commonjs, import/unambiguous
module.exports = {
root: true,
env: {
node: true,
},
parser: '@typescript-eslint/parser',
plugins: ['prettier', '@typescript-eslint', 'type-graphql'],
extends: ['standard', 'eslint:recommended', 'plugin:prettier/recommended'],
// add your custom rules here
plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'jest', 'import'],
extends: [
'standard',
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:import/recommended',
'plugin:import/typescript',
],
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: true,
node: true,
},
},
rules: {
'no-console': ['error'],
'no-debugger': 'error',
@ -16,11 +31,81 @@ module.exports = {
htmlWhitespaceSensitivity: 'ignore',
},
],
// jest
'jest/no-disabled-tests': 'error',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/prefer-to-have-length': 'error',
'jest/valid-expect': 'error',
// import
'import/export': 'error',
'import/no-deprecated': 'error',
'import/no-empty-named-blocks': 'error',
'import/no-extraneous-dependencies': 'error',
'import/no-mutable-exports': 'error',
'import/no-unused-modules': 'error',
'import/no-named-as-default': 'error',
'import/no-named-as-default-member': 'error',
'import/no-amd': 'error',
'import/no-commonjs': 'error',
'import/no-import-module-exports': 'error',
'import/no-nodejs-modules': 'off',
'import/unambiguous': 'error',
'import/default': 'error',
'import/named': 'error',
'import/namespace': 'error',
'import/no-absolute-path': 'error',
'import/no-cycle': 'off',
'import/no-dynamic-require': 'error',
'import/no-internal-modules': 'off',
'import/no-relative-packages': 'error',
'import/no-relative-parent-imports': ['error', { ignore: ['@/*'] }],
'import/no-self-import': 'error',
'import/no-unresolved': 'error',
'import/no-useless-path-segments': 'error',
'import/no-webpack-loader-syntax': 'error',
'import/consistent-type-specifier-style': 'error',
'import/exports-last': 'off',
'import/extensions': 'error',
'import/first': 'error',
'import/group-exports': 'off',
'import/newline-after-import': 'error',
'import/no-anonymous-default-export': 'error',
'import/no-default-export': 'off',
'import/no-duplicates': 'error',
'import/no-named-default': 'error',
'import/no-namespace': 'error',
'import/no-unassigned-import': '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
},
overrides: [
// only for ts files
{
files: ['*.ts'],
files: ['*.ts', '*.tsx'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
@ -32,9 +117,11 @@ module.exports = {
'no-void': ['error', { allowAsStatement: true }],
// ignore prefer-regexp-exec rule to allow string.match(regex)
'@typescript-eslint/prefer-regexp-exec': 'off',
// this should not run on ts files: https://github.com/import-js/eslint-plugin-import/issues/2215#issuecomment-911245486
'import/unambiguous': 'off',
},
parserOptions: {
tsconfigRootDir: __dirname,
tsconfigRootDir: './',
project: ['./tsconfig.json'],
// this is to properly reference the referenced project database without requirement of compiling it
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,

View File

@ -0,0 +1 @@
declare module 'klicktipp-api'

View File

@ -1,4 +1,5 @@
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
// eslint-disable-next-line import/no-commonjs, import/unambiguous
module.exports = {
verbose: true,
preset: 'ts-jest',
@ -6,7 +7,7 @@ module.exports = {
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 81,
lines: 85,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'],

View File

@ -12,7 +12,7 @@
"clean": "tsc --build --clean",
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts",
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
"lint": "eslint --max-warnings=0 .",
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --forceExit --detectOpenHandles",
"seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
@ -29,6 +29,7 @@
"dotenv": "^10.0.0",
"email-templates": "^10.0.1",
"express": "^4.17.1",
"gradido-database": "file:../database",
"graphql": "^15.5.1",
"graphql-request": "5.0.0",
"i18n": "^0.15.1",
@ -61,13 +62,17 @@
"eslint": "^7.29.0",
"eslint-config-prettier": "^8.3.0",
"eslint-config-standard": "^16.0.3",
"eslint-plugin-import": "^2.23.4",
"eslint-import-resolver-typescript": "^3.5.3",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jest": "^27.2.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-type-graphql": "^1.0.0",
"faker": "^5.5.3",
"graphql-tag": "^2.12.6",
"jest": "^27.2.4",
"klicktipp-api": "^1.0.2",
"nodemon": "^2.0.7",
"prettier": "^2.3.1",
"ts-jest": "^27.0.5",

View File

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

View File

@ -1,10 +1,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { KlicktippConnector } from './klicktippConnector'
import CONFIG from '@/config'
// eslint-disable-next-line import/no-relative-parent-imports
import KlicktippConnector from 'klicktipp-api'
const klicktippConnector = new KlicktippConnector()
export const klicktippSignIn = async (

View File

@ -1,624 +0,0 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/restrict-template-expressions */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import axios, { AxiosRequestConfig, Method } from 'axios'
export class KlicktippConnector {
private baseURL: string
private sessionName: string
private sessionId: string
private error: string
constructor(service?: string) {
this.baseURL = service !== undefined ? service : 'https://api.klicktipp.com'
this.sessionName = ''
this.sessionId = ''
}
/**
* Get last error
*
* @return string an error description of the last error
*/
getLastError(): string {
const result = this.error
return result
}
/**
* login
*
* @param username The login name of the user to login.
* @param password The password of the user.
* @return TRUE on success
*/
async login(username: string, password: string): Promise<boolean> {
if (!(username.length > 0 && password.length > 0)) {
throw new Error('Klicktipp Login failed: Illegal Arguments')
}
const res = await this.httpRequest('/account/login', 'POST', { username, password }, false)
if (!res.isAxiosError) {
this.sessionId = res.data.sessid
this.sessionName = res.data.session_name
return true
}
throw new Error(`Klicktipp Login failed: ${res.response.statusText}`)
}
/**
* Logs out the user currently logged in.
*
* @return TRUE on success
*/
async logout(): Promise<boolean> {
const res = await this.httpRequest('/account/logout', 'POST')
if (!res.isAxiosError) {
this.sessionId = ''
this.sessionName = ''
return true
}
throw new Error(`Klicktipp Logout failed: ${res.response.statusText}`)
}
/**
* Get all subscription processes (lists) of the logged in user. Requires to be logged in.
*
* @return A associative obeject <list id> => <list name>
*/
async subscriptionProcessIndex(): Promise<any> {
const res = await this.httpRequest('/list', 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscription process index failed: ${res.response.statusText}`)
}
/**
* Get subscription process (list) definition. Requires to be logged in.
*
* @param listid The id of the subscription process
*
* @return An object representing the Klicktipp subscription process.
*/
async subscriptionProcessGet(listid: string): Promise<any> {
if (!listid || listid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// retrieve
const res = await this.httpRequest(`/subscriber/${listid}`, 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscription process get failed: ${res.response.statusText}`)
}
/**
* Get subscription process (list) redirection url for given subscription.
*
* @param listid The id of the subscription process.
* @param email The email address of the subscriber.
*
* @return A redirection url as defined in the subscription process.
*/
async subscriptionProcessRedirect(listid: string, email: string): Promise<any> {
if (!listid || listid === '' || !email || email === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// update
const data = { listid, email }
const res = await this.httpRequest('/list/redirect', 'POST', data)
if (!res.isAxiosError) {
return res.data
}
throw new Error(
`Klicktipp Subscription process get redirection url failed: ${res.response.statusText}`,
)
}
/**
* Get all manual tags of the logged in user. Requires to be logged in.
*
* @return A associative object <tag id> => <tag name>
*/
async tagIndex(): Promise<any> {
const res = await this.httpRequest('/tag', 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Tag index failed: ${res.response.statusText}`)
}
/**
* Get a tag definition. Requires to be logged in.
*
* @param tagid The tag id.
*
* @return An object representing the Klicktipp tag object.
*/
async tagGet(tagid: string): Promise<any> {
if (!tagid || tagid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
const res = await this.httpRequest(`/tag/${tagid}`, 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Tag get failed: ${res.response.statusText}`)
}
/**
* Create a new manual tag. Requires to be logged in.
*
* @param name The name of the tag.
* @param text (optional) An additional description of the tag.
*
* @return The id of the newly created tag or false if failed.
*/
async tagCreate(name: string, text?: string): Promise<boolean> {
if (!name || name === '') {
throw new Error('Klicktipp Illegal Arguments')
}
const data = {
name,
text: text !== undefined ? text : '',
}
const res = await this.httpRequest('/tag', 'POST', data, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Tag creation failed: ${res.response.statusText}`)
}
/**
* Updates a tag. Requires to be logged in.
*
* @param tagid The tag id used to identify which tag to modify.
* @param name (optional) The new tag name. Set empty to leave it unchanged.
* @param text (optional) The new tag description. Set empty to leave it unchanged.
*
* @return TRUE on success
*/
async tagUpdate(tagid: string, name?: string, text?: string): Promise<boolean> {
if (!tagid || tagid === '' || (name === '' && text === '')) {
throw new Error('Klicktipp Illegal Arguments')
}
const data = {
name: name !== undefined ? name : '',
text: text !== undefined ? text : '',
}
const res = await this.httpRequest(`/tag/${tagid}`, 'PUT', data, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Tag update failed: ${res.response.statusText}`)
}
/**
* Deletes a tag. Requires to be logged in.
*
* @param tagid The user id of the user to delete.
*
* @return TRUE on success
*/
async tagDelete(tagid: string): Promise<boolean> {
if (!tagid || tagid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
const res = await this.httpRequest(`/tag/${tagid}`, 'DELETE')
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Tag deletion failed: ${res.response.statusText}`)
}
/**
* Get all contact fields of the logged in user. Requires to be logged in.
*
* @return A associative object <field id> => <field name>
*/
async fieldIndex(): Promise<any> {
const res = await this.httpRequest('/field', 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Field index failed: ${res.response.statusText}`)
}
/**
* Subscribe an email. Requires to be logged in.
*
* @param email The email address of the subscriber.
* @param listid (optional) The id subscription process.
* @param tagid (optional) The id of the manual tag the subscriber will be tagged with.
* @param fields (optional) Additional fields of the subscriber.
*
* @return An object representing the Klicktipp subscriber object.
*/
async subscribe(
email: string,
listid?: number,
tagid?: number,
fields?: any,
smsnumber?: string,
): Promise<any> {
if ((!email || email === '') && smsnumber === '') {
throw new Error('Illegal Arguments')
}
// subscribe
const data = {
email,
fields: fields !== undefined ? fields : {},
smsnumber: smsnumber !== undefined ? smsnumber : '',
listid: listid !== undefined ? listid : 0,
tagid: tagid !== undefined ? tagid : 0,
}
const res = await this.httpRequest('/subscriber', 'POST', data, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`)
}
/**
* Unsubscribe an email. Requires to be logged in.
*
* @param email The email address of the subscriber.
*
* @return TRUE on success
*/
async unsubscribe(email: string): Promise<boolean> {
if (!email || email === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// unsubscribe
const data = { email }
const res = await this.httpRequest('/subscriber/unsubscribe', 'POST', data, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`)
}
/**
* Tag an email. Requires to be logged in.
*
* @param email The email address of the subscriber.
* @param tagids an array of the manual tag(s) the subscriber will be tagged with.
*
* @return TRUE on success
*/
async tag(email: string, tagids: string): Promise<boolean> {
if (!email || email === '' || !tagids || tagids === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// tag
const data = {
email,
tagids,
}
const res = await this.httpRequest('/subscriber/tag', 'POST', data, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Tagging failed: ${res.response.statusText}`)
}
/**
* Untag an email. Requires to be logged in.
*
* @param mixed $email The email address of the subscriber.
* @param mixed $tagid The id of the manual tag that will be removed from the subscriber.
*
* @return TRUE on success.
*/
async untag(email: string, tagid: string): Promise<boolean> {
if (!email || email === '' || !tagid || tagid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// subscribe
const data = {
email,
tagid,
}
const res = await this.httpRequest('/subscriber/untag', 'POST', data, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`)
}
/**
* Resend an autoresponder for an email address. Requires to be logged in.
*
* @param email A valid email address
* @param autoresponder An id of the autoresponder
*
* @return TRUE on success
*/
async resend(email: string, autoresponder: string): Promise<boolean> {
if (!email || email === '' || !autoresponder || autoresponder === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// resend/reset autoresponder
const data = { email, autoresponder }
const res = await this.httpRequest('/subscriber/resend', 'POST', data, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Resend failed: ${res.response.statusText}`)
}
/**
* Get all active subscribers. Requires to be logged in.
*
* @return An array of subscriber ids.
*/
async subscriberIndex(): Promise<[string]> {
const res = await this.httpRequest('/subscriber', 'GET', undefined, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscriber index failed: ${res.response.statusText}`)
}
/**
* Get subscriber information. Requires to be logged in.
*
* @param subscriberid The subscriber id.
*
* @return An object representing the Klicktipp subscriber.
*/
async subscriberGet(subscriberid: string): Promise<any> {
if (!subscriberid || subscriberid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// retrieve
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'GET', {}, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscriber get failed: ${res.response.statusText}`)
}
/**
* Get a subscriber id by email. Requires to be logged in.
*
* @param email The email address of the subscriber.
*
* @return The id of the subscriber. Use subscriber_get to get subscriber details.
*/
async subscriberSearch(email: string): Promise<any> {
if (!email || email === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// search
const data = { email }
const res = await this.httpRequest('/subscriber/search', 'POST', data, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp Subscriber search failed: ${res.response.statusText}`)
}
/**
* Get all active subscribers tagged with the given tag id. Requires to be logged in.
*
* @param tagid The id of the tag.
*
* @return An array with id -> subscription date of the tagged subscribers. Use subscriber_get to get subscriber details.
*/
async subscriberTagged(tagid: string): Promise<any> {
if (!tagid || tagid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// search
const data = { tagid }
const res = await this.httpRequest('/subscriber/tagged', 'POST', data, true)
if (!res.isAxiosError) {
return res.data
}
throw new Error(`Klicktipp subscriber tagged failed: ${res.response.statusText}`)
}
/**
* Updates a subscriber. Requires to be logged in.
*
* @param subscriberid The id of the subscriber to update.
* @param fields (optional) The fields of the subscriber to update
* @param newemail (optional) The new email of the subscriber to update
*
* @return TRUE on success
*/
async subscriberUpdate(
subscriberid: string,
fields?: any,
newemail?: string,
newsmsnumber?: string,
): Promise<boolean> {
if (!subscriberid || subscriberid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// update
const data = {
fields: fields !== undefined ? fields : {},
newemail: newemail !== undefined ? newemail : '',
newsmsnumber: newsmsnumber !== undefined ? newsmsnumber : '',
}
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'PUT', data, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Subscriber update failed: ${res.response.statusText}`)
}
/**
* Delete a subscribe. Requires to be logged in.
*
* @param subscriberid The id of the subscriber to update.
*
* @return TRUE on success.
*/
async subscriberDelete(subscriberid: string): Promise<boolean> {
if (!subscriberid || subscriberid === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// delete
const res = await this.httpRequest(`/subscriber/${subscriberid}`, 'DELETE', {}, true)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Subscriber deletion failed: ${res.response.statusText}`)
}
/**
* Subscribe an email. Requires an api key.
*
* @param apikey The api key (listbuildng configuration).
* @param email The email address of the subscriber.
* @param fields (optional) Additional fields of the subscriber.
*
* @return A redirection url as defined in the subscription process.
*/
async signin(apikey: string, email: string, fields?: any, smsnumber?: string): Promise<boolean> {
if (!apikey || apikey === '' || ((!email || email === '') && smsnumber === '')) {
throw new Error('Klicktipp Illegal Arguments')
}
// subscribe
const data = {
apikey,
email,
fields: fields !== undefined ? fields : {},
smsnumber: smsnumber !== undefined ? smsnumber : '',
}
const res = await this.httpRequest('/subscriber/signin', 'POST', data)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Subscription failed: ${res.response.statusText}`)
}
/**
* Untag an email. Requires an api key.
*
* @param apikey The api key (listbuildng configuration).
* @param email The email address of the subscriber.
*
* @return TRUE on success
*/
async signout(apikey: string, email: string): Promise<boolean> {
if (!apikey || apikey === '' || !email || email === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// untag
const data = { apikey, email }
const res = await this.httpRequest('/subscriber/signout', 'POST', data)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Untagging failed: ${res.response.statusText}`)
}
/**
* Unsubscribe an email. Requires an api key.
*
* @param apikey The api key (listbuildng configuration).
* @param email The email address of the subscriber.
*
* @return TRUE on success
*/
async signoff(apikey: string, email: string): Promise<boolean> {
if (!apikey || apikey === '' || !email || email === '') {
throw new Error('Klicktipp Illegal Arguments')
}
// unsubscribe
const data = { apikey, email }
const res = await this.httpRequest('/subscriber/signoff', 'POST', data)
if (!res.isAxiosError) {
return true
}
throw new Error(`Klicktipp Unsubscription failed: ${res.response.statusText}`)
}
async httpRequest(path: string, method?: Method, data?: any, usesession?: boolean): Promise<any> {
if (method === undefined) {
method = 'GET'
}
const options: AxiosRequestConfig = {
baseURL: this.baseURL,
method,
url: path,
data,
headers: {
'Content-Type': 'application/json',
Content: 'application/json',
Cookie:
usesession && this.sessionName !== '' ? `${this.sessionName}=${this.sessionId}` : '',
},
}
return axios(options)
.then((res) => res)
.catch((error) => error)
}
}

View File

@ -1,19 +1,21 @@
import jwt from 'jsonwebtoken'
import { verify, sign } from 'jsonwebtoken'
import CONFIG from '@/config/'
import { CustomJwtPayload } from './CustomJwtPayload'
import LogError from '@/server/LogError'
import { CustomJwtPayload } from './CustomJwtPayload'
export const decode = (token: string): CustomJwtPayload | null => {
if (!token) throw new LogError('401 Unauthorized')
try {
return <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
return <CustomJwtPayload>verify(token, CONFIG.JWT_SECRET)
} catch (err) {
return null
}
}
export const encode = (gradidoID: string): string => {
const token = jwt.sign({ gradidoID }, CONFIG.JWT_SECRET, {
const token = sign({ gradidoID }, CONFIG.JWT_SECRET, {
expiresIn: CONFIG.JWT_EXPIRES_IN,
})
return token

View File

@ -1,7 +1,8 @@
// ATTENTION: DO NOT PUT ANY SECRETS IN HERE (or the .env)
import { Decimal } from 'decimal.js-light'
import dotenv from 'dotenv'
import Decimal from 'decimal.js-light'
dotenv.config()
Decimal.set({
@ -10,7 +11,7 @@ Decimal.set({
})
const constants = {
DB_VERSION: '0063-event_link_fields',
DB_VERSION: '0064-event_rename',
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
LOG4JS_CONFIG: 'log4js-config.json',
// default log level on production should be info

View File

@ -1,8 +1,11 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/unbound-method */
import { createTransport } from 'nodemailer'
import { logger, i18n } from '@test/testSetup'
import CONFIG from '@/config'
import { sendEmailTranslated } from './sendEmailTranslated'
CONFIG.EMAIL = false
@ -102,10 +105,12 @@ describe('sendEmailTranslated', () => {
})
})
// eslint-disable-next-line jest/no-disabled-tests
it.skip('calls "i18n.setLocale" with "en"', () => {
expect(i18n.setLocale).toBeCalledWith('en')
})
// eslint-disable-next-line jest/no-disabled-tests
it.skip('calls "i18n.__" for translation', () => {
expect(i18n.__).toBeCalled()
})

View File

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

View File

@ -3,10 +3,14 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* 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 {
sendAddedContributionMessageEmail,
sendAccountActivationEmail,
@ -18,7 +22,6 @@ import {
sendTransactionLinkRedeemedEmail,
sendTransactionReceivedEmail,
} from './sendEmailVariants'
import { sendEmailTranslated } from './sendEmailTranslated'
let con: any
let testEnv: any

View File

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

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_CONFIRM = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_CREATE = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_DELETE = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_DENY = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_LINK_CREATE = async (

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_LINK_DELETE = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE = async (

View File

@ -0,0 +1,24 @@
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE = async (
user: DbUser,
moderator: DbUser,
contribution: DbContribution,
contributionMessage: DbContributionMessage,
): Promise<DbEvent> =>
Event(
EventType.ADMIN_CONTRIBUTION_MESSAGE_CREATE,
user,
moderator,
null,
null,
contribution,
contributionMessage,
null,
null,
).save()

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async (

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_USER_DELETE = async (user: DbUser, moderator: DbUser): Promise<DbEvent> =>
Event(EventType.ADMIN_USER_DELETE, user, moderator).save()

View File

@ -1,8 +1,9 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (
export const EVENT_ADMIN_USER_ROLE_SET = async (
user: DbUser,
moderator: DbUser,
): Promise<DbEvent> => Event(EventType.ADMIN_SEND_CONFIRMATION_EMAIL, user, moderator).save()
): Promise<DbEvent> => Event(EventType.ADMIN_USER_ROLE_SET, user, moderator).save()

View File

@ -0,0 +1,9 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ADMIN_USER_UNDELETE = async (
user: DbUser,
moderator: DbUser,
): Promise<DbEvent> => Event(EventType.ADMIN_USER_UNDELETE, user, moderator).save()

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_CONTRIBUTION_CREATE = async (

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_CONTRIBUTION_DELETE = async (

View File

@ -0,0 +1,28 @@
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
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, EventType } from './Event'
export const EVENT_CONTRIBUTION_LINK_REDEEM = async (
user: DbUser,
transaction: DbTransaction,
contribution: DbContribution,
contributionLink: DbContributionLink,
amount: Decimal,
): Promise<DbEvent> =>
Event(
EventType.CONTRIBUTION_LINK_REDEEM,
user,
user,
null,
transaction,
contribution,
null,
null,
contributionLink,
amount,
).save()

View File

@ -0,0 +1,23 @@
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_CONTRIBUTION_MESSAGE_CREATE = async (
user: DbUser,
contribution: DbContribution,
contributionMessage: DbContributionMessage,
): Promise<DbEvent> =>
Event(
EventType.CONTRIBUTION_MESSAGE_CREATE,
user,
user,
null,
null,
contribution,
contributionMessage,
null,
null,
).save()

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 { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_CONTRIBUTION_UPDATE = async (

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.EMAIL_ACCOUNT_MULTIREGISTRATION, user, { id: 0 } as DbUser).save()

View File

@ -0,0 +1,9 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_EMAIL_ADMIN_CONFIRMATION = async (
user: DbUser,
moderator: DbUser,
): Promise<DbEvent> => Event(EventType.EMAIL_ADMIN_CONFIRMATION, user, moderator).save()

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_EMAIL_CONFIRMATION = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.EMAIL_CONFIRMATION, user, user).save()

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_EMAIL_FORGOT_PASSWORD = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.EMAIL_FORGOT_PASSWORD, user, { id: 0 } as DbUser).save()

View File

@ -1,6 +0,0 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { Event, EventType } from './Event'
export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, user, { id: 0 } as DbUser).save()

View File

@ -1,6 +0,0 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { Event, EventType } from './Event'
export const EVENT_SEND_CONFIRMATION_EMAIL = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.SEND_CONFIRMATION_EMAIL, user, user).save()

View File

@ -0,0 +1,24 @@
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, EventType } from './Event'
export const EVENT_TRANSACTION_LINK_CREATE = async (
user: DbUser,
transactionLink: DbTransactionLink,
amount: Decimal,
): Promise<DbEvent> =>
Event(
EventType.TRANSACTION_LINK_CREATE,
user,
user,
null,
null,
null,
null,
transactionLink,
null,
amount,
).save()

View File

@ -0,0 +1,20 @@
import { Event as DbEvent } from '@entity/Event'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_TRANSACTION_LINK_DELETE = async (
user: DbUser,
transactionLink: DbTransactionLink,
): Promise<DbEvent> =>
Event(
EventType.TRANSACTION_LINK_DELETE,
user,
user,
null,
null,
null,
null,
transactionLink,
).save()

View File

@ -0,0 +1,25 @@
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, EventType } from './Event'
export const EVENT_TRANSACTION_LINK_REDEEM = async (
user: DbUser,
senderUser: DbUser,
transactionLink: DbTransactionLink,
amount: Decimal,
): Promise<DbEvent> =>
Event(
EventType.TRANSACTION_LINK_REDEEM,
user,
user,
senderUser,
null,
null,
null,
transactionLink,
null,
amount,
).save()

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 { Transaction as DbTransaction } from '@entity/Transaction'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_TRANSACTION_RECEIVE = async (

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 { Transaction as DbTransaction } from '@entity/Transaction'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { Event, EventType } from './Event'
export const EVENT_TRANSACTION_SEND = async (

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_USER_ACTIVATE_ACCOUNT = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.USER_ACTIVATE_ACCOUNT, user, user).save()

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_ACTIVATE_ACCOUNT = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.ACTIVATE_ACCOUNT, user, user).save()
export const EVENT_USER_INFO_UPDATE = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.USER_INFO_UPDATE, user, user).save()

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_REGISTER = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.REGISTER, user, user).save()
export const EVENT_USER_LOGIN = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.USER_LOGIN, user, user).save()

View File

@ -1,6 +1,7 @@
import { User as DbUser } from '@entity/User'
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_LOGIN = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.LOGIN, user, user).save()
export const EVENT_USER_LOGOUT = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.USER_LOGOUT, user, user).save()

View File

@ -0,0 +1,7 @@
import { Event as DbEvent } from '@entity/Event'
import { User as DbUser } from '@entity/User'
import { Event, EventType } from './Event'
export const EVENT_USER_REGISTER = async (user: DbUser): Promise<DbEvent> =>
Event(EventType.USER_REGISTER, user, user).save()

View File

@ -1,12 +1,13 @@
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 { User as DbUser } from '@entity/User'
import { Transaction as DbTransaction } from '@entity/Transaction'
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
import { Contribution as DbContribution } from '@entity/Contribution'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import Decimal from 'decimal.js-light'
import { EventType } from './Event'
import { User as DbUser } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { EventType } from './EventType'
export const Event = (
type: EventType,
@ -34,9 +35,8 @@ export const Event = (
return event
}
export { EventType } from './EventType'
export { EventType }
export { EVENT_ACTIVATE_ACCOUNT } from './EVENT_ACTIVATE_ACCOUNT'
export { EVENT_ADMIN_CONTRIBUTION_CONFIRM } from './EVENT_ADMIN_CONTRIBUTION_CONFIRM'
export { EVENT_ADMIN_CONTRIBUTION_CREATE } from './EVENT_ADMIN_CONTRIBUTION_CREATE'
export { EVENT_ADMIN_CONTRIBUTION_DELETE } from './EVENT_ADMIN_CONTRIBUTION_DELETE'
@ -45,13 +45,26 @@ export { EVENT_ADMIN_CONTRIBUTION_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_UPDA
export { EVENT_ADMIN_CONTRIBUTION_LINK_CREATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_CREATE'
export { EVENT_ADMIN_CONTRIBUTION_LINK_DELETE } from './EVENT_ADMIN_CONTRIBUTION_LINK_DELETE'
export { EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_LINK_UPDATE'
export { EVENT_ADMIN_SEND_CONFIRMATION_EMAIL } from './EVENT_ADMIN_SEND_CONFIRMATION_EMAIL'
export { EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE'
export { EVENT_ADMIN_USER_DELETE } from './EVENT_ADMIN_USER_DELETE'
export { EVENT_ADMIN_USER_UNDELETE } from './EVENT_ADMIN_USER_UNDELETE'
export { EVENT_ADMIN_USER_ROLE_SET } from './EVENT_ADMIN_USER_ROLE_SET'
export { EVENT_CONTRIBUTION_CREATE } from './EVENT_CONTRIBUTION_CREATE'
export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE'
export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE'
export { EVENT_LOGIN } from './EVENT_LOGIN'
export { EVENT_REGISTER } from './EVENT_REGISTER'
export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL'
export { EVENT_SEND_CONFIRMATION_EMAIL } from './EVENT_SEND_CONFIRMATION_EMAIL'
export { EVENT_CONTRIBUTION_MESSAGE_CREATE } from './EVENT_CONTRIBUTION_MESSAGE_CREATE'
export { EVENT_CONTRIBUTION_LINK_REDEEM } from './EVENT_CONTRIBUTION_LINK_REDEEM'
export { EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION } from './EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION'
export { EVENT_EMAIL_ADMIN_CONFIRMATION } from './EVENT_EMAIL_ADMIN_CONFIRMATION'
export { EVENT_EMAIL_CONFIRMATION } from './EVENT_EMAIL_CONFIRMATION'
export { EVENT_EMAIL_FORGOT_PASSWORD } from './EVENT_EMAIL_FORGOT_PASSWORD'
export { EVENT_TRANSACTION_SEND } from './EVENT_TRANSACTION_SEND'
export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE'
export { EVENT_TRANSACTION_LINK_CREATE } from './EVENT_TRANSACTION_LINK_CREATE'
export { EVENT_TRANSACTION_LINK_DELETE } from './EVENT_TRANSACTION_LINK_DELETE'
export { EVENT_TRANSACTION_LINK_REDEEM } from './EVENT_TRANSACTION_LINK_REDEEM'
export { EVENT_USER_ACTIVATE_ACCOUNT } from './EVENT_USER_ACTIVATE_ACCOUNT'
export { EVENT_USER_INFO_UPDATE } from './EVENT_USER_INFO_UPDATE'
export { EVENT_USER_LOGIN } from './EVENT_USER_LOGIN'
export { EVENT_USER_LOGOUT } from './EVENT_USER_LOGOUT'
export { EVENT_USER_REGISTER } from './EVENT_USER_REGISTER'

View File

@ -1,5 +1,4 @@
export enum EventType {
ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT',
// TODO CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM',
ADMIN_CONTRIBUTION_CONFIRM = 'ADMIN_CONTRIBUTION_CONFIRM',
ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE',
@ -9,23 +8,35 @@ export enum EventType {
ADMIN_CONTRIBUTION_LINK_CREATE = 'ADMIN_CONTRIBUTION_LINK_CREATE',
ADMIN_CONTRIBUTION_LINK_DELETE = 'ADMIN_CONTRIBUTION_LINK_DELETE',
ADMIN_CONTRIBUTION_LINK_UPDATE = 'ADMIN_CONTRIBUTION_LINK_UPDATE',
ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL',
ADMIN_CONTRIBUTION_MESSAGE_CREATE = 'ADMIN_CONTRIBUTION_MESSAGE_CREATE',
ADMIN_USER_DELETE = 'ADMIN_USER_DELETE',
ADMIN_USER_UNDELETE = 'ADMIN_USER_UNDELETE',
ADMIN_USER_ROLE_SET = 'ADMIN_USER_ROLE_SET',
CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE',
CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE',
CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE',
LOGIN = 'LOGIN',
REGISTER = 'REGISTER',
REDEEM_REGISTER = 'REDEEM_REGISTER',
SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL',
SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL',
CONTRIBUTION_MESSAGE_CREATE = 'CONTRIBUTION_MESSAGE_CREATE',
CONTRIBUTION_LINK_REDEEM = 'CONTRIBUTION_LINK_REDEEM',
EMAIL_ACCOUNT_MULTIREGISTRATION = 'EMAIL_ACCOUNT_MULTIREGISTRATION',
EMAIL_ADMIN_CONFIRMATION = 'EMAIL_ADMIN_CONFIRMATION',
EMAIL_CONFIRMATION = 'EMAIL_CONFIRMATION',
EMAIL_FORGOT_PASSWORD = 'EMAIL_FORGOT_PASSWORD',
TRANSACTION_SEND = 'TRANSACTION_SEND',
TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE',
TRANSACTION_LINK_CREATE = 'TRANSACTION_LINK_CREATE',
TRANSACTION_LINK_DELETE = 'TRANSACTION_LINK_DELETE',
TRANSACTION_LINK_REDEEM = 'TRANSACTION_LINK_REDEEM',
USER_ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT',
USER_INFO_UPDATE = 'USER_INFO_UPDATE',
USER_LOGIN = 'USER_LOGIN',
USER_LOGOUT = 'USER_LOGOUT',
USER_REGISTER = 'USER_REGISTER',
USER_REGISTER_REDEEM = 'USER_REGISTER_REDEEM',
// VISIT_GRADIDO = 'VISIT_GRADIDO',
// VERIFY_REDEEM = 'VERIFY_REDEEM',
// INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT',
// CONFIRM_EMAIL = 'CONFIRM_EMAIL',
// REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP',
// LOGOUT = 'LOGOUT',
// REDEEM_LOGIN = 'REDEEM_LOGIN',
// SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL',
// PASSWORD_CHANGE = 'PASSWORD_CHANGE',

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +0,0 @@
import { ArgsType, Field } from 'type-graphql'
@ArgsType()
export default class SubscribeNewsletterArgs {
@Field(() => String)
email: string
@Field(() => String)
language: string
}

View File

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

View File

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

View File

@ -2,13 +2,13 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { User } from '@entity/User'
import { AuthChecker } from 'type-graphql'
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 { User } from '@entity/User'
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'
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,9 +3,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { GdtEntry } from './GdtEntry'
import { ObjectType, Field, Int, Float } from 'type-graphql'
import { GdtEntry } from './GdtEntry'
@ObjectType()
export class GdtEntryList {
constructor(json: any) {

View File

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

View File

@ -1,8 +1,10 @@
import { ObjectType, Field, Int } from 'type-graphql'
import { Decay } from './Decay'
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 { User } from './User'
@ObjectType()

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 { User } from './User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
import CONFIG from '@/config'
import { User } from './User'
@ObjectType()
export class TransactionLink {
constructor(transactionLink: dbTransactionLink, user: User, redeemedBy: User | null = null) {

View File

@ -1,6 +1,7 @@
import { ObjectType, Field } from 'type-graphql'
import { Transaction } from './Transaction'
import { Balance } from './Balance'
import { Transaction } from './Transaction'
@ObjectType()
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 { User } from '@entity/User'
import { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType()
export class UnconfirmedContribution {

View File

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

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 { Decimal } from 'decimal.js-light'
import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType()
export class UserAdmin {

View File

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

View File

@ -1,21 +1,19 @@
/* 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 { Transaction as dbTransaction } from '@entity/Transaction'
import { TransactionLink as dbTransactionLink } from '@entity/TransactionLink'
import { TransactionLinkRepository } from '@repository/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 { backendLogger as logger } from '@/server/logger'
import { Context, getUser } from '@/server/context'
import { calculateDecay } from '@/util/decay'
import { RIGHTS } from '@/auth/RIGHTS'
import { GdtResolver } from './GdtResolver'
import { Context, getUser } from '@/server/context'
import { backendLogger as logger } from '@/server/logger'
import { calculateDecay } from '@/util/decay'
import { GdtResolver } from './GdtResolver'
import { getLastTransaction } from './util/getLastTransaction'
@Resolver()

View File

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

View File

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

View File

@ -4,9 +4,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
import Decimal from 'decimal.js-light'
import { logger } from '@test/testSetup'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
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 { EventType } from '@/event/Event'
import { userFactory } from '@/seeds/factory/user'
import {
login,
createContributionLink,
@ -14,13 +21,8 @@ import {
updateContributionLink,
} from '@/seeds/graphql/mutations'
import { listContributionLinks } from '@/seeds/graphql/queries'
import { cleanDB, testEnvironment, resetToken } from '@test/helpers'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig'
import { userFactory } from '@/seeds/factory/user'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { EventType } from '@/event/Event'
import { Event as DbEvent } from '@entity/Event'
let mutate: any, query: any, con: any
let testEnv: any
@ -280,7 +282,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "A Start-Date must be set"', () => {
expect(logger.error).toBeCalledWith('A Start-Date must be set')
})
@ -301,7 +303,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "An End-Date must be set"', () => {
expect(logger.error).toBeCalledWith('An End-Date must be set')
})
@ -325,7 +327,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The value of validFrom must before or equals the validTo"', () => {
expect(logger.error).toBeCalledWith(
`The value of validFrom must before or equals the validTo`,
)
@ -348,7 +350,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The value of name is too short"', () => {
expect(logger.error).toBeCalledWith('The value of name is too short', 3)
})
@ -369,7 +371,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The value of name is too long"', () => {
expect(logger.error).toBeCalledWith('The value of name is too long', 101)
})
@ -390,7 +392,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The value of memo is too short"', () => {
expect(logger.error).toBeCalledWith('The value of memo is too short', 3)
})
@ -411,7 +413,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The value of memo is too long"', () => {
expect(logger.error).toBeCalledWith('The value of memo is too long', 256)
})
@ -432,7 +434,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "The amount must be a positiv value"', () => {
expect(logger.error).toBeCalledWith('The amount must be a positiv value', new Decimal(0))
})
})
@ -490,7 +492,7 @@ describe('Contribution Links', () => {
})
})
it('logs the error thrown', () => {
it('logs the error "Contribution Link not found"', () => {
expect(logger.error).toBeCalledWith('Contribution Link not found', -1)
})
@ -572,7 +574,7 @@ describe('Contribution Links', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "Contribution Link not found"', () => {
expect(logger.error).toBeCalledWith('Contribution Link not found', -1)
})
})

View File

@ -1,6 +1,23 @@
import Decimal from 'decimal.js-light'
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'
import { Decimal } from 'decimal.js-light'
import { Resolver, Args, Arg, Authorized, Mutation, Query, Int, Ctx } from 'type-graphql'
// 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/Event'
import { Context, getUser } from '@/server/context'
import LogError from '@/server/LogError'
import {
CONTRIBUTIONLINK_NAME_MAX_CHARS,
@ -8,24 +25,8 @@ import {
MEMO_MAX_CHARS,
MEMO_MIN_CHARS,
} from './const/const'
import { isStartEndDateValid } from './util/creations'
import { ContributionLinkList } from '@model/ContributionLinkList'
import { ContributionLink } from '@model/ContributionLink'
import ContributionLinkArgs from '@arg/ContributionLinkArgs'
import { RIGHTS } from '@/auth/RIGHTS'
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
import { Order } from '@enum/Order'
import Paginated from '@arg/Paginated'
// TODO: this is a strange construct
import { transactionLinkCode as contributionLinkCode } from './TransactionLinkResolver'
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/Event'
import { isStartEndDateValid } from './util/creations'
@Resolver()
export class ContributionLinkResolver {

View File

@ -6,9 +6,15 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { Event as DbEvent } from '@entity/Event'
import { GraphQLError } from 'graphql'
import { cleanDB, resetToken, testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup'
import { GraphQLError } from 'graphql'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
import { EventType } from '@/event/Event'
import { userFactory } from '@/seeds/factory/user'
import {
adminCreateContributionMessage,
createContribution,
@ -16,10 +22,8 @@ import {
login,
} from '@/seeds/graphql/mutations'
import { listContributionMessages } from '@/seeds/graphql/queries'
import { userFactory } from '@/seeds/factory/user'
import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg'
import { peterLustig } from '@/seeds/users/peter-lustig'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
jest.mock('@/emails/sendEmailVariants', () => {
const originalModule = jest.requireActual('@/emails/sendEmailVariants')
@ -113,7 +117,7 @@ describe('ContributionMessageResolver', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "ContributionMessage was not sent successfully: Error: Contribution not found"', () => {
expect(logger.error).toBeCalledWith(
'ContributionMessage was not sent successfully: Error: Contribution not found',
new Error('Contribution not found'),
@ -153,7 +157,7 @@ describe('ContributionMessageResolver', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution"', () => {
expect(logger.error).toBeCalledWith(
'ContributionMessage was not sent successfully: Error: Admin can not answer on his own contribution',
new Error('Admin can not answer on his own contribution'),
@ -197,6 +201,18 @@ describe('ContributionMessageResolver', () => {
contributionMemo: 'Test env contribution',
})
})
it('stores the ADMIN_CONTRIBUTION_MESSAGE_CREATE event in the database', async () => {
await expect(DbEvent.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventType.ADMIN_CONTRIBUTION_MESSAGE_CREATE,
affectedUserId: expect.any(Number),
actingUserId: expect.any(Number),
involvedContributionId: result.data.createContribution.id,
involvedContributionMessageId: expect.any(Number),
}),
)
})
})
})
})
@ -251,7 +267,7 @@ describe('ContributionMessageResolver', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "ContributionMessage was not sent successfully: Error: Contribution not found"', () => {
expect(logger.error).toBeCalledWith(
'ContributionMessage was not sent successfully: Error: Contribution not found',
new Error('Contribution not found'),
@ -283,7 +299,7 @@ describe('ContributionMessageResolver', () => {
)
})
it('logs the error thrown', () => {
it('logs the error "ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user"', () => {
expect(logger.error).toBeCalledWith(
'ContributionMessage was not sent successfully: Error: Can not send message to contribution of another user',
new Error('Can not send message to contribution of another user'),
@ -322,6 +338,18 @@ describe('ContributionMessageResolver', () => {
}),
)
})
it('stores the CONTRIBUTION_MESSAGE_CREATE event in the database', async () => {
await expect(DbEvent.find()).resolves.toContainEqual(
expect.objectContaining({
type: EventType.CONTRIBUTION_MESSAGE_CREATE,
affectedUserId: expect.any(Number),
actingUserId: expect.any(Number),
involvedContributionId: result.data.createContribution.id,
involvedContributionMessageId: expect.any(Number),
}),
)
})
})
})
})

View File

@ -1,21 +1,25 @@
/* 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 { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
import { Contribution as DbContribution } from '@entity/Contribution'
import { UserContact } from '@entity/UserContact'
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
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 { ContributionMessageType } from '@enum/MessageType'
import { ContributionStatus } from '@enum/ContributionStatus'
import { Order } from '@enum/Order'
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 { Context, getUser } from '@/server/context'
import { sendAddedContributionMessageEmail } from '@/emails/sendEmailVariants'
import {
EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE,
EVENT_CONTRIBUTION_MESSAGE_CREATE,
} from '@/event/Event'
import { Context, getUser } from '@/server/context'
import LogError from '@/server/LogError'
@Resolver()
@ -57,6 +61,11 @@ export class ContributionMessageResolver {
await queryRunner.manager.update(DbContribution, { id: contributionId }, contribution)
}
await queryRunner.commitTransaction()
await EVENT_CONTRIBUTION_MESSAGE_CREATE(
user,
{ id: contributionMessage.contributionId } as DbContribution,
contributionMessage,
)
} catch (e) {
await queryRunner.rollbackTransaction()
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
@ -98,7 +107,7 @@ export class ContributionMessageResolver {
@Args() { contributionId, message }: ContributionMessageArgs,
@Ctx() context: Context,
): Promise<ContributionMessage> {
const user = getUser(context)
const moderator = getUser(context)
const queryRunner = getConnection().createQueryRunner()
await queryRunner.connect()
@ -112,18 +121,18 @@ export class ContributionMessageResolver {
if (!contribution) {
throw new LogError('Contribution not found', contributionId)
}
if (contribution.userId === user.id) {
if (contribution.userId === moderator.id) {
throw new LogError('Admin can not answer on his own contribution', contributionId)
}
if (!contribution.user.emailContact) {
contribution.user.emailContact = await UserContact.findOneOrFail({
contribution.user.emailContact = await DbUserContact.findOneOrFail({
where: { id: contribution.user.emailId },
})
}
contributionMessage.contributionId = contributionId
contributionMessage.createdAt = new Date()
contributionMessage.message = message
contributionMessage.userId = user.id
contributionMessage.userId = moderator.id
contributionMessage.type = ContributionMessageType.DIALOG
contributionMessage.isModerator = true
await queryRunner.manager.insert(DbContributionMessage, contributionMessage)
@ -142,17 +151,23 @@ export class ContributionMessageResolver {
lastName: contribution.user.lastName,
email: contribution.user.emailContact.email,
language: contribution.user.language,
senderFirstName: user.firstName,
senderLastName: user.lastName,
senderFirstName: moderator.firstName,
senderLastName: moderator.lastName,
contributionMemo: contribution.memo,
})
await queryRunner.commitTransaction()
await EVENT_ADMIN_CONTRIBUTION_MESSAGE_CREATE(
{ id: contribution.userId } as DbUser,
moderator,
contribution,
contributionMessage,
)
} catch (e) {
await queryRunner.rollbackTransaction()
throw new LogError(`ContributionMessage was not sent successfully: ${e}`, e)
} finally {
await queryRunner.release()
}
return new ContributionMessage(contributionMessage, user)
return new ContributionMessage(contributionMessage, moderator)
}
}

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