Merge branch 'master' into separate-admin-interface-workflow

This commit is contained in:
mahula 2023-03-14 14:05:45 +01:00 committed by GitHub
commit 278529e692
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
244 changed files with 2947 additions and 1445 deletions

View File

@ -202,7 +202,7 @@ jobs:
# LINT BACKEND ########################################################### # LINT BACKEND ###########################################################
########################################################################## ##########################################################################
- name: backend | Lint - name: backend | Lint
run: cd backend && yarn && yarn run lint run: cd database && yarn && cd ../backend && yarn && yarn run lint
############################################################################## ##############################################################################
# JOB: LOCALES BACKEND ####################################################### # JOB: LOCALES BACKEND #######################################################
@ -256,20 +256,7 @@ jobs:
# UNIT TESTS FRONTEND #################################################### # UNIT TESTS FRONTEND ####################################################
########################################################################## ##########################################################################
- name: Frontend | Unit tests - name: Frontend | Unit tests
run: | run: cd frontend && yarn && yarn run test
cd frontend && yarn && yarn run test
cp -r ./coverage ../
##########################################################################
# COVERAGE CHECK FRONTEND ################################################
##########################################################################
- name: frontend | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage Frontend
type: lcov
result_path: ./frontend/coverage/lcov.info
min_coverage: 95
token: ${{ github.token }}
############################################################################## ##############################################################################
# JOB: UNIT TEST BACKEND #################################################### # JOB: UNIT TEST BACKEND ####################################################
@ -305,20 +292,7 @@ jobs:
- name: backend | docker-compose database - name: backend | docker-compose database
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
- name: backend Unit tests | test - name: backend Unit tests | test
run: | run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test
cd database && yarn && yarn build && cd ../backend && yarn && yarn test
cp -r ./coverage ../
##########################################################################
# COVERAGE CHECK BACKEND #################################################
##########################################################################
- name: backend | Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage Backend
type: lcov
result_path: ./backend/coverage/lcov.info
min_coverage: 80
token: ${{ github.token }}
########################################################################## ##########################################################################
# DATABASE MIGRATION TEST UP + RESET ##################################### # DATABASE MIGRATION TEST UP + RESET #####################################
@ -433,7 +407,7 @@ jobs:
run: | run: |
cd e2e-tests/ cd e2e-tests/
yarn yarn
yarn run cypress run --spec cypress/e2e/User.Authentication.feature,cypress/e2e/User.Authentication.ResetPassword.feature yarn run cypress run
- name: End-to-end tests | if tests failed, upload screenshots - name: End-to-end tests | if tests failed, upload screenshots
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }} if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3

View File

@ -1,4 +1,4 @@
name: gradido test_dht-node CI name: Gradido DHT Node Test CI
on: push on: push
@ -7,7 +7,7 @@ jobs:
# JOB: DOCKER BUILD TEST ##################################################### # JOB: DOCKER BUILD TEST #####################################################
############################################################################## ##############################################################################
build: build:
name: Docker Build Test name: Docker Build Test - DHT Node
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@ -28,7 +28,7 @@ jobs:
# JOB: LINT ################################################################## # JOB: LINT ##################################################################
############################################################################## ##############################################################################
lint: lint:
name: Lint name: Lint - DHT Node
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
steps: steps:
@ -50,7 +50,7 @@ jobs:
# JOB: UNIT TEST ############################################################# # JOB: UNIT TEST #############################################################
############################################################################## ##############################################################################
unit_test: unit_test:
name: Unit tests name: Unit Tests - DHT Node
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
steps: steps:
@ -83,16 +83,4 @@ jobs:
#- name: Unit tests #- name: Unit tests
# run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test # run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test
- name: Unit tests - name: Unit tests
run: | run: docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/dht-node:test yarn run test
docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net -v ~/coverage:/app/coverage --rm gradido/dht-node:test yarn run test
cp -r ~/coverage ./coverage
- name: Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage dht-node
type: lcov
#result_path: ./dht-node/coverage/lcov.info
result_path: ./coverage/lcov.info
min_coverage: 79
token: ${{ github.token }}

View File

@ -1,4 +1,4 @@
name: gradido test_federation CI name: Gradido Federation Test CI
on: push on: push
@ -7,7 +7,7 @@ jobs:
# JOB: DOCKER BUILD TEST ##################################################### # JOB: DOCKER BUILD TEST #####################################################
############################################################################## ##############################################################################
build: build:
name: Docker Build Test name: Docker Build Test - Federation
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout code - name: Checkout code
@ -28,7 +28,7 @@ jobs:
# JOB: LINT ################################################################## # JOB: LINT ##################################################################
############################################################################## ##############################################################################
lint: lint:
name: Lint name: Lint - Federation
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
steps: steps:
@ -50,7 +50,7 @@ jobs:
# JOB: UNIT TEST ############################################################# # JOB: UNIT TEST #############################################################
############################################################################## ##############################################################################
unit_test: unit_test:
name: Unit tests name: Unit Tests - Federation
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build] needs: [build]
steps: steps:
@ -84,15 +84,4 @@ jobs:
# run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test # run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test
- name: Unit tests - name: Unit tests
run: | run: |
docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net -v ~/coverage:/app/coverage --rm gradido/federation:test yarn run test docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/federation:test yarn run test
cp -r ~/coverage ./coverage
- name: Coverage check
uses: webcraftmedia/coverage-check-action@master
with:
report_name: Coverage federation
type: lcov
#result_path: ./federation/coverage/lcov.info
result_path: ./coverage/lcov.info
min_coverage: 72
token: ${{ github.token }}

View File

@ -4,8 +4,76 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
#### [1.19.1](https://github.com/gradido/gradido/compare/1.19.0...1.19.1)
- fix(frontend): admin question clickable [`#2810`](https://github.com/gradido/gradido/pull/2810)
- refactor(frontend): change b-img to b-icon send [`#2809`](https://github.com/gradido/gradido/pull/2809)
- fix(admin): update openCreation in case of tab open. [`#2806`](https://github.com/gradido/gradido/pull/2806)
- fix(admin): english language for contributions in admin [`#2804`](https://github.com/gradido/gradido/pull/2804)
- refactor(admin): event buttons for myself turned off in open contributions [`#2760`](https://github.com/gradido/gradido/pull/2760)
- fix(admin): contribution page [`#2794`](https://github.com/gradido/gradido/pull/2794)
- fix(frontend): info.svg [`#2798`](https://github.com/gradido/gradido/pull/2798)
- fix(backend): add relation messages to database query [`#2795`](https://github.com/gradido/gradido/pull/2795)
- fix(admin): header and menu [`#2793`](https://github.com/gradido/gradido/pull/2793)
- fix(frontend): send gdd - change first submit button text to 'Check Now' [`#2774`](https://github.com/gradido/gradido/pull/2774)
- refactor(frontend): creations generated by link NL [`#2771`](https://github.com/gradido/gradido/pull/2771)
- refactor(frontend): creations generated by link (FR) + (NL) [`#2770`](https://github.com/gradido/gradido/pull/2770)
- refactor(frontend): update German locales [`#2765`](https://github.com/gradido/gradido/pull/2765)
- refactor(backend): use find contributions helper for list contributions [`#2762`](https://github.com/gradido/gradido/pull/2762)
#### [1.19.0](https://github.com/gradido/gradido/compare/1.18.2...1.19.0)
> 7 March 2023
- chore(release): version 1.19.0 [`#2786`](https://github.com/gradido/gradido/pull/2786)
- fix(frontend): change contribution design [`#2731`](https://github.com/gradido/gradido/pull/2731)
- refactor(frontend): commnity navbar- & unauthenticated b-gradido styles [`#2732`](https://github.com/gradido/gradido/pull/2732)
- fix(database): change downwards migration to delete entries with last_announced_at IS NULL [`#2767`](https://github.com/gradido/gradido/pull/2767)
- feat(admin): deleted contributions visible [`#2759`](https://github.com/gradido/gradido/pull/2759)
- feat(other): e2e test user story user registration [`#2753`](https://github.com/gradido/gradido/pull/2753)
- refactor(frontend): style and design changes to a contribution [`#2648`](https://github.com/gradido/gradido/pull/2648)
- test(backend): add tests that ``sendContributionDeleted`` and ``sendContributionDenied`` are called [`#2740`](https://github.com/gradido/gradido/pull/2740)
- fix(backend): set email tls true in test [`#2763`](https://github.com/gradido/gradido/pull/2763)
- refactor(frontend): add visible event an answer question [`#2750`](https://github.com/gradido/gradido/pull/2750)
- refactor(frontend): style sidebar, add icons [`#2737`](https://github.com/gradido/gradido/pull/2737)
- fix(backend): possible flaky test [`#2761`](https://github.com/gradido/gradido/pull/2761)
- fix(backend): emails adjust namings of menus to new design [`#2756`](https://github.com/gradido/gradido/pull/2756)
- ci(other): rename dht node and federation workflow jobs for better branch protection maintenance [`#2743`](https://github.com/gradido/gradido/pull/2743)
- refactor(backend): combine logic for `listTransactionLinks` & `listTransactionLinksAdmin` [`#2706`](https://github.com/gradido/gradido/pull/2706)
- refactor(backend): remove admin create contributions [`#2724`](https://github.com/gradido/gradido/pull/2724)
- refactor(frontend): remove .vue as imports [`#2725`](https://github.com/gradido/gradido/pull/2725)
- feat(federation): add dht-node to deployment scripts [`#2729`](https://github.com/gradido/gradido/pull/2729)
- fix(frontend): change fetchPolicy, add scripts.update [`#2718`](https://github.com/gradido/gradido/pull/2718)
- refactor(backend): list unconfirmed contribution to admin list all contribution [`#2666`](https://github.com/gradido/gradido/pull/2666)
- refactor(frontend): community routes [`#2721`](https://github.com/gradido/gradido/pull/2721)
- test(backend): authentication tests for TransactionLinkResolver [`#2705`](https://github.com/gradido/gradido/pull/2705)
- refactor(backend): use LogError on errors [`#2679`](https://github.com/gradido/gradido/pull/2679)
- refactor(backend): use LogError on encryptorUtils [`#2678`](https://github.com/gradido/gradido/pull/2678)
- refactor(backend): use LogError on creations [`#2677`](https://github.com/gradido/gradido/pull/2677)
- refactor(other): decrease docker build dependencies in test workflow [`#2719`](https://github.com/gradido/gradido/pull/2719)
- refactor(backend): unit test for the method denyContribution [`#2639`](https://github.com/gradido/gradido/pull/2639)
- refactor(frontend): logo inserted with better quality. [`#2646`](https://github.com/gradido/gradido/pull/2646)
- feat(federation): harmonize and sync modules and data of federation [`#2665`](https://github.com/gradido/gradido/pull/2665)
- feat(federation): add docker and github-workflow files [`#2680`](https://github.com/gradido/gradido/pull/2680)
- feat(other): e2e test user authentication reset password [`#2644`](https://github.com/gradido/gradido/pull/2644)
- refactor(admin): add tabs for all statusus on contributions [`#2623`](https://github.com/gradido/gradido/pull/2623)
- feat(federation): implement a graphql client to request getpublickey [`#2511`](https://github.com/gradido/gradido/pull/2511)
- refactor(frontend): style refactor mobil auth area [`#2643`](https://github.com/gradido/gradido/pull/2643)
- refactor(backend): use LogError on TransactionResolver [`#2676`](https://github.com/gradido/gradido/pull/2676)
- refactor(backend): event protocol rework [`#2691`](https://github.com/gradido/gradido/pull/2691)
- refactor(backend): use LogError on TransactionLinkResolver [`#2673`](https://github.com/gradido/gradido/pull/2673)
- fix(frontend): simple disabled function on submit send [`#2647`](https://github.com/gradido/gradido/pull/2647)
- refactor(frontend): missing message on old transactions [`#2660`](https://github.com/gradido/gradido/pull/2660)
- refactor(admin): remove overview and multi creation menu entry [`#2661`](https://github.com/gradido/gradido/pull/2661)
- feat(other): add locales check to backend and integrate it to test workflow [`#2693`](https://github.com/gradido/gradido/pull/2693)
- refactor(other): add linting rules like in backend modul [`#2695`](https://github.com/gradido/gradido/pull/2695)
- refactor(backend): use LogError on contributionResolver [`#2669`](https://github.com/gradido/gradido/pull/2669)
#### [1.18.2](https://github.com/gradido/gradido/compare/1.18.1...1.18.2) #### [1.18.2](https://github.com/gradido/gradido/compare/1.18.1...1.18.2)
> 10 February 2023
- chore(release): version 1.18.2 [`#2700`](https://github.com/gradido/gradido/pull/2700)
- fix(admin): deny contribution button to left [`#2699`](https://github.com/gradido/gradido/pull/2699) - fix(admin): deny contribution button to left [`#2699`](https://github.com/gradido/gradido/pull/2699)
#### [1.18.1](https://github.com/gradido/gradido/compare/1.18.0...1.18.1) #### [1.18.1](https://github.com/gradido/gradido/compare/1.18.0...1.18.1)

View File

@ -1,11 +1,17 @@
module.exports = { module.exports = {
verbose: true, verbose: true,
collectCoverage: true,
collectCoverageFrom: [ collectCoverageFrom: [
'src/**/*.{js,vue}', 'src/**/*.{js,vue}',
'!**/node_modules/**', '!**/node_modules/**',
'!src/assets/**', '!src/assets/**',
'!**/?(*.)+(spec|test).js?(x)', '!**/?(*.)+(spec|test).js?(x)',
], ],
coverageThreshold: {
global: {
lines: 97,
},
},
moduleFileExtensions: [ moduleFileExtensions: [
'js', 'js',
// 'jsx', // 'jsx',

View File

@ -3,7 +3,7 @@
"description": "Administraion Interface for Gradido", "description": "Administraion Interface for Gradido",
"main": "index.js", "main": "index.js",
"author": "Moriz Wahl", "author": "Moriz Wahl",
"version": "1.18.2", "version": "1.19.1",
"license": "Apache-2.0", "license": "Apache-2.0",
"private": false, "private": false,
"scripts": { "scripts": {
@ -14,7 +14,7 @@
"analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json",
"lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .",
"stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'", "stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'",
"test": "cross-env TZ=UTC jest --coverage", "test": "cross-env TZ=UTC jest",
"locales": "scripts/sort.sh" "locales": "scripts/sort.sh"
}, },
"dependencies": { "dependencies": {

View File

@ -6,7 +6,7 @@
</template> </template>
<script> <script>
import defaultLayout from '@/layouts/defaultLayout.vue' import defaultLayout from '@/layouts/defaultLayout'
export default { export default {
name: 'app', name: 'app',

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ChangeUserRoleFormular from './ChangeUserRoleFormular.vue' import ChangeUserRoleFormular from './ChangeUserRoleFormular'
import { setUserRole } from '../graphql/setUserRole' import { setUserRole } from '../graphql/setUserRole'
import { toastSuccessSpy, toastErrorSpy } from '../../test/testSetup' import { toastSuccessSpy, toastErrorSpy } from '../../test/testSetup'

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ConfirmRegisterMailFormular from './ConfirmRegisterMailFormular.vue' import ConfirmRegisterMailFormular from './ConfirmRegisterMailFormular'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionLink from './ContributionLink.vue' import ContributionLink from './ContributionLink'
const localVue = global.localVue const localVue = global.localVue

View File

@ -43,8 +43,8 @@
</div> </div>
</template> </template>
<script> <script>
import ContributionLinkForm from '../ContributionLink/ContributionLinkForm.vue' import ContributionLinkForm from '../ContributionLink/ContributionLinkForm'
import ContributionLinkList from '../ContributionLink/ContributionLinkList.vue' import ContributionLinkList from '../ContributionLink/ContributionLinkList'
export default { export default {
name: 'ContributionLink', name: 'ContributionLink',

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionLinkForm from './ContributionLinkForm.vue' import ContributionLinkForm from './ContributionLinkForm'
import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup'
import { createContributionLink } from '@/graphql/createContributionLink.js' import { createContributionLink } from '@/graphql/createContributionLink.js'

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionLinkList from './ContributionLinkList.vue' import ContributionLinkList from './ContributionLinkList'
import { toastSuccessSpy, toastErrorSpy } from '../../../test/testSetup' import { toastSuccessSpy, toastErrorSpy } from '../../../test/testSetup'
// import { deleteContributionLink } from '../graphql/deleteContributionLink' // import { deleteContributionLink } from '../graphql/deleteContributionLink'

View File

@ -46,7 +46,7 @@
</template> </template>
<script> <script>
import { deleteContributionLink } from '@/graphql/deleteContributionLink.js' import { deleteContributionLink } from '@/graphql/deleteContributionLink.js'
import FigureQrCode from '../FigureQrCode.vue' import FigureQrCode from '../FigureQrCode'
export default { export default {
name: 'ContributionLinkList', name: 'ContributionLinkList',

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionMessagesFormular from './ContributionMessagesFormular.vue' import ContributionMessagesFormular from './ContributionMessagesFormular'
import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../../test/testSetup'
const localVue = global.localVue const localVue = global.localVue

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionMessagesList from './ContributionMessagesList.vue' import ContributionMessagesList from './ContributionMessagesList'
const localVue = global.localVue const localVue = global.localVue
@ -10,6 +10,7 @@ describe('ContributionMessagesList', () => {
const propsData = { const propsData = {
contributionId: 42, contributionId: 42,
contributionState: 'PENDING',
} }
const mocks = { const mocks = {

View File

@ -1,22 +1,23 @@
<template> <template>
<div class="contribution-messages-list"> <div class="contribution-messages-list">
<b-container> <b-container>
{{ messages.lenght }}
<div v-for="message in messages" v-bind:key="message.id"> <div v-for="message in messages" v-bind:key="message.id">
<contribution-messages-list-item :message="message" /> <contribution-messages-list-item :message="message" />
</div> </div>
</b-container> </b-container>
<contribution-messages-formular <div v-if="contributionState === 'PENDING' || contributionState === 'IN_PROGRESS'">
:contributionId="contributionId" <contribution-messages-formular
@get-list-contribution-messages="getListContributionMessages" :contributionId="contributionId"
@update-state="updateState" @get-list-contribution-messages="getListContributionMessages"
/> @update-state="updateState"
/>
</div>
</div> </div>
</template> </template>
<script> <script>
import ContributionMessagesListItem from './slots/ContributionMessagesListItem.vue' import ContributionMessagesListItem from './slots/ContributionMessagesListItem'
import ContributionMessagesFormular from '../ContributionMessages/ContributionMessagesFormular.vue' import ContributionMessagesFormular from '../ContributionMessages/ContributionMessagesFormular'
import { listContributionMessages } from '../../graphql/listContributionMessages.js' import { listContributionMessages } from '../../graphql/listContributionMessages.js'
export default { export default {
@ -30,6 +31,10 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
contributionState: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionMessagesListItem from './ContributionMessagesListItem.vue' import ContributionMessagesListItem from './ContributionMessagesListItem'
const localVue = global.localVue const localVue = global.localVue

View File

@ -16,7 +16,7 @@
</div> </div>
</template> </template>
<script> <script>
import ParseMessage from '@/components/ContributionMessages/ParseMessage.vue' import ParseMessage from '@/components/ContributionMessages/ParseMessage'
export default { export default {
name: 'ContributionMessagesListItem', name: 'ContributionMessagesListItem',

View File

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

View File

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

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import CreationTransactionList from './CreationTransactionList.vue' import CreationTransactionList from './CreationTransactionList'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'
const localVue = global.localVue const localVue = global.localVue

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import DeletedUserFormular from './DeletedUserFormular.vue' import DeletedUserFormular from './DeletedUserFormular'
import { deleteUser } from '../graphql/deleteUser' import { deleteUser } from '../graphql/deleteUser'
import { unDeleteUser } from '../graphql/unDeleteUser' import { unDeleteUser } from '../graphql/unDeleteUser'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import EditCreationFormular from './EditCreationFormular.vue' import EditCreationFormular from './EditCreationFormular'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
const localVue = global.localVue const localVue = global.localVue

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import FigureQrCode from './FigureQrCode.vue' import FigureQrCode from './FigureQrCode'
const localVue = global.localVue const localVue = global.localVue

View File

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

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="component-nabvar"> <div class="component-nabvar">
<b-navbar toggleable="md" type="dark" variant="success" class="p-3"> <b-navbar toggleable="md" type="dark" variant="success">
<b-navbar-brand to="/"> <b-navbar-brand class="mb-2" to="/">
<img src="img/brand/gradido_logo_w.png" class="navbar-brand-img" alt="..." /> <img src="img/brand/gradido_logo_w.png" class="navbar-brand-img pl-2" alt="..." />
</b-navbar-brand> </b-navbar-brand>
<b-navbar-toggle target="nav-collapse"></b-navbar-toggle> <b-navbar-toggle target="nav-collapse"></b-navbar-toggle>
@ -10,7 +10,7 @@
<b-collapse id="nav-collapse" is-nav> <b-collapse id="nav-collapse" is-nav>
<b-navbar-nav> <b-navbar-nav>
<b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item> <b-nav-item to="/user">{{ $t('navbar.user_search') }}</b-nav-item>
<b-nav-item class="bg-color-creation p-1" to="/creation-confirm"> <b-nav-item class="bg-color-creation" to="/creation-confirm">
{{ $t('creation') }} {{ $t('creation') }}
<b-badge v-show="$store.state.openCreations > 0" variant="danger"> <b-badge v-show="$store.state.openCreations > 0" variant="danger">
{{ $store.state.openCreations }} {{ $store.state.openCreations }}
@ -52,6 +52,5 @@ export default {
<style> <style>
.navbar-brand-img { .navbar-brand-img {
height: 2rem; height: 2rem;
padding-left: 10px;
} }
</style> </style>

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import Overlay from './Overlay.vue' import Overlay from './Overlay'
const localVue = global.localVue const localVue = global.localVue

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import OpenCreationsTable from './OpenCreationsTable.vue' import OpenCreationsTable from './OpenCreationsTable'
const localVue = global.localVue const localVue = global.localVue

View File

@ -13,17 +13,19 @@
<b-icon :icon="getStatusIcon(row.item.state)"></b-icon> <b-icon :icon="getStatusIcon(row.item.state)"></b-icon>
</template> </template>
<template #cell(bookmark)="row"> <template #cell(bookmark)="row">
<b-button <div v-if="!myself(row.item)">
variant="danger" <b-button
size="md" variant="danger"
@click="$emit('show-overlay', row.item, 'delete')" size="md"
class="mr-2" @click="$emit('show-overlay', row.item, 'delete')"
> class="mr-2"
<b-icon icon="trash" variant="light"></b-icon> >
</b-button> <b-icon icon="trash" variant="light"></b-icon>
</b-button>
</div>
</template> </template>
<template #cell(editCreation)="row"> <template #cell(editCreation)="row">
<div v-if="$store.state.moderator.id !== row.item.userId"> <div v-if="!myself(row.item)">
<b-button <b-button
v-if="row.item.moderator" v-if="row.item.moderator"
variant="info" variant="info"
@ -36,30 +38,26 @@
<b-button v-else @click="rowToggleDetails(row, 0)"> <b-button v-else @click="rowToggleDetails(row, 0)">
<b-icon icon="chat-dots"></b-icon> <b-icon icon="chat-dots"></b-icon>
<b-icon <b-icon
v-if="row.item.state === 'PENDING' && row.item.messageCount > 0" v-if="row.item.state === 'PENDING' && row.item.messagesCount > 0"
icon="exclamation-circle-fill" icon="exclamation-circle-fill"
variant="warning" variant="warning"
></b-icon> ></b-icon>
<b-icon <b-icon
v-if="row.item.state === 'IN_PROGRESS' && row.item.messageCount > 0" v-if="row.item.state === 'IN_PROGRESS' && row.item.messagesCount > 0"
icon="question-diamond" icon="question-diamond"
variant="light" variant="warning"
class="pl-1"
></b-icon> ></b-icon>
</b-button> </b-button>
</div> </div>
</template> </template>
<template #cell(reActive)>
<b-button variant="warning" size="md" class="mr-2">
<b-icon icon="arrow-up" variant="light"></b-icon>
</b-button>
</template>
<template #cell(chatCreation)="row"> <template #cell(chatCreation)="row">
<b-button v-if="row.item.messagesCount > 0" @click="rowToggleDetails(row, 0)"> <b-button v-if="row.item.messagesCount > 0" @click="rowToggleDetails(row, 0)">
<b-icon icon="chat-dots"></b-icon> <b-icon icon="chat-dots"></b-icon>
</b-button> </b-button>
</template> </template>
<template #cell(deny)="row"> <template #cell(deny)="row">
<div v-if="$store.state.moderator.id !== row.item.userId"> <div v-if="!myself(row.item)">
<b-button <b-button
variant="warning" variant="warning"
size="md" size="md"
@ -71,7 +69,7 @@
</div> </div>
</template> </template>
<template #cell(confirm)="row"> <template #cell(confirm)="row">
<div v-if="$store.state.moderator.id !== row.item.userId"> <div v-if="!myself(row.item)">
<b-button <b-button
variant="success" variant="success"
size="md" size="md"
@ -104,6 +102,7 @@
<div v-else> <div v-else>
<contribution-messages-list <contribution-messages-list
:contributionId="row.item.id" :contributionId="row.item.id"
:contributionState="row.item.state"
@update-state="updateState" @update-state="updateState"
@update-user-data="updateUserData" @update-user-data="updateUserData"
/> />
@ -117,9 +116,9 @@
<script> <script>
import { toggleRowDetails } from '../../mixins/toggleRowDetails' import { toggleRowDetails } from '../../mixins/toggleRowDetails'
import RowDetails from '../RowDetails.vue' import RowDetails from '../RowDetails'
import EditCreationFormular from '../EditCreationFormular.vue' import EditCreationFormular from '../EditCreationFormular'
import ContributionMessagesList from '../ContributionMessages/ContributionMessagesList.vue' import ContributionMessagesList from '../ContributionMessages/ContributionMessagesList'
const iconMap = { const iconMap = {
IN_PROGRESS: 'question-square', IN_PROGRESS: 'question-square',
@ -158,13 +157,22 @@ export default {
} }
}, },
methods: { methods: {
myself(item) {
return (
`${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'
}, },
rowClass(item, type) { rowClass(item, type) {
if (!item || type !== 'row') return if (!item || type !== 'row') return
if (item.state === 'CONFIRMED') return 'table-success' if (item.state === 'CONFIRMED') return 'table-success'
if (item.state === 'DENIED') return 'table-info' if (item.state === 'DENIED') return 'table-warning'
if (item.state === 'DELETED') return 'table-danger'
if (item.state === 'IN_PROGRESS') return 'table-primary'
if (item.state === 'PENDING') return 'table-primary'
}, },
updateCreationData(data) { updateCreationData(data) {
const row = data.row const row = data.row

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import SearchUserTable from './SearchUserTable.vue' import SearchUserTable from './SearchUserTable'
const localVue = global.localVue const localVue = global.localVue

View File

@ -53,7 +53,6 @@
<b-tab :title="$t('creation')" active :disabled="row.item.deletedAt !== null"> <b-tab :title="$t('creation')" active :disabled="row.item.deletedAt !== null">
<creation-formular <creation-formular
v-if="!row.item.deletedAt" v-if="!row.item.deletedAt"
type="singleCreation"
pagetype="singleCreation" pagetype="singleCreation"
:creation="row.item.creation" :creation="row.item.creation"
:item="row.item" :item="row.item"
@ -92,12 +91,12 @@
</div> </div>
</template> </template>
<script> <script>
import CreationFormular from '../CreationFormular.vue' import CreationFormular from '../CreationFormular'
import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular.vue' import ConfirmRegisterMailFormular from '../ConfirmRegisterMailFormular'
import CreationTransactionList from '../CreationTransactionList.vue' import CreationTransactionList from '../CreationTransactionList'
import TransactionLinkList from '../TransactionLinkList.vue' import TransactionLinkList from '../TransactionLinkList'
import ChangeUserRoleFormular from '../ChangeUserRoleFormular.vue' import ChangeUserRoleFormular from '../ChangeUserRoleFormular'
import DeletedUserFormular from '../DeletedUserFormular.vue' import DeletedUserFormular from '../DeletedUserFormular'
export default { export default {
name: 'SearchUserTable', name: 'SearchUserTable',

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import StatisticTable from './StatisticTable.vue' import StatisticTable from './StatisticTable'
const localVue = global.localVue const localVue = global.localVue

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import TransactionLinkList from './TransactionLinkList.vue' import TransactionLinkList from './TransactionLinkList'
import { listTransactionLinksAdmin } from '../graphql/listTransactionLinksAdmin.js' import { listTransactionLinksAdmin } from '../graphql/listTransactionLinksAdmin.js'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'
@ -9,8 +9,8 @@ const apolloQueryMock = jest.fn()
apolloQueryMock.mockResolvedValue({ apolloQueryMock.mockResolvedValue({
data: { data: {
listTransactionLinksAdmin: { listTransactionLinksAdmin: {
linkCount: 8, count: 8,
linkList: [ links: [
{ {
amount: '19.99', amount: '19.99',
code: '62ef8236ace7217fbd066c5a', code: '62ef8236ace7217fbd066c5a',

View File

@ -42,8 +42,8 @@ export default {
}, },
}) })
.then((result) => { .then((result) => {
this.rows = result.data.listTransactionLinksAdmin.linkCount this.rows = result.data.listTransactionLinksAdmin.count
this.items = result.data.listTransactionLinksAdmin.linkList this.items = result.data.listTransactionLinksAdmin.links
}) })
.catch((error) => { .catch((error) => {
this.toastError(error.message) this.toastError(error.message)

View File

@ -1,7 +1,7 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
export const adminCreateContributionMessage = gql` export const adminCreateContributionMessage = gql`
mutation ($contributionId: Float!, $message: String!) { mutation ($contributionId: Int!, $message: String!) {
adminCreateContributionMessage(contributionId: $contributionId, message: $message) { adminCreateContributionMessage(contributionId: $contributionId, message: $message) {
id id
message message

View File

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

View File

@ -1,13 +1,13 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
export const listAllContributions = gql` export const adminListAllContributions = gql`
query ( query (
$currentPage: Int = 1 $currentPage: Int = 1
$pageSize: Int = 25 $pageSize: Int = 25
$order: Order = DESC $order: Order = DESC
$statusFilter: [ContributionStatus!] $statusFilter: [ContributionStatus!]
) { ) {
listAllContributions( adminListAllContributions(
currentPage: $currentPage currentPage: $currentPage
pageSize: $pageSize pageSize: $pageSize
order: $order order: $order
@ -28,6 +28,8 @@ export const listAllContributions = gql`
messagesCount messagesCount
deniedAt deniedAt
deniedBy deniedBy
deletedAt
deletedBy
} }
} }
} }

View File

@ -1,7 +1,7 @@
import gql from 'graphql-tag' import gql from 'graphql-tag'
export const listContributionMessages = gql` export const listContributionMessages = gql`
query ($contributionId: Float!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) { query ($contributionId: Int!, $pageSize: Int = 25, $currentPage: Int = 1, $order: Order = ASC) {
listContributionMessages( listContributionMessages(
contributionId: $contributionId contributionId: $contributionId
pageSize: $pageSize pageSize: $pageSize

View File

@ -8,8 +8,8 @@ export const listTransactionLinksAdmin = gql`
userId: $userId userId: $userId
filters: { withRedeemed: true, withExpired: true, withDeleted: true } filters: { withRedeemed: true, withExpired: true, withDeleted: true }
) { ) {
linkCount count
linkList { links {
id id
amount amount
holdAvailableAmount holdAvailableAmount

View File

@ -7,8 +7,8 @@
</template> </template>
<script> <script>
import NavBar from '@/components/NavBar.vue' import NavBar from '@/components/NavBar'
import ContentFooter from '@/components/ContentFooter.vue' import ContentFooter from '@/components/ContentFooter'
export default { export default {
name: 'defaultLayout', name: 'defaultLayout',
components: { components: {

View File

@ -63,7 +63,6 @@
"deleted_user": "Alle gelöschten Nutzer", "deleted_user": "Alle gelöschten Nutzer",
"delete_user": "Nutzer löschen", "delete_user": "Nutzer löschen",
"deny": "Ablehnen", "deny": "Ablehnen",
"edit": "Bearbeiten",
"enabled": "aktiviert", "enabled": "aktiviert",
"error": "Fehler", "error": "Fehler",
"expired": "abgelaufen", "expired": "abgelaufen",
@ -101,7 +100,6 @@
"message": { "message": {
"request": "Die Anfrage wurde gesendet." "request": "Die Anfrage wurde gesendet."
}, },
"mod": "Mod",
"moderator": "Moderator", "moderator": "Moderator",
"name": "Name", "name": "Name",
"navbar": { "navbar": {

View File

@ -34,11 +34,11 @@
"all": "All", "all": "All",
"confirms": "Confirmed", "confirms": "Confirmed",
"deleted": "Deleted", "deleted": "Deleted",
"denied": "Denied", "denied": "Rejected",
"open": "Open" "open": "Open"
}, },
"created": "Confirmed", "created": "Created for",
"createdAt": "Created", "createdAt": "Created at",
"creation": "Creation", "creation": "Creation",
"creationList": "Creation list", "creationList": "Creation list",
"creation_form": { "creation_form": {
@ -53,7 +53,7 @@
"toasted": "Open creation ({value} GDD) for {email} has been saved and is ready for confirmation.", "toasted": "Open creation ({value} GDD) for {email} has been saved and is ready for confirmation.",
"toasted_created": "Creation has been successfully saved", "toasted_created": "Creation has been successfully saved",
"toasted_delete": "Open creation has been deleted", "toasted_delete": "Open creation has been deleted",
"toasted_denied": "Open creation has been denied", "toasted_denied": "Open creation has been rejected",
"toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.", "toasted_update": "Open creation {value} GDD) for {email} has been changed and is ready for confirmation.",
"update_creation": "Creation update" "update_creation": "Creation update"
}, },
@ -63,7 +63,6 @@
"deleted_user": "All deleted user", "deleted_user": "All deleted user",
"delete_user": "Delete user", "delete_user": "Delete user",
"deny": "Reject", "deny": "Reject",
"edit": "Edit",
"enabled": "enabled", "enabled": "enabled",
"error": "Error", "error": "Error",
"expired": "expired", "expired": "expired",
@ -87,7 +86,7 @@
"transactionlist": { "transactionlist": {
"confirmed": "When was it confirmed by a moderator / admin.", "confirmed": "When was it confirmed by a moderator / admin.",
"periods": "For what period was it submitted by the member.", "periods": "For what period was it submitted by the member.",
"state": "[PENDING = submitted, DELETED = deleted, IN_PROGRESS = in dialogue with moderator, DENIED = denied, CONFIRMED = confirmed]", "state": "[PENDING = submitted, DELETED = deleted, IN_PROGRESS = in dialogue with moderator, DENIED = rejected, CONFIRMED = confirmed]",
"submitted": "When was it submitted by the member" "submitted": "When was it submitted by the member"
} }
}, },
@ -101,7 +100,6 @@
"message": { "message": {
"request": "Request has been sent." "request": "Request has been sent."
}, },
"mod": "Mod",
"moderator": "Moderator", "moderator": "Moderator",
"name": "Name", "name": "Name",
"navbar": { "navbar": {

View File

@ -1,5 +1,5 @@
import Vue from 'vue' import Vue from 'vue'
import App from './App.vue' import App from './App'
// without this async calls are not working // without this async calls are not working
import 'regenerator-runtime' import 'regenerator-runtime'

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import CommunityStatistic from './CommunityStatistic.vue' import CommunityStatistic from './CommunityStatistic'
import { communityStatistics } from '@/graphql/communityStatistics.js' import { communityStatistics } from '@/graphql/communityStatistics.js'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'
import VueApollo from 'vue-apollo' import VueApollo from 'vue-apollo'

View File

@ -5,7 +5,7 @@
</template> </template>
<script> <script>
import { communityStatistics } from '@/graphql/communityStatistics.js' import { communityStatistics } from '@/graphql/communityStatistics.js'
import StatisticTable from '../components/Tables/StatisticTable.vue' import StatisticTable from '../components/Tables/StatisticTable'
export default { export default {
name: 'CommunityStatistic', name: 'CommunityStatistic',

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import ContributionLinks from './ContributionLinks.vue' import ContributionLinks from './ContributionLinks'
import { listContributionLinks } from '@/graphql/listContributionLinks.js' import { listContributionLinks } from '@/graphql/listContributionLinks.js'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'

View File

@ -9,7 +9,7 @@
</template> </template>
<script> <script>
import { listContributionLinks } from '@/graphql/listContributionLinks.js' import { listContributionLinks } from '@/graphql/listContributionLinks.js'
import ContributionLink from '../components/ContributionLink/ContributionLink.vue' import ContributionLink from '../components/ContributionLink/ContributionLink'
export default { export default {
name: 'ContributionLinks', name: 'ContributionLinks',

View File

@ -1,8 +1,8 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import CreationConfirm from './CreationConfirm.vue' import CreationConfirm from './CreationConfirm'
import { adminDeleteContribution } from '../graphql/adminDeleteContribution' import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
import { denyContribution } from '../graphql/denyContribution' import { denyContribution } from '../graphql/denyContribution'
import { listAllContributions } from '../graphql/listAllContributions' import { adminListAllContributions } from '../graphql/adminListAllContributions'
import { confirmContribution } from '../graphql/confirmContribution' import { confirmContribution } from '../graphql/confirmContribution'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
import VueApollo from 'vue-apollo' import VueApollo from 'vue-apollo'
@ -38,7 +38,7 @@ const mocks = {
const defaultData = () => { const defaultData = () => {
return { return {
listAllContributions: { adminListAllContributions: {
contributionCount: 2, contributionCount: 2,
contributionList: [ contributionList: [
{ {
@ -92,14 +92,14 @@ const defaultData = () => {
describe('CreationConfirm', () => { describe('CreationConfirm', () => {
let wrapper let wrapper
const adminListAllContributionsMock = jest.fn()
const adminDeleteContributionMock = jest.fn() const adminDeleteContributionMock = jest.fn()
const adminDenyContributionMock = jest.fn() const adminDenyContributionMock = jest.fn()
const confirmContributionMock = jest.fn() const confirmContributionMock = jest.fn()
mockClient.setRequestHandler( mockClient.setRequestHandler(
listAllContributions, adminListAllContributions,
jest adminListAllContributionsMock
.fn()
.mockRejectedValueOnce({ message: 'Ouch!' }) .mockRejectedValueOnce({ message: 'Ouch!' })
.mockResolvedValue({ data: defaultData() }), .mockResolvedValue({ data: defaultData() }),
) )
@ -331,78 +331,82 @@ describe('CreationConfirm', () => {
describe('filter tabs', () => { describe('filter tabs', () => {
describe('click tab "confirmed"', () => { describe('click tab "confirmed"', () => {
let refetchSpy
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks() jest.clearAllMocks()
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
await wrapper.find('a[data-test="confirmed"]').trigger('click') await wrapper.find('a[data-test="confirmed"]').trigger('click')
}) })
it('has statusFilter set to ["CONFIRMED"]', () => { it('refetches contributions with proper filter', () => {
expect( expect(adminListAllContributionsMock).toBeCalledWith({
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables, currentPage: 1,
).toMatchObject({ statusFilter: ['CONFIRMED'] }) order: 'DESC',
}) pageSize: 25,
statusFilter: ['CONFIRMED'],
it('refetches contributions', () => { })
expect(refetchSpy).toBeCalled()
}) })
describe('click tab "open"', () => { describe('click tab "open"', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks() jest.clearAllMocks()
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
await wrapper.find('a[data-test="open"]').trigger('click') await wrapper.find('a[data-test="open"]').trigger('click')
}) })
it('has statusFilter set to ["IN_PROGRESS", "PENDING"]', () => { it('refetches contributions with proper filter', () => {
expect( expect(adminListAllContributionsMock).toBeCalledWith({
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables, currentPage: 1,
).toMatchObject({ statusFilter: ['IN_PROGRESS', 'PENDING'] }) order: 'DESC',
}) pageSize: 25,
statusFilter: ['IN_PROGRESS', 'PENDING'],
it('refetches contributions', () => { })
expect(refetchSpy).toBeCalled()
}) })
}) })
describe('click tab "denied"', () => { describe('click tab "denied"', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks() jest.clearAllMocks()
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
await wrapper.find('a[data-test="denied"]').trigger('click') await wrapper.find('a[data-test="denied"]').trigger('click')
}) })
it('has statusFilter set to ["DENIED"]', () => { it('refetches contributions with proper filter', () => {
expect( expect(adminListAllContributionsMock).toBeCalledWith({
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables, currentPage: 1,
).toMatchObject({ statusFilter: ['DENIED'] }) order: 'DESC',
pageSize: 25,
statusFilter: ['DENIED'],
})
})
})
describe('click tab "deleted"', () => {
beforeEach(async () => {
jest.clearAllMocks()
await wrapper.find('a[data-test="deleted"]').trigger('click')
}) })
it('refetches contributions', () => { it('refetches contributions with proper filter', () => {
expect(refetchSpy).toBeCalled() expect(adminListAllContributionsMock).toBeCalledWith({
currentPage: 1,
order: 'DESC',
pageSize: 25,
statusFilter: ['DELETED'],
})
}) })
}) })
describe('click tab "all"', () => { describe('click tab "all"', () => {
beforeEach(async () => { beforeEach(async () => {
jest.clearAllMocks() jest.clearAllMocks()
refetchSpy = jest.spyOn(wrapper.vm.$apollo.queries.ListAllContributions, 'refetch')
await wrapper.find('a[data-test="all"]').trigger('click') await wrapper.find('a[data-test="all"]').trigger('click')
}) })
it('has statusFilter set to ["IN_PROGRESS", "PENDING", "CONFIRMED", "DENIED", "DELETED"]', () => { it('refetches contributions with proper filter', () => {
expect( expect(adminListAllContributionsMock).toBeCalledWith({
wrapper.vm.$apollo.queries.ListAllContributions.observer.options.variables, currentPage: 1,
).toMatchObject({ order: 'DESC',
pageSize: 25,
statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'], statusFilter: ['IN_PROGRESS', 'PENDING', 'CONFIRMED', 'DENIED', 'DELETED'],
}) })
}) })
it('refetches contributions', () => {
expect(refetchSpy).toBeCalled()
})
}) })
}) })
}) })
@ -412,10 +416,20 @@ describe('CreationConfirm', () => {
await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('update-state', 2) await wrapper.findComponent({ name: 'OpenCreationsTable' }).vm.$emit('update-state', 2)
}) })
it.skip('updates the status', () => { it('updates the status', () => {
expect(wrapper.vm.items.find((obj) => obj.id === 2).messagesCount).toBe(1) expect(wrapper.vm.items.find((obj) => obj.id === 2).messagesCount).toBe(1)
expect(wrapper.vm.items.find((obj) => obj.id === 2).state).toBe('IN_PROGRESS') expect(wrapper.vm.items.find((obj) => obj.id === 2).state).toBe('IN_PROGRESS')
}) })
}) })
describe('unknown variant', () => {
beforeEach(async () => {
await wrapper.setData({ variant: 'unknown' })
})
it('has overlay icon "info"', () => {
expect(wrapper.vm.overlayIcon).toBe('info')
})
})
}) })
}) })

View File

@ -5,25 +5,37 @@
<b-tabs v-model="tabIndex" content-class="mt-3" fill> <b-tabs v-model="tabIndex" content-class="mt-3" fill>
<b-tab active :title-link-attributes="{ 'data-test': 'open' }"> <b-tab active :title-link-attributes="{ 'data-test': 'open' }">
<template #title> <template #title>
<b-icon icon="bell-fill" variant="primary"></b-icon>
{{ $t('contributions.open') }} {{ $t('contributions.open') }}
<b-badge v-if="$store.state.openCreations > 0" variant="danger"> <b-badge v-if="$store.state.openCreations > 0" variant="danger">
{{ $store.state.openCreations }} {{ $store.state.openCreations }}
</b-badge> </b-badge>
</template> </template>
</b-tab> </b-tab>
<b-tab <b-tab :title-link-attributes="{ 'data-test': 'confirmed' }">
:title="$t('contributions.confirms')" <template #title>
:title-link-attributes="{ 'data-test': 'confirmed' }" <b-icon icon="check" variant="success"></b-icon>
/> {{ $t('contributions.confirms') }}
<b-tab </template>
:title="$t('contributions.denied')" </b-tab>
:title-link-attributes="{ 'data-test': 'denied' }" <b-tab :title-link-attributes="{ 'data-test': 'denied' }">
/> <template #title>
<b-tab <b-icon icon="x-circle" variant="warning"></b-icon>
:title="$t('contributions.deleted')" {{ $t('contributions.denied') }}
:title-link-attributes="{ 'data-test': 'deleted' }" </template>
/> </b-tab>
<b-tab :title="$t('contributions.all')" :title-link-attributes="{ 'data-test': 'all' }" /> <b-tab :title-link-attributes="{ 'data-test': 'deleted' }">
<template #title>
<b-icon icon="trash" variant="danger"></b-icon>
{{ $t('contributions.deleted') }}
</template>
</b-tab>
<b-tab :title-link-attributes="{ 'data-test': 'all' }">
<template #title>
<b-icon icon="list"></b-icon>
{{ $t('contributions.all') }}
</template>
</b-tab>
</b-tabs> </b-tabs>
</div> </div>
<open-creations-table <open-creations-table
@ -71,9 +83,9 @@
</div> </div>
</template> </template>
<script> <script>
import Overlay from '../components/Overlay.vue' import Overlay from '../components/Overlay'
import OpenCreationsTable from '../components/Tables/OpenCreationsTable.vue' import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
import { listAllContributions } from '../graphql/listAllContributions' import { adminListAllContributions } from '../graphql/adminListAllContributions'
import { adminDeleteContribution } from '../graphql/adminDeleteContribution' import { adminDeleteContribution } from '../graphql/adminDeleteContribution'
import { confirmContribution } from '../graphql/confirmContribution' import { confirmContribution } from '../graphql/confirmContribution'
import { denyContribution } from '../graphql/denyContribution' import { denyContribution } from '../graphql/denyContribution'
@ -172,19 +184,17 @@ export default {
this.items.find((obj) => obj.id === id).messagesCount++ this.items.find((obj) => obj.id === id).messagesCount++
this.items.find((obj) => obj.id === id).state = 'IN_PROGRESS' this.items.find((obj) => obj.id === id).state = 'IN_PROGRESS'
}, },
}, formatDateOrDash(value) {
watch: { return value ? this.$d(new Date(value), 'short') : '—'
statusFilter() {
this.$apollo.queries.ListAllContributions.refetch()
}, },
}, },
computed: { computed: {
fields() { fields() {
return [ return [
[ [
// open contributions
{ key: 'bookmark', label: this.$t('delete') }, { key: 'bookmark', label: this.$t('delete') },
{ key: 'deny', label: this.$t('deny') }, { key: 'deny', label: this.$t('deny') },
{ key: 'email', label: this.$t('e_mail') },
{ key: 'firstName', label: this.$t('firstname') }, { key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') }, { key: 'lastName', label: this.$t('lastname') },
{ {
@ -199,14 +209,15 @@ export default {
key: 'contributionDate', key: 'contributionDate',
label: this.$t('created'), label: this.$t('created'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ key: 'moderator', label: this.$t('moderator') }, { key: 'moderator', label: this.$t('moderator') },
{ key: 'editCreation', label: this.$t('edit') }, { key: 'editCreation', label: this.$t('chat') },
{ key: 'confirm', label: this.$t('save') }, { key: 'confirm', label: this.$t('save') },
], ],
[ [
// confirmed contributions
{ key: 'firstName', label: this.$t('firstname') }, { key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') }, { key: 'lastName', label: this.$t('lastname') },
{ {
@ -221,27 +232,28 @@ export default {
key: 'contributionDate', key: 'contributionDate',
label: this.$t('created'), label: this.$t('created'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'createdAt', key: 'createdAt',
label: this.$t('createdAt'), label: this.$t('createdAt'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'confirmedAt', key: 'confirmedAt',
label: this.$t('contributions.confirms'), label: this.$t('contributions.confirms'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ key: 'confirmedBy', label: this.$t('moderator') },
{ key: 'chatCreation', label: this.$t('chat') }, { key: 'chatCreation', label: this.$t('chat') },
], ],
[ [
{ key: 'reActive', label: 'reActive' }, // denied contributions
{ key: 'firstName', label: this.$t('firstname') }, { key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') }, { key: 'lastName', label: this.$t('lastname') },
{ {
@ -256,29 +268,28 @@ export default {
key: 'contributionDate', key: 'contributionDate',
label: this.$t('created'), label: this.$t('created'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'createdAt', key: 'createdAt',
label: this.$t('createdAt'), label: this.$t('createdAt'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'deniedAt', key: 'deniedAt',
label: this.$t('contributions.denied'), label: this.$t('contributions.denied'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ key: 'deniedBy', label: this.$t('mod') }, { key: 'deniedBy', label: this.$t('moderator') },
{ key: 'chatCreation', label: this.$t('chat') }, { key: 'chatCreation', label: this.$t('chat') },
], ],
[],
[ [
{ key: 'state', label: 'state' }, // deleted contributions
{ key: 'firstName', label: this.$t('firstname') }, { key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') }, { key: 'lastName', label: this.$t('lastname') },
{ {
@ -293,24 +304,61 @@ export default {
key: 'contributionDate', key: 'contributionDate',
label: this.$t('created'), label: this.$t('created'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'createdAt', key: 'createdAt',
label: this.$t('createdAt'), label: this.$t('createdAt'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
},
},
{
key: 'deletedAt',
label: this.$t('contributions.deleted'),
formatter: (value) => {
return this.formatDateOrDash(value)
},
},
{ key: 'deletedBy', label: this.$t('moderator') },
{ key: 'chatCreation', label: this.$t('chat') },
],
[
// all contributions
{ key: 'state', label: this.$t('status') },
{ key: 'firstName', label: this.$t('firstname') },
{ key: 'lastName', label: this.$t('lastname') },
{
key: 'amount',
label: this.$t('creation'),
formatter: (value) => {
return value + ' GDD'
},
},
{ key: 'memo', label: this.$t('text'), class: 'text-break' },
{
key: 'contributionDate',
label: this.$t('created'),
formatter: (value) => {
return this.formatDateOrDash(value)
},
},
{
key: 'createdAt',
label: this.$t('createdAt'),
formatter: (value) => {
return this.formatDateOrDash(value)
}, },
}, },
{ {
key: 'confirmedAt', key: 'confirmedAt',
label: this.$t('contributions.confirms'), label: this.$t('contributions.confirms'),
formatter: (value) => { formatter: (value) => {
return this.$d(new Date(value), 'short') return this.formatDateOrDash(value)
}, },
}, },
{ key: 'confirmedBy', label: this.$t('mod') }, { key: 'confirmedBy', label: this.$t('moderator') },
{ key: 'chatCreation', label: this.$t('chat') }, { key: 'chatCreation', label: this.$t('chat') },
], ],
][this.tabIndex] ][this.tabIndex]
@ -349,19 +397,22 @@ export default {
apollo: { apollo: {
ListAllContributions: { ListAllContributions: {
query() { query() {
return listAllContributions return adminListAllContributions
}, },
variables() { variables() {
// may be at some point we need a pagination here
return { return {
currentPage: this.currentPage, currentPage: this.currentPage,
pageSize: this.pageSize, pageSize: this.pageSize,
statusFilter: this.statusFilter, statusFilter: this.statusFilter,
} }
}, },
update({ listAllContributions }) { fetchPolicy: 'no-cache',
this.rows = listAllContributions.contributionCount update({ adminListAllContributions }) {
this.items = listAllContributions.contributionList this.rows = adminListAllContributions.contributionCount
this.items = adminListAllContributions.contributionList
if (this.statusFilter === FILTER_TAB_MAP[0]) {
this.$store.commit('setOpenCreations', adminListAllContributions.contributionCount)
}
}, },
error({ message }) { error({ message }) {
this.toastError(message) this.toastError(message)

View File

@ -1,6 +1,6 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import Overview from './Overview.vue' import Overview from './Overview'
import { listAllContributions } from '../graphql/listAllContributions' import { adminListAllContributions } from '../graphql/adminListAllContributions'
import VueApollo from 'vue-apollo' import VueApollo from 'vue-apollo'
import { createMockClient } from 'mock-apollo-client' import { createMockClient } from 'mock-apollo-client'
import { toastErrorSpy } from '../../test/testSetup' import { toastErrorSpy } from '../../test/testSetup'
@ -30,7 +30,7 @@ const mocks = {
const defaultData = () => { const defaultData = () => {
return { return {
listAllContributions: { adminListAllContributions: {
contributionCount: 2, contributionCount: 2,
contributionList: [ contributionList: [
{ {
@ -84,11 +84,11 @@ const defaultData = () => {
describe('Overview', () => { describe('Overview', () => {
let wrapper let wrapper
const listAllContributionsMock = jest.fn() const adminListAllContributionsMock = jest.fn()
mockClient.setRequestHandler( mockClient.setRequestHandler(
listAllContributions, adminListAllContributions,
listAllContributionsMock adminListAllContributionsMock
.mockRejectedValueOnce({ message: 'Ouch!' }) .mockRejectedValueOnce({ message: 'Ouch!' })
.mockResolvedValue({ data: defaultData() }), .mockResolvedValue({ data: defaultData() }),
) )
@ -109,8 +109,8 @@ describe('Overview', () => {
}) })
}) })
it('calls the listAllContributions query', () => { it('calls the adminListAllContributions query', () => {
expect(listAllContributionsMock).toBeCalledWith({ expect(adminListAllContributionsMock).toBeCalledWith({
currentPage: 1, currentPage: 1,
order: 'DESC', order: 'DESC',
pageSize: 25, pageSize: 25,

View File

@ -31,7 +31,7 @@
</div> </div>
</template> </template>
<script> <script>
import { listAllContributions } from '../graphql/listAllContributions' import { adminListAllContributions } from '../graphql/adminListAllContributions'
export default { export default {
name: 'overview', name: 'overview',
@ -43,7 +43,7 @@ export default {
apollo: { apollo: {
AllContributions: { AllContributions: {
query() { query() {
return listAllContributions return adminListAllContributions
}, },
variables() { variables() {
// may be at some point we need a pagination here // may be at some point we need a pagination here
@ -51,8 +51,8 @@ export default {
statusFilter: this.statusFilter, statusFilter: this.statusFilter,
} }
}, },
update({ listAllContributions }) { update({ adminListAllContributions }) {
this.$store.commit('setOpenCreations', listAllContributions.contributionCount) this.$store.commit('setOpenCreations', adminListAllContributions.contributionCount)
}, },
error({ message }) { error({ message }) {
this.toastError(message) this.toastError(message)

View File

@ -1,5 +1,5 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import UserSearch from './UserSearch.vue' import UserSearch from './UserSearch'
import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup' import { toastErrorSpy, toastSuccessSpy } from '../../test/testSetup'
const localVue = global.localVue const localVue = global.localVue

View File

@ -58,7 +58,7 @@
</div> </div>
</template> </template>
<script> <script>
import SearchUserTable from '../components/Tables/SearchUserTable.vue' import SearchUserTable from '../components/Tables/SearchUserTable'
import { searchUsers } from '../graphql/searchUsers' import { searchUsers } from '../graphql/searchUsers'
import { creationMonths } from '../mixins/creationMonths' import { creationMonths } from '../mixins/creationMonths'
@ -72,7 +72,6 @@ export default {
return { return {
showArrays: false, showArrays: false,
searchResult: [], searchResult: [],
massCreation: [],
criteria: '', criteria: '',
filters: { filters: {
byActivated: null, byActivated: null,

View File

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

View File

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

View File

@ -1,5 +1,5 @@
# Server # Server
JWT_EXPIRES_IN=1m JWT_EXPIRES_IN=2m
# Email # Email
EMAIL=true EMAIL=true

View File

@ -2,16 +2,10 @@ module.exports = {
root: true, root: true,
env: { env: {
node: true, node: true,
// jest: true,
}, },
parser: '@typescript-eslint/parser', parser: '@typescript-eslint/parser',
plugins: ['prettier', '@typescript-eslint' /*, 'jest' */], plugins: ['prettier', '@typescript-eslint', 'type-graphql'],
extends: [ extends: ['standard', 'eslint:recommended', 'plugin:prettier/recommended'],
'standard',
'eslint:recommended',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
],
// add your custom rules here // add your custom rules here
rules: { rules: {
'no-console': ['error'], 'no-console': ['error'],
@ -23,4 +17,28 @@ module.exports = {
}, },
], ],
}, },
overrides: [
// only for ts files
{
files: ['*.ts'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:type-graphql/recommended',
],
rules: {
// allow explicitly defined dangling promises
'@typescript-eslint/no-floating-promises': ['error', { ignoreVoid: true }],
'no-void': ['error', { allowAsStatement: true }],
// ignore prefer-regexp-exec rule to allow string.match(regex)
'@typescript-eslint/prefer-regexp-exec': 'off',
},
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
// this is to properly reference the referenced project database without requirement of compiling it
EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true,
},
},
],
} }

View File

@ -4,6 +4,11 @@ module.exports = {
preset: 'ts-jest', preset: 'ts-jest',
collectCoverage: true, collectCoverage: true,
collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'],
coverageThreshold: {
global: {
lines: 80,
},
},
setupFiles: ['<rootDir>/test/testSetup.ts'], setupFiles: ['<rootDir>/test/testSetup.ts'],
setupFilesAfterEnv: ['<rootDir>/test/extensions.ts'], setupFilesAfterEnv: ['<rootDir>/test/extensions.ts'],
modulePathIgnorePatterns: ['<rootDir>/build/'], modulePathIgnorePatterns: ['<rootDir>/build/'],

View File

@ -1,6 +1,6 @@
{ {
"name": "gradido-backend", "name": "gradido-backend",
"version": "1.18.2", "version": "1.19.1",
"description": "Gradido unified backend providing an API-Service for Gradido Transactions", "description": "Gradido unified backend providing an API-Service for Gradido Transactions",
"main": "src/index.ts", "main": "src/index.ts",
"repository": "https://github.com/gradido/gradido/backend", "repository": "https://github.com/gradido/gradido/backend",
@ -13,7 +13,7 @@
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js", "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", "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 --ext .js,.ts .",
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles", "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", "seed": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/seeds/index.ts",
"klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts", "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts",
"locales": "scripts/sort.sh" "locales": "scripts/sort.sh"
@ -65,6 +65,7 @@
"eslint-plugin-node": "^11.1.0", "eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.4.0", "eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-promise": "^5.1.0", "eslint-plugin-promise": "^5.1.0",
"eslint-plugin-type-graphql": "^1.0.0",
"faker": "^5.5.3", "faker": "^5.5.3",
"jest": "^27.2.4", "jest": "^27.2.4",
"nodemon": "^2.0.7", "nodemon": "^2.0.7",

View File

@ -1,16 +1,19 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import axios from 'axios' import axios from 'axios'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import LogError from '@/server/LogError'
// 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> => {
logger.trace('POST: url=' + url + ' payload=' + payload) logger.trace('POST', url, payload)
return axios return axios
.post(url, payload) .post(url, payload)
.then((result) => { .then((result) => {
logger.trace('POST-Response: result=' + result) logger.trace('POST-Response', result)
if (result.status !== 200) { if (result.status !== 200) {
throw new Error('HTTP Status Error ' + result.status) throw new LogError('HTTP Status Error', result.status)
} }
if (result.data.state !== 'success') { if (result.data.state !== 'success') {
throw new Error(result.data.msg) throw new Error(result.data.msg)
@ -28,9 +31,9 @@ export const apiGet = async (url: string): Promise<any> => {
return axios return axios
.get(url) .get(url)
.then((result) => { .then((result) => {
logger.trace('GET-Response: result=' + result) logger.trace('GET-Response', result)
if (result.status !== 200) { if (result.status !== 200) {
throw new Error('HTTP Status Error ' + result.status) throw new LogError('HTTP Status Error', result.status)
} }
if (!['success', 'warning'].includes(result.data.state)) { if (!['success', 'warning'].includes(result.data.state)) {
throw new Error(result.data.msg) throw new Error(result.data.msg)

View File

@ -1,3 +1,5 @@
/* 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/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { KlicktippConnector } from './klicktippConnector' import { KlicktippConnector } from './klicktippConnector'

View File

@ -1,3 +1,7 @@
/* 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/no-explicit-any */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import axios, { AxiosRequestConfig, Method } from 'axios' import axios, { AxiosRequestConfig, Method } from 'axios'

View File

@ -42,7 +42,6 @@ export enum RIGHTS {
DELETE_USER = 'DELETE_USER', DELETE_USER = 'DELETE_USER',
UNDELETE_USER = 'UNDELETE_USER', UNDELETE_USER = 'UNDELETE_USER',
ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION', ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION',
ADMIN_CREATE_CONTRIBUTIONS = 'ADMIN_CREATE_CONTRIBUTIONS',
ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION', ADMIN_UPDATE_CONTRIBUTION = 'ADMIN_UPDATE_CONTRIBUTION',
ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION', ADMIN_DELETE_CONTRIBUTION = 'ADMIN_DELETE_CONTRIBUTION',
LIST_UNCONFIRMED_CONTRIBUTIONS = 'LIST_UNCONFIRMED_CONTRIBUTIONS', LIST_UNCONFIRMED_CONTRIBUTIONS = 'LIST_UNCONFIRMED_CONTRIBUTIONS',
@ -55,4 +54,5 @@ export enum RIGHTS {
UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK', UPDATE_CONTRIBUTION_LINK = 'UPDATE_CONTRIBUTION_LINK',
ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE', ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
DENY_CONTRIBUTION = 'DENY_CONTRIBUTION', DENY_CONTRIBUTION = 'DENY_CONTRIBUTION',
ADMIN_OPEN_CREATIONS = 'ADMIN_OPEN_CREATIONS',
} }

View File

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/unbound-method */
import { createTransport } from 'nodemailer' import { createTransport } from 'nodemailer'
import { logger, i18n } from '@test/testSetup' import { logger, i18n } from '@test/testSetup'
import CONFIG from '@/config' import CONFIG from '@/config'
@ -8,6 +10,7 @@ CONFIG.EMAIL_SMTP_URL = 'EMAIL_SMTP_URL'
CONFIG.EMAIL_SMTP_PORT = '1234' CONFIG.EMAIL_SMTP_PORT = '1234'
CONFIG.EMAIL_USERNAME = 'user' CONFIG.EMAIL_USERNAME = 'user'
CONFIG.EMAIL_PASSWORD = 'pwd' CONFIG.EMAIL_PASSWORD = 'pwd'
CONFIG.EMAIL_TLS = true
jest.mock('nodemailer', () => { jest.mock('nodemailer', () => {
return { return {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import CONFIG from '@/config' import CONFIG from '@/config'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import path from 'path' import path from 'path'

View File

@ -1,5 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* 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 { testEnvironment } from '@test/helpers'
import { logger, i18n as localization } from '@test/testSetup' import { logger, i18n as localization } from '@test/testSetup'
@ -106,7 +109,7 @@ describe('sendEmailVariants', () => {
'you have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.', 'you have received a message from Bibi Bloxberg regarding your common good contribution “My contribution.”.',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
'To view and reply to the message, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!', 'To view and reply to the message, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`, `Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
@ -424,7 +427,7 @@ describe('sendEmailVariants', () => {
'Your public good contribution “My contribution.” was rejected by Bibi Bloxberg.', 'Your public good contribution “My contribution.” was rejected by Bibi Bloxberg.',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
'To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!', 'To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`, `Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,
@ -502,7 +505,7 @@ describe('sendEmailVariants', () => {
'Your public good contribution “My contribution.” was deleted by Bibi Bloxberg.', 'Your public good contribution “My contribution.” was deleted by Bibi Bloxberg.',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
'To see your common good contributions and related messages, go to the “Community” menu in your Gradido account and click on the “My contributions to the common good” tab!', 'To see your common good contributions and related messages, go to the “Creation” menu in your Gradido account and click on the “My contributions” tab!',
) )
expect(result.originalMessage.html).toContain( expect(result.originalMessage.html).toContain(
`Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`, `Link to your account: <a href="${CONFIG.EMAIL_LINK_OVERVIEW}">${CONFIG.EMAIL_LINK_OVERVIEW}</a>`,

View File

@ -1,3 +1,6 @@
/* 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 { gql } from 'graphql-request'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'

View File

@ -1,3 +1,6 @@
/* 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 { gql } from 'graphql-request'
import { backendLogger as logger } from '@/server/logger' import { backendLogger as logger } from '@/server/logger'
import { Community as DbCommunity } from '@entity/Community' import { Community as DbCommunity } from '@entity/Community'

View File

@ -1,3 +1,7 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/unbound-method */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* 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 */
@ -150,7 +154,8 @@ describe('validate Communities', () => {
}) })
it('logs unsupported api for community with api 2_0 ', () => { it('logs unsupported api for community with api 2_0 ', () => {
expect(logger.warn).toBeCalledWith( expect(logger.warn).toBeCalledWith(
`Federation: dbCom: ${dbCom.id} with unsupported apiVersion=2_0; supported versions=1_0,1_1`, `Federation: dbCom: ${dbCom.id} with unsupported apiVersion=2_0; supported versions`,
['1_0', '1_1'],
) )
}) })
}) })

View File

@ -8,14 +8,14 @@ import { backendLogger as logger } from '@/server/logger'
import { ApiVersionType } from './enum/apiVersionType' import { ApiVersionType } from './enum/apiVersionType'
import LogError from '@/server/LogError' import LogError from '@/server/LogError'
export async function startValidateCommunities(timerInterval: number): Promise<void> { export function startValidateCommunities(timerInterval: number): void {
logger.info( logger.info(
`Federation: startValidateCommunities loop with an interval of ${timerInterval} ms...`, `Federation: startValidateCommunities loop with an interval of ${timerInterval} ms...`,
) )
// TODO: replace the timer-loop by an event-based communication to verify announced foreign communities // TODO: replace the timer-loop by an event-based communication to verify announced foreign communities
// better to use setTimeout twice than setInterval once -> see https://javascript.info/settimeout-setinterval // better to use setTimeout twice than setInterval once -> see https://javascript.info/settimeout-setinterval
setTimeout(function run() { setTimeout(function run() {
validateCommunities() void validateCommunities()
setTimeout(run, timerInterval) setTimeout(run, timerInterval)
}, timerInterval) }, timerInterval)
} }
@ -27,8 +27,8 @@ export async function validateCommunities(): Promise<void> {
.getMany() .getMany()
logger.debug(`Federation: found ${dbCommunities.length} dbCommunities`) logger.debug(`Federation: found ${dbCommunities.length} dbCommunities`)
dbCommunities.forEach(async function (dbCom) { for (const dbCom of dbCommunities) {
logger.debug(`Federation: dbCom: ${JSON.stringify(dbCom)}`) logger.debug('Federation: dbCom', dbCom)
const apiValueStrings: string[] = Object.values(ApiVersionType) const apiValueStrings: string[] = Object.values(ApiVersionType)
logger.debug(`suppported ApiVersions=`, apiValueStrings) logger.debug(`suppported ApiVersions=`, apiValueStrings)
if (apiValueStrings.includes(dbCom.apiVersion)) { if (apiValueStrings.includes(dbCom.apiVersion)) {
@ -38,11 +38,13 @@ export async function validateCommunities(): Promise<void> {
try { try {
const pubKey = await invokeVersionedRequestGetPublicKey(dbCom) const pubKey = await invokeVersionedRequestGetPublicKey(dbCom)
logger.info( logger.info(
`Federation: received publicKey=${pubKey} from endpoint=${dbCom.endPoint}/${dbCom.apiVersion}`, 'Federation: received publicKey from endpoint',
pubKey,
`${dbCom.endPoint}/${dbCom.apiVersion}`,
) )
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) { if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {
logger.info(`Federation: matching publicKey: ${pubKey}`) logger.info(`Federation: matching publicKey: ${pubKey}`)
DbCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() }) await DbCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() })
logger.debug(`Federation: updated dbCom: ${JSON.stringify(dbCom)}`) logger.debug(`Federation: updated dbCom: ${JSON.stringify(dbCom)}`)
} }
/* /*
@ -58,10 +60,11 @@ export async function validateCommunities(): Promise<void> {
} }
} else { } else {
logger.warn( logger.warn(
`Federation: dbCom: ${dbCom.id} with unsupported apiVersion=${dbCom.apiVersion}; supported versions=${apiValueStrings}`, `Federation: dbCom: ${dbCom.id} with unsupported apiVersion=${dbCom.apiVersion}; supported versions`,
apiValueStrings,
) )
} }
}) }
} }
function isLogError(err: unknown) { function isLogError(err: unknown) {

View File

@ -22,7 +22,7 @@ export default class ContributionLinkArgs {
validTo?: string | null validTo?: string | null
@Field(() => Decimal, { nullable: true }) @Field(() => Decimal, { nullable: true })
maxAmountPerMonth: Decimal | null maxAmountPerMonth?: Decimal | null
@Field(() => Int) @Field(() => Int)
maxPerCycle: number maxPerCycle: number

View File

@ -1,9 +1,9 @@
import { ArgsType, Field, InputType } from 'type-graphql' import { ArgsType, Field, Int, InputType } from 'type-graphql'
@InputType() @InputType()
@ArgsType() @ArgsType()
export default class ContributionMessageArgs { export default class ContributionMessageArgs {
@Field(() => Number) @Field(() => Int)
contributionId: number contributionId: number
@Field(() => String) @Field(() => String)

View File

@ -11,11 +11,11 @@ export default class CreateUserArgs {
@Field(() => String) @Field(() => String)
lastName: string lastName: string
@Field(() => String) @Field(() => String, { nullable: true })
language?: string // Will default to DEFAULT_LANGUAGE language?: string | null
@Field(() => Int, { nullable: true }) @Field(() => Int, { nullable: true })
publisherId: number publisherId?: number | null
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
redeemCode?: string | null redeemCode?: string | null

View File

@ -1,3 +1,4 @@
/* 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'

View File

@ -7,11 +7,14 @@ export default class SearchUsersArgs {
searchText: string searchText: string
@Field(() => Int, { nullable: true }) @Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
currentPage?: number currentPage?: number
@Field(() => Int, { nullable: true }) @Field(() => Int, { nullable: true })
// eslint-disable-next-line type-graphql/invalid-nullable-input-type
pageSize?: number pageSize?: number
// eslint-disable-next-line type-graphql/wrong-decorator-signature
@Field(() => SearchUsersFilters, { nullable: true, defaultValue: null }) @Field(() => SearchUsersFilters, { nullable: true, defaultValue: null })
filters: SearchUsersFilters filters?: SearchUsersFilters | null
} }

View File

@ -3,8 +3,8 @@ import { Field, InputType } from 'type-graphql'
@InputType() @InputType()
export default class SearchUsersFilters { export default class SearchUsersFilters {
@Field(() => Boolean, { nullable: true, defaultValue: null }) @Field(() => Boolean, { nullable: true, defaultValue: null })
byActivated: boolean byActivated?: boolean | null
@Field(() => Boolean, { nullable: true, defaultValue: null }) @Field(() => Boolean, { nullable: true, defaultValue: null })
byDeleted: boolean byDeleted?: boolean | null
} }

View File

@ -1,13 +1,14 @@
/* eslint-disable type-graphql/invalid-nullable-input-type */
import { Field, InputType } from 'type-graphql' import { Field, InputType } from 'type-graphql'
@InputType() @InputType()
export default class TransactionLinkFilters { export default class TransactionLinkFilters {
@Field(() => Boolean, { nullable: true }) @Field(() => Boolean, { nullable: true })
withDeleted: boolean withDeleted?: boolean
@Field(() => Boolean, { nullable: true }) @Field(() => Boolean, { nullable: true })
withExpired: boolean withExpired?: boolean
@Field(() => Boolean, { nullable: true }) @Field(() => Boolean, { nullable: true })
withRedeemed: boolean withRedeemed?: boolean
} }

View File

@ -9,5 +9,5 @@ export default class UnsecureLoginArgs {
password: string password: string
@Field(() => Int, { nullable: true }) @Field(() => Int, { nullable: true })
publisherId: number publisherId?: number | null
} }

View File

@ -1,4 +1,4 @@
import { ArgsType, Field } from 'type-graphql' import { ArgsType, Field, Int } from 'type-graphql'
@ArgsType() @ArgsType()
export default class UpdateUserInfosArgs { export default class UpdateUserInfosArgs {
@ -11,8 +11,8 @@ export default class UpdateUserInfosArgs {
@Field({ nullable: true }) @Field({ nullable: true })
language?: string language?: string
@Field({ nullable: true }) @Field(() => Int, { nullable: true })
publisherId?: number publisherId?: number | null
@Field({ nullable: true }) @Field({ nullable: true })
password?: string password?: string

View File

@ -1,3 +1,5 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
import { AuthChecker } from 'type-graphql' import { AuthChecker } from 'type-graphql'

View File

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

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int, Float } from 'type-graphql'
import Decimal from 'decimal.js-light' import Decimal from 'decimal.js-light'
@ObjectType() @ObjectType()
@ -19,14 +19,14 @@ export class Balance {
@Field(() => Decimal) @Field(() => Decimal)
balance: Decimal balance: Decimal
@Field(() => Number, { nullable: true }) @Field(() => Float, { nullable: true })
balanceGDT: number | null balanceGDT: number | null
// the count of all transactions // the count of all transactions
@Field(() => Number) @Field(() => Int)
count: number count: number
// the count of transaction links // the count of transaction links
@Field(() => Number) @Field(() => Int)
linkCount: number linkCount: number
} }

View File

@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* 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 { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
@ObjectType() @ObjectType()
export class Community { export class Community {
@ -14,7 +16,7 @@ export class Community {
} }
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => String) @Field(() => String)

View File

@ -1,9 +1,9 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import Decimal from 'decimal.js-light' import Decimal from 'decimal.js-light'
@ObjectType() @ObjectType()
export class DynamicStatisticsFields { export class DynamicStatisticsFields {
@Field(() => Number) @Field(() => Int)
activeUsers: number activeUsers: number
@Field(() => Decimal) @Field(() => Decimal)
@ -15,13 +15,13 @@ export class DynamicStatisticsFields {
@ObjectType() @ObjectType()
export class CommunityStatistics { export class CommunityStatistics {
@Field(() => Number) @Field(() => Int)
allUsers: number allUsers: number
@Field(() => Number) @Field(() => Int)
totalUsers: number totalUsers: number
@Field(() => Number) @Field(() => Int)
deletedUsers: number deletedUsers: number
@Field(() => Decimal) @Field(() => Decimal)

View File

@ -12,7 +12,6 @@ export class Contribution {
this.amount = contribution.amount this.amount = contribution.amount
this.memo = contribution.memo this.memo = contribution.memo
this.createdAt = contribution.createdAt this.createdAt = contribution.createdAt
this.deletedAt = contribution.deletedAt
this.confirmedAt = contribution.confirmedAt this.confirmedAt = contribution.confirmedAt
this.confirmedBy = contribution.confirmedBy this.confirmedBy = contribution.confirmedBy
this.contributionDate = contribution.contributionDate this.contributionDate = contribution.contributionDate
@ -20,9 +19,11 @@ export class Contribution {
this.messagesCount = contribution.messages ? contribution.messages.length : 0 this.messagesCount = contribution.messages ? contribution.messages.length : 0
this.deniedAt = contribution.deniedAt this.deniedAt = contribution.deniedAt
this.deniedBy = contribution.deniedBy this.deniedBy = contribution.deniedBy
this.deletedAt = contribution.deletedAt
this.deletedBy = contribution.deletedBy
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
@ -40,25 +41,28 @@ export class Contribution {
@Field(() => Date) @Field(() => Date)
createdAt: Date createdAt: Date
@Field(() => Date, { nullable: true })
deletedAt: Date | null
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })
confirmedAt: Date | null confirmedAt: Date | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
confirmedBy: number | null confirmedBy: number | null
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })
deniedAt: Date | null deniedAt: Date | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
deniedBy: number | null deniedBy: number | null
@Field(() => Date, { nullable: true })
deletedAt: Date | null
@Field(() => Int, { nullable: true })
deletedBy: number | null
@Field(() => Date) @Field(() => Date)
contributionDate: Date contributionDate: Date
@Field(() => Number) @Field(() => Int)
messagesCount: number messagesCount: number
@Field(() => String) @Field(() => String)

View File

@ -21,7 +21,7 @@ export class ContributionLink {
this.link = CONFIG.COMMUNITY_REDEEM_CONTRIBUTION_URL.replace(/{code}/g, this.code) this.link = CONFIG.COMMUNITY_REDEEM_CONTRIBUTION_URL.replace(/{code}/g, this.code)
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => Decimal) @Field(() => Decimal)

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import { ContributionLink } from '@model/ContributionLink' import { ContributionLink } from '@model/ContributionLink'
@ObjectType() @ObjectType()
@ -6,6 +6,6 @@ export class ContributionLinkList {
@Field(() => [ContributionLink]) @Field(() => [ContributionLink])
links: ContributionLink[] links: ContributionLink[]
@Field(() => Number) @Field(() => Int)
count: number count: number
} }

View File

@ -1,4 +1,4 @@
import { Field, ObjectType } from 'type-graphql' 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'
@ -16,7 +16,7 @@ export class ContributionMessage {
this.isModerator = contributionMessage.isModerator this.isModerator = contributionMessage.isModerator
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => String) @Field(() => String)
@ -26,7 +26,7 @@ export class ContributionMessage {
createdAt: Date createdAt: Date
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })
updatedAt?: Date | null updatedAt: Date | null
@Field(() => String) @Field(() => String)
type: string type: string
@ -37,7 +37,7 @@ export class ContributionMessage {
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
userLastName: string | null userLastName: string | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
userId: number | null userId: number | null
@Field(() => Boolean) @Field(() => Boolean)
@ -45,7 +45,7 @@ export class ContributionMessage {
} }
@ObjectType() @ObjectType()
export class ContributionMessageListResult { export class ContributionMessageListResult {
@Field(() => Number) @Field(() => Int)
count: number count: number
@Field(() => [ContributionMessage]) @Field(() => [ContributionMessage])

View File

@ -1,6 +1,8 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* 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 { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Float, Int } from 'type-graphql'
import { GdtEntryType } from '@enum/GdtEntryType' import { GdtEntryType } from '@enum/GdtEntryType'
@ObjectType() @ObjectType()
@ -19,10 +21,10 @@ export class GdtEntry {
this.gdt = json.gdt this.gdt = json.gdt
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => Number) @Field(() => Float)
amount: number amount: number
@Field(() => String) @Field(() => String)
@ -40,15 +42,15 @@ export class GdtEntry {
@Field(() => GdtEntryType) @Field(() => GdtEntryType)
gdtEntryType: GdtEntryType gdtEntryType: GdtEntryType
@Field(() => Number) @Field(() => Float)
factor: number factor: number
@Field(() => Number) @Field(() => Float)
amount2: number amount2: number
@Field(() => Number) @Field(() => Float)
factor2: number factor2: number
@Field(() => Number) @Field(() => Float)
gdt: number gdt: number
} }

View File

@ -1,7 +1,10 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* 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 { GdtEntry } from './GdtEntry' import { GdtEntry } from './GdtEntry'
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int, Float } from 'type-graphql'
@ObjectType() @ObjectType()
export class GdtEntryList { export class GdtEntryList {
@ -16,15 +19,15 @@ export class GdtEntryList {
@Field(() => String) @Field(() => String)
state: string state: string
@Field(() => Number) @Field(() => Int)
count: number count: number
@Field(() => [GdtEntry], { nullable: true }) @Field(() => [GdtEntry], { nullable: true })
gdtEntries?: GdtEntry[] gdtEntries: GdtEntry[] | null
@Field(() => Number) @Field(() => Float)
gdtSum: number gdtSum: number
@Field(() => Number) @Field(() => Float)
timeUsed: number timeUsed: number
} }

View File

@ -1,4 +1,5 @@
/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field } from 'type-graphql'

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import { Decay } from './Decay' import { Decay } from './Decay'
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'
@ -41,19 +41,19 @@ export class Transaction {
this.memo = transaction.memo this.memo = transaction.memo
this.creationDate = transaction.creationDate this.creationDate = transaction.creationDate
this.linkedUser = linkedUser this.linkedUser = linkedUser
this.linkedTransactionId = transaction.linkedTransactionId this.linkedTransactionId = transaction.linkedTransactionId || null
this.linkId = transaction.contribution this.linkId = transaction.contribution
? transaction.contribution.contributionLinkId ? transaction.contribution.contributionLinkId
: transaction.transactionLinkId : transaction.transactionLinkId || null
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => User) @Field(() => User)
user: User user: User
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
previous: number | null previous: number | null
@Field(() => TransactionTypeId) @Field(() => TransactionTypeId)
@ -80,10 +80,10 @@ export class Transaction {
@Field(() => User, { nullable: true }) @Field(() => User, { nullable: true })
linkedUser: User | null linkedUser: User | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
linkedTransactionId?: number | null linkedTransactionId: number | null
// Links to the TransactionLink/ContributionLink when transaction was created by a link // Links to the TransactionLink/ContributionLink when transaction was created by a link
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
linkId?: number | null linkId: number | null
} }

View File

@ -21,7 +21,7 @@ export class TransactionLink {
this.link = CONFIG.COMMUNITY_REDEEM_URL.replace(/{code}/g, this.code) this.link = CONFIG.COMMUNITY_REDEEM_URL.replace(/{code}/g, this.code)
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => User) @Field(() => User)
@ -61,8 +61,8 @@ export class TransactionLink {
@ObjectType() @ObjectType()
export class TransactionLinkResult { export class TransactionLinkResult {
@Field(() => Int) @Field(() => Int)
linkCount: number count: number
@Field(() => [TransactionLink]) @Field(() => [TransactionLink])
linkList: TransactionLink[] links: TransactionLink[]
} }

View File

@ -24,12 +24,12 @@ export class UnconfirmedContribution {
firstName: string firstName: string
@Field(() => Int) @Field(() => Int)
id?: number id: number
@Field(() => String) @Field(() => String)
lastName: string lastName: string
@Field(() => Number) @Field(() => Int)
userId: number userId: number
@Field(() => String) @Field(() => String)
@ -44,7 +44,7 @@ export class UnconfirmedContribution {
@Field(() => Decimal) @Field(() => Decimal)
amount: Decimal amount: Decimal
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
moderator: number | null moderator: number | null
@Field(() => [Decimal]) @Field(() => [Decimal])
@ -53,6 +53,6 @@ export class UnconfirmedContribution {
@Field(() => String) @Field(() => String)
state: string state: string
@Field(() => Number) @Field(() => Int)
messageCount: number messageCount: number
} }

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import { KlickTipp } from './KlickTipp' import { KlickTipp } from './KlickTipp'
import { User as dbUser } from '@entity/User' import { User as dbUser } from '@entity/User'
import { UserContact } from './UserContact' import { UserContact } from './UserContact'
@ -28,21 +28,21 @@ export class User {
this.hideAmountGDT = user.hideAmountGDT this.hideAmountGDT = user.hideAmountGDT
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => String) @Field(() => String)
gradidoID: string gradidoID: string
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
alias?: string alias: string | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
emailId: number | null emailId: number | null
// TODO privacy issue here // TODO privacy issue here
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
email: string email: string | null
@Field(() => UserContact) @Field(() => UserContact)
emailContact: UserContact emailContact: UserContact
@ -72,7 +72,7 @@ export class User {
hideAmountGDT: boolean hideAmountGDT: boolean
// This is not the users publisherId, but the one of the users who recommend him // This is not the users publisherId, but the one of the users who recommend him
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
publisherId: number | null publisherId: number | null
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })

View File

@ -17,7 +17,7 @@ export class UserAdmin {
this.isAdmin = user.isAdmin this.isAdmin = user.isAdmin
} }
@Field(() => Number) @Field(() => Int)
userId: number userId: number
@Field(() => String) @Field(() => String)
@ -39,10 +39,10 @@ export class UserAdmin {
hasElopage: boolean hasElopage: boolean
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })
deletedAt?: Date | null deletedAt: Date | null
@Field(() => String, { nullable: true }) @Field(() => String, { nullable: true })
emailConfirmationSend?: string emailConfirmationSend: string | null
@Field(() => Date, { nullable: true }) @Field(() => Date, { nullable: true })
isAdmin: Date | null isAdmin: Date | null

View File

@ -1,4 +1,4 @@
import { ObjectType, Field } from 'type-graphql' import { ObjectType, Field, Int } from 'type-graphql'
import { UserContact as dbUserContact } from '@entity/UserContact' import { UserContact as dbUserContact } from '@entity/UserContact'
@ObjectType() @ObjectType()
@ -18,13 +18,13 @@ export class UserContact {
this.deletedAt = userContact.deletedAt this.deletedAt = userContact.deletedAt
} }
@Field(() => Number) @Field(() => Int)
id: number id: number
@Field(() => String) @Field(() => String)
type: string type: string
@Field(() => Number) @Field(() => Int)
userId: number userId: number
@Field(() => String) @Field(() => String)
@ -33,10 +33,10 @@ export class UserContact {
// @Field(() => BigInt, { nullable: true }) // @Field(() => BigInt, { nullable: true })
// emailVerificationCode: BigInt | null // emailVerificationCode: BigInt | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
emailOptInTypeId: number | null emailOptInTypeId: number | null
@Field(() => Number, { nullable: true }) @Field(() => Int, { nullable: true })
emailResendCount: number | null emailResendCount: number | null
@Field(() => Boolean) @Field(() => Boolean)

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/restrict-template-expressions */
import Decimal from 'decimal.js-light' import Decimal from 'decimal.js-light'
import { Resolver, Query, Ctx, Authorized } from 'type-graphql' import { Resolver, Query, Ctx, Authorized } from 'type-graphql'
import { getCustomRepository } from '@dbTools/typeorm' import { getCustomRepository } from '@dbTools/typeorm'

View File

@ -1,3 +1,6 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/unbound-method */
/* 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 */

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