mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into feat-send-coins-via-gradido-ID
This commit is contained in:
commit
9c84ca185d
46
.github/file-filters.yml
vendored
Normal file
46
.github/file-filters.yml
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# These file filter patterns are used by the action https://github.com/dorny/paths-filter
|
||||||
|
|
||||||
|
# more differentiated filters for admin interface, which might be used later
|
||||||
|
# admin_locales: &admin_locales
|
||||||
|
# - 'admin/src/locales/**'
|
||||||
|
# - 'admin/scripts/sort*'
|
||||||
|
|
||||||
|
# admin_stylelinting: &admin_stylelinting
|
||||||
|
# - 'admin/{components,layouts,pages}/**/*.{scss,vue}'
|
||||||
|
# - 'admin/.stylelintrc.js'
|
||||||
|
|
||||||
|
# admin_linting: &admin_linting
|
||||||
|
# - 'admin/.eslint*'
|
||||||
|
# - 'admin/babel.config.js'
|
||||||
|
# - 'admin/package.json'
|
||||||
|
# - 'admin/**/*.{js,vue}'
|
||||||
|
# - *admin_locales
|
||||||
|
|
||||||
|
# admin_unit_testing: &admin_unit_testing
|
||||||
|
# - 'admin/package.json'
|
||||||
|
# - 'admin/{jest,vue}.config.js'
|
||||||
|
# - 'admin/{public,run,test}/**/*'
|
||||||
|
# - 'admin/src/!(locales)/**/*'
|
||||||
|
|
||||||
|
# admin_docker_building: &admin_docker_building
|
||||||
|
# - 'admin/.dockerignore'
|
||||||
|
# - 'admin/Dockerfile'
|
||||||
|
# - *admin_unit_testing
|
||||||
|
|
||||||
|
admin: &admin
|
||||||
|
- 'admin/**/*'
|
||||||
|
|
||||||
|
dht_node: &dht_node
|
||||||
|
- 'dht-node/**/*'
|
||||||
|
|
||||||
|
docker: &docker
|
||||||
|
- 'docker-compose.*'
|
||||||
|
|
||||||
|
federation: &federation
|
||||||
|
- 'federation/**/*'
|
||||||
|
|
||||||
|
frontend: &frontend
|
||||||
|
- 'frontend/**/*'
|
||||||
|
|
||||||
|
nginx: &nginx
|
||||||
|
- 'nginx/**/*'
|
||||||
58
.github/workflows/e2e-test.yml
vendored
Normal file
58
.github/workflows/e2e-test.yml
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
name: Gradido End-to-End Test CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
end-to-end-tests:
|
||||||
|
name: End-to-End Tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Boot up test system | docker-compose mariadb
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
|
||||||
|
|
||||||
|
- name: Boot up test system | docker-compose database
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
||||||
|
|
||||||
|
- name: Boot up test system | docker-compose backend
|
||||||
|
run: |
|
||||||
|
cd backend
|
||||||
|
cp .env.test_e2e .env
|
||||||
|
cd ..
|
||||||
|
docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend
|
||||||
|
|
||||||
|
- name: Sleep for 10 seconds
|
||||||
|
run: sleep 10s
|
||||||
|
|
||||||
|
- name: Boot up test system | seed backend
|
||||||
|
run: |
|
||||||
|
sudo chown runner:docker -R *
|
||||||
|
cd database
|
||||||
|
yarn && yarn dev_reset
|
||||||
|
cd ../backend
|
||||||
|
yarn && yarn seed
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
- name: Boot up test system | docker-compose frontends
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx
|
||||||
|
|
||||||
|
- name: Boot up test system | docker-compose mailserver
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver
|
||||||
|
|
||||||
|
- name: Sleep for 15 seconds
|
||||||
|
run: sleep 15s
|
||||||
|
|
||||||
|
- name: End-to-end tests | run tests
|
||||||
|
id: e2e-tests
|
||||||
|
run: |
|
||||||
|
cd e2e-tests/
|
||||||
|
yarn
|
||||||
|
yarn run cypress run
|
||||||
|
- name: End-to-end tests | if tests failed, upload screenshots
|
||||||
|
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: cypress-screenshots
|
||||||
|
path: /home/runner/work/gradido/gradido/e2e-tests/cypress/screenshots/
|
||||||
84
.github/workflows/test-admin-interface.yml
vendored
Normal file
84
.github/workflows/test-admin-interface.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Gradido Admin Interface Test CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# only (but most important) job from this workflow required for pull requests
|
||||||
|
# check results serve as run conditions for all other jobs here
|
||||||
|
files-changed:
|
||||||
|
name: Detect File Changes - Admin Interface
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
admin: ${{ steps.changes.outputs.admin }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.3.0
|
||||||
|
|
||||||
|
- name: Check for admin interface file changes
|
||||||
|
uses: dorny/paths-filter@v2.11.1
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
filters: .github/file-filters.yml
|
||||||
|
list-files: shell
|
||||||
|
|
||||||
|
|
||||||
|
build_test:
|
||||||
|
if: needs.files-changed.outputs.admin == 'true'
|
||||||
|
name: Docker Build Test - Admin Interface
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Admin Interface | Build 'test' image
|
||||||
|
run: docker build --target test -t "gradido/admin:test" admin/ --build-arg NODE_ENV="test"
|
||||||
|
|
||||||
|
unit_test:
|
||||||
|
if: needs.files-changed.outputs.admin == 'true'
|
||||||
|
name: Unit Tests - Admin Interface
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Admin Interface | Unit tests
|
||||||
|
run: cd admin && yarn && yarn run test
|
||||||
|
|
||||||
|
lint:
|
||||||
|
if: needs.files-changed.outputs.admin == 'true'
|
||||||
|
name: Lint - Admin Interface
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Admin Interface | Lint
|
||||||
|
run: cd admin && yarn && yarn run lint
|
||||||
|
|
||||||
|
stylelint:
|
||||||
|
if: needs.files-changed.outputs.admin == 'true'
|
||||||
|
name: Stylelint - Admin Interface
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Admin Interface | Stylelint
|
||||||
|
run: cd admin && yarn && yarn run stylelint
|
||||||
|
|
||||||
|
locales:
|
||||||
|
if: needs.files-changed.outputs.admin == 'true'
|
||||||
|
name: Locales - Admin Interface
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Admin Interface | Locales
|
||||||
|
run: cd admin && yarn && yarn run locales
|
||||||
32
.github/workflows/test-nginx.yml
vendored
Normal file
32
.github/workflows/test-nginx.yml
vendored
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
name: Gradido Nginx Test CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
files-changed:
|
||||||
|
name: Detect File Changes - Nginx
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
nginx: ${{ steps.changes.outputs.nginx }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.3.0
|
||||||
|
|
||||||
|
- name: Check for nginx file changes
|
||||||
|
uses: dorny/paths-filter@v2.11.1
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
filters: .github/file-filters.yml
|
||||||
|
list-files: shell
|
||||||
|
|
||||||
|
build_test_nginx:
|
||||||
|
name: Docker Build Test - Nginx
|
||||||
|
if: needs.files-changed.outputs.nginx == 'true'
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: nginx | Build 'test' image
|
||||||
|
run: docker build -t "gradido/nginx:test" nginx/
|
||||||
366
.github/workflows/test.yml
vendored
366
.github/workflows/test.yml
vendored
@ -3,57 +3,6 @@ name: gradido test CI
|
|||||||
on: push
|
on: push
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
##############################################################################
|
|
||||||
# JOB: DOCKER BUILD TEST FRONTEND ############################################
|
|
||||||
##############################################################################
|
|
||||||
build_test_frontend:
|
|
||||||
name: Docker Build Test - Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
#needs: [nothing]
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# FRONTEND ###############################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Frontend | Build `test` image
|
|
||||||
run: |
|
|
||||||
docker build --target test -t "gradido/frontend:test" frontend/
|
|
||||||
docker save "gradido/frontend:test" > /tmp/frontend.tar
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp/frontend.tar
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: DOCKER BUILD TEST ADMIN INTERFACE #####################################
|
|
||||||
##############################################################################
|
|
||||||
build_test_admin:
|
|
||||||
name: Docker Build Test - Admin Interface
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# ADMIN INTERFACE ########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin | Build `test` image
|
|
||||||
run: |
|
|
||||||
docker build --target test -t "gradido/admin:test" admin/ --build-arg NODE_ENV="test"
|
|
||||||
docker save "gradido/admin:test" > /tmp/admin.tar
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp/admin.tar
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
# JOB: DOCKER BUILD TEST BACKEND #############################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -132,139 +81,6 @@ jobs:
|
|||||||
name: docker-mariadb-test
|
name: docker-mariadb-test
|
||||||
path: /tmp/mariadb.tar
|
path: /tmp/mariadb.tar
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: DOCKER BUILD TEST NGINX ###############################################
|
|
||||||
##############################################################################
|
|
||||||
build_test_nginx:
|
|
||||||
name: Docker Build Test - Nginx
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# BUILD NGINX DOCKER IMAGE ###############################################
|
|
||||||
##########################################################################
|
|
||||||
- name: nginx | Build `test` image
|
|
||||||
run: |
|
|
||||||
docker build -t "gradido/nginx:test" nginx/
|
|
||||||
docker save "gradido/nginx:test" > /tmp/nginx.tar
|
|
||||||
- name: Upload Artifact
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-nginx-test
|
|
||||||
path: /tmp/nginx.tar
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: LOCALES FRONTEND ######################################################
|
|
||||||
##############################################################################
|
|
||||||
locales_frontend:
|
|
||||||
name: Locales - Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# LOCALES FRONTEND #######################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Frontend | Locales
|
|
||||||
run: cd frontend && yarn && yarn run locales
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: LINT FRONTEND #########################################################
|
|
||||||
##############################################################################
|
|
||||||
lint_frontend:
|
|
||||||
name: Lint - Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# LINT FRONTEND ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Frontend | Lint
|
|
||||||
run: cd frontend && yarn && yarn run lint
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: STYLELINT FRONTEND ####################################################
|
|
||||||
##############################################################################
|
|
||||||
stylelint_frontend:
|
|
||||||
name: Stylelint - Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# STYLELINT FRONTEND #####################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Frontend | Stylelint
|
|
||||||
run: cd frontend && yarn && yarn run stylelint
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: LINT ADMIN INTERFACE ##################################################
|
|
||||||
##############################################################################
|
|
||||||
lint_admin:
|
|
||||||
name: Lint - Admin Interface
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# LINT ADMIN INTERFACE ###################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin Interface | Lint
|
|
||||||
run: cd admin && yarn && yarn run lint
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: STYLELINT ADMIN INTERFACE #############################################
|
|
||||||
##############################################################################
|
|
||||||
stylelint_admin:
|
|
||||||
name: Stylelint - Admin Interface
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# STYLELINT ADMIN INTERFACE ##############################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin Interface | Stylelint
|
|
||||||
run: cd admin && yarn && yarn run stylelint
|
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: LOCALES ADMIN #########################################################
|
|
||||||
##############################################################################
|
|
||||||
locales_admin:
|
|
||||||
name: Locales - Admin Interface
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# LOCALES FRONTEND #######################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin | Locales
|
|
||||||
run: cd admin && yarn && yarn run locales
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT BACKEND ##########################################################
|
# JOB: LINT BACKEND ##########################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -319,68 +135,6 @@ jobs:
|
|||||||
- name: Database | Lint
|
- name: Database | Lint
|
||||||
run: cd database && yarn && yarn run lint
|
run: cd database && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: UNIT TEST FRONTEND ###################################################
|
|
||||||
##############################################################################
|
|
||||||
unit_test_frontend:
|
|
||||||
name: Unit tests - Frontend
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# UNIT TESTS FRONTEND ####################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Frontend | Unit tests
|
|
||||||
run: |
|
|
||||||
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 ADMIN INTERFACE #############################################
|
|
||||||
##############################################################################
|
|
||||||
unit_test_admin:
|
|
||||||
name: Unit tests - Admin Interface
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# UNIT TESTS ADMIN INTERFACE #############################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin Interface | Unit tests
|
|
||||||
run: |
|
|
||||||
cd admin && yarn && yarn run test
|
|
||||||
cp -r ./coverage ../
|
|
||||||
##########################################################################
|
|
||||||
# COVERAGE CHECK ADMIN INTERFACE #########################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Admin Interface | Coverage check
|
|
||||||
uses: webcraftmedia/coverage-check-action@master
|
|
||||||
with:
|
|
||||||
report_name: Coverage Admin Interface
|
|
||||||
type: lcov
|
|
||||||
result_path: ./admin/coverage/lcov.info
|
|
||||||
min_coverage: 97
|
|
||||||
token: ${{ github.token }}
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST BACKEND ####################################################
|
# JOB: UNIT TEST BACKEND ####################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
@ -415,20 +169,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 #####################################
|
||||||
@ -452,108 +193,3 @@ jobs:
|
|||||||
run: docker-compose -f docker-compose.yml run -T database yarn up
|
run: docker-compose -f docker-compose.yml run -T database yarn up
|
||||||
- name: database | reset
|
- name: database | reset
|
||||||
run: docker-compose -f docker-compose.yml run -T database yarn reset
|
run: docker-compose -f docker-compose.yml run -T database yarn reset
|
||||||
|
|
||||||
##############################################################################
|
|
||||||
# JOB: END-TO-END TESTS #####################################################
|
|
||||||
##############################################################################
|
|
||||||
end-to-end-tests:
|
|
||||||
name: End-to-End Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: [build_test_mariadb, build_test_database_up, build_test_admin, build_test_frontend, build_test_nginx]
|
|
||||||
steps:
|
|
||||||
##########################################################################
|
|
||||||
# CHECKOUT CODE ##########################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
##########################################################################
|
|
||||||
# DOWNLOAD DOCKER IMAGES #################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Mariadb)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-mariadb-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image (Mariadb)
|
|
||||||
run: docker load < /tmp/mariadb.tar
|
|
||||||
- name: Download Docker Image (Database Up)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-database-test_up
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image (Database Up)
|
|
||||||
run: docker load < /tmp/database_up.tar
|
|
||||||
- name: Download Docker Image (Frontend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image (Frontend)
|
|
||||||
run: docker load < /tmp/frontend.tar
|
|
||||||
- name: Download Docker Image (Admin Interface)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image (Admin Interface)
|
|
||||||
run: docker load < /tmp/admin.tar
|
|
||||||
- name: Download Docker Image (Nginx)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-nginx-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image (Nginx)
|
|
||||||
run: docker load < /tmp/nginx.tar
|
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# BOOT UP THE TEST SYSTEM ################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Boot up test system | docker-compose mariadb
|
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach mariadb
|
|
||||||
|
|
||||||
- name: Boot up test system | docker-compose database
|
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
|
||||||
|
|
||||||
- name: Boot up test system | docker-compose backend
|
|
||||||
run: |
|
|
||||||
cd backend
|
|
||||||
cp .env.test_e2e .env
|
|
||||||
cd ..
|
|
||||||
docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps backend
|
|
||||||
|
|
||||||
- name: Sleep for 10 seconds
|
|
||||||
run: sleep 10s
|
|
||||||
|
|
||||||
- name: Boot up test system | seed backend
|
|
||||||
run: |
|
|
||||||
sudo chown runner:docker -R *
|
|
||||||
cd database
|
|
||||||
yarn && yarn dev_reset
|
|
||||||
cd ../backend
|
|
||||||
yarn && yarn seed
|
|
||||||
cd ..
|
|
||||||
|
|
||||||
- name: Boot up test system | docker-compose frontends
|
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps frontend admin nginx
|
|
||||||
|
|
||||||
- name: Boot up test system | docker-compose mailserver
|
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mailserver
|
|
||||||
|
|
||||||
- name: Sleep for 15 seconds
|
|
||||||
run: sleep 15s
|
|
||||||
|
|
||||||
##########################################################################
|
|
||||||
# END-TO-END TESTS #######################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: End-to-end tests | run tests
|
|
||||||
id: e2e-tests
|
|
||||||
run: |
|
|
||||||
cd e2e-tests/
|
|
||||||
yarn
|
|
||||||
yarn run cypress run --spec cypress/e2e/User.Authentication.feature,cypress/e2e/User.Authentication.ResetPassword.feature,cypress/e2e/User.Registration.feature
|
|
||||||
- name: End-to-end tests | if tests failed, upload screenshots
|
|
||||||
if: ${{ failure() && steps.e2e-tests.conclusion == 'failure' }}
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: cypress-screenshots
|
|
||||||
path: /home/runner/work/gradido/gradido/e2e-tests/cypress/screenshots/
|
|
||||||
|
|||||||
53
.github/workflows/test_dht-node.yml
vendored
53
.github/workflows/test_dht-node.yml
vendored
@ -3,17 +3,38 @@ name: Gradido DHT Node Test CI
|
|||||||
on: push
|
on: push
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# only (but most important) job from this workflow required for pull requests
|
||||||
|
# check results serve as run conditions for all other jobs here
|
||||||
|
files-changed:
|
||||||
|
name: Detect File Changes - DHT Node
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
dht_node: ${{ steps.changes.outputs.dht_node }}
|
||||||
|
docker: ${{ steps.changes.outputs.docker }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.3.0
|
||||||
|
|
||||||
|
- name: Check for frontend file changes
|
||||||
|
uses: dorny/paths-filter@v2.11.1
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
filters: .github/file-filters.yml
|
||||||
|
list-files: shell
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: DOCKER BUILD TEST #####################################################
|
# JOB: DOCKER BUILD TEST #####################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
build:
|
build:
|
||||||
name: Docker Build Test - DHT Node
|
name: Docker Build Test - DHT Node
|
||||||
|
if: needs.files-changed.outputs.dht_node == 'true' || needs.files-changed.outputs.docker == 'true'
|
||||||
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Build `test` image
|
- name: Build 'test' image
|
||||||
run: |
|
run: |
|
||||||
docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile .
|
docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile .
|
||||||
docker save "gradido/dht-node:test" > /tmp/dht-node.tar
|
docker save "gradido/dht-node:test" > /tmp/dht-node.tar
|
||||||
@ -29,30 +50,24 @@ jobs:
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
lint:
|
lint:
|
||||||
name: Lint - DHT Node
|
name: Lint - DHT Node
|
||||||
|
if: needs.files-changed.outputs.dht_node == 'true'
|
||||||
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download Docker Image
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-dht-node-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/dht-node.tar
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: docker run --rm gradido/dht-node:test yarn run lint
|
run: cd dht-node && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST #############################################################
|
# JOB: UNIT TEST #############################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
unit_test:
|
unit_test:
|
||||||
name: Unit Tests - DHT Node
|
name: Unit Tests - DHT Node
|
||||||
|
if: needs.files-changed.outputs.dht_node == 'true' || needs.files-changed.outputs.docker == 'true'
|
||||||
|
needs: [files-changed, build]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -83,16 +98,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 }}
|
|
||||||
|
|||||||
50
.github/workflows/test_federation.yml
vendored
50
.github/workflows/test_federation.yml
vendored
@ -3,11 +3,32 @@ name: Gradido Federation Test CI
|
|||||||
on: push
|
on: push
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
# only (but most important) job from this workflow required for pull requests
|
||||||
|
# check results serve as run conditions for all other jobs here
|
||||||
|
files-changed:
|
||||||
|
name: Detect File Changes - Federation
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
docker: ${{ steps.changes.outputs.docker }}
|
||||||
|
federation: ${{ steps.changes.outputs.federation }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.3.0
|
||||||
|
|
||||||
|
- name: Check for frontend file changes
|
||||||
|
uses: dorny/paths-filter@v2.11.1
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
filters: .github/file-filters.yml
|
||||||
|
list-files: shell
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: DOCKER BUILD TEST #####################################################
|
# JOB: DOCKER BUILD TEST #####################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
build:
|
build:
|
||||||
name: Docker Build Test - Federation
|
name: Docker Build Test - Federation
|
||||||
|
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.federation == 'true'
|
||||||
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@ -29,30 +50,24 @@ jobs:
|
|||||||
##############################################################################
|
##############################################################################
|
||||||
lint:
|
lint:
|
||||||
name: Lint - Federation
|
name: Lint - Federation
|
||||||
|
if: needs.files-changed.outputs.federation == 'true'
|
||||||
|
needs: files-changed
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Download Docker Image
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-federation-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/federation.tar
|
|
||||||
|
|
||||||
- name: Lint
|
- name: Lint
|
||||||
run: docker run --rm gradido/federation:test yarn run lint
|
run: cd federation && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST #############################################################
|
# JOB: UNIT TEST #############################################################
|
||||||
##############################################################################
|
##############################################################################
|
||||||
unit_test:
|
unit_test:
|
||||||
name: Unit Tests - Federation
|
name: Unit Tests - Federation
|
||||||
|
if: needs.files-changed.outputs.docker == 'true' || needs.files-changed.outputs.federation == 'true'
|
||||||
|
needs: [files-changed, build]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@ -84,15 +99,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 }}
|
|
||||||
|
|||||||
84
.github/workflows/test_frontend.yml
vendored
Normal file
84
.github/workflows/test_frontend.yml
vendored
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
name: Gradido Frontend Test CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# only (but most important) job from this workflow required for pull requests
|
||||||
|
# check results serve as run conditions for all other jobs here
|
||||||
|
files-changed:
|
||||||
|
name: Detect File Changes - Frontend
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
frontend: ${{ steps.changes.outputs.frontend }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3.3.0
|
||||||
|
|
||||||
|
- name: Check for frontend file changes
|
||||||
|
uses: dorny/paths-filter@v2.11.1
|
||||||
|
id: changes
|
||||||
|
with:
|
||||||
|
token: ${{ github.token }}
|
||||||
|
filters: .github/file-filters.yml
|
||||||
|
list-files: shell
|
||||||
|
|
||||||
|
|
||||||
|
build_test:
|
||||||
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
|
name: Docker Build Test - Frontend
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Frontend | Build 'test' image
|
||||||
|
run: docker build --target test -t "gradido/frontend:test" frontend/ --build-arg NODE_ENV="test"
|
||||||
|
|
||||||
|
unit_test:
|
||||||
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
|
name: Unit Tests - Frontend
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Frontend | Unit tests
|
||||||
|
run: cd frontend && yarn && yarn run test
|
||||||
|
|
||||||
|
lint:
|
||||||
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
|
name: Lint - Frontend
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Frontend | Lint
|
||||||
|
run: cd frontend && yarn && yarn run lint
|
||||||
|
|
||||||
|
stylelint:
|
||||||
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
|
name: Stylelint - Frontend
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Frontend | Stylelint
|
||||||
|
run: cd frontend && yarn && yarn run stylelint
|
||||||
|
|
||||||
|
locales:
|
||||||
|
if: needs.files-changed.outputs.frontend == 'true'
|
||||||
|
name: Locales - Frontend
|
||||||
|
needs: files-changed
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Frontend | Locales
|
||||||
|
run: cd frontend && yarn && yarn run locales
|
||||||
@ -76,7 +76,11 @@ git clone git@github.com:gradido/gradido.git
|
|||||||
git submodule update --recursive --init
|
git submodule update --recursive --init
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2. Run docker-compose
|
### 2. Install modules
|
||||||
|
|
||||||
|
You can go in each under folder (admin, frontend, database, backend, ...) and call ``yarn`` in each folder or you can call ``yarn installAll``.
|
||||||
|
|
||||||
|
### 3. Run docker-compose
|
||||||
|
|
||||||
Run docker-compose to bring up the development environment
|
Run docker-compose to bring up the development environment
|
||||||
|
|
||||||
|
|||||||
@ -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',
|
||||||
|
|||||||
@ -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": {
|
||||||
|
|||||||
@ -1,43 +1,75 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import CreationTransactionList from './CreationTransactionList'
|
import CreationTransactionList from './CreationTransactionList'
|
||||||
import { toastErrorSpy } from '../../test/testSetup'
|
import { toastErrorSpy } from '../../test/testSetup'
|
||||||
|
import VueApollo from 'vue-apollo'
|
||||||
|
import { createMockClient } from 'mock-apollo-client'
|
||||||
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
|
|
||||||
|
const mockClient = createMockClient()
|
||||||
|
const apolloProvider = new VueApollo({
|
||||||
|
defaultClient: mockClient,
|
||||||
|
})
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
localVue.use(VueApollo)
|
||||||
|
|
||||||
const apolloQueryMock = jest.fn().mockResolvedValue({
|
const defaultData = () => {
|
||||||
data: {
|
return {
|
||||||
creationTransactionList: {
|
adminListContributions: {
|
||||||
contributionCount: 2,
|
contributionCount: 2,
|
||||||
contributionList: [
|
contributionList: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
amount: 5.8,
|
firstName: 'Bibi',
|
||||||
createdAt: '2022-09-21T11:09:51.000Z',
|
lastName: 'Bloxberg',
|
||||||
confirmedAt: null,
|
userId: 99,
|
||||||
contributionDate: '2022-08-01T00:00:00.000Z',
|
email: 'bibi@bloxberg.de',
|
||||||
memo: 'für deine Hilfe, Fräulein Rottenmeier',
|
amount: 500,
|
||||||
|
memo: 'Danke für alles',
|
||||||
|
date: new Date(),
|
||||||
|
moderator: 1,
|
||||||
state: 'PENDING',
|
state: 'PENDING',
|
||||||
|
creation: [500, 500, 500],
|
||||||
|
messagesCount: 0,
|
||||||
|
deniedBy: null,
|
||||||
|
deniedAt: null,
|
||||||
|
confirmedBy: null,
|
||||||
|
confirmedAt: null,
|
||||||
|
contributionDate: new Date(),
|
||||||
|
deletedBy: null,
|
||||||
|
deletedAt: null,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
amount: '47',
|
firstName: 'Räuber',
|
||||||
createdAt: '2022-09-21T11:09:28.000Z',
|
lastName: 'Hotzenplotz',
|
||||||
confirmedAt: '2022-09-21T11:09:28.000Z',
|
userId: 100,
|
||||||
contributionDate: '2022-08-01T00:00:00.000Z',
|
email: 'raeuber@hotzenplotz.de',
|
||||||
memo: 'für deine Hilfe, Frau Holle',
|
amount: 1000000,
|
||||||
state: 'CONFIRMED',
|
memo: 'Gut Ergattert',
|
||||||
|
date: new Date(),
|
||||||
|
moderator: 1,
|
||||||
|
state: 'PENDING',
|
||||||
|
creation: [500, 500, 500],
|
||||||
|
messagesCount: 0,
|
||||||
|
deniedBy: null,
|
||||||
|
deniedAt: null,
|
||||||
|
confirmedBy: null,
|
||||||
|
confirmedAt: new Date(),
|
||||||
|
contributionDate: new Date(),
|
||||||
|
deletedBy: null,
|
||||||
|
deletedAt: null,
|
||||||
|
createdAt: new Date(),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
})
|
}
|
||||||
|
|
||||||
const mocks = {
|
const mocks = {
|
||||||
$d: jest.fn((t) => t),
|
$d: jest.fn((t) => t),
|
||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$apollo: {
|
|
||||||
query: apolloQueryMock,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const propsData = {
|
const propsData = {
|
||||||
@ -48,47 +80,44 @@ const propsData = {
|
|||||||
describe('CreationTransactionList', () => {
|
describe('CreationTransactionList', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
|
|
||||||
|
const adminListContributionsMock = jest.fn()
|
||||||
|
mockClient.setRequestHandler(
|
||||||
|
adminListContributions,
|
||||||
|
adminListContributionsMock
|
||||||
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
|
)
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
return mount(CreationTransactionList, { localVue, mocks, propsData })
|
return mount(CreationTransactionList, { localVue, mocks, propsData, apolloProvider })
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
jest.clearAllMocks()
|
||||||
wrapper = Wrapper()
|
wrapper = Wrapper()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('server error', () => {
|
||||||
|
it('toast error', () => {
|
||||||
|
expect(toastErrorSpy).toBeCalledWith('Ouch!')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('sever success', () => {
|
||||||
it('sends query to Apollo when created', () => {
|
it('sends query to Apollo when created', () => {
|
||||||
expect(apolloQueryMock).toBeCalledWith(
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
expect.objectContaining({
|
|
||||||
variables: {
|
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
userId: 1,
|
userId: 1,
|
||||||
},
|
})
|
||||||
}),
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has two values for the transaction', () => {
|
it('has two values for the transaction', () => {
|
||||||
expect(wrapper.find('tbody').findAll('tr').length).toBe(2)
|
expect(wrapper.find('tbody').findAll('tr').length).toBe(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('query transaction with error', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
apolloQueryMock.mockRejectedValue({ message: 'OUCH!' })
|
|
||||||
wrapper = Wrapper()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('calls the API', () => {
|
|
||||||
expect(apolloQueryMock).toBeCalled()
|
|
||||||
})
|
|
||||||
|
|
||||||
it('toast error', () => {
|
|
||||||
expect(toastErrorSpy).toBeCalledWith('OUCH!')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('watch currentPage', () => {
|
describe('watch currentPage', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
jest.clearAllMocks()
|
jest.clearAllMocks()
|
||||||
@ -101,3 +130,4 @@ describe('CreationTransactionList', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|||||||
@ -42,7 +42,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { creationTransactionList } from '../graphql/creationTransactionList'
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
export default {
|
export default {
|
||||||
name: 'CreationTransactionList',
|
name: 'CreationTransactionList',
|
||||||
props: {
|
props: {
|
||||||
@ -92,33 +92,26 @@ export default {
|
|||||||
],
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
apollo: {
|
||||||
getTransactions() {
|
AdminListContributions: {
|
||||||
this.$apollo
|
query() {
|
||||||
.query({
|
return adminListContributions
|
||||||
query: creationTransactionList,
|
},
|
||||||
variables: {
|
variables() {
|
||||||
|
return {
|
||||||
currentPage: this.currentPage,
|
currentPage: this.currentPage,
|
||||||
pageSize: this.perPage,
|
pageSize: this.perPage,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
userId: parseInt(this.userId),
|
userId: parseInt(this.userId),
|
||||||
|
}
|
||||||
},
|
},
|
||||||
})
|
update({ adminListContributions }) {
|
||||||
.then((result) => {
|
this.rows = adminListContributions.contributionCount
|
||||||
this.rows = result.data.creationTransactionList.contributionCount
|
this.items = adminListContributions.contributionList
|
||||||
this.items = result.data.creationTransactionList.contributionList
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.toastError(error.message)
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
|
error({ message }) {
|
||||||
|
this.toastError(message)
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.getTransactions()
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
currentPage() {
|
|
||||||
this.getTransactions()
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,19 @@
|
|||||||
import gql from 'graphql-tag'
|
import gql from 'graphql-tag'
|
||||||
|
|
||||||
export const adminListAllContributions = gql`
|
export const adminListContributions = 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!]
|
||||||
|
$userId: Int
|
||||||
) {
|
) {
|
||||||
adminListAllContributions(
|
adminListContributions(
|
||||||
currentPage: $currentPage
|
currentPage: $currentPage
|
||||||
pageSize: $pageSize
|
pageSize: $pageSize
|
||||||
order: $order
|
order: $order
|
||||||
statusFilter: $statusFilter
|
statusFilter: $statusFilter
|
||||||
|
userId: $userId
|
||||||
) {
|
) {
|
||||||
contributionCount
|
contributionCount
|
||||||
contributionList {
|
contributionList {
|
||||||
@ -1,23 +0,0 @@
|
|||||||
import gql from 'graphql-tag'
|
|
||||||
|
|
||||||
export const creationTransactionList = gql`
|
|
||||||
query ($currentPage: Int = 1, $pageSize: Int = 25, $order: Order = DESC, $userId: Int!) {
|
|
||||||
creationTransactionList(
|
|
||||||
currentPage: $currentPage
|
|
||||||
pageSize: $pageSize
|
|
||||||
order: $order
|
|
||||||
userId: $userId
|
|
||||||
) {
|
|
||||||
contributionCount
|
|
||||||
contributionList {
|
|
||||||
id
|
|
||||||
amount
|
|
||||||
createdAt
|
|
||||||
confirmedAt
|
|
||||||
contributionDate
|
|
||||||
memo
|
|
||||||
state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
@ -39,7 +39,7 @@
|
|||||||
},
|
},
|
||||||
"created": "Created for",
|
"created": "Created for",
|
||||||
"createdAt": "Created at",
|
"createdAt": "Created at",
|
||||||
"creation": "Amount",
|
"creation": "Creation",
|
||||||
"creationList": "Creation list",
|
"creationList": "Creation list",
|
||||||
"creation_form": {
|
"creation_form": {
|
||||||
"creation_for": "Active Basic Income for",
|
"creation_for": "Active Basic Income for",
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { mount } from '@vue/test-utils'
|
|||||||
import CreationConfirm from './CreationConfirm'
|
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 { adminListAllContributions } from '../graphql/adminListAllContributions'
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
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 {
|
||||||
adminListAllContributions: {
|
adminListContributions: {
|
||||||
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 adminListContributionsMock = 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(
|
||||||
adminListAllContributions,
|
adminListContributions,
|
||||||
adminListAllContributionsMock
|
adminListContributionsMock
|
||||||
.mockRejectedValueOnce({ message: 'Ouch!' })
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
.mockResolvedValue({ data: defaultData() }),
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
)
|
)
|
||||||
@ -337,7 +337,7 @@ describe('CreationConfirm', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions with proper filter', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
@ -352,7 +352,7 @@ describe('CreationConfirm', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions with proper filter', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
@ -368,7 +368,7 @@ describe('CreationConfirm', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions with proper filter', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
@ -384,7 +384,7 @@ describe('CreationConfirm', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions with proper filter', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
@ -400,7 +400,7 @@ describe('CreationConfirm', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('refetches contributions with proper filter', () => {
|
it('refetches contributions with proper filter', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
|
|||||||
@ -85,7 +85,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import Overlay from '../components/Overlay'
|
import Overlay from '../components/Overlay'
|
||||||
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
|
import OpenCreationsTable from '../components/Tables/OpenCreationsTable'
|
||||||
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
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'
|
||||||
@ -397,7 +397,7 @@ export default {
|
|||||||
apollo: {
|
apollo: {
|
||||||
ListAllContributions: {
|
ListAllContributions: {
|
||||||
query() {
|
query() {
|
||||||
return adminListAllContributions
|
return adminListContributions
|
||||||
},
|
},
|
||||||
variables() {
|
variables() {
|
||||||
return {
|
return {
|
||||||
@ -407,11 +407,11 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
fetchPolicy: 'no-cache',
|
fetchPolicy: 'no-cache',
|
||||||
update({ adminListAllContributions }) {
|
update({ adminListContributions }) {
|
||||||
this.rows = adminListAllContributions.contributionCount
|
this.rows = adminListContributions.contributionCount
|
||||||
this.items = adminListAllContributions.contributionList
|
this.items = adminListContributions.contributionList
|
||||||
if (this.statusFilter === FILTER_TAB_MAP[0]) {
|
if (this.statusFilter === FILTER_TAB_MAP[0]) {
|
||||||
this.$store.commit('setOpenCreations', adminListAllContributions.contributionCount)
|
this.$store.commit('setOpenCreations', adminListContributions.contributionCount)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import Overview from './Overview'
|
import Overview from './Overview'
|
||||||
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
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 {
|
||||||
adminListAllContributions: {
|
adminListContributions: {
|
||||||
contributionCount: 2,
|
contributionCount: 2,
|
||||||
contributionList: [
|
contributionList: [
|
||||||
{
|
{
|
||||||
@ -84,11 +84,11 @@ const defaultData = () => {
|
|||||||
|
|
||||||
describe('Overview', () => {
|
describe('Overview', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
const adminListAllContributionsMock = jest.fn()
|
const adminListContributionsMock = jest.fn()
|
||||||
|
|
||||||
mockClient.setRequestHandler(
|
mockClient.setRequestHandler(
|
||||||
adminListAllContributions,
|
adminListContributions,
|
||||||
adminListAllContributionsMock
|
adminListContributionsMock
|
||||||
.mockRejectedValueOnce({ message: 'Ouch!' })
|
.mockRejectedValueOnce({ message: 'Ouch!' })
|
||||||
.mockResolvedValue({ data: defaultData() }),
|
.mockResolvedValue({ data: defaultData() }),
|
||||||
)
|
)
|
||||||
@ -109,8 +109,8 @@ describe('Overview', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('calls the adminListAllContributions query', () => {
|
it('calls the adminListContributions query', () => {
|
||||||
expect(adminListAllContributionsMock).toBeCalledWith({
|
expect(adminListContributionsMock).toBeCalledWith({
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
order: 'DESC',
|
order: 'DESC',
|
||||||
pageSize: 25,
|
pageSize: 25,
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import { adminListAllContributions } from '../graphql/adminListAllContributions'
|
import { adminListContributions } from '../graphql/adminListContributions'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'overview',
|
name: 'overview',
|
||||||
@ -43,7 +43,7 @@ export default {
|
|||||||
apollo: {
|
apollo: {
|
||||||
AllContributions: {
|
AllContributions: {
|
||||||
query() {
|
query() {
|
||||||
return adminListAllContributions
|
return adminListContributions
|
||||||
},
|
},
|
||||||
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({ adminListAllContributions }) {
|
update({ adminListContributions }) {
|
||||||
this.$store.commit('setOpenCreations', adminListAllContributions.contributionCount)
|
this.$store.commit('setOpenCreations', adminListContributions.contributionCount)
|
||||||
},
|
},
|
||||||
error({ message }) {
|
error({ message }) {
|
||||||
this.toastError(message)
|
this.toastError(message)
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Server
|
# Server
|
||||||
JWT_EXPIRES_IN=1m
|
JWT_EXPIRES_IN=2m
|
||||||
|
|
||||||
# Email
|
# Email
|
||||||
EMAIL=true
|
EMAIL=true
|
||||||
|
|||||||
@ -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: 81,
|
||||||
|
},
|
||||||
|
},
|
||||||
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/'],
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { RIGHTS } from './RIGHTS'
|
|||||||
|
|
||||||
export const INALIENABLE_RIGHTS = [
|
export const INALIENABLE_RIGHTS = [
|
||||||
RIGHTS.LOGIN,
|
RIGHTS.LOGIN,
|
||||||
RIGHTS.GET_COMMUNITY_INFO,
|
|
||||||
RIGHTS.COMMUNITIES,
|
RIGHTS.COMMUNITIES,
|
||||||
RIGHTS.CREATE_USER,
|
RIGHTS.CREATE_USER,
|
||||||
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
|
RIGHTS.SEND_RESET_PASSWORD_EMAIL,
|
||||||
|
|||||||
@ -2,7 +2,6 @@ export enum RIGHTS {
|
|||||||
LOGIN = 'LOGIN',
|
LOGIN = 'LOGIN',
|
||||||
VERIFY_LOGIN = 'VERIFY_LOGIN',
|
VERIFY_LOGIN = 'VERIFY_LOGIN',
|
||||||
BALANCE = 'BALANCE',
|
BALANCE = 'BALANCE',
|
||||||
GET_COMMUNITY_INFO = 'GET_COMMUNITY_INFO',
|
|
||||||
COMMUNITIES = 'COMMUNITIES',
|
COMMUNITIES = 'COMMUNITIES',
|
||||||
LIST_GDT_ENTRIES = 'LIST_GDT_ENTRIES',
|
LIST_GDT_ENTRIES = 'LIST_GDT_ENTRIES',
|
||||||
EXIST_PID = 'EXIST_PID',
|
EXIST_PID = 'EXIST_PID',
|
||||||
@ -45,10 +44,9 @@ export enum RIGHTS {
|
|||||||
ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION',
|
ADMIN_CREATE_CONTRIBUTION = 'ADMIN_CREATE_CONTRIBUTION',
|
||||||
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',
|
ADMIN_LIST_CONTRIBUTIONS = 'ADMIN_LIST_CONTRIBUTIONS',
|
||||||
CONFIRM_CONTRIBUTION = 'CONFIRM_CONTRIBUTION',
|
CONFIRM_CONTRIBUTION = 'CONFIRM_CONTRIBUTION',
|
||||||
SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL',
|
SEND_ACTIVATION_EMAIL = 'SEND_ACTIVATION_EMAIL',
|
||||||
CREATION_TRANSACTION_LIST = 'CREATION_TRANSACTION_LIST',
|
|
||||||
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
|
LIST_TRANSACTION_LINKS_ADMIN = 'LIST_TRANSACTION_LINKS_ADMIN',
|
||||||
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
CREATE_CONTRIBUTION_LINK = 'CREATE_CONTRIBUTION_LINK',
|
||||||
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
DELETE_CONTRIBUTION_LINK = 'DELETE_CONTRIBUTION_LINK',
|
||||||
|
|||||||
@ -10,7 +10,7 @@ Decimal.set({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0060-update_communities_table',
|
DB_VERSION: '0062-event_contribution_confirm',
|
||||||
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||||
LOG4JS_CONFIG: 'log4js-config.json',
|
LOG4JS_CONFIG: 'log4js-config.json',
|
||||||
// default log level on production should be info
|
// default log level on production should be info
|
||||||
|
|||||||
6
backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts
Normal file
6
backend/src/event/EVENT_ACTIVATE_ACCOUNT.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ACTIVATE_ACCOUNT = async (user: DbUser): Promise<DbEvent> =>
|
||||||
|
Event(EventType.ACTIVATE_ACCOUNT, user, user).save()
|
||||||
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_CONFIRM.ts
Normal file
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_CONFIRM.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_CONTRIBUTION_CONFIRM = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.ADMIN_CONTRIBUTION_CONFIRM,
|
||||||
|
user,
|
||||||
|
moderator,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
contribution,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_CREATE.ts
Normal file
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_CREATE.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_CONTRIBUTION_CREATE = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.ADMIN_CONTRIBUTION_CREATE,
|
||||||
|
user,
|
||||||
|
moderator,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
contribution,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_DELETE.ts
Normal file
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_DELETE.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_CONTRIBUTION_DELETE = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.ADMIN_CONTRIBUTION_DELETE,
|
||||||
|
user,
|
||||||
|
moderator,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
contribution,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_DENY.ts
Normal file
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_DENY.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_CONTRIBUTION_DENY = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.ADMIN_CONTRIBUTION_DENY,
|
||||||
|
user,
|
||||||
|
moderator,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
contribution,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_UPDATE.ts
Normal file
22
backend/src/event/EVENT_ADMIN_CONTRIBUTION_UPDATE.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.ADMIN_CONTRIBUTION_UPDATE,
|
||||||
|
user,
|
||||||
|
moderator,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
contribution,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
8
backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts
Normal file
8
backend/src/event/EVENT_ADMIN_SEND_CONFIRMATION_EMAIL.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (
|
||||||
|
user: DbUser,
|
||||||
|
moderator: DbUser,
|
||||||
|
): Promise<DbEvent> => Event(EventType.ADMIN_SEND_CONFIRMATION_EMAIL, user, moderator).save()
|
||||||
12
backend/src/event/EVENT_CONTRIBUTION_CREATE.ts
Normal file
12
backend/src/event/EVENT_CONTRIBUTION_CREATE.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_CONTRIBUTION_CREATE = async (
|
||||||
|
user: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(EventType.CONTRIBUTION_CREATE, user, user, null, null, contribution, null, amount).save()
|
||||||
12
backend/src/event/EVENT_CONTRIBUTION_DELETE.ts
Normal file
12
backend/src/event/EVENT_CONTRIBUTION_DELETE.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_CONTRIBUTION_DELETE = async (
|
||||||
|
user: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(EventType.CONTRIBUTION_DELETE, user, user, null, null, contribution, null, amount).save()
|
||||||
12
backend/src/event/EVENT_CONTRIBUTION_UPDATE.ts
Normal file
12
backend/src/event/EVENT_CONTRIBUTION_UPDATE.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_CONTRIBUTION_UPDATE = async (
|
||||||
|
user: DbUser,
|
||||||
|
contribution: DbContribution,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(EventType.CONTRIBUTION_UPDATE, user, user, null, null, contribution, null, amount).save()
|
||||||
6
backend/src/event/EVENT_LOGIN.ts
Normal file
6
backend/src/event/EVENT_LOGIN.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_LOGIN = async (user: DbUser): Promise<DbEvent> =>
|
||||||
|
Event(EventType.LOGIN, user, user).save()
|
||||||
6
backend/src/event/EVENT_REGISTER.ts
Normal file
6
backend/src/event/EVENT_REGISTER.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_REGISTER = async (user: DbUser): Promise<DbEvent> =>
|
||||||
|
Event(EventType.REGISTER, user, user).save()
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async (user: DbUser): Promise<DbEvent> =>
|
||||||
|
Event(EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, user, { id: 0 } as DbUser).save()
|
||||||
6
backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts
Normal file
6
backend/src/event/EVENT_SEND_CONFIRMATION_EMAIL.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_SEND_CONFIRMATION_EMAIL = async (user: DbUser): Promise<DbEvent> =>
|
||||||
|
Event(EventType.SEND_CONFIRMATION_EMAIL, user, user).save()
|
||||||
22
backend/src/event/EVENT_TRANSACTION_RECEIVE.ts
Normal file
22
backend/src/event/EVENT_TRANSACTION_RECEIVE.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_TRANSACTION_RECEIVE = async (
|
||||||
|
user: DbUser,
|
||||||
|
involvedUser: DbUser,
|
||||||
|
transaction: DbTransaction,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.TRANSACTION_RECEIVE,
|
||||||
|
user,
|
||||||
|
involvedUser,
|
||||||
|
involvedUser,
|
||||||
|
transaction,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
22
backend/src/event/EVENT_TRANSACTION_SEND.ts
Normal file
22
backend/src/event/EVENT_TRANSACTION_SEND.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { Event, EventType } from './Event'
|
||||||
|
|
||||||
|
export const EVENT_TRANSACTION_SEND = async (
|
||||||
|
user: DbUser,
|
||||||
|
involvedUser: DbUser,
|
||||||
|
transaction: DbTransaction,
|
||||||
|
amount: Decimal,
|
||||||
|
): Promise<DbEvent> =>
|
||||||
|
Event(
|
||||||
|
EventType.TRANSACTION_SEND,
|
||||||
|
user,
|
||||||
|
user,
|
||||||
|
involvedUser,
|
||||||
|
transaction,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
amount,
|
||||||
|
).save()
|
||||||
@ -1,212 +1,48 @@
|
|||||||
import { EventProtocol as DbEvent } from '@entity/EventProtocol'
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
|
import { User as DbUser } from '@entity/User'
|
||||||
|
import { ContributionMessage as DbContributionMessage } from '@entity/ContributionMessage'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { EventProtocolType } from './EventProtocolType'
|
import { EventType } from './Event'
|
||||||
|
|
||||||
export const Event = (
|
export const Event = (
|
||||||
type: EventProtocolType,
|
type: EventType,
|
||||||
userId: number,
|
affectedUser: DbUser,
|
||||||
xUserId: number | null = null,
|
actingUser: DbUser,
|
||||||
xCommunityId: number | null = null,
|
involvedUser: DbUser | null = null,
|
||||||
transactionId: number | null = null,
|
involvedTransaction: DbTransaction | null = null,
|
||||||
contributionId: number | null = null,
|
involvedContribution: DbContribution | null = null,
|
||||||
|
involvedContributionMessage: DbContributionMessage | null = null,
|
||||||
amount: Decimal | null = null,
|
amount: Decimal | null = null,
|
||||||
messageId: number | null = null,
|
|
||||||
): DbEvent => {
|
): DbEvent => {
|
||||||
const event = new DbEvent()
|
const event = new DbEvent()
|
||||||
event.type = type
|
event.type = type
|
||||||
event.userId = userId
|
event.affectedUser = affectedUser
|
||||||
event.xUserId = xUserId
|
event.actingUser = actingUser
|
||||||
event.xCommunityId = xCommunityId
|
event.involvedUser = involvedUser
|
||||||
event.transactionId = transactionId
|
event.involvedTransaction = involvedTransaction
|
||||||
event.contributionId = contributionId
|
event.involvedContribution = involvedContribution
|
||||||
|
event.involvedContributionMessage = involvedContributionMessage
|
||||||
event.amount = amount
|
event.amount = amount
|
||||||
event.messageId = messageId
|
|
||||||
return event
|
return event
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EVENT_CONTRIBUTION_CREATE = async (
|
export { EventType } from './EventType'
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.CONTRIBUTION_CREATE,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_CONTRIBUTION_DELETE = async (
|
export { EVENT_ACTIVATE_ACCOUNT } from './EVENT_ACTIVATE_ACCOUNT'
|
||||||
userId: number,
|
export { EVENT_ADMIN_CONTRIBUTION_CONFIRM } from './EVENT_ADMIN_CONTRIBUTION_CONFIRM'
|
||||||
contributionId: number,
|
export { EVENT_ADMIN_CONTRIBUTION_CREATE } from './EVENT_ADMIN_CONTRIBUTION_CREATE'
|
||||||
amount: Decimal,
|
export { EVENT_ADMIN_CONTRIBUTION_DELETE } from './EVENT_ADMIN_CONTRIBUTION_DELETE'
|
||||||
): Promise<DbEvent> =>
|
export { EVENT_ADMIN_CONTRIBUTION_DENY } from './EVENT_ADMIN_CONTRIBUTION_DENY'
|
||||||
Event(
|
export { EVENT_ADMIN_CONTRIBUTION_UPDATE } from './EVENT_ADMIN_CONTRIBUTION_UPDATE'
|
||||||
EventProtocolType.CONTRIBUTION_DELETE,
|
export { EVENT_ADMIN_SEND_CONFIRMATION_EMAIL } from './EVENT_ADMIN_SEND_CONFIRMATION_EMAIL'
|
||||||
userId,
|
export { EVENT_CONTRIBUTION_CREATE } from './EVENT_CONTRIBUTION_CREATE'
|
||||||
null,
|
export { EVENT_CONTRIBUTION_DELETE } from './EVENT_CONTRIBUTION_DELETE'
|
||||||
null,
|
export { EVENT_CONTRIBUTION_UPDATE } from './EVENT_CONTRIBUTION_UPDATE'
|
||||||
null,
|
export { EVENT_LOGIN } from './EVENT_LOGIN'
|
||||||
contributionId,
|
export { EVENT_REGISTER } from './EVENT_REGISTER'
|
||||||
amount,
|
export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL'
|
||||||
).save()
|
export { EVENT_SEND_CONFIRMATION_EMAIL } from './EVENT_SEND_CONFIRMATION_EMAIL'
|
||||||
|
export { EVENT_TRANSACTION_SEND } from './EVENT_TRANSACTION_SEND'
|
||||||
export const EVENT_CONTRIBUTION_UPDATE = async (
|
export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE'
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.CONTRIBUTION_UPDATE,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_ADMIN_CONTRIBUTION_CREATE = async (
|
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.ADMIN_CONTRIBUTION_CREATE,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async (
|
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.ADMIN_CONTRIBUTION_UPDATE,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_ADMIN_CONTRIBUTION_DELETE = async (
|
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.ADMIN_CONTRIBUTION_DELETE,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_CONTRIBUTION_CONFIRM = async (
|
|
||||||
userId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.CONTRIBUTION_CONFIRM,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_ADMIN_CONTRIBUTION_DENY = async (
|
|
||||||
userId: number,
|
|
||||||
xUserId: number,
|
|
||||||
contributionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.ADMIN_CONTRIBUTION_DENY,
|
|
||||||
userId,
|
|
||||||
xUserId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
contributionId,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_TRANSACTION_SEND = async (
|
|
||||||
userId: number,
|
|
||||||
xUserId: number,
|
|
||||||
transactionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.TRANSACTION_SEND,
|
|
||||||
userId,
|
|
||||||
xUserId,
|
|
||||||
null,
|
|
||||||
transactionId,
|
|
||||||
null,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_TRANSACTION_RECEIVE = async (
|
|
||||||
userId: number,
|
|
||||||
xUserId: number,
|
|
||||||
transactionId: number,
|
|
||||||
amount: Decimal,
|
|
||||||
): Promise<DbEvent> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.TRANSACTION_RECEIVE,
|
|
||||||
userId,
|
|
||||||
xUserId,
|
|
||||||
null,
|
|
||||||
transactionId,
|
|
||||||
null,
|
|
||||||
amount,
|
|
||||||
).save()
|
|
||||||
|
|
||||||
export const EVENT_LOGIN = async (userId: number): Promise<DbEvent> =>
|
|
||||||
Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save()
|
|
||||||
|
|
||||||
export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async (
|
|
||||||
userId: number,
|
|
||||||
): Promise<DbEvent> => Event(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save()
|
|
||||||
|
|
||||||
export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise<DbEvent> =>
|
|
||||||
Event(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save()
|
|
||||||
|
|
||||||
export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise<DbEvent> =>
|
|
||||||
Event(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save()
|
|
||||||
|
|
||||||
/* export const EVENT_REDEEM_REGISTER = async (
|
|
||||||
userId: number,
|
|
||||||
transactionId: number | null = null,
|
|
||||||
contributionId: number | null = null,
|
|
||||||
): Promise<Event> =>
|
|
||||||
Event(
|
|
||||||
EventProtocolType.REDEEM_REGISTER,
|
|
||||||
userId,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
transactionId,
|
|
||||||
contributionId,
|
|
||||||
).save()
|
|
||||||
*/
|
|
||||||
|
|
||||||
export const EVENT_REGISTER = async (userId: number): Promise<DbEvent> =>
|
|
||||||
Event(EventProtocolType.REGISTER, userId).save()
|
|
||||||
|
|
||||||
export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise<DbEvent> =>
|
|
||||||
Event(EventProtocolType.ACTIVATE_ACCOUNT, userId).save()
|
|
||||||
|
|||||||
@ -1,42 +1,48 @@
|
|||||||
export enum EventProtocolType {
|
export { EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL } from './EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL'
|
||||||
// VISIT_GRADIDO = 'VISIT_GRADIDO',
|
export { EVENT_SEND_CONFIRMATION_EMAIL } from './EVENT_SEND_CONFIRMATION_EMAIL'
|
||||||
REGISTER = 'REGISTER',
|
export { EVENT_TRANSACTION_SEND } from './EVENT_TRANSACTION_SEND'
|
||||||
REDEEM_REGISTER = 'REDEEM_REGISTER',
|
export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE'
|
||||||
// VERIFY_REDEEM = 'VERIFY_REDEEM',
|
|
||||||
// INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT',
|
export enum EventType {
|
||||||
SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL',
|
|
||||||
ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL',
|
|
||||||
SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL',
|
|
||||||
// CONFIRM_EMAIL = 'CONFIRM_EMAIL',
|
|
||||||
// REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP',
|
|
||||||
LOGIN = 'LOGIN',
|
|
||||||
// LOGOUT = 'LOGOUT',
|
|
||||||
// REDEEM_LOGIN = 'REDEEM_LOGIN',
|
|
||||||
ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT',
|
ACTIVATE_ACCOUNT = 'ACTIVATE_ACCOUNT',
|
||||||
// SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL',
|
// TODO CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM',
|
||||||
// PASSWORD_CHANGE = 'PASSWORD_CHANGE',
|
ADMIN_CONTRIBUTION_CONFIRM = 'ADMIN_CONTRIBUTION_CONFIRM',
|
||||||
// SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL',
|
|
||||||
// SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL',
|
|
||||||
TRANSACTION_SEND = 'TRANSACTION_SEND',
|
|
||||||
// TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM',
|
|
||||||
// TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM',
|
|
||||||
// TRANSACTION_CREATION = 'TRANSACTION_CREATION',
|
|
||||||
TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE',
|
|
||||||
// TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM',
|
|
||||||
// SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL',
|
|
||||||
// SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL',
|
|
||||||
// SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL',
|
|
||||||
CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE',
|
|
||||||
CONTRIBUTION_CONFIRM = 'CONTRIBUTION_CONFIRM',
|
|
||||||
// CONTRIBUTION_DENY = 'CONTRIBUTION_DENY',
|
|
||||||
// CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE',
|
|
||||||
// CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM',
|
|
||||||
CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE',
|
|
||||||
CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE',
|
|
||||||
ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE',
|
ADMIN_CONTRIBUTION_CREATE = 'ADMIN_CONTRIBUTION_CREATE',
|
||||||
ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE',
|
ADMIN_CONTRIBUTION_DELETE = 'ADMIN_CONTRIBUTION_DELETE',
|
||||||
ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY',
|
ADMIN_CONTRIBUTION_DENY = 'ADMIN_CONTRIBUTION_DENY',
|
||||||
ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE',
|
ADMIN_CONTRIBUTION_UPDATE = 'ADMIN_CONTRIBUTION_UPDATE',
|
||||||
|
ADMIN_SEND_CONFIRMATION_EMAIL = 'ADMIN_SEND_CONFIRMATION_EMAIL',
|
||||||
|
CONTRIBUTION_CREATE = 'CONTRIBUTION_CREATE',
|
||||||
|
CONTRIBUTION_DELETE = 'CONTRIBUTION_DELETE',
|
||||||
|
CONTRIBUTION_UPDATE = 'CONTRIBUTION_UPDATE',
|
||||||
|
LOGIN = 'LOGIN',
|
||||||
|
REGISTER = 'REGISTER',
|
||||||
|
REDEEM_REGISTER = 'REDEEM_REGISTER',
|
||||||
|
SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = 'SEND_ACCOUNT_MULTIREGISTRATION_EMAIL',
|
||||||
|
SEND_CONFIRMATION_EMAIL = 'SEND_CONFIRMATION_EMAIL',
|
||||||
|
TRANSACTION_SEND = 'TRANSACTION_SEND',
|
||||||
|
TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE',
|
||||||
|
// VISIT_GRADIDO = 'VISIT_GRADIDO',
|
||||||
|
// VERIFY_REDEEM = 'VERIFY_REDEEM',
|
||||||
|
// INACTIVE_ACCOUNT = 'INACTIVE_ACCOUNT',
|
||||||
|
// CONFIRM_EMAIL = 'CONFIRM_EMAIL',
|
||||||
|
// REGISTER_EMAIL_KLICKTIPP = 'REGISTER_EMAIL_KLICKTIPP',
|
||||||
|
// LOGOUT = 'LOGOUT',
|
||||||
|
// REDEEM_LOGIN = 'REDEEM_LOGIN',
|
||||||
|
// SEND_FORGOT_PASSWORD_EMAIL = 'SEND_FORGOT_PASSWORD_EMAIL',
|
||||||
|
// PASSWORD_CHANGE = 'PASSWORD_CHANGE',
|
||||||
|
// SEND_TRANSACTION_SEND_EMAIL = 'SEND_TRANSACTION_SEND_EMAIL',
|
||||||
|
// SEND_TRANSACTION_RECEIVE_EMAIL = 'SEND_TRANSACTION_RECEIVE_EMAIL',
|
||||||
|
// TRANSACTION_SEND_REDEEM = 'TRANSACTION_SEND_REDEEM',
|
||||||
|
// TRANSACTION_REPEATE_REDEEM = 'TRANSACTION_REPEATE_REDEEM',
|
||||||
|
// TRANSACTION_CREATION = 'TRANSACTION_CREATION',
|
||||||
|
// TRANSACTION_RECEIVE_REDEEM = 'TRANSACTION_RECEIVE_REDEEM',
|
||||||
|
// SEND_TRANSACTION_LINK_REDEEM_EMAIL = 'SEND_TRANSACTION_LINK_REDEEM_EMAIL',
|
||||||
|
// SEND_ADDED_CONTRIBUTION_EMAIL = 'SEND_ADDED_CONTRIBUTION_EMAIL',
|
||||||
|
// SEND_CONTRIBUTION_CONFIRM_EMAIL = 'SEND_CONTRIBUTION_CONFIRM_EMAIL',
|
||||||
|
// CONTRIBUTION_DENY = 'CONTRIBUTION_DENY',
|
||||||
|
// CONTRIBUTION_LINK_DEFINE = 'CONTRIBUTION_LINK_DEFINE',
|
||||||
|
// CONTRIBUTION_LINK_ACTIVATE_REDEEM = 'CONTRIBUTION_LINK_ACTIVATE_REDEEM',
|
||||||
// USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE',
|
// USER_CREATE_CONTRIBUTION_MESSAGE = 'USER_CREATE_CONTRIBUTION_MESSAGE',
|
||||||
// ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
|
// ADMIN_CREATE_CONTRIBUTION_MESSAGE = 'ADMIN_CREATE_CONTRIBUTION_MESSAGE',
|
||||||
// DELETE_USER = 'DELETE_USER',
|
// DELETE_USER = 'DELETE_USER',
|
||||||
@ -21,9 +21,13 @@ export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string |
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const variables = {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest(query)
|
const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest(
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
)
|
||||||
logger.debug(`Response-Data:`, data, errors, extensions, headers, status)
|
logger.debug(`Response-Data:`, data, errors, extensions, headers, status)
|
||||||
if (data) {
|
if (data) {
|
||||||
logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey)
|
logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey)
|
||||||
|
|||||||
@ -21,9 +21,13 @@ export async function requestGetPublicKey(dbCom: DbCommunity): Promise<string |
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
const variables = {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest(query)
|
const { data, errors, extensions, headers, status } = await graphQLClient.rawRequest(
|
||||||
|
query,
|
||||||
|
variables,
|
||||||
|
)
|
||||||
logger.debug(`Response-Data:`, data, errors, extensions, headers, status)
|
logger.debug(`Response-Data:`, data, errors, extensions, headers, status)
|
||||||
if (data) {
|
if (data) {
|
||||||
logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey)
|
logger.debug(`Response-PublicKey:`, data.getPublicKey.publicKey)
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import { GraphQLClient } from 'graphql-request'
|
import { GraphQLClient } from 'graphql-request'
|
||||||
import { PatchedRequestInit } from 'graphql-request/dist/types'
|
import { PatchedRequestInit } from 'graphql-request/dist/types'
|
||||||
|
|
||||||
|
type ClientInstance = {
|
||||||
|
url: string
|
||||||
|
// eslint-disable-next-line no-use-before-define
|
||||||
|
client: GraphQLGetClient
|
||||||
|
}
|
||||||
|
|
||||||
export class GraphQLGetClient extends GraphQLClient {
|
export class GraphQLGetClient extends GraphQLClient {
|
||||||
private static instance: GraphQLGetClient
|
private static instanceArray: ClientInstance[] = []
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Singleton's constructor should always be private to prevent direct
|
* The Singleton's constructor should always be private to prevent direct
|
||||||
@ -20,16 +26,18 @@ export class GraphQLGetClient extends GraphQLClient {
|
|||||||
* just one instance of each subclass around.
|
* just one instance of each subclass around.
|
||||||
*/
|
*/
|
||||||
public static getInstance(url: string): GraphQLGetClient {
|
public static getInstance(url: string): GraphQLGetClient {
|
||||||
if (!GraphQLGetClient.instance) {
|
const instance = GraphQLGetClient.instanceArray.find((instance) => instance.url === url)
|
||||||
GraphQLGetClient.instance = new GraphQLGetClient(url, {
|
if (instance) {
|
||||||
|
return instance.client
|
||||||
|
}
|
||||||
|
const client = new GraphQLGetClient(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
jsonSerializer: {
|
jsonSerializer: {
|
||||||
parse: JSON.parse,
|
parse: JSON.parse,
|
||||||
stringify: JSON.stringify,
|
stringify: JSON.stringify,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
GraphQLGetClient.instanceArray.push({ url, client } as ClientInstance)
|
||||||
|
return client
|
||||||
return GraphQLGetClient.instance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,17 +42,18 @@ export async function validateCommunities(): Promise<void> {
|
|||||||
pubKey,
|
pubKey,
|
||||||
`${dbCom.endPoint}/${dbCom.apiVersion}`,
|
`${dbCom.endPoint}/${dbCom.apiVersion}`,
|
||||||
)
|
)
|
||||||
if (pubKey && pubKey === dbCom.publicKey.toString('hex')) {
|
if (pubKey && pubKey === dbCom.publicKey.toString()) {
|
||||||
logger.info(`Federation: matching publicKey: ${pubKey}`)
|
logger.info(`Federation: matching publicKey: ${pubKey}`)
|
||||||
await 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)}`)
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
`Federation: received not matching publicKey -> received: ${
|
||||||
|
pubKey || 'null'
|
||||||
|
}, expected: ${dbCom.publicKey.toString()} `,
|
||||||
|
)
|
||||||
|
// DbCommunity.delete({ id: dbCom.id })
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
else {
|
|
||||||
logger.warn(`Federation: received unknown publicKey -> delete dbCom with id=${dbCom.id} `)
|
|
||||||
DbCommunity.delete({ id: dbCom.id })
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (!isLogError(err)) {
|
if (!isLogError(err)) {
|
||||||
logger.error(`Error:`, err)
|
logger.error(`Error:`, err)
|
||||||
|
|||||||
@ -1,33 +1,47 @@
|
|||||||
/* 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/explicit-module-boundary-types */
|
|
||||||
import { ObjectType, Field, Int } from 'type-graphql'
|
import { ObjectType, Field, Int } from 'type-graphql'
|
||||||
|
import { Community as DbCommunity } from '@entity/Community'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
export class Community {
|
export class Community {
|
||||||
constructor(json?: any) {
|
constructor(dbCom: DbCommunity) {
|
||||||
if (json) {
|
this.id = dbCom.id
|
||||||
this.id = Number(json.id)
|
this.foreign = dbCom.foreign
|
||||||
this.name = json.name
|
this.publicKey = dbCom.publicKey.toString()
|
||||||
this.url = json.url
|
this.url =
|
||||||
this.description = json.description
|
(dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') +
|
||||||
this.registerUrl = json.registerUrl
|
'api/' +
|
||||||
}
|
dbCom.apiVersion
|
||||||
|
this.lastAnnouncedAt = dbCom.lastAnnouncedAt
|
||||||
|
this.verifiedAt = dbCom.verifiedAt
|
||||||
|
this.lastErrorAt = dbCom.lastErrorAt
|
||||||
|
this.createdAt = dbCom.createdAt
|
||||||
|
this.updatedAt = dbCom.updatedAt
|
||||||
}
|
}
|
||||||
|
|
||||||
@Field(() => Int)
|
@Field(() => Int)
|
||||||
id: number
|
id: number
|
||||||
|
|
||||||
|
@Field(() => Boolean)
|
||||||
|
foreign: boolean
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
name: string
|
publicKey: string
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => String)
|
||||||
url: string
|
url: string
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => Date, { nullable: true })
|
||||||
description: string
|
lastAnnouncedAt: Date | null
|
||||||
|
|
||||||
@Field(() => String)
|
@Field(() => Date, { nullable: true })
|
||||||
registerUrl: string
|
verifiedAt: Date | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
lastErrorAt: Date | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
createdAt: Date | null
|
||||||
|
|
||||||
|
@Field(() => Date, { nullable: true })
|
||||||
|
updatedAt: Date | null
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,24 +1,25 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
/* eslint-disable @typescript-eslint/no-unsafe-call */
|
||||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||||
/* eslint-disable @typescript-eslint/unbound-method */
|
/* 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 */
|
||||||
|
|
||||||
import { createTestClient } from 'apollo-server-testing'
|
import { getCommunities } from '@/seeds/graphql/queries'
|
||||||
import createServer from '@/server/createServer'
|
import { Community as DbCommunity } from '@entity/Community'
|
||||||
import CONFIG from '@/config'
|
import { testEnvironment } from '@test/helpers'
|
||||||
|
|
||||||
jest.mock('@/config')
|
|
||||||
|
|
||||||
let query: any
|
let query: any
|
||||||
|
|
||||||
// to do: We need a setup for the tests that closes the connection
|
// to do: We need a setup for the tests that closes the connection
|
||||||
let con: any
|
let con: any
|
||||||
|
let testEnv: any
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const server = await createServer({})
|
testEnv = await testEnvironment()
|
||||||
con = server.con
|
query = testEnv.query
|
||||||
query = createTestClient(server.apollo).query
|
con = testEnv.con
|
||||||
|
await DbCommunity.clear()
|
||||||
})
|
})
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -26,74 +27,90 @@ afterAll(async () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('CommunityResolver', () => {
|
describe('CommunityResolver', () => {
|
||||||
const getCommunityInfoQuery = `
|
describe('getCommunities', () => {
|
||||||
query {
|
let homeCom1: DbCommunity
|
||||||
getCommunityInfo {
|
let homeCom2: DbCommunity
|
||||||
name
|
let homeCom3: DbCommunity
|
||||||
description
|
let foreignCom1: DbCommunity
|
||||||
url
|
let foreignCom2: DbCommunity
|
||||||
registerUrl
|
let foreignCom3: DbCommunity
|
||||||
}
|
describe('with empty list', () => {
|
||||||
}
|
it('returns no community entry', async () => {
|
||||||
`
|
// const result: Community[] = await query({ query: getCommunities })
|
||||||
|
// expect(result.length).toEqual(0)
|
||||||
const communities = `
|
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||||
query {
|
|
||||||
communities {
|
|
||||||
id
|
|
||||||
name
|
|
||||||
url
|
|
||||||
description
|
|
||||||
registerUrl
|
|
||||||
}
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
describe('getCommunityInfo', () => {
|
|
||||||
it('returns the default values', async () => {
|
|
||||||
await expect(query({ query: getCommunityInfoQuery })).resolves.toMatchObject({
|
|
||||||
data: {
|
data: {
|
||||||
getCommunityInfo: {
|
getCommunities: [],
|
||||||
name: 'Gradido Entwicklung',
|
|
||||||
description: 'Die lokale Entwicklungsumgebung von Gradido.',
|
|
||||||
url: 'http://localhost/',
|
|
||||||
registerUrl: 'http://localhost/register',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('communities', () => {
|
describe('only home-communities entries', () => {
|
||||||
describe('PRODUCTION = false', () => {
|
beforeEach(async () => {
|
||||||
beforeEach(() => {
|
jest.clearAllMocks()
|
||||||
CONFIG.PRODUCTION = false
|
|
||||||
|
homeCom1 = DbCommunity.create()
|
||||||
|
homeCom1.foreign = false
|
||||||
|
homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
|
homeCom1.apiVersion = '1_0'
|
||||||
|
homeCom1.endPoint = 'http://localhost'
|
||||||
|
homeCom1.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(homeCom1)
|
||||||
|
|
||||||
|
homeCom2 = DbCommunity.create()
|
||||||
|
homeCom2.foreign = false
|
||||||
|
homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
|
homeCom2.apiVersion = '1_1'
|
||||||
|
homeCom2.endPoint = 'http://localhost'
|
||||||
|
homeCom2.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(homeCom2)
|
||||||
|
|
||||||
|
homeCom3 = DbCommunity.create()
|
||||||
|
homeCom3.foreign = false
|
||||||
|
homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity')
|
||||||
|
homeCom3.apiVersion = '2_0'
|
||||||
|
homeCom3.endPoint = 'http://localhost'
|
||||||
|
homeCom3.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(homeCom3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns three communities', async () => {
|
it('returns three home-community entries', async () => {
|
||||||
await expect(query({ query: communities })).resolves.toMatchObject({
|
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||||
data: {
|
data: {
|
||||||
communities: [
|
getCommunities: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Gradido Entwicklung',
|
foreign: homeCom1.foreign,
|
||||||
description: 'Die lokale Entwicklungsumgebung von Gradido.',
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: 'http://localhost/',
|
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||||
registerUrl: 'http://localhost/register-community',
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom1.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
name: 'Gradido Staging',
|
foreign: homeCom2.foreign,
|
||||||
description: 'Der Testserver der Gradido-Akademie.',
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: 'https://stage1.gradido.net/',
|
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||||
registerUrl: 'https://stage1.gradido.net/register-community',
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom2.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Gradido-Akademie',
|
foreign: homeCom3.foreign,
|
||||||
description: 'Freies Institut für Wirtschaftsbionik.',
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: 'https://gradido.net',
|
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||||
registerUrl: 'https://gdd1.gradido.com/register-community',
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom3.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -101,21 +118,104 @@ describe('CommunityResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('PRODUCTION = true', () => {
|
describe('plus foreign-communities entries', () => {
|
||||||
beforeEach(() => {
|
beforeEach(async () => {
|
||||||
CONFIG.PRODUCTION = true
|
jest.clearAllMocks()
|
||||||
|
|
||||||
|
foreignCom1 = DbCommunity.create()
|
||||||
|
foreignCom1.foreign = true
|
||||||
|
foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
|
foreignCom1.apiVersion = '1_0'
|
||||||
|
foreignCom1.endPoint = 'http://remotehost'
|
||||||
|
foreignCom1.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(foreignCom1)
|
||||||
|
|
||||||
|
foreignCom2 = DbCommunity.create()
|
||||||
|
foreignCom2.foreign = true
|
||||||
|
foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
|
foreignCom2.apiVersion = '1_1'
|
||||||
|
foreignCom2.endPoint = 'http://remotehost'
|
||||||
|
foreignCom2.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(foreignCom2)
|
||||||
|
|
||||||
|
foreignCom3 = DbCommunity.create()
|
||||||
|
foreignCom3.foreign = true
|
||||||
|
foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity')
|
||||||
|
foreignCom3.apiVersion = '1_2'
|
||||||
|
foreignCom3.endPoint = 'http://remotehost'
|
||||||
|
foreignCom3.createdAt = new Date()
|
||||||
|
await DbCommunity.insert(foreignCom3)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns one community', async () => {
|
it('returns 3x home and 3x foreign-community entries', async () => {
|
||||||
await expect(query({ query: communities })).resolves.toMatchObject({
|
await expect(query({ query: getCommunities })).resolves.toMatchObject({
|
||||||
data: {
|
data: {
|
||||||
communities: [
|
getCommunities: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
foreign: homeCom1.foreign,
|
||||||
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
|
url: expect.stringMatching('http://localhost/api/1_0'),
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom1.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
foreign: homeCom2.foreign,
|
||||||
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
|
url: expect.stringMatching('http://localhost/api/1_1'),
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom2.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 3,
|
||||||
name: 'Gradido-Akademie',
|
foreign: homeCom3.foreign,
|
||||||
description: 'Freies Institut für Wirtschaftsbionik.',
|
publicKey: expect.stringMatching('publicKey-HomeCommunity'),
|
||||||
url: 'https://gradido.net',
|
url: expect.stringMatching('http://localhost/api/2_0'),
|
||||||
registerUrl: 'https://gdd1.gradido.com/register-community',
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: homeCom3.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
foreign: foreignCom1.foreign,
|
||||||
|
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||||
|
url: expect.stringMatching('http://remotehost/api/1_0'),
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: foreignCom1.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
foreign: foreignCom2.foreign,
|
||||||
|
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||||
|
url: expect.stringMatching('http://remotehost/api/1_1'),
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: foreignCom2.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
foreign: foreignCom3.foreign,
|
||||||
|
publicKey: expect.stringMatching('publicKey-ForeignCommunity'),
|
||||||
|
url: expect.stringMatching('http://remotehost/api/1_2'),
|
||||||
|
lastAnnouncedAt: null,
|
||||||
|
verifiedAt: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
createdAt: foreignCom3.createdAt.toISOString(),
|
||||||
|
updatedAt: null,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,58 +1,18 @@
|
|||||||
import { Resolver, Query, Authorized } from 'type-graphql'
|
import { Resolver, Query, Authorized } from 'type-graphql'
|
||||||
|
|
||||||
import { Community } from '@model/Community'
|
import { Community } from '@model/Community'
|
||||||
|
import { Community as DbCommunity } from '@entity/Community'
|
||||||
|
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import CONFIG from '@/config'
|
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class CommunityResolver {
|
export class CommunityResolver {
|
||||||
@Authorized([RIGHTS.GET_COMMUNITY_INFO])
|
|
||||||
@Query(() => Community)
|
|
||||||
getCommunityInfo(): Community {
|
|
||||||
return new Community({
|
|
||||||
name: CONFIG.COMMUNITY_NAME,
|
|
||||||
description: CONFIG.COMMUNITY_DESCRIPTION,
|
|
||||||
url: CONFIG.COMMUNITY_URL,
|
|
||||||
registerUrl: CONFIG.COMMUNITY_REGISTER_URL,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
@Authorized([RIGHTS.COMMUNITIES])
|
@Authorized([RIGHTS.COMMUNITIES])
|
||||||
@Query(() => [Community])
|
@Query(() => [Community])
|
||||||
communities(): Community[] {
|
async getCommunities(): Promise<Community[]> {
|
||||||
if (CONFIG.PRODUCTION)
|
const dbCommunities: DbCommunity[] = await DbCommunity.find({
|
||||||
return [
|
order: { foreign: 'ASC', publicKey: 'ASC', apiVersion: 'ASC' },
|
||||||
new Community({
|
})
|
||||||
id: 3,
|
return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom))
|
||||||
name: 'Gradido-Akademie',
|
|
||||||
description: 'Freies Institut für Wirtschaftsbionik.',
|
|
||||||
url: 'https://gradido.net',
|
|
||||||
registerUrl: 'https://gdd1.gradido.com/register-community',
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
return [
|
|
||||||
new Community({
|
|
||||||
id: 1,
|
|
||||||
name: 'Gradido Entwicklung',
|
|
||||||
description: 'Die lokale Entwicklungsumgebung von Gradido.',
|
|
||||||
url: 'http://localhost/',
|
|
||||||
registerUrl: 'http://localhost/register-community',
|
|
||||||
}),
|
|
||||||
new Community({
|
|
||||||
id: 2,
|
|
||||||
name: 'Gradido Staging',
|
|
||||||
description: 'Der Testserver der Gradido-Akademie.',
|
|
||||||
url: 'https://stage1.gradido.net/',
|
|
||||||
registerUrl: 'https://stage1.gradido.net/register-community',
|
|
||||||
}),
|
|
||||||
new Community({
|
|
||||||
id: 3,
|
|
||||||
name: 'Gradido-Akademie',
|
|
||||||
description: 'Freies Institut für Wirtschaftsbionik.',
|
|
||||||
url: 'https://gradido.net',
|
|
||||||
registerUrl: 'https://gdd1.gradido.com/register-community',
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
listAllContributions,
|
listAllContributions,
|
||||||
listContributions,
|
listContributions,
|
||||||
adminListAllContributions,
|
adminListContributions,
|
||||||
} from '@/seeds/graphql/queries'
|
} from '@/seeds/graphql/queries'
|
||||||
import {
|
import {
|
||||||
sendContributionConfirmedEmail,
|
sendContributionConfirmedEmail,
|
||||||
@ -46,11 +46,11 @@ import { userFactory } from '@/seeds/factory/user'
|
|||||||
import { creationFactory } from '@/seeds/factory/creation'
|
import { creationFactory } from '@/seeds/factory/creation'
|
||||||
import { creations } from '@/seeds/creation/index'
|
import { creations } from '@/seeds/creation/index'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
import { Contribution } from '@entity/Contribution'
|
import { Contribution } from '@entity/Contribution'
|
||||||
import { Transaction as DbTransaction } from '@entity/Transaction'
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
import { EventType } from '@/event/Event'
|
||||||
import { logger, i18n as localization } from '@test/testSetup'
|
import { logger, i18n as localization } from '@test/testSetup'
|
||||||
import { UserInputError } from 'apollo-server-express'
|
import { UserInputError } from 'apollo-server-express'
|
||||||
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
import { raeuberHotzenplotz } from '@/seeds/users/raeuber-hotzenplotz'
|
||||||
@ -279,12 +279,13 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the CONTRIBUTION_CREATE event in the database', async () => {
|
it('stores the CONTRIBUTION_CREATE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.CONTRIBUTION_CREATE,
|
type: EventType.CONTRIBUTION_CREATE,
|
||||||
|
affectedUserId: bibi.id,
|
||||||
|
actingUserId: bibi.id,
|
||||||
|
involvedContributionId: pendingContribution.data.createContribution.id,
|
||||||
amount: expect.decimalEqual(100),
|
amount: expect.decimalEqual(100),
|
||||||
contributionId: pendingContribution.data.createContribution.id,
|
|
||||||
userId: bibi.id,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -584,12 +585,13 @@ describe('ContributionResolver', () => {
|
|||||||
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' },
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.CONTRIBUTION_UPDATE,
|
type: EventType.CONTRIBUTION_UPDATE,
|
||||||
|
affectedUserId: bibi.id,
|
||||||
|
actingUserId: bibi.id,
|
||||||
|
involvedContributionId: pendingContribution.data.createContribution.id,
|
||||||
amount: expect.decimalEqual(10),
|
amount: expect.decimalEqual(10),
|
||||||
contributionId: pendingContribution.data.createContribution.id,
|
|
||||||
userId: bibi.id,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -814,12 +816,12 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ADMIN_CONTRIBUTION_DENY event in the database', async () => {
|
it('stores the ADMIN_CONTRIBUTION_DENY event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_CONTRIBUTION_DENY,
|
type: EventType.ADMIN_CONTRIBUTION_DENY,
|
||||||
userId: bibi.id,
|
affectedUserId: bibi.id,
|
||||||
xUserId: admin.id,
|
actingUserId: admin.id,
|
||||||
contributionId: contributionToDeny.data.createContribution.id,
|
involvedContributionId: contributionToDeny.data.createContribution.id,
|
||||||
amount: expect.decimalEqual(100),
|
amount: expect.decimalEqual(100),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -942,12 +944,13 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the CONTRIBUTION_DELETE event in the database', async () => {
|
it('stores the CONTRIBUTION_DELETE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.CONTRIBUTION_DELETE,
|
type: EventType.CONTRIBUTION_DELETE,
|
||||||
contributionId: contributionToDelete.data.createContribution.id,
|
affectedUserId: bibi.id,
|
||||||
|
actingUserId: bibi.id,
|
||||||
|
involvedContributionId: contributionToDelete.data.createContribution.id,
|
||||||
amount: expect.decimalEqual(100),
|
amount: expect.decimalEqual(100),
|
||||||
userId: bibi.id,
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -2031,10 +2034,11 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ADMIN_CONTRIBUTION_CREATE event in the database', async () => {
|
it('stores the ADMIN_CONTRIBUTION_CREATE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_CONTRIBUTION_CREATE,
|
type: EventType.ADMIN_CONTRIBUTION_CREATE,
|
||||||
userId: admin.id,
|
affectedUserId: bibi.id,
|
||||||
|
actingUserId: admin.id,
|
||||||
amount: expect.decimalEqual(200),
|
amount: expect.decimalEqual(200),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -2232,7 +2236,7 @@ describe('ContributionResolver', () => {
|
|||||||
mutate({
|
mutate({
|
||||||
mutation: adminUpdateContribution,
|
mutation: adminUpdateContribution,
|
||||||
variables: {
|
variables: {
|
||||||
id: creation ? creation.id : -1,
|
id: creation?.id,
|
||||||
email: 'peter@lustig.de',
|
email: 'peter@lustig.de',
|
||||||
amount: new Decimal(300),
|
amount: new Decimal(300),
|
||||||
memo: 'Danke Peter!',
|
memo: 'Danke Peter!',
|
||||||
@ -2256,10 +2260,11 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => {
|
it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE,
|
type: EventType.ADMIN_CONTRIBUTION_UPDATE,
|
||||||
userId: admin.id,
|
affectedUserId: creation?.userId,
|
||||||
|
actingUserId: admin.id,
|
||||||
amount: 300,
|
amount: 300,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -2273,7 +2278,7 @@ describe('ContributionResolver', () => {
|
|||||||
mutate({
|
mutate({
|
||||||
mutation: adminUpdateContribution,
|
mutation: adminUpdateContribution,
|
||||||
variables: {
|
variables: {
|
||||||
id: creation ? creation.id : -1,
|
id: creation?.id,
|
||||||
email: 'peter@lustig.de',
|
email: 'peter@lustig.de',
|
||||||
amount: new Decimal(200),
|
amount: new Decimal(200),
|
||||||
memo: 'Das war leider zu Viel!',
|
memo: 'Das war leider zu Viel!',
|
||||||
@ -2297,10 +2302,11 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => {
|
it('stores the ADMIN_CONTRIBUTION_UPDATE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE,
|
type: EventType.ADMIN_CONTRIBUTION_UPDATE,
|
||||||
userId: admin.id,
|
affectedUserId: creation?.userId,
|
||||||
|
actingUserId: admin.id,
|
||||||
amount: expect.decimalEqual(200),
|
amount: expect.decimalEqual(200),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -2371,7 +2377,7 @@ describe('ContributionResolver', () => {
|
|||||||
mutate({
|
mutate({
|
||||||
mutation: adminDeleteContribution,
|
mutation: adminDeleteContribution,
|
||||||
variables: {
|
variables: {
|
||||||
id: creation ? creation.id : -1,
|
id: creation?.id,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
@ -2382,10 +2388,12 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ADMIN_CONTRIBUTION_DELETE event in the database', async () => {
|
it('stores the ADMIN_CONTRIBUTION_DELETE event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_CONTRIBUTION_DELETE,
|
type: EventType.ADMIN_CONTRIBUTION_DELETE,
|
||||||
userId: admin.id,
|
affectedUserId: creation?.userId,
|
||||||
|
actingUserId: admin.id,
|
||||||
|
involvedContributionId: creation?.id,
|
||||||
amount: expect.decimalEqual(200),
|
amount: expect.decimalEqual(200),
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -2538,9 +2546,9 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the CONTRIBUTION_CONFIRM event in the database', async () => {
|
it('stores the CONTRIBUTION_CONFIRM event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.CONTRIBUTION_CONFIRM,
|
type: EventType.ADMIN_CONTRIBUTION_CONFIRM,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -2570,9 +2578,9 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => {
|
it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.SEND_CONFIRMATION_EMAIL,
|
type: EventType.SEND_CONFIRMATION_EMAIL,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -2661,12 +2669,12 @@ describe('ContributionResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('adminListAllContribution', () => {
|
describe('adminListContributions', () => {
|
||||||
describe('unauthenticated', () => {
|
describe('unauthenticated', () => {
|
||||||
it('returns an error', async () => {
|
it('returns an error', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
query({
|
query({
|
||||||
query: adminListAllContributions,
|
query: adminListContributions,
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@ -2691,7 +2699,7 @@ describe('ContributionResolver', () => {
|
|||||||
it('returns an error', async () => {
|
it('returns an error', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
query({
|
query({
|
||||||
query: adminListAllContributions,
|
query: adminListContributions,
|
||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@ -2715,9 +2723,9 @@ describe('ContributionResolver', () => {
|
|||||||
|
|
||||||
it('returns 17 creations in total', async () => {
|
it('returns 17 creations in total', async () => {
|
||||||
const {
|
const {
|
||||||
data: { adminListAllContributions: contributionListObject },
|
data: { adminListContributions: contributionListObject },
|
||||||
}: { data: { adminListAllContributions: ContributionListResult } } = await query({
|
}: { data: { adminListContributions: ContributionListResult } } = await query({
|
||||||
query: adminListAllContributions,
|
query: adminListContributions,
|
||||||
})
|
})
|
||||||
expect(contributionListObject.contributionList).toHaveLength(17)
|
expect(contributionListObject.contributionList).toHaveLength(17)
|
||||||
expect(contributionListObject).toMatchObject({
|
expect(contributionListObject).toMatchObject({
|
||||||
@ -2882,9 +2890,9 @@ describe('ContributionResolver', () => {
|
|||||||
|
|
||||||
it('returns two pending creations with page size set to 2', async () => {
|
it('returns two pending creations with page size set to 2', async () => {
|
||||||
const {
|
const {
|
||||||
data: { adminListAllContributions: contributionListObject },
|
data: { adminListContributions: contributionListObject },
|
||||||
}: { data: { adminListAllContributions: ContributionListResult } } = await query({
|
}: { data: { adminListContributions: ContributionListResult } } = await query({
|
||||||
query: adminListAllContributions,
|
query: adminListContributions,
|
||||||
variables: {
|
variables: {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
pageSize: 2,
|
pageSize: 2,
|
||||||
|
|||||||
@ -42,7 +42,7 @@ import {
|
|||||||
EVENT_ADMIN_CONTRIBUTION_CREATE,
|
EVENT_ADMIN_CONTRIBUTION_CREATE,
|
||||||
EVENT_ADMIN_CONTRIBUTION_UPDATE,
|
EVENT_ADMIN_CONTRIBUTION_UPDATE,
|
||||||
EVENT_ADMIN_CONTRIBUTION_DELETE,
|
EVENT_ADMIN_CONTRIBUTION_DELETE,
|
||||||
EVENT_CONTRIBUTION_CONFIRM,
|
EVENT_ADMIN_CONTRIBUTION_CONFIRM,
|
||||||
EVENT_ADMIN_CONTRIBUTION_DENY,
|
EVENT_ADMIN_CONTRIBUTION_DENY,
|
||||||
} from '@/event/Event'
|
} from '@/event/Event'
|
||||||
import { calculateDecay } from '@/util/decay'
|
import { calculateDecay } from '@/util/decay'
|
||||||
@ -91,7 +91,7 @@ export class ContributionResolver {
|
|||||||
logger.trace('contribution to save', contribution)
|
logger.trace('contribution to save', contribution)
|
||||||
await DbContribution.save(contribution)
|
await DbContribution.save(contribution)
|
||||||
|
|
||||||
await EVENT_CONTRIBUTION_CREATE(user.id, contribution.id, amount)
|
await EVENT_CONTRIBUTION_CREATE(user, contribution, amount)
|
||||||
|
|
||||||
return new UnconfirmedContribution(contribution, user, creations)
|
return new UnconfirmedContribution(contribution, user, creations)
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ export class ContributionResolver {
|
|||||||
contribution.deletedAt = new Date()
|
contribution.deletedAt = new Date()
|
||||||
await contribution.save()
|
await contribution.save()
|
||||||
|
|
||||||
await EVENT_CONTRIBUTION_DELETE(user.id, contribution.id, contribution.amount)
|
await EVENT_CONTRIBUTION_DELETE(user, contribution, contribution.amount)
|
||||||
|
|
||||||
const res = await contribution.softRemove()
|
const res = await contribution.softRemove()
|
||||||
return !!res
|
return !!res
|
||||||
@ -136,15 +136,15 @@ export class ContributionResolver {
|
|||||||
): Promise<ContributionListResult> {
|
): Promise<ContributionListResult> {
|
||||||
const user = getUser(context)
|
const user = getUser(context)
|
||||||
|
|
||||||
const [dbContributions, count] = await findContributions(
|
const [dbContributions, count] = await findContributions({
|
||||||
order,
|
order,
|
||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
true,
|
withDeleted: true,
|
||||||
['messages'],
|
relations: ['messages'],
|
||||||
user.id,
|
userId: user.id,
|
||||||
statusFilter,
|
statusFilter,
|
||||||
)
|
})
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
dbContributions.map((contribution) => new Contribution(contribution, user)),
|
dbContributions.map((contribution) => new Contribution(contribution, user)),
|
||||||
@ -159,15 +159,13 @@ export class ContributionResolver {
|
|||||||
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
||||||
statusFilter?: ContributionStatus[] | null,
|
statusFilter?: ContributionStatus[] | null,
|
||||||
): Promise<ContributionListResult> {
|
): Promise<ContributionListResult> {
|
||||||
const [dbContributions, count] = await findContributions(
|
const [dbContributions, count] = await findContributions({
|
||||||
order,
|
order,
|
||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
false,
|
relations: ['user'],
|
||||||
['user'],
|
|
||||||
undefined,
|
|
||||||
statusFilter,
|
statusFilter,
|
||||||
)
|
})
|
||||||
|
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
@ -249,7 +247,7 @@ export class ContributionResolver {
|
|||||||
contributionToUpdate.updatedAt = new Date()
|
contributionToUpdate.updatedAt = new Date()
|
||||||
await DbContribution.save(contributionToUpdate)
|
await DbContribution.save(contributionToUpdate)
|
||||||
|
|
||||||
await EVENT_CONTRIBUTION_UPDATE(user.id, contributionId, amount)
|
await EVENT_CONTRIBUTION_UPDATE(user, contributionToUpdate, amount)
|
||||||
|
|
||||||
return new UnconfirmedContribution(contributionToUpdate, user, creations)
|
return new UnconfirmedContribution(contributionToUpdate, user, creations)
|
||||||
}
|
}
|
||||||
@ -306,7 +304,7 @@ export class ContributionResolver {
|
|||||||
|
|
||||||
await DbContribution.save(contribution)
|
await DbContribution.save(contribution)
|
||||||
|
|
||||||
await EVENT_ADMIN_CONTRIBUTION_CREATE(moderator.id, contribution.id, amount)
|
await EVENT_ADMIN_CONTRIBUTION_CREATE(emailContact.user, moderator, contribution, amount)
|
||||||
|
|
||||||
return getUserCreation(emailContact.userId, clientTimezoneOffset)
|
return getUserCreation(emailContact.userId, clientTimezoneOffset)
|
||||||
}
|
}
|
||||||
@ -374,28 +372,35 @@ export class ContributionResolver {
|
|||||||
|
|
||||||
result.creation = await getUserCreation(emailContact.user.id, clientTimezoneOffset)
|
result.creation = await getUserCreation(emailContact.user.id, clientTimezoneOffset)
|
||||||
|
|
||||||
await EVENT_ADMIN_CONTRIBUTION_UPDATE(emailContact.user.id, contributionToUpdate.id, amount)
|
await EVENT_ADMIN_CONTRIBUTION_UPDATE(
|
||||||
|
emailContact.user,
|
||||||
|
moderator,
|
||||||
|
contributionToUpdate,
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS])
|
@Authorized([RIGHTS.ADMIN_LIST_CONTRIBUTIONS])
|
||||||
@Query(() => ContributionListResult) // [UnconfirmedContribution]
|
@Query(() => ContributionListResult)
|
||||||
async adminListAllContributions(
|
async adminListContributions(
|
||||||
@Args()
|
@Args()
|
||||||
{ currentPage = 1, pageSize = 3, order = Order.DESC }: Paginated,
|
{ currentPage = 1, pageSize = 3, order = Order.DESC }: Paginated,
|
||||||
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
||||||
statusFilter?: ContributionStatus[] | null,
|
statusFilter?: ContributionStatus[] | null,
|
||||||
|
@Arg('userId', () => Int, { nullable: true })
|
||||||
|
userId?: number | null,
|
||||||
): Promise<ContributionListResult> {
|
): Promise<ContributionListResult> {
|
||||||
const [dbContributions, count] = await findContributions(
|
const [dbContributions, count] = await findContributions({
|
||||||
order,
|
order,
|
||||||
currentPage,
|
currentPage,
|
||||||
pageSize,
|
pageSize,
|
||||||
true,
|
withDeleted: true,
|
||||||
['user', 'messages'],
|
userId,
|
||||||
undefined,
|
relations: ['user', 'messages'],
|
||||||
statusFilter,
|
statusFilter,
|
||||||
)
|
})
|
||||||
|
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
@ -432,7 +437,12 @@ export class ContributionResolver {
|
|||||||
await contribution.save()
|
await contribution.save()
|
||||||
const res = await contribution.softRemove()
|
const res = await contribution.softRemove()
|
||||||
|
|
||||||
await EVENT_ADMIN_CONTRIBUTION_DELETE(contribution.userId, contribution.id, contribution.amount)
|
await EVENT_ADMIN_CONTRIBUTION_DELETE(
|
||||||
|
{ id: contribution.userId } as DbUser,
|
||||||
|
moderator,
|
||||||
|
contribution,
|
||||||
|
contribution.amount,
|
||||||
|
)
|
||||||
|
|
||||||
void sendContributionDeletedEmail({
|
void sendContributionDeletedEmail({
|
||||||
firstName: user.firstName,
|
firstName: user.firstName,
|
||||||
@ -545,40 +555,13 @@ export class ContributionResolver {
|
|||||||
await queryRunner.release()
|
await queryRunner.release()
|
||||||
}
|
}
|
||||||
|
|
||||||
await EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount)
|
await EVENT_ADMIN_CONTRIBUTION_CONFIRM(user, moderatorUser, contribution, contribution.amount)
|
||||||
} finally {
|
} finally {
|
||||||
releaseLock()
|
releaseLock()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.CREATION_TRANSACTION_LIST])
|
|
||||||
@Query(() => ContributionListResult)
|
|
||||||
async creationTransactionList(
|
|
||||||
@Args()
|
|
||||||
{ currentPage = 1, pageSize = 25, order = Order.DESC }: Paginated,
|
|
||||||
@Arg('userId', () => Int) userId: number,
|
|
||||||
): Promise<ContributionListResult> {
|
|
||||||
const offset = (currentPage - 1) * pageSize
|
|
||||||
const [contributionResult, count] = await getConnection()
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('c')
|
|
||||||
.from(DbContribution, 'c')
|
|
||||||
.leftJoinAndSelect('c.user', 'u')
|
|
||||||
.where(`user_id = ${userId}`)
|
|
||||||
.withDeleted()
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset(offset)
|
|
||||||
.orderBy('c.created_at', order)
|
|
||||||
.getManyAndCount()
|
|
||||||
|
|
||||||
return new ContributionListResult(
|
|
||||||
count,
|
|
||||||
contributionResult.map((contribution) => new Contribution(contribution, contribution.user)),
|
|
||||||
)
|
|
||||||
// return userTransactions.map((t) => new Transaction(t, new User(user), communityUser))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Authorized([RIGHTS.OPEN_CREATIONS])
|
@Authorized([RIGHTS.OPEN_CREATIONS])
|
||||||
@Query(() => [OpenCreation])
|
@Query(() => [OpenCreation])
|
||||||
async openCreations(@Ctx() context: Context): Promise<OpenCreation[]> {
|
async openCreations(@Ctx() context: Context): Promise<OpenCreation[]> {
|
||||||
@ -632,9 +615,9 @@ export class ContributionResolver {
|
|||||||
const res = await contributionToUpdate.save()
|
const res = await contributionToUpdate.save()
|
||||||
|
|
||||||
await EVENT_ADMIN_CONTRIBUTION_DENY(
|
await EVENT_ADMIN_CONTRIBUTION_DENY(
|
||||||
contributionToUpdate.userId,
|
user,
|
||||||
moderator.id,
|
moderator,
|
||||||
contributionToUpdate.id,
|
contributionToUpdate,
|
||||||
contributionToUpdate.amount,
|
contributionToUpdate.amount,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
import { EventType } from '@/event/Event'
|
||||||
import { userFactory } from '@/seeds/factory/user'
|
import { userFactory } from '@/seeds/factory/user'
|
||||||
import {
|
import {
|
||||||
confirmContribution,
|
confirmContribution,
|
||||||
@ -18,7 +18,7 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister'
|
|||||||
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
import { garrickOllivander } from '@/seeds/users/garrick-ollivander'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
import { stephenHawking } from '@/seeds/users/stephen-hawking'
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
import { Transaction } from '@entity/Transaction'
|
import { Transaction } from '@entity/Transaction'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
import { cleanDB, testEnvironment } from '@test/helpers'
|
import { cleanDB, testEnvironment } from '@test/helpers'
|
||||||
@ -339,12 +339,13 @@ describe('send coins', () => {
|
|||||||
memo: 'unrepeatable memo',
|
memo: 'unrepeatable memo',
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.TRANSACTION_SEND,
|
type: EventType.TRANSACTION_SEND,
|
||||||
userId: user[1].id,
|
affectedUserId: user[1].id,
|
||||||
transactionId: transaction[0].id,
|
actingUserId: user[1].id,
|
||||||
xUserId: user[0].id,
|
involvedUserId: user[0].id,
|
||||||
|
involvedTransactionId: transaction[0].id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -356,12 +357,13 @@ describe('send coins', () => {
|
|||||||
memo: 'unrepeatable memo',
|
memo: 'unrepeatable memo',
|
||||||
})
|
})
|
||||||
|
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.TRANSACTION_RECEIVE,
|
type: EventType.TRANSACTION_RECEIVE,
|
||||||
userId: user[0].id,
|
affectedUserId: user[0].id,
|
||||||
transactionId: transaction[0].id,
|
actingUserId: user[1].id,
|
||||||
xUserId: user[1].id,
|
involvedUserId: user[1].id,
|
||||||
|
involvedTransactionId: transaction[0].id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -138,17 +138,12 @@ export const executeTransaction = async (
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.info(`commit Transaction successful...`)
|
logger.info(`commit Transaction successful...`)
|
||||||
|
|
||||||
await EVENT_TRANSACTION_SEND(
|
await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount)
|
||||||
transactionSend.userId,
|
|
||||||
transactionSend.linkedUserId,
|
|
||||||
transactionSend.id,
|
|
||||||
transactionSend.amount.mul(-1),
|
|
||||||
)
|
|
||||||
|
|
||||||
await EVENT_TRANSACTION_RECEIVE(
|
await EVENT_TRANSACTION_RECEIVE(
|
||||||
transactionReceive.userId,
|
recipient,
|
||||||
transactionReceive.linkedUserId,
|
sender,
|
||||||
transactionReceive.id,
|
transactionReceive,
|
||||||
transactionReceive.amount,
|
transactionReceive.amount,
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@ -45,8 +45,8 @@ import { contributionLinkFactory } from '@/seeds/factory/contributionLink'
|
|||||||
import { transactionLinkFactory } from '@/seeds/factory/transactionLink'
|
import { transactionLinkFactory } from '@/seeds/factory/transactionLink'
|
||||||
import { ContributionLink } from '@model/ContributionLink'
|
import { ContributionLink } from '@model/ContributionLink'
|
||||||
import { TransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink } from '@entity/TransactionLink'
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
import { EventType } from '@/event/Event'
|
||||||
import { EventProtocol } from '@entity/EventProtocol'
|
import { Event as DbEvent } from '@entity/Event'
|
||||||
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
import { validate as validateUUID, version as versionUUID } from 'uuid'
|
||||||
import { peterLustig } from '@/seeds/users/peter-lustig'
|
import { peterLustig } from '@/seeds/users/peter-lustig'
|
||||||
import { UserContact } from '@entity/UserContact'
|
import { UserContact } from '@entity/UserContact'
|
||||||
@ -193,10 +193,11 @@ describe('UserResolver', () => {
|
|||||||
{ email: 'peter@lustig.de' },
|
{ email: 'peter@lustig.de' },
|
||||||
{ relations: ['user'] },
|
{ relations: ['user'] },
|
||||||
)
|
)
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.REGISTER,
|
type: EventType.REGISTER,
|
||||||
userId: userConatct.user.id,
|
affectedUserId: userConatct.user.id,
|
||||||
|
actingUserId: userConatct.user.id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -222,10 +223,11 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => {
|
it('stores the SEND_CONFIRMATION_EMAIL event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.SEND_CONFIRMATION_EMAIL,
|
type: EventType.SEND_CONFIRMATION_EMAIL,
|
||||||
userId: user[0].id,
|
affectedUserId: user[0].id,
|
||||||
|
actingUserId: user[0].id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -267,10 +269,11 @@ describe('UserResolver', () => {
|
|||||||
{ email: 'peter@lustig.de' },
|
{ email: 'peter@lustig.de' },
|
||||||
{ relations: ['user'] },
|
{ relations: ['user'] },
|
||||||
)
|
)
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL,
|
type: EventType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL,
|
||||||
userId: userConatct.user.id,
|
affectedUserId: userConatct.user.id,
|
||||||
|
actingUserId: 0,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -367,20 +370,22 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the ACTIVATE_ACCOUNT event in the database', async () => {
|
it('stores the ACTIVATE_ACCOUNT event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ACTIVATE_ACCOUNT,
|
type: EventType.ACTIVATE_ACCOUNT,
|
||||||
userId: user[0].id,
|
affectedUserId: user[0].id,
|
||||||
|
actingUserId: user[0].id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('stores the REDEEM_REGISTER event in the database', async () => {
|
it('stores the REDEEM_REGISTER event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.REDEEM_REGISTER,
|
type: EventType.REDEEM_REGISTER,
|
||||||
userId: result.data.createUser.id,
|
affectedUserId: result.data.createUser.id,
|
||||||
contributionId: link.id,
|
actingUserId: result.data.createUser.id,
|
||||||
|
involvedContributionId: link.id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -460,10 +465,12 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the REDEEM_REGISTER event in the database', async () => {
|
it('stores the REDEEM_REGISTER event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.REDEEM_REGISTER,
|
type: EventType.REDEEM_REGISTER,
|
||||||
userId: newUser.data.createUser.id,
|
affectedUserId: newUser.data.createUser.id,
|
||||||
|
actingUserId: newUser.data.createUser.id,
|
||||||
|
involvedTransactionId: transactionLink.id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -691,10 +698,11 @@ describe('UserResolver', () => {
|
|||||||
{ email: 'bibi@bloxberg.de' },
|
{ email: 'bibi@bloxberg.de' },
|
||||||
{ relations: ['user'] },
|
{ relations: ['user'] },
|
||||||
)
|
)
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.LOGIN,
|
type: EventType.LOGIN,
|
||||||
userId: userConatct.user.id,
|
affectedUserId: userConatct.user.id,
|
||||||
|
actingUserId: userConatct.user.id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -939,10 +947,11 @@ describe('UserResolver', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('stores the LOGIN event in the database', async () => {
|
it('stores the LOGIN event in the database', async () => {
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.LOGIN,
|
type: EventType.LOGIN,
|
||||||
userId: user[0].id,
|
affectedUserId: user[0].id,
|
||||||
|
actingUserId: user[0].id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -1858,10 +1867,11 @@ describe('UserResolver', () => {
|
|||||||
{ email: 'bibi@bloxberg.de' },
|
{ email: 'bibi@bloxberg.de' },
|
||||||
{ relations: ['user'] },
|
{ relations: ['user'] },
|
||||||
)
|
)
|
||||||
await expect(EventProtocol.find()).resolves.toContainEqual(
|
await expect(DbEvent.find()).resolves.toContainEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
type: EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL,
|
type: EventType.ADMIN_SEND_CONFIRMATION_EMAIL,
|
||||||
userId: userConatct.user.id,
|
affectedUserId: userConatct.user.id,
|
||||||
|
actingUserId: admin.id,
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@ -20,7 +20,9 @@ import { getConnection, getCustomRepository, IsNull, Not } from '@dbTools/typeor
|
|||||||
import { User as DbUser } from '@entity/User'
|
import { User as DbUser } from '@entity/User'
|
||||||
import { UserContact as DbUserContact } from '@entity/UserContact'
|
import { UserContact as DbUserContact } from '@entity/UserContact'
|
||||||
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
|
import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink'
|
||||||
|
import { Transaction as DbTransaction } from '@entity/Transaction'
|
||||||
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
import { ContributionLink as DbContributionLink } from '@entity/ContributionLink'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
import { UserRepository } from '@repository/User'
|
import { UserRepository } from '@repository/User'
|
||||||
|
|
||||||
import { User } from '@model/User'
|
import { User } from '@model/User'
|
||||||
@ -54,6 +56,7 @@ import { RIGHTS } from '@/auth/RIGHTS'
|
|||||||
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
import { hasElopageBuys } from '@/util/hasElopageBuys'
|
||||||
import {
|
import {
|
||||||
Event,
|
Event,
|
||||||
|
EventType,
|
||||||
EVENT_LOGIN,
|
EVENT_LOGIN,
|
||||||
EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL,
|
EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL,
|
||||||
EVENT_SEND_CONFIRMATION_EMAIL,
|
EVENT_SEND_CONFIRMATION_EMAIL,
|
||||||
@ -67,7 +70,6 @@ import { FULL_CREATION_AVAILABLE } from './const/const'
|
|||||||
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
import { encryptPassword, verifyPassword } from '@/password/PasswordEncryptor'
|
||||||
import { PasswordEncryptionType } from '../enum/PasswordEncryptionType'
|
import { PasswordEncryptionType } from '../enum/PasswordEncryptionType'
|
||||||
import LogError from '@/server/LogError'
|
import LogError from '@/server/LogError'
|
||||||
import { EventProtocolType } from '@/event/EventProtocolType'
|
|
||||||
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
import { findUserByIdentifier } from './util/findUserByIdentifier'
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
@ -183,7 +185,7 @@ export class UserResolver {
|
|||||||
value: encode(dbUser.gradidoID),
|
value: encode(dbUser.gradidoID),
|
||||||
})
|
})
|
||||||
|
|
||||||
await EVENT_LOGIN(user.id)
|
await EVENT_LOGIN(dbUser)
|
||||||
logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`)
|
logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
@ -253,7 +255,7 @@ export class UserResolver {
|
|||||||
language: foundUser.language, // use language of the emails owner for sending
|
language: foundUser.language, // use language of the emails owner for sending
|
||||||
})
|
})
|
||||||
|
|
||||||
await EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser.id)
|
await EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL(foundUser)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`,
|
`sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`,
|
||||||
@ -271,7 +273,11 @@ export class UserResolver {
|
|||||||
|
|
||||||
const gradidoID = await newGradidoID()
|
const gradidoID = await newGradidoID()
|
||||||
|
|
||||||
const eventRegisterRedeem = Event(EventProtocolType.REDEEM_REGISTER, 0)
|
const eventRegisterRedeem = Event(
|
||||||
|
EventType.REDEEM_REGISTER,
|
||||||
|
{ id: 0 } as DbUser,
|
||||||
|
{ id: 0 } as DbUser,
|
||||||
|
)
|
||||||
let dbUser = new DbUser()
|
let dbUser = new DbUser()
|
||||||
dbUser.gradidoID = gradidoID
|
dbUser.gradidoID = gradidoID
|
||||||
dbUser.firstName = firstName
|
dbUser.firstName = firstName
|
||||||
@ -288,14 +294,16 @@ export class UserResolver {
|
|||||||
logger.info('redeemCode found contributionLink', contributionLink)
|
logger.info('redeemCode found contributionLink', contributionLink)
|
||||||
if (contributionLink) {
|
if (contributionLink) {
|
||||||
dbUser.contributionLinkId = contributionLink.id
|
dbUser.contributionLinkId = contributionLink.id
|
||||||
eventRegisterRedeem.contributionId = contributionLink.id
|
// TODO this is so wrong
|
||||||
|
eventRegisterRedeem.involvedContribution = { id: contributionLink.id } as DbContribution
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const transactionLink = await DbTransactionLink.findOne({ code: redeemCode })
|
const transactionLink = await DbTransactionLink.findOne({ code: redeemCode })
|
||||||
logger.info('redeemCode found transactionLink', transactionLink)
|
logger.info('redeemCode found transactionLink', transactionLink)
|
||||||
if (transactionLink) {
|
if (transactionLink) {
|
||||||
dbUser.referrerId = transactionLink.userId
|
dbUser.referrerId = transactionLink.userId
|
||||||
eventRegisterRedeem.transactionId = transactionLink.id
|
// TODO this is so wrong
|
||||||
|
eventRegisterRedeem.involvedTransaction = { id: transactionLink.id } as DbTransaction
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,7 +342,7 @@ export class UserResolver {
|
|||||||
})
|
})
|
||||||
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`)
|
||||||
|
|
||||||
await EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id)
|
await EVENT_SEND_CONFIRMATION_EMAIL(dbUser)
|
||||||
|
|
||||||
if (!emailSent) {
|
if (!emailSent) {
|
||||||
logger.debug(`Account confirmation link: ${activationLink}`)
|
logger.debug(`Account confirmation link: ${activationLink}`)
|
||||||
@ -351,10 +359,11 @@ export class UserResolver {
|
|||||||
logger.info('createUser() successful...')
|
logger.info('createUser() successful...')
|
||||||
|
|
||||||
if (redeemCode) {
|
if (redeemCode) {
|
||||||
eventRegisterRedeem.userId = dbUser.id
|
eventRegisterRedeem.affectedUser = dbUser
|
||||||
|
eventRegisterRedeem.actingUser = dbUser
|
||||||
await eventRegisterRedeem.save()
|
await eventRegisterRedeem.save()
|
||||||
} else {
|
} else {
|
||||||
await EVENT_REGISTER(dbUser.id)
|
await EVENT_REGISTER(dbUser)
|
||||||
}
|
}
|
||||||
|
|
||||||
return new User(dbUser)
|
return new User(dbUser)
|
||||||
@ -470,7 +479,7 @@ export class UserResolver {
|
|||||||
await queryRunner.commitTransaction()
|
await queryRunner.commitTransaction()
|
||||||
logger.info('User and UserContact data written successfully...')
|
logger.info('User and UserContact data written successfully...')
|
||||||
|
|
||||||
await EVENT_ACTIVATE_ACCOUNT(user.id)
|
await EVENT_ACTIVATE_ACCOUNT(user)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
await queryRunner.rollbackTransaction()
|
await queryRunner.rollbackTransaction()
|
||||||
throw new LogError('Error on writing User and User Contact data', e)
|
throw new LogError('Error on writing User and User Contact data', e)
|
||||||
@ -780,9 +789,13 @@ export class UserResolver {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO this is an admin function - needs refactor
|
||||||
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
@Authorized([RIGHTS.SEND_ACTIVATION_EMAIL])
|
||||||
@Mutation(() => Boolean)
|
@Mutation(() => Boolean)
|
||||||
async sendActivationEmail(@Arg('email') email: string): Promise<boolean> {
|
async sendActivationEmail(
|
||||||
|
@Arg('email') email: string,
|
||||||
|
@Ctx() context: Context,
|
||||||
|
): Promise<boolean> {
|
||||||
email = email.trim().toLowerCase()
|
email = email.trim().toLowerCase()
|
||||||
// const user = await dbUser.findOne({ id: emailContact.userId })
|
// const user = await dbUser.findOne({ id: emailContact.userId })
|
||||||
const user = await findUserByEmail(email)
|
const user = await findUserByEmail(email)
|
||||||
@ -807,7 +820,7 @@ export class UserResolver {
|
|||||||
if (!emailSent) {
|
if (!emailSent) {
|
||||||
logger.info(`Account confirmation link: ${activationLink}`)
|
logger.info(`Account confirmation link: ${activationLink}`)
|
||||||
} else {
|
} else {
|
||||||
await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user.id)
|
await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user, getUser(context))
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -3,21 +3,30 @@ import { Order } from '@enum/Order'
|
|||||||
import { Contribution as DbContribution } from '@entity/Contribution'
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
import { In } from '@dbTools/typeorm'
|
import { In } from '@dbTools/typeorm'
|
||||||
|
|
||||||
|
interface FindContributionsOptions {
|
||||||
|
order: Order
|
||||||
|
currentPage: number
|
||||||
|
pageSize: number
|
||||||
|
withDeleted?: boolean
|
||||||
|
relations?: string[]
|
||||||
|
userId?: number | null
|
||||||
|
statusFilter?: ContributionStatus[] | null
|
||||||
|
}
|
||||||
|
|
||||||
export const findContributions = async (
|
export const findContributions = async (
|
||||||
order: Order,
|
options: FindContributionsOptions,
|
||||||
currentPage: number,
|
): Promise<[DbContribution[], number]> => {
|
||||||
pageSize: number,
|
const { order, currentPage, pageSize, withDeleted, relations, userId, statusFilter } = {
|
||||||
withDeleted: boolean,
|
withDeleted: false,
|
||||||
relations: string[],
|
relations: [],
|
||||||
userId?: number,
|
...options,
|
||||||
statusFilter?: ContributionStatus[] | null,
|
}
|
||||||
): Promise<[DbContribution[], number]> =>
|
return DbContribution.findAndCount({
|
||||||
DbContribution.findAndCount({
|
|
||||||
where: {
|
where: {
|
||||||
...(statusFilter && statusFilter.length && { contributionStatus: In(statusFilter) }),
|
...(statusFilter && statusFilter.length && { contributionStatus: In(statusFilter) }),
|
||||||
...(userId && { userId }),
|
...(userId && { userId }),
|
||||||
},
|
},
|
||||||
withDeleted: withDeleted,
|
withDeleted,
|
||||||
order: {
|
order: {
|
||||||
createdAt: order,
|
createdAt: order,
|
||||||
id: order,
|
id: order,
|
||||||
@ -26,3 +35,4 @@ export const findContributions = async (
|
|||||||
skip: (currentPage - 1) * pageSize,
|
skip: (currentPage - 1) * pageSize,
|
||||||
take: pageSize,
|
take: pageSize,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -133,6 +133,22 @@ export const communities = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const getCommunities = gql`
|
||||||
|
query {
|
||||||
|
getCommunities {
|
||||||
|
id
|
||||||
|
foreign
|
||||||
|
publicKey
|
||||||
|
url
|
||||||
|
lastAnnouncedAt
|
||||||
|
verifiedAt
|
||||||
|
lastErrorAt
|
||||||
|
createdAt
|
||||||
|
updatedAt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const queryTransactionLink = gql`
|
export const queryTransactionLink = gql`
|
||||||
query ($code: String!) {
|
query ($code: String!) {
|
||||||
queryTransactionLink(code: $code) {
|
queryTransactionLink(code: $code) {
|
||||||
@ -204,18 +220,20 @@ query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusF
|
|||||||
`
|
`
|
||||||
// from admin interface
|
// from admin interface
|
||||||
|
|
||||||
export const adminListAllContributions = gql`
|
export const adminListContributions = 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!]
|
||||||
|
$userId: Int
|
||||||
) {
|
) {
|
||||||
adminListAllContributions(
|
adminListContributions(
|
||||||
currentPage: $currentPage
|
currentPage: $currentPage
|
||||||
pageSize: $pageSize
|
pageSize: $pageSize
|
||||||
order: $order
|
order: $order
|
||||||
statusFilter: $statusFilter
|
statusFilter: $statusFilter
|
||||||
|
userId: $userId
|
||||||
) {
|
) {
|
||||||
contributionCount
|
contributionCount
|
||||||
contributionList {
|
contributionList {
|
||||||
|
|||||||
@ -25,13 +25,13 @@ export class Community extends BaseEntity {
|
|||||||
endPoint: string
|
endPoint: string
|
||||||
|
|
||||||
@Column({ name: 'last_announced_at', type: 'datetime', nullable: true })
|
@Column({ name: 'last_announced_at', type: 'datetime', nullable: true })
|
||||||
lastAnnouncedAt: Date
|
lastAnnouncedAt: Date | null
|
||||||
|
|
||||||
@Column({ name: 'verified_at', type: 'datetime', nullable: true })
|
@Column({ name: 'verified_at', type: 'datetime', nullable: true })
|
||||||
verifiedAt: Date
|
verifiedAt: Date | null
|
||||||
|
|
||||||
@Column({ name: 'last_error_at', type: 'datetime', nullable: true })
|
@Column({ name: 'last_error_at', type: 'datetime', nullable: true })
|
||||||
lastErrorAt: Date
|
lastErrorAt: Date | null
|
||||||
|
|
||||||
@CreateDateColumn({
|
@CreateDateColumn({
|
||||||
name: 'created_at',
|
name: 'created_at',
|
||||||
|
|||||||
83
database/entity/0061-event_refactoring/Event.ts
Normal file
83
database/entity/0061-event_refactoring/Event.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import { Contribution } from '../Contribution'
|
||||||
|
import { ContributionMessage } from '../ContributionMessage'
|
||||||
|
import { User } from '../User'
|
||||||
|
import { Transaction } from '../Transaction'
|
||||||
|
import Decimal from 'decimal.js-light'
|
||||||
|
import {
|
||||||
|
BaseEntity,
|
||||||
|
Entity,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
ManyToOne,
|
||||||
|
JoinColumn,
|
||||||
|
} from 'typeorm'
|
||||||
|
import { DecimalTransformer } from '../../src/typeorm/DecimalTransformer'
|
||||||
|
|
||||||
|
@Entity('events')
|
||||||
|
export class Event extends BaseEntity {
|
||||||
|
@PrimaryGeneratedColumn('increment', { unsigned: true })
|
||||||
|
id: number
|
||||||
|
|
||||||
|
@Column({ length: 100, nullable: false, collation: 'utf8mb4_unicode_ci' })
|
||||||
|
type: string
|
||||||
|
|
||||||
|
@CreateDateColumn({
|
||||||
|
name: 'created_at',
|
||||||
|
type: 'datetime',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP(3)',
|
||||||
|
nullable: false,
|
||||||
|
})
|
||||||
|
createdAt: Date
|
||||||
|
|
||||||
|
@Column({ name: 'affected_user_id', unsigned: true, nullable: false })
|
||||||
|
affectedUserId: number
|
||||||
|
|
||||||
|
@ManyToOne(() => User)
|
||||||
|
@JoinColumn({ name: 'affected_user_id', referencedColumnName: 'id' })
|
||||||
|
affectedUser: User
|
||||||
|
|
||||||
|
@Column({ name: 'acting_user_id', unsigned: true, nullable: false })
|
||||||
|
actingUserId: number
|
||||||
|
|
||||||
|
@ManyToOne(() => User)
|
||||||
|
@JoinColumn({ name: 'acting_user_id', referencedColumnName: 'id' })
|
||||||
|
actingUser: User
|
||||||
|
|
||||||
|
@Column({ name: 'involved_user_id', type: 'int', unsigned: true, nullable: true })
|
||||||
|
involvedUserId: number | null
|
||||||
|
|
||||||
|
@ManyToOne(() => User)
|
||||||
|
@JoinColumn({ name: 'involved_user_id', referencedColumnName: 'id' })
|
||||||
|
involvedUser: User | null
|
||||||
|
|
||||||
|
@Column({ name: 'involved_transaction_id', type: 'int', unsigned: true, nullable: true })
|
||||||
|
involvedTransactionId: number | null
|
||||||
|
|
||||||
|
@ManyToOne(() => Transaction)
|
||||||
|
@JoinColumn({ name: 'involved_transaction_id', referencedColumnName: 'id' })
|
||||||
|
involvedTransaction: Transaction | null
|
||||||
|
|
||||||
|
@Column({ name: 'involved_contribution_id', type: 'int', unsigned: true, nullable: true })
|
||||||
|
involvedContributionId: number | null
|
||||||
|
|
||||||
|
@ManyToOne(() => Contribution)
|
||||||
|
@JoinColumn({ name: 'involved_contribution_id', referencedColumnName: 'id' })
|
||||||
|
involvedContribution: Contribution | null
|
||||||
|
|
||||||
|
@Column({ name: 'involved_contribution_message_id', type: 'int', unsigned: true, nullable: true })
|
||||||
|
involvedContributionMessageId: number | null
|
||||||
|
|
||||||
|
@ManyToOne(() => ContributionMessage)
|
||||||
|
@JoinColumn({ name: 'involved_contribution_message_id', referencedColumnName: 'id' })
|
||||||
|
involvedContributionMessage: ContributionMessage | null
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'decimal',
|
||||||
|
precision: 40,
|
||||||
|
scale: 20,
|
||||||
|
nullable: true,
|
||||||
|
transformer: DecimalTransformer,
|
||||||
|
})
|
||||||
|
amount: Decimal | null
|
||||||
|
}
|
||||||
1
database/entity/Event.ts
Normal file
1
database/entity/Event.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { Event } from './0061-event_refactoring/Event'
|
||||||
@ -1 +0,0 @@
|
|||||||
export { EventProtocol } from './0050-add_messageId_to_event_protocol/EventProtocol'
|
|
||||||
@ -7,21 +7,21 @@ import { TransactionLink } from './TransactionLink'
|
|||||||
import { User } from './User'
|
import { User } from './User'
|
||||||
import { UserContact } from './UserContact'
|
import { UserContact } from './UserContact'
|
||||||
import { Contribution } from './Contribution'
|
import { Contribution } from './Contribution'
|
||||||
import { EventProtocol } from './EventProtocol'
|
import { Event } from './Event'
|
||||||
import { ContributionMessage } from './ContributionMessage'
|
import { ContributionMessage } from './ContributionMessage'
|
||||||
import { Community } from './Community'
|
import { Community } from './Community'
|
||||||
|
|
||||||
export const entities = [
|
export const entities = [
|
||||||
|
Community,
|
||||||
Contribution,
|
Contribution,
|
||||||
ContributionLink,
|
ContributionLink,
|
||||||
|
ContributionMessage,
|
||||||
|
Event,
|
||||||
LoginElopageBuys,
|
LoginElopageBuys,
|
||||||
LoginEmailOptIn,
|
LoginEmailOptIn,
|
||||||
Migration,
|
Migration,
|
||||||
Transaction,
|
Transaction,
|
||||||
TransactionLink,
|
TransactionLink,
|
||||||
User,
|
User,
|
||||||
EventProtocol,
|
|
||||||
ContributionMessage,
|
|
||||||
UserContact,
|
UserContact,
|
||||||
Community,
|
|
||||||
]
|
]
|
||||||
|
|||||||
98
database/migrations/0061-event_refactoring.ts
Normal file
98
database/migrations/0061-event_refactoring.ts
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/* MIGRATION TO REFACTOR THE EVENT_PROTOCOL TABLE
|
||||||
|
*
|
||||||
|
* This migration refactors the `event_protocol` table.
|
||||||
|
* It renames the table to `event`, introduces new fields and renames others.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn('RENAME TABLE `event_protocol` TO `events`;')
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `user_id` TO `affected_user_id`;')
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` ADD COLUMN `acting_user_id` int(10) unsigned DEFAULT NULL AFTER `affected_user_id`;',
|
||||||
|
)
|
||||||
|
await queryFn('UPDATE `events` SET `acting_user_id` = `affected_user_id`;')
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` MODIFY COLUMN `acting_user_id` int(10) unsigned NOT NULL AFTER `affected_user_id`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `x_user_id` TO `involved_user_id`;')
|
||||||
|
await queryFn('ALTER TABLE `events` DROP COLUMN `x_community_id`;')
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `transaction_id` TO `involved_transaction_id`;')
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` RENAME COLUMN `contribution_id` TO `involved_contribution_id`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` MODIFY COLUMN `message_id` int(10) unsigned DEFAULT NULL AFTER `involved_contribution_id`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` RENAME COLUMN `message_id` TO `involved_contribution_message_id`;',
|
||||||
|
)
|
||||||
|
|
||||||
|
// Moderator id was saved in former user_id
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` LEFT JOIN `contributions` ON events.involved_contribution_id = contributions.id SET affected_user_id=contributions.user_id WHERE `type` = "ADMIN_CONTRIBUTION_CREATE";',
|
||||||
|
)
|
||||||
|
|
||||||
|
// inconsistent data on this type, since not all data can be reconstructed
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` LEFT JOIN `contributions` ON events.involved_contribution_id = contributions.id SET acting_user_id=0 WHERE `type` = "ADMIN_CONTRIBUTION_UPDATE";',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` LEFT JOIN `contributions` ON events.involved_contribution_id = contributions.id SET acting_user_id=contributions.deleted_by WHERE `type` = "ADMIN_CONTRIBUTION_DELETE";',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` LEFT JOIN `contributions` ON events.involved_contribution_id = contributions.id SET acting_user_id=contributions.confirmed_by WHERE `type` = "CONTRIBUTION_CONFIRM";',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` LEFT JOIN `contributions` ON events.involved_contribution_id = contributions.id SET involved_user_id=NULL, acting_user_id=contributions.denied_by WHERE `type` = "ADMIN_CONTRIBUTION_DENY";',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` SET acting_user_id=involved_user_id WHERE `type` = "TRANSACTION_RECEIVE";',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn('UPDATE `events` SET amount = amount * -1 WHERE `type` = "TRANSACTION_SEND";')
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` MODIFY COLUMN `created_at` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` MODIFY COLUMN `created_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP();',
|
||||||
|
)
|
||||||
|
|
||||||
|
await queryFn('UPDATE `events` SET amount = amount * -1 WHERE `type` = "TRANSACTION_SEND";')
|
||||||
|
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` SET involved_user_id=acting_user_id WHERE `type` = "ADMIN_CONTRIBUTION_DENY";',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` SET affected_user_id=acting_user_id WHERE `type` = "ADMIN_CONTRIBUTION_CREATE";',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` RENAME COLUMN `involved_contribution_message_id` TO `message_id`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` MODIFY COLUMN `message_id` int(10) unsigned DEFAULT NULL AFTER `amount`;',
|
||||||
|
)
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` RENAME COLUMN `involved_contribution_id` TO `contribution_id`;',
|
||||||
|
)
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `involved_transaction_id` TO `transaction_id`;')
|
||||||
|
await queryFn(
|
||||||
|
'ALTER TABLE `events` ADD COLUMN `x_community_id` int(10) unsigned DEFAULT NULL AFTER `involved_user_id`;',
|
||||||
|
)
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `involved_user_id` TO `x_user_id`;')
|
||||||
|
await queryFn('ALTER TABLE `events` DROP COLUMN `acting_user_id`;')
|
||||||
|
await queryFn('ALTER TABLE `events` RENAME COLUMN `affected_user_id` TO `user_id`;')
|
||||||
|
await queryFn('RENAME TABLE `events` TO `event_protocol`;')
|
||||||
|
}
|
||||||
20
database/migrations/0062-event_contribution_confirm.ts
Normal file
20
database/migrations/0062-event_contribution_confirm.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/* MIGRATION TO RENAME CONTRIBUTION_CONFIRM EVENT
|
||||||
|
*
|
||||||
|
* This migration renames the CONTRIBUTION_CONFIRM Event
|
||||||
|
* to ADMIN_CONTRIBUTION_CONFIRM
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
|
export async function upgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` SET `type` = "ADMIN_CONTRIBUTION_CONFIRM" WHERE `type` = "CONTRIBUTION_CONFIRM";',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function downgrade(queryFn: (query: string, values?: any[]) => Promise<Array<any>>) {
|
||||||
|
await queryFn(
|
||||||
|
'UPDATE `events` SET `type` = "CONTRIBUTION_CONFIRM" WHERE `type` = "ADMIN_CONTRIBUTION_CONFIRM";',
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -16,9 +16,7 @@
|
|||||||
"dev_up": "cross-env TZ=UTC ts-node src/index.ts up",
|
"dev_up": "cross-env TZ=UTC ts-node src/index.ts up",
|
||||||
"dev_down": "cross-env TZ=UTC ts-node src/index.ts down",
|
"dev_down": "cross-env TZ=UTC ts-node src/index.ts down",
|
||||||
"dev_reset": "cross-env TZ=UTC ts-node src/index.ts reset",
|
"dev_reset": "cross-env TZ=UTC ts-node src/index.ts reset",
|
||||||
"lint": "eslint --max-warnings=0 --ext .js,.ts .",
|
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||||
"seed:config": "ts-node ./node_modules/typeorm-seeding/dist/cli.js config",
|
|
||||||
"seed": "cross-env TZ=UTC ts-node src/index.ts seed"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/faker": "^5.5.9",
|
"@types/faker": "^5.5.9",
|
||||||
|
|||||||
@ -65,7 +65,9 @@ FEDERATION_COMMUNITY_URL=http://stage1.gradido.net
|
|||||||
# the api port is the baseport, which will be added with the api-version, e.g. 1_0 = 5010
|
# the api port is the baseport, which will be added with the api-version, e.g. 1_0 = 5010
|
||||||
FEDERATION_COMMUNITY_API_PORT=5000
|
FEDERATION_COMMUNITY_API_PORT=5000
|
||||||
|
|
||||||
|
FEDERATION_CONFIG_VERSION=v1.2023-01-09
|
||||||
|
# comma separated list of api-versions, which cause starting several federation modules
|
||||||
|
FEDERATION_COMMUNITY_APIS=1_0,1_1,2_0
|
||||||
|
|
||||||
# database
|
# database
|
||||||
DATABASE_CONFIG_VERSION=v1.2022-03-18
|
DATABASE_CONFIG_VERSION=v1.2022-03-18
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
location /api/$FEDERATION_APIVERSION {
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection 'upgrade';
|
||||||
|
proxy_set_header X-Forwarded-For $remote_addr;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
|
||||||
|
proxy_pass http://127.0.0.1:$FEDERATION_PORT;
|
||||||
|
proxy_redirect off;
|
||||||
|
|
||||||
|
access_log $GRADIDO_LOG_PATH/nginx-access.federation-$FEDERATION_PORT.log gradido_log;
|
||||||
|
error_log $GRADIDO_LOG_PATH/nginx-error.federation-$FEDERATION_PORT.log warn;
|
||||||
|
}
|
||||||
@ -112,6 +112,9 @@ server {
|
|||||||
error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn;
|
error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
$FEDERATION_NGINX_CONF
|
||||||
|
|
||||||
# TODO this could be a performance optimization
|
# TODO this could be a performance optimization
|
||||||
#location /vue {
|
#location /vue {
|
||||||
# alias /var/www/html/gradido/frontend/dist;
|
# alias /var/www/html/gradido/frontend/dist;
|
||||||
|
|||||||
@ -98,6 +98,9 @@ server {
|
|||||||
error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn;
|
error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
$FEDERATION_NGINX_CONF
|
||||||
|
|
||||||
# TODO this could be a performance optimization
|
# TODO this could be a performance optimization
|
||||||
#location /vue {
|
#location /vue {
|
||||||
# alias /var/www/html/gradido/frontend/dist;
|
# alias /var/www/html/gradido/frontend/dist;
|
||||||
|
|||||||
@ -59,8 +59,9 @@ ln -s /etc/nginx/sites-available/update-page.conf /etc/nginx/sites-enabled/
|
|||||||
sudo /etc/init.d/nginx restart
|
sudo /etc/init.d/nginx restart
|
||||||
|
|
||||||
# stop all services
|
# stop all services
|
||||||
echo 'Stopping all Gradido services' >> $UPDATE_HTML
|
echo 'Stop and delete all Gradido services' >> $UPDATE_HTML
|
||||||
pm2 stop all
|
pm2 delete all
|
||||||
|
pm2 save
|
||||||
|
|
||||||
# git
|
# git
|
||||||
BRANCH=${1:-master}
|
BRANCH=${1:-master}
|
||||||
@ -73,12 +74,41 @@ git pull
|
|||||||
export BUILD_COMMIT="$(git rev-parse HEAD)"
|
export BUILD_COMMIT="$(git rev-parse HEAD)"
|
||||||
|
|
||||||
# Generate gradido.conf from template
|
# Generate gradido.conf from template
|
||||||
|
# *** 1st prepare for each apiversion the federation conf for nginx from federation-template
|
||||||
|
# *** set FEDERATION_PORT from FEDERATION_COMMUNITY_APIS and create gradido-federation.conf file
|
||||||
|
rm -f $NGINX_CONFIG_DIR/gradido.conf.tmp
|
||||||
|
rm -f $NGINX_CONFIG_DIR/gradido-federation.conf.locations
|
||||||
|
echo "====================================================================================================" >> $UPDATE_HTML
|
||||||
|
IFS="," read -a API_ARRAY <<< $FEDERATION_COMMUNITY_APIS
|
||||||
|
for api in "${API_ARRAY[@]}"
|
||||||
|
do
|
||||||
|
export FEDERATION_APIVERSION=$api
|
||||||
|
# calculate port by remove '_' and add value of api to baseport
|
||||||
|
port=${api//_/}
|
||||||
|
FEDERATION_PORT=${FEDERATION_COMMUNITY_API_PORT:-5000}
|
||||||
|
FEDERATION_PORT=$(($FEDERATION_PORT + $port))
|
||||||
|
export FEDERATION_PORT
|
||||||
|
echo "create ngingx config: location /api/$FEDERATION_APIVERSION to http://127.0.0.1:$FEDERATION_PORT" >> $UPDATE_HTML
|
||||||
|
envsubst '$FEDERATION_APIVERSION, $FEDERATION_PORT' < $NGINX_CONFIG_DIR/gradido-federation.conf.template >> $NGINX_CONFIG_DIR/gradido-federation.conf.locations
|
||||||
|
done
|
||||||
|
unset FEDERATION_APIVERSION
|
||||||
|
unset FEDERATION_PORT
|
||||||
|
echo "====================================================================================================" >> $UPDATE_HTML
|
||||||
|
|
||||||
|
# *** 2nd read gradido-federation.conf file in env variable to be replaced in 3rd step
|
||||||
|
export FEDERATION_NGINX_CONF=$(< $NGINX_CONFIG_DIR/gradido-federation.conf.locations)
|
||||||
|
|
||||||
|
# *** 3rd generate gradido nginx config including federation modules per api-version
|
||||||
echo 'Generate new gradido nginx config' >> $UPDATE_HTML
|
echo 'Generate new gradido nginx config' >> $UPDATE_HTML
|
||||||
case "$NGINX_SSL" in
|
case "$NGINX_SSL" in
|
||||||
true) TEMPLATE_FILE="gradido.conf.ssl.template" ;;
|
true) TEMPLATE_FILE="gradido.conf.ssl.template" ;;
|
||||||
*) TEMPLATE_FILE="gradido.conf.template" ;;
|
*) TEMPLATE_FILE="gradido.conf.template" ;;
|
||||||
esac
|
esac
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/gradido.conf
|
envsubst '$FEDERATION_NGINX_CONF' < $NGINX_CONFIG_DIR/$TEMPLATE_FILE > $NGINX_CONFIG_DIR/gradido.conf.tmp
|
||||||
|
unset FEDERATION_NGINX_CONF
|
||||||
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $NGINX_CONFIG_DIR/gradido.conf.tmp > $NGINX_CONFIG_DIR/gradido.conf
|
||||||
|
rm $NGINX_CONFIG_DIR/gradido.conf.tmp
|
||||||
|
rm $NGINX_CONFIG_DIR/gradido-federation.conf.locations
|
||||||
|
|
||||||
# Generate update-page.conf from template
|
# Generate update-page.conf from template
|
||||||
echo 'Generate new update-page nginx config' >> $UPDATE_HTML
|
echo 'Generate new update-page nginx config' >> $UPDATE_HTML
|
||||||
@ -94,11 +124,13 @@ cp -f $PROJECT_ROOT/backend/.env $PROJECT_ROOT/backend/.env.bak
|
|||||||
cp -f $PROJECT_ROOT/frontend/.env $PROJECT_ROOT/frontend/.env.bak
|
cp -f $PROJECT_ROOT/frontend/.env $PROJECT_ROOT/frontend/.env.bak
|
||||||
cp -f $PROJECT_ROOT/admin/.env $PROJECT_ROOT/admin/.env.bak
|
cp -f $PROJECT_ROOT/admin/.env $PROJECT_ROOT/admin/.env.bak
|
||||||
cp -f $PROJECT_ROOT/dht-node/.env $PROJECT_ROOT/dht-node/.env.bak
|
cp -f $PROJECT_ROOT/dht-node/.env $PROJECT_ROOT/dht-node/.env.bak
|
||||||
|
cp -f $PROJECT_ROOT/federation/.env $PROJECT_ROOT/federation/.env.bak
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/database/.env.template > $PROJECT_ROOT/database/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/database/.env.template > $PROJECT_ROOT/database/.env
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/backend/.env.template > $PROJECT_ROOT/backend/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/backend/.env.template > $PROJECT_ROOT/backend/.env
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env.template > $PROJECT_ROOT/frontend/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/frontend/.env.template > $PROJECT_ROOT/frontend/.env
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/admin/.env.template > $PROJECT_ROOT/admin/.env
|
||||||
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/dht-node/.env.template > $PROJECT_ROOT/dht-node/.env
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/dht-node/.env.template > $PROJECT_ROOT/dht-node/.env
|
||||||
|
envsubst "$(env | sed -e 's/=.*//' -e 's/^/\$/g')" < $PROJECT_ROOT/federation/.env.template > $PROJECT_ROOT/federation/.env
|
||||||
|
|
||||||
# Install & build database
|
# Install & build database
|
||||||
echo 'Updating database' >> $UPDATE_HTML
|
echo 'Updating database' >> $UPDATE_HTML
|
||||||
@ -124,7 +156,6 @@ if [ "$DEPLOY_SEED_DATA" = "true" ]; then
|
|||||||
fi
|
fi
|
||||||
# TODO maybe handle this differently?
|
# TODO maybe handle this differently?
|
||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
pm2 delete gradido-backend
|
|
||||||
pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
pm2 start --name gradido-backend "yarn --cwd $PROJECT_ROOT/backend start" -l $GRADIDO_LOG_PATH/pm2.backend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
@ -137,7 +168,6 @@ yarn install
|
|||||||
yarn build
|
yarn build
|
||||||
# TODO maybe handle this differently?
|
# TODO maybe handle this differently?
|
||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
pm2 delete gradido-frontend
|
|
||||||
pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
pm2 start --name gradido-frontend "yarn --cwd $PROJECT_ROOT/frontend start" -l $GRADIDO_LOG_PATH/pm2.frontend.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
@ -150,7 +180,6 @@ yarn install
|
|||||||
yarn build
|
yarn build
|
||||||
# TODO maybe handle this differently?
|
# TODO maybe handle this differently?
|
||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
pm2 delete gradido-admin
|
|
||||||
pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
pm2 start --name gradido-admin "yarn --cwd $PROJECT_ROOT/admin start" -l $GRADIDO_LOG_PATH/pm2.admin.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
pm2 save
|
pm2 save
|
||||||
|
|
||||||
@ -163,15 +192,49 @@ yarn install
|
|||||||
yarn build
|
yarn build
|
||||||
# TODO maybe handle this differently?
|
# TODO maybe handle this differently?
|
||||||
export NODE_ENV=production
|
export NODE_ENV=production
|
||||||
pm2 delete gradido-dht-node
|
|
||||||
if [ ! -z $FEDERATION_DHT_TOPIC ]; then
|
if [ ! -z $FEDERATION_DHT_TOPIC ]; then
|
||||||
pm2 start --name gradido-dht-node "yarn --cwd $PROJECT_ROOT/dht-node start" -l $GRADIDO_LOG_PATH/pm2.dht-node.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
pm2 start --name gradido-dht-node "yarn --cwd $PROJECT_ROOT/dht-node start" -l $GRADIDO_LOG_PATH/pm2.dht-node.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
else
|
|
||||||
echo "====================================================================="
|
|
||||||
echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..."
|
|
||||||
echo "====================================================================="
|
|
||||||
fi
|
|
||||||
pm2 save
|
pm2 save
|
||||||
|
else
|
||||||
|
echo "=====================================================================" >> $UPDATE_HTML
|
||||||
|
echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..." >> $UPDATE_HTML
|
||||||
|
echo "=====================================================================" >> $UPDATE_HTML
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Install & build federation
|
||||||
|
echo 'Updating federation' >> $UPDATE_HTML
|
||||||
|
cd $PROJECT_ROOT/federation
|
||||||
|
# TODO maybe handle this differently?
|
||||||
|
unset NODE_ENV
|
||||||
|
yarn install
|
||||||
|
yarn build
|
||||||
|
# TODO maybe handle this differently?
|
||||||
|
export NODE_ENV=production
|
||||||
|
|
||||||
|
# set FEDERATION_PORT from FEDERATION_COMMUNITY_APIS
|
||||||
|
IFS="," read -a API_ARRAY <<< $FEDERATION_COMMUNITY_APIS
|
||||||
|
for api in "${API_ARRAY[@]}"
|
||||||
|
do
|
||||||
|
export FEDERATION_API=$api
|
||||||
|
echo "FEDERATION_API=$FEDERATION_API" >> $UPDATE_HTML
|
||||||
|
export MODULENAME=gradido-federation-$api
|
||||||
|
echo "MODULENAME=$MODULENAME" >> $UPDATE_HTML
|
||||||
|
# calculate port by remove '_' and add value of api to baseport
|
||||||
|
port=${api//_/}
|
||||||
|
FEDERATION_PORT=${FEDERATION_COMMUNITY_API_PORT:-5000}
|
||||||
|
FEDERATION_PORT=$(($FEDERATION_PORT + $port))
|
||||||
|
export FEDERATION_PORT
|
||||||
|
echo "====================================================" >> $UPDATE_HTML
|
||||||
|
echo " start $MODULENAME listening on port=$FEDERATION_PORT" >> $UPDATE_HTML
|
||||||
|
echo "====================================================" >> $UPDATE_HTML
|
||||||
|
# pm2 delete $MODULENAME
|
||||||
|
pm2 start --name $MODULENAME "yarn --cwd $PROJECT_ROOT/federation start" -l $GRADIDO_LOG_PATH/pm2.$MODULENAME.$TODAY.log --log-date-format 'YYYY-MM-DD HH:mm:ss.SSS'
|
||||||
|
pm2 save
|
||||||
|
done
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# let nginx showing gradido
|
# let nginx showing gradido
|
||||||
echo 'Configuring nginx to serve gradido again' >> $UPDATE_HTML
|
echo 'Configuring nginx to serve gradido again' >> $UPDATE_HTML
|
||||||
|
|||||||
@ -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: [],
|
setupFilesAfterEnv: [],
|
||||||
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||||
|
|||||||
@ -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 dotenv/config -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@hyperswarm/dht": "^6.4.4",
|
"@hyperswarm/dht": "^6.4.4",
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import dotenv from 'dotenv'
|
|||||||
dotenv.config()
|
dotenv.config()
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0060-update_communities_table',
|
DB_VERSION: '0062-event_contribution_confirm',
|
||||||
LOG4JS_CONFIG: 'log4js-config.json',
|
LOG4JS_CONFIG: 'log4js-config.json',
|
||||||
// default log level on production should be info
|
// default log level on production should be info
|
||||||
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
LOG_LEVEL: process.env.LOG_LEVEL || 'info',
|
||||||
|
|||||||
@ -719,11 +719,11 @@ describe('federation', () => {
|
|||||||
JSON.stringify([
|
JSON.stringify([
|
||||||
{
|
{
|
||||||
api: '1_0',
|
api: '1_0',
|
||||||
url: 'http://localhost:5001/api/',
|
url: 'http://localhost/api/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: '2_0',
|
api: '2_0',
|
||||||
url: 'http://localhost:5002/api/',
|
url: 'http://localhost/api/',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
@ -747,7 +747,7 @@ describe('federation', () => {
|
|||||||
foreign: true,
|
foreign: true,
|
||||||
publicKey: expect.any(Buffer),
|
publicKey: expect.any(Buffer),
|
||||||
apiVersion: '1_0',
|
apiVersion: '1_0',
|
||||||
endPoint: 'http://localhost:5001/api/',
|
endPoint: 'http://localhost/api/',
|
||||||
lastAnnouncedAt: expect.any(Date),
|
lastAnnouncedAt: expect.any(Date),
|
||||||
createdAt: expect.any(Date),
|
createdAt: expect.any(Date),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
@ -764,7 +764,7 @@ describe('federation', () => {
|
|||||||
foreign: true,
|
foreign: true,
|
||||||
publicKey: expect.any(Buffer),
|
publicKey: expect.any(Buffer),
|
||||||
apiVersion: '2_0',
|
apiVersion: '2_0',
|
||||||
endPoint: 'http://localhost:5002/api/',
|
endPoint: 'http://localhost/api/',
|
||||||
lastAnnouncedAt: expect.any(Date),
|
lastAnnouncedAt: expect.any(Date),
|
||||||
createdAt: expect.any(Date),
|
createdAt: expect.any(Date),
|
||||||
updatedAt: null,
|
updatedAt: null,
|
||||||
@ -786,15 +786,15 @@ describe('federation', () => {
|
|||||||
JSON.stringify([
|
JSON.stringify([
|
||||||
{
|
{
|
||||||
api: '1_0',
|
api: '1_0',
|
||||||
url: 'http://localhost:5001/api/',
|
url: 'http://localhost/api/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: '1_1',
|
api: '1_1',
|
||||||
url: 'http://localhost:5002/api/',
|
url: 'http://localhost/api/',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
api: '2_0',
|
api: '2_0',
|
||||||
url: 'http://localhost:5003/api/',
|
url: 'http://localhost/api/',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
|
|||||||
@ -181,11 +181,9 @@ export const startDHT = async (topic: string): Promise<void> => {
|
|||||||
|
|
||||||
async function writeHomeCommunityEnries(pubKey: any): Promise<CommunityApi[]> {
|
async function writeHomeCommunityEnries(pubKey: any): Promise<CommunityApi[]> {
|
||||||
const homeApiVersions: CommunityApi[] = Object.values(ApiVersionType).map(function (apiEnum) {
|
const homeApiVersions: CommunityApi[] = Object.values(ApiVersionType).map(function (apiEnum) {
|
||||||
const port =
|
|
||||||
Number.parseInt(CONFIG.FEDERATION_COMMUNITY_API_PORT) + Number(apiEnum.replace('_', ''))
|
|
||||||
const comApi: CommunityApi = {
|
const comApi: CommunityApi = {
|
||||||
api: apiEnum,
|
api: apiEnum,
|
||||||
url: CONFIG.FEDERATION_COMMUNITY_URL + ':' + port.toString() + '/api/',
|
url: CONFIG.FEDERATION_COMMUNITY_URL + '/api/',
|
||||||
}
|
}
|
||||||
return comApi
|
return comApi
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,29 +4,6 @@ This document contains the concept and technical details for the *federation* of
|
|||||||
|
|
||||||
But meanwhile the usage of a DHT like HyperSwarm promises more coverage of the gradido requirements out of the box. More details about HyperSwarm can be found here [@hyperswarm/dht](https://github.com/hyperswarm/dht).
|
But meanwhile the usage of a DHT like HyperSwarm promises more coverage of the gradido requirements out of the box. More details about HyperSwarm can be found here [@hyperswarm/dht](https://github.com/hyperswarm/dht).
|
||||||
|
|
||||||
## ActivityPub (deprecated)
|
|
||||||
|
|
||||||
The activity pub defines a server-to-server federation protocol to share information between decentralized instances and will be the main komponent for the gradido community federation.
|
|
||||||
|
|
||||||
At first we asume a *gradido community* as an *ActivityPub user*. A user is represented by "*actors*" via the users's accounts on servers. User's accounts on different servers corrsponds to different actors, which means community accounts on different servers corrsponds to different communities.
|
|
||||||
|
|
||||||
Every community (actor) has an:
|
|
||||||
|
|
||||||
* inbox: to get messages from the world
|
|
||||||
* outbox: to send messages to others
|
|
||||||
|
|
||||||
and are simple endpoints or just URLs, which are described in the *ActivityStream* of each *ActivityPub community*.
|
|
||||||
|
|
||||||
### Open Decision:
|
|
||||||
|
|
||||||
It has to be decided, if the Federation will work with an internal or with external ActivityPub-Server, as shown in the picture below:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
The Variant A with an internal server contains the benefit to be as independent as possible from third party service providers and will not cause additional hosting costs. But this solution will cause the additional efforts of impementing an ActivityPub-Server in the gradido application and the responsibility for this component.
|
|
||||||
|
|
||||||
The Varaint B with an external server contains the benefit to reduce the implementation efforts and the responsibility for an own ActivitPub-Server. But it will cause an additional dependency to a third party service provider and the growing hosting costs.
|
|
||||||
|
|
||||||
## HyperSwarm
|
## HyperSwarm
|
||||||
|
|
||||||
The decision to switch from ActivityPub to HyperSwarm base on the arguments, that the *hyperswarm/dht* library will satify the most federation requirements out of the box. It is now to design the business requirements of the [gradido community communication](../BusinessRequirements/CommunityVerwaltung.md#UC-createCommunity) in a technical conception.
|
The decision to switch from ActivityPub to HyperSwarm base on the arguments, that the *hyperswarm/dht* library will satify the most federation requirements out of the box. It is now to design the business requirements of the [gradido community communication](../BusinessRequirements/CommunityVerwaltung.md#UC-createCommunity) in a technical conception.
|
||||||
@ -41,12 +18,30 @@ To enable such a relationship between an existing community and a new community
|
|||||||
2. Authentication
|
2. Authentication
|
||||||
3. Autorized Communication
|
3. Autorized Communication
|
||||||
|
|
||||||
### Overview
|
### Overview of Federation-Handshake
|
||||||
|
|
||||||
At first the following diagramm gives an overview of the three stages and shows the handshake between an existing community-A and a new created community-B including the data exchange for buildup such a federated, authenticated and autorized relationship.
|
At first the following diagramm gives an overview of the three stages and shows the handshake between an existing community-A and a new created community-B including the data exchange for buildup such a federated, authenticated and autorized relationship.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
### Technical Architecture
|
||||||
|
|
||||||
|
The previous described handshake will be done by several technical modules of the gradido system. The following picture gives an overview about the modules and how the communicate with each other.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
As soon as a Gradido Community is up and running the DHT-Modul first writes the home-community-entries in the database and starts with the federation via HyperSwarm. Each community, which is configured with the configuration key GRADIDO_HUB to listen on the correct topic of the DHT will be part of the Gradido-Net-Federation. That means each DHT-Modul of each community will receive the publicKey of all connected communities. The DHT-Modul will open for each received publicKey a communication-socket with the associated community DHT-Modul. Over this open socket the connected communities exchange the data "api-version" and "url" for later direct communication between both communities. The exchanged api-version info and urls will be written in the own database.
|
||||||
|
|
||||||
|
The background of this exchanged data base on the supported api-versions a community will support with its own federation-modules. Each running federation-module in a community will support exact one graphql api-version of a cross-community-communication. To reach a dedicated federation-module with the correct api-version during a cross-community-communication the url for this federation-module must be known by both communities. As shown in the picture above the graphql-client with api-version V1_0 in the left community will interact with the federation-module with api-version V1_0 on the right community. During the lifecycle of the gradido-application it will be necessary to extent the features and interfaces for the cross-community-communication. To keep a backwards compatibilty and not to force each community to always upgrade their running software version on the last api-version at the same time, it will be necessary to support several api-versions in parallel. The different running api-version modules are responsible to convert and treat the exchanged data in a correct way to ensure konsistent data in the local database of the community.
|
||||||
|
|
||||||
|
The up and running Backend-Module contains a validation logic to verify the community entries from the own DHT-Module. For each announced but unverified community-entry the GraphQL-Client is used to invoke a getPublicKey-Request. Depending on the containing api-version the matching GraphQL-Client is used and the getPublicKey-Request will be send to the given URL.
|
||||||
|
|
||||||
|
As soon as the federation-module of the associated community received the getPublicKey-request the own publicKey is read from database and send back in the response.
|
||||||
|
|
||||||
|
The GraphQL-Client will read the publicKey of the other community from the returned response data and compare it with the data of the community-entry, which caused the getPublicKey-Request. If they match the community-entry will be updated by inserting the current timestamp in the verifiedAt-field of this community-entry.
|
||||||
|
|
||||||
|
This federation and verification logic will work the whole time and can be monitored by observing the communities-table changes. The Admin-UI will contain a Page to have a look on the current state of the communities table content.
|
||||||
|
|
||||||
### Prerequisits
|
### Prerequisits
|
||||||
|
|
||||||
Before starting in describing the details of the federation handshake, some prerequisits have to be defined.
|
Before starting in describing the details of the federation handshake, some prerequisits have to be defined.
|
||||||
@ -235,7 +230,6 @@ For the first federation release the *DHT-Node* will be part of the *apollo serv
|
|||||||
| communityApiVersion.apiversion | keep existing value |
|
| communityApiVersion.apiversion | keep existing value |
|
||||||
| communityApiVersion.validFrom | exchangedData.API.validFrom |
|
| communityApiVersion.validFrom | exchangedData.API.validFrom |
|
||||||
| communityApiVersion.verifiedAt | keep existing value |
|
| communityApiVersion.verifiedAt | keep existing value |
|
||||||
*
|
|
||||||
3. After all received data is stored successfully, the *DHT-Node* starts the *stage2 - Authentication* of the federation handshake
|
3. After all received data is stored successfully, the *DHT-Node* starts the *stage2 - Authentication* of the federation handshake
|
||||||
|
|
||||||
### Stage2 - Authentication
|
### Stage2 - Authentication
|
||||||
@ -284,8 +278,6 @@ As soon the *openConnection* request is invoked:
|
|||||||
3. check if the decrypted `parameter.signedAndEncryptedURL` is equals the selected url from the previous selected CommunityFederationEntry
|
3. check if the decrypted `parameter.signedAndEncryptedURL` is equals the selected url from the previous selected CommunityFederationEntry
|
||||||
1. if not then break the further processing of this request by only writing an error-log event. There will be no answer to the invoker community, because this community will only go on with a `openConnectionRedirect`-request from this community.
|
1. if not then break the further processing of this request by only writing an error-log event. There will be no answer to the invoker community, because this community will only go on with a `openConnectionRedirect`-request from this community.
|
||||||
2. if yes then verify the signature of `parameter.signedAndEncryptedURL` with the `cf.pubKey` read in step 2 before
|
2. if yes then verify the signature of `parameter.signedAndEncryptedURL` with the `cf.pubKey` read in step 2 before
|
||||||
3.
|
|
||||||
4.
|
|
||||||
|
|
||||||
### Stage3 - Autorized Business Communication
|
### Stage3 - Autorized Business Communication
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,282 @@
|
|||||||
|
<mxfile host="65bd71144e">
|
||||||
|
<diagram id="RqE3izjX3TYt3HTUOB95" name="Seite-1">
|
||||||
|
<mxGraphModel dx="3343" dy="773" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2336" pageHeight="1654" math="0" shadow="0">
|
||||||
|
<root>
|
||||||
|
<mxCell id="0"/>
|
||||||
|
<mxCell id="1" parent="0"/>
|
||||||
|
<mxCell id="2" value="Community&nbsp; "Gradido-Akademie"" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;verticalAlign=top;fontSize=16;fontStyle=1;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-158.26" y="80" width="870" height="800" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="3" value="Gradido - technical Infrastructure-Overview<br><font style="font-size: 12px">State of 02.2023</font>" style="text;html=1;strokeColor=#82b366;fillColor=#d5e8d4;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontSize=16;fontStyle=1;labelBorderColor=none;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1160" y="20" width="1880" height="40" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="4" value="<b>Backend-Modul</b><br>GraphQL-API" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;verticalAlign=top;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-118.25999999999999" y="269" width="540" height="101" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="7" value="CommunityServer DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#dae8fc;strokeColor=#6c8ebf;fontSize=15;fontStyle=1;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="211.74" y="760" width="150" height="80" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="8" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.75;entryY=1;entryDx=0;entryDy=0;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="7" target="4" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="191.74" y="590" as="sourcePoint"/>
|
||||||
|
<mxPoint x="241.74" y="540" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="15" value="<b>Layer 1:</b>" style="text;html=1;strokeColor=#6c8ebf;fillColor=#dae8fc;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;opacity=0;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-148.26" y="735" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="16" value="" style="endArrow=none;dashed=1;html=1;fontSize=15;fontColor=#000000;entryX=1.002;entryY=0.801;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="2" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-158.26" y="720" as="sourcePoint"/>
|
||||||
|
<mxPoint x="603.74" y="720" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="17" value="<b>Layer 2:</b>" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;fontColor=#000000;opacity=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-148.26" y="240" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="21" value=""<b>GDT-Server</b>" <br>base on cakephp + mySQL" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#fff2cc;strokeColor=#d6b656;gradientColor=#ffd966;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="491.74" y="269" width="210" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="22" value="GDT-Server DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#fff2cc;strokeColor=#d6b656;fontSize=15;fontStyle=1;gradientColor=#ffd966;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="521.74" y="760" width="150" height="80" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="23" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="22" target="21" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="381.74" y="590" as="sourcePoint"/>
|
||||||
|
<mxPoint x="586.6200000000001" y="370" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="24" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;fontColor=#000000;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" parent="1" target="21" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="421.74" y="299" as="sourcePoint"/>
|
||||||
|
<mxPoint x="401.74" y="479" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="25" value="<font style="font-size: 12px">json-<br>ajax-<br>request</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=15;fontColor=#000000;labelBackgroundColor=none;" parent="24" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.3429" relative="1" as="geometry">
|
||||||
|
<mxPoint x="10" y="-28" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="30" value="" style="endArrow=none;dashed=1;html=1;fontSize=15;fontColor=#000000;entryX=1.002;entryY=0.401;entryDx=0;entryDy=0;entryPerimeter=0;exitX=0;exitY=0.4;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-159.9999999999999" y="230" as="sourcePoint"/>
|
||||||
|
<mxPoint x="711.7399999999999" y="230.79999999999995" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="31" value="<b>Layer 3:</b>" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;fontColor=#000000;opacity=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-148.26" y="90" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="32" value=""<b>Elopage</b>" <br>external Service-Portal" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#fff2cc;strokeColor=#d6b656;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="471.74" y="120" width="210" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="33" value=""<b>User-UI</b>"" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-48.25999999999999" y="120" width="200" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="34" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;fontColor=#000000;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.315;exitY=-0.05;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="4" target="33" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="351.74" y="420" as="sourcePoint"/>
|
||||||
|
<mxPoint x="401.74" y="370" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="35" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;fontColor=#000000;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.395;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="21" target="32" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="576.24" y="310" as="sourcePoint"/>
|
||||||
|
<mxPoint x="576.24" y="180" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="36" value="<span style="font-size: 12px">graphql</span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=15;fontColor=#000000;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="191.74" y="216" as="geometry">
|
||||||
|
<mxPoint x="-169" y="-4" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="37" value="<font style="font-size: 12px">json-<br>request</font>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=15;fontColor=#000000;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="551.74" y="216" as="geometry">
|
||||||
|
<mxPoint x="-2" y="-15" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="39" style="edgeStyle=none;html=1;entryX=0.767;entryY=-0.017;entryDx=0;entryDy=0;startArrow=classic;startFill=1;entryPerimeter=0;" parent="1" source="38" target="4" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="40" value="graphql" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;" parent="39" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.4305" y="1" relative="1" as="geometry">
|
||||||
|
<mxPoint x="23" y="-1" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="38" value=""<b>Admin-UI</b>"" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="191.74" y="120" width="210" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="42" style="edgeStyle=none;html=1;entryX=0.145;entryY=0;entryDx=0;entryDy=4.35;entryPerimeter=0;startArrow=classic;startFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="41" target="7" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="41" value="<b>DHT-Modul</b><br>HyperSwarm" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-118.25999999999999" y="640" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="102" style="edgeStyle=none;html=1;fontSize=16;startArrow=classic;startFill=1;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="43" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<mxPoint x="260" y="760" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="43" value="<b>Federation-Modul</b><br>GraphQL-API V2_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;dashed=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-118.25999999999999" y="380" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="101" style="edgeStyle=none;html=1;fontSize=16;startArrow=classic;startFill=1;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="44" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry">
|
||||||
|
<mxPoint x="260" y="760" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="44" value="<b>Federation-Modul</b><br>GraphQL-API V1_x" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;dashed=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-88.25999999999999" y="430" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="100" style="edgeStyle=none;html=1;entryX=0.335;entryY=0.013;entryDx=0;entryDy=0;entryPerimeter=0;fontSize=16;startArrow=classic;startFill=1;exitX=0.78;exitY=0.667;exitDx=0;exitDy=0;exitPerimeter=0;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="45" target="7" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="45" value="<b>Federation-Modul</b><br>GraphQL-API V1_1" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;dashed=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-58.25999999999999" y="480" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="47" style="edgeStyle=none;html=1;entryX=0.333;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;startArrow=classic;startFill=1;exitX=0.996;exitY=0.633;exitDx=0;exitDy=0;exitPerimeter=0;fillColor=#dae8fc;gradientColor=#7ea6e0;strokeColor=#6c8ebf;" parent="1" source="46" target="7" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="46" value="<b>Federation-Modul</b><br>GraphQL-API V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-28.25999999999999" y="530" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="48" value="GraphQL-Client V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-108.25999999999999" y="289" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="49" value="GraphQL-Client V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-98.25999999999999" y="299" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="50" value="GraphQL-Client V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-88.25999999999999" y="309" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="51" value="GraphQL-Client V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#dae8fc;strokeColor=#6c8ebf;gradientColor=#7ea6e0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-78.25999999999999" y="319" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="52" value="Community "GallischesDorf-TBB"" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;verticalAlign=top;fontStyle=1;fontSize=16;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1148.26" y="80" width="628.26" height="800" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="54" value="<b>Backend-Modul</b><br>GraphQL-API" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;verticalAlign=top;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1108.26" y="269" width="568.26" height="101" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="55" value="CommunityServer DB" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#d5e8d4;strokeColor=#82b366;fontSize=15;fontStyle=1;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1048.26" y="770" width="150" height="80" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="56" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;" parent="1" source="55" target="54" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-798.26" y="590" as="sourcePoint"/>
|
||||||
|
<mxPoint x="-748.26" y="540" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="57" value="<b>Layer 1:</b>" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;fontColor=#000000;opacity=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1138.26" y="735" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="58" value="" style="endArrow=none;dashed=1;html=1;fontSize=15;fontColor=#000000;entryX=1.002;entryY=0.801;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" target="52" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-1148.26" y="720" as="sourcePoint"/>
|
||||||
|
<mxPoint x="-386.26" y="720" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="59" value="<b>Layer 2:</b>" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;fontColor=#000000;opacity=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1138.26" y="240" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="65" value="" style="endArrow=none;dashed=1;html=1;fontSize=15;fontColor=#000000;exitX=0;exitY=0.4;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-1149.9999999999998" y="230" as="sourcePoint"/>
|
||||||
|
<mxPoint x="-520" y="231" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="66" value="<b>Layer 3:</b>" style="text;html=1;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;whiteSpace=wrap;rounded=0;glass=0;labelBackgroundColor=none;fontSize=15;fontColor=#000000;opacity=0;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1138.26" y="90" width="70" height="20" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="68" value=""<b>User-UI</b>"" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-1030" y="120" width="200" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="69" value="" style="endArrow=classic;startArrow=classic;html=1;fontSize=15;fontColor=#000000;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.315;exitY=-0.05;exitDx=0;exitDy=0;exitPerimeter=0;" parent="1" source="54" target="68" edge="1">
|
||||||
|
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-638.26" y="420" as="sourcePoint"/>
|
||||||
|
<mxPoint x="-588.26" y="370" as="targetPoint"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="71" value="<span style="font-size: 12px">graphql</span>" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=15;fontColor=#000000;labelBackgroundColor=none;" parent="1" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-798.26" y="216" as="geometry">
|
||||||
|
<mxPoint x="-169" y="-4" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="73" style="edgeStyle=none;html=1;entryX=0.767;entryY=-0.017;entryDx=0;entryDy=0;startArrow=classic;startFill=1;entryPerimeter=0;" parent="1" source="75" target="54" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="74" value="graphql" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];labelBackgroundColor=none;" parent="73" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.4305" y="1" relative="1" as="geometry">
|
||||||
|
<mxPoint x="23" y="-1" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="75" value=""<b>Admin-UI</b>"" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-775" y="120" width="210" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="76" style="edgeStyle=none;html=1;entryX=0.855;entryY=0;entryDx=0;entryDy=4.35;entryPerimeter=0;startArrow=classic;startFill=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="77" target="55" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="90" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" parent="1" source="77" target="41" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="91" value="&nbsp;DHT-Socket Communication&nbsp;" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=16;" parent="90" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.1307" y="-1" relative="1" as="geometry">
|
||||||
|
<mxPoint as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="77" value="<b>DHT-Modul</b><br>HyperSwarm" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-745" y="640" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="87" style="edgeStyle=none;html=1;entryX=0.655;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;startArrow=classic;startFill=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="80" target="55" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="80" value="<b>Federation-Modul</b><br>GraphQL-API V1_1" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;dashed=1;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-760" y="500" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="81" style="edgeStyle=none;html=1;entryX=0.655;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;startArrow=classic;startFill=1;exitX=0;exitY=0.75;exitDx=0;exitDy=0;" parent="1" source="82" target="55" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="82" value="<b>Federation-Modul</b><br>GraphQL-API V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-730" y="550" width="190" height="60" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="98" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;startArrow=classic;startFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="85" target="45" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="99" value="graphQL-Handshake" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=16;" parent="98" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.5775" relative="1" as="geometry">
|
||||||
|
<mxPoint x="28" y="-14" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="85" value="GraphQL-Client V1_1" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-745" y="289" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="96" style="edgeStyle=none;html=1;entryX=0;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;startArrow=classic;startFill=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="86" target="46" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="97" value="graphQL-Handshake" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=16;" parent="96" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.5322" y="-1" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-11" y="16" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="86" value="GraphQL-Client V1_0" style="rounded=1;whiteSpace=wrap;html=1;fontSize=16;align=center;fillColor=#d5e8d4;strokeColor=#82b366;gradientColor=#97d077;" parent="1" vertex="1">
|
||||||
|
<mxGeometry x="-730" y="314" width="180" height="31" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="92" style="edgeStyle=none;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;startArrow=classic;startFill=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="51" target="82" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="94" value="graphQL-Handshake" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=16;" parent="92" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="0.1267" y="2" relative="1" as="geometry">
|
||||||
|
<mxPoint x="-93" y="56" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="93" style="edgeStyle=none;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;fontSize=16;startArrow=classic;startFill=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;" parent="1" source="50" target="80" edge="1">
|
||||||
|
<mxGeometry relative="1" as="geometry"/>
|
||||||
|
</mxCell>
|
||||||
|
<mxCell id="95" value="graphQL-Handshake" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];fontSize=16;" parent="93" vertex="1" connectable="0">
|
||||||
|
<mxGeometry x="-0.0706" relative="1" as="geometry">
|
||||||
|
<mxPoint x="63" y="-60" as="offset"/>
|
||||||
|
</mxGeometry>
|
||||||
|
</mxCell>
|
||||||
|
</root>
|
||||||
|
</mxGraphModel>
|
||||||
|
</diagram>
|
||||||
|
</mxfile>
|
||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 61 KiB |
@ -56,7 +56,7 @@ export default defineConfig({
|
|||||||
env: {
|
env: {
|
||||||
backendURL: 'http://localhost:4000',
|
backendURL: 'http://localhost:4000',
|
||||||
mailserverURL: 'http://localhost:1080',
|
mailserverURL: 'http://localhost:1080',
|
||||||
loginQuery: `query ($email: String!, $password: String!, $publisherId: Int) {
|
loginQuery: `mutation ($email: String!, $password: String!, $publisherId: Int) {
|
||||||
login(email: $email, password: $password, publisherId: $publisherId) {
|
login(email: $email, password: $password, publisherId: $publisherId) {
|
||||||
email
|
email
|
||||||
firstName
|
firstName
|
||||||
@ -69,7 +69,8 @@ export default defineConfig({
|
|||||||
hasElopage
|
hasElopage
|
||||||
publisherId
|
publisherId
|
||||||
isAdmin
|
isAdmin
|
||||||
creation
|
hideAmountGDD
|
||||||
|
hideAmountGDT
|
||||||
__typename
|
__typename
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
|
|||||||
@ -6,12 +6,12 @@ Feature: User Authentication - reset password
|
|||||||
# Background:
|
# Background:
|
||||||
# Given the following "users" are in the database:
|
# Given the following "users" are in the database:
|
||||||
# | email | password | name |
|
# | email | password | name |
|
||||||
# | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg |
|
# | raeuber@hotzenplotz.de | Aa12345_ | Räuber Hotzenplotz |
|
||||||
|
|
||||||
Scenario: Reset password from signin page successfully
|
Scenario: Reset password from signin page successfully
|
||||||
Given the user navigates to page "/login"
|
Given the user navigates to page "/login"
|
||||||
And the user navigates to the forgot password page
|
And the user navigates to the forgot password page
|
||||||
When the user enters the e-mail address "bibi@bloxberg.de"
|
When the user enters the e-mail address "raeuber@hotzenplotz.de"
|
||||||
And the user submits the e-mail form
|
And the user submits the e-mail form
|
||||||
Then the user receives an e-mail containing the "password reset" link
|
Then the user receives an e-mail containing the "password reset" link
|
||||||
When the user opens the "password reset" link in the browser
|
When the user opens the "password reset" link in the browser
|
||||||
@ -19,7 +19,7 @@ Feature: User Authentication - reset password
|
|||||||
And the user repeats the password "12345Aa_"
|
And the user repeats the password "12345Aa_"
|
||||||
And the user submits the password form
|
And the user submits the password form
|
||||||
And the user clicks the sign in button
|
And the user clicks the sign in button
|
||||||
Then the user submits the credentials "bibi@bloxberg.de" "Aa12345_"
|
Then the user submits the credentials "raeuber@hotzenplotz.de" "Aa12345_"
|
||||||
And the user cannot login
|
And the user cannot login
|
||||||
But the user submits the credentials "bibi@bloxberg.de" "12345Aa_"
|
But the user submits the credentials "raeuber@hotzenplotz.de" "12345Aa_"
|
||||||
And the user is logged in with username "Bibi Bloxberg"
|
And the user is logged in with username "Räuber Hotzenplotz"
|
||||||
|
|||||||
@ -6,13 +6,13 @@ Feature: User profile - change password
|
|||||||
# TODO for these pre-conditions utilize seeding or API check, if user exists in test system
|
# TODO for these pre-conditions utilize seeding or API check, if user exists in test system
|
||||||
# Given the following "users" are in the database:
|
# Given the following "users" are in the database:
|
||||||
# | email | password | name |
|
# | email | password | name |
|
||||||
# | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | |
|
# | bob@baumeister.de | Aa12345_ | Bob der Baumeister |
|
||||||
|
|
||||||
# TODO instead of credentials use the name of an user object (see seeds in backend)
|
# TODO instead of credentials use the name of an user object (see seeds in backend)
|
||||||
Given the user is logged in as "bibi@bloxberg.de" "Aa12345_"
|
Given the user is logged in as "bob@baumeister.de" "Aa12345_"
|
||||||
|
|
||||||
Scenario: Change password successfully
|
Scenario: Change password successfully
|
||||||
Given the user navigates to page "/profile"
|
Given the user navigates to page "/settings"
|
||||||
And the user opens the change password menu
|
And the user opens the change password menu
|
||||||
When the user fills the password form with:
|
When the user fills the password form with:
|
||||||
| Old password | Aa12345_ |
|
| Old password | Aa12345_ |
|
||||||
@ -21,7 +21,7 @@ Feature: User profile - change password
|
|||||||
And the user submits the password form
|
And the user submits the password form
|
||||||
And the user is presented a "success" message
|
And the user is presented a "success" message
|
||||||
And the user logs out
|
And the user logs out
|
||||||
Then the user submits the credentials "bibi@bloxberg.de" "Aa12345_"
|
Then the user submits the credentials "bob@baumeister.de" "Aa12345_"
|
||||||
And the user cannot login
|
And the user cannot login
|
||||||
But the user submits the credentials "bibi@bloxberg.de" "12345Aa_"
|
But the user submits the credentials "bob@baumeister.de" "12345Aa_"
|
||||||
And the user is logged in with username "Bibi Bloxberg"
|
And the user is logged in with username "Bob der Baumeister"
|
||||||
|
|||||||
@ -11,7 +11,7 @@ export class SideNavMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logout() {
|
logout() {
|
||||||
cy.get(this.logoutMenu).click()
|
cy.get('.main-sidebar').find(this.logoutMenu).click()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
11
federation/.env.dist
Normal file
11
federation/.env.dist
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# Database
|
||||||
|
DB_HOST=localhost
|
||||||
|
DB_PORT=3306
|
||||||
|
DB_DATABASE=gradido_community
|
||||||
|
DB_USER=root
|
||||||
|
DB_PASSWORD=
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
FEDERATION_API=1_0
|
||||||
|
FEDERATION_PORT=5010
|
||||||
|
FEDERATION_COMMUNITY_URL=http://localhost
|
||||||
16
federation/.env.template
Normal file
16
federation/.env.template
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
CONFIG_VERSION=$FEDERATION_CONFIG_VERSION
|
||||||
|
|
||||||
|
LOG_LEVEL=$LOG_LEVEL
|
||||||
|
# this is set fix to false, because it is important for 'production' environments. only set to true if a graphql-playground should be in use
|
||||||
|
GRAPHIQL=false
|
||||||
|
|
||||||
|
# Database
|
||||||
|
DB_HOST=$DB_HOST
|
||||||
|
DB_PORT=$DB_PORT
|
||||||
|
DB_DATABASE=$DB_DATABASE
|
||||||
|
DB_USER=$DB_USER
|
||||||
|
DB_PASSWORD=$DB_PASSWORD
|
||||||
|
|
||||||
|
# Federation
|
||||||
|
FEDERATION_COMMUNITY_URL=$FEDERATION_COMMUNITY_URL
|
||||||
|
|
||||||
@ -9,6 +9,11 @@ module.exports = {
|
|||||||
'!src/seeds/**',
|
'!src/seeds/**',
|
||||||
'!build/**',
|
'!build/**',
|
||||||
],
|
],
|
||||||
|
coverageThreshold: {
|
||||||
|
global: {
|
||||||
|
lines: 72,
|
||||||
|
},
|
||||||
|
},
|
||||||
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||||
setupFilesAfterEnv: [],
|
setupFilesAfterEnv: [],
|
||||||
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
"build": "tsc --build",
|
"build": "tsc --build",
|
||||||
"clean": "tsc --build --clean",
|
"clean": "tsc --build --clean",
|
||||||
"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",
|
||||||
"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",
|
||||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
|
||||||
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||||
},
|
},
|
||||||
|
|||||||
@ -11,7 +11,7 @@ Decimal.set({
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const constants = {
|
const constants = {
|
||||||
DB_VERSION: '0060-update_communities_table',
|
DB_VERSION: '0062-event_contribution_confirm',
|
||||||
// DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
// DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0
|
||||||
LOG4JS_CONFIG: 'log4js-config.json',
|
LOG4JS_CONFIG: 'log4js-config.json',
|
||||||
// default log level on production should be info
|
// default log level on production should be info
|
||||||
@ -24,7 +24,6 @@ const constants = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const server = {
|
const server = {
|
||||||
PORT: process.env.PORT || 5010,
|
|
||||||
// JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
// JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
||||||
// JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m',
|
// JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m',
|
||||||
GRAPHIQL: process.env.GRAPHIQL === 'true' || false,
|
GRAPHIQL: process.env.GRAPHIQL === 'true' || false,
|
||||||
@ -40,21 +39,6 @@ const database = {
|
|||||||
TYPEORM_LOGGING_RELATIVE_PATH:
|
TYPEORM_LOGGING_RELATIVE_PATH:
|
||||||
process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.backend.log',
|
process.env.TYPEORM_LOGGING_RELATIVE_PATH || 'typeorm.backend.log',
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
const community = {
|
|
||||||
COMMUNITY_NAME: process.env.COMMUNITY_NAME || 'Gradido Entwicklung',
|
|
||||||
COMMUNITY_URL: process.env.COMMUNITY_URL || 'http://localhost/',
|
|
||||||
COMMUNITY_REGISTER_URL: process.env.COMMUNITY_REGISTER_URL || 'http://localhost/register',
|
|
||||||
COMMUNITY_REDEEM_URL: process.env.COMMUNITY_REDEEM_URL || 'http://localhost/redeem/{code}',
|
|
||||||
COMMUNITY_REDEEM_CONTRIBUTION_URL:
|
|
||||||
process.env.COMMUNITY_REDEEM_CONTRIBUTION_URL || 'http://localhost/redeem/CL-{code}',
|
|
||||||
COMMUNITY_DESCRIPTION:
|
|
||||||
process.env.COMMUNITY_DESCRIPTION || 'Die lokale Entwicklungsumgebung von Gradido.',
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This is needed by graphql-directive-auth
|
|
||||||
// process.env.APP_SECRET = server.JWT_SECRET
|
|
||||||
|
|
||||||
// Check config version
|
// Check config version
|
||||||
constants.CONFIG_VERSION.CURRENT =
|
constants.CONFIG_VERSION.CURRENT =
|
||||||
@ -71,10 +55,8 @@ if (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const federation = {
|
const federation = {
|
||||||
// FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || null,
|
|
||||||
// FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
|
||||||
FEDERATION_PORT: process.env.FEDERATION_PORT || 5010,
|
|
||||||
FEDERATION_API: process.env.FEDERATION_API || '1_0',
|
FEDERATION_API: process.env.FEDERATION_API || '1_0',
|
||||||
|
FEDERATION_PORT: process.env.FEDERATION_PORT || 5010,
|
||||||
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null,
|
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
federation/src/graphql/api/1_0/model/GetPublicKeyResult.ts
Normal file
13
federation/src/graphql/api/1_0/model/GetPublicKeyResult.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { Field, ObjectType } from 'type-graphql'
|
||||||
|
|
||||||
|
@ObjectType()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export class GetPublicKeyResult {
|
||||||
|
constructor(pubKey: string) {
|
||||||
|
this.publicKey = pubKey
|
||||||
|
}
|
||||||
|
|
||||||
|
@Field(() => String)
|
||||||
|
publicKey: string
|
||||||
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
|
import createServer from '@/server/createServer'
|
||||||
|
import { Community as DbCommunity } from '@entity/Community'
|
||||||
|
|
||||||
|
let query: any
|
||||||
|
|
||||||
|
// to do: We need a setup for the tests that closes the connection
|
||||||
|
let con: any
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const server = await createServer()
|
||||||
|
con = server.con
|
||||||
|
query = createTestClient(server.apollo).query
|
||||||
|
DbCommunity.clear()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('PublicKeyResolver', () => {
|
||||||
|
const getPublicKeyQuery = `
|
||||||
|
query {
|
||||||
|
getPublicKey
|
||||||
|
{
|
||||||
|
publicKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
describe('getPublicKey', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
const homeCom = new DbCommunity()
|
||||||
|
homeCom.foreign = false
|
||||||
|
homeCom.apiVersion = '1_0'
|
||||||
|
homeCom.endPoint = 'endpoint-url'
|
||||||
|
homeCom.publicKey = Buffer.from('homeCommunity-publicKey')
|
||||||
|
await DbCommunity.insert(homeCom)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('returns homeCommunity-publicKey', async () => {
|
||||||
|
await expect(query({ query: getPublicKeyQuery })).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
getPublicKey: {
|
||||||
|
publicKey: expect.stringMatching('homeCommunity-publicKey'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
20
federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts
Normal file
20
federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { Query, Resolver } from 'type-graphql'
|
||||||
|
import { federationLogger as logger } from '@/server/logger'
|
||||||
|
import { Community as DbCommunity } from '@entity/Community'
|
||||||
|
import { GetPublicKeyResult } from '../model/GetPublicKeyResult'
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
export class PublicKeyResolver {
|
||||||
|
@Query(() => GetPublicKeyResult)
|
||||||
|
async getPublicKey(): Promise<GetPublicKeyResult> {
|
||||||
|
logger.debug(`getPublicKey() via apiVersion=1_0 ...`)
|
||||||
|
const homeCom = await DbCommunity.findOneOrFail({
|
||||||
|
foreign: false,
|
||||||
|
apiVersion: '1_0',
|
||||||
|
})
|
||||||
|
logger.info(`getPublicKey()-1_0... return publicKey=${homeCom.publicKey}`)
|
||||||
|
return new GetPublicKeyResult(homeCom.publicKey.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,7 +20,7 @@ async function main() {
|
|||||||
if (CONFIG.GRAPHIQL) {
|
if (CONFIG.GRAPHIQL) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.log(
|
console.log(
|
||||||
`GraphIQL available at http://localhost:${CONFIG.FEDERATION_PORT}`
|
`GraphIQL available at ${CONFIG.FEDERATION_COMMUNITY_URL}/api/${CONFIG.FEDERATION_API}`
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
verbose: true,
|
verbose: true,
|
||||||
|
collectCoverage: true,
|
||||||
collectCoverageFrom: ['src/**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
|
collectCoverageFrom: ['src/**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'],
|
||||||
|
coverageThreshold: {
|
||||||
|
global: {
|
||||||
|
lines: 95,
|
||||||
|
},
|
||||||
|
},
|
||||||
moduleFileExtensions: [
|
moduleFileExtensions: [
|
||||||
'js',
|
'js',
|
||||||
// 'jsx',
|
// 'jsx',
|
||||||
'json',
|
'json',
|
||||||
'vue',
|
'vue',
|
||||||
],
|
],
|
||||||
// coverageReporters: ['lcov', 'text'],
|
|
||||||
moduleNameMapper: {
|
moduleNameMapper: {
|
||||||
'\\.(css|less)$': 'identity-obj-proxy',
|
'\\.(css|less)$': 'identity-obj-proxy',
|
||||||
'\\.(scss)$': '<rootDir>/src/assets/mocks/styleMock.js',
|
'\\.(scss)$': '<rootDir>/src/assets/mocks/styleMock.js',
|
||||||
|
|||||||
@ -10,7 +10,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": {
|
||||||
|
|||||||
@ -49,15 +49,15 @@ describe('Sidebar', () => {
|
|||||||
expect(wrapper.findAll('.nav-item').at(2).text()).toEqual('navigation.transactions')
|
expect(wrapper.findAll('.nav-item').at(2).text()).toEqual('navigation.transactions')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has nav-item "gdt.gdt" in navbar', () => {
|
it('has nav-item "creation" in navbar', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(3).text()).toEqual('creation')
|
expect(wrapper.findAll('.nav-item').at(3).text()).toEqual('creation')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has nav-item "creation" in navbar', () => {
|
it('has nav-item "GDT" in navbar', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(4).text()).toContain('GDT')
|
expect(wrapper.findAll('.nav-item').at(4).text()).toContain('GDT')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has nav-item "Information" in navbar', () => {
|
it('has nav-item "navigation.info" in navbar', () => {
|
||||||
expect(wrapper.findAll('.nav-item').at(5).text()).toContain('navigation.info')
|
expect(wrapper.findAll('.nav-item').at(5).text()).toContain('navigation.info')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -68,13 +68,13 @@ describe('Sidebar', () => {
|
|||||||
expect(wrapper.findAll('ul').at(1).findAll('.nav-item')).toHaveLength(2)
|
expect(wrapper.findAll('ul').at(1).findAll('.nav-item')).toHaveLength(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has nav-item "navigation.info" in navbar', () => {
|
it('has nav-item "navigation.settings" in navbar', () => {
|
||||||
expect(wrapper.findAll('ul').at(1).findAll('.nav-item').at(0).text()).toEqual(
|
expect(wrapper.findAll('ul').at(1).findAll('.nav-item').at(0).text()).toEqual(
|
||||||
'navigation.settings',
|
'navigation.settings',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has nav-item "navigation.settings" in navbar', () => {
|
it('has nav-item "navigation.logout" in navbar', () => {
|
||||||
expect(wrapper.findAll('ul').at(1).findAll('.nav-item').at(1).text()).toEqual(
|
expect(wrapper.findAll('ul').at(1).findAll('.nav-item').at(1).text()).toEqual(
|
||||||
'navigation.logout',
|
'navigation.logout',
|
||||||
)
|
)
|
||||||
|
|||||||
@ -48,7 +48,12 @@
|
|||||||
<b-icon icon="shield-check" aria-hidden="true"></b-icon>
|
<b-icon icon="shield-check" aria-hidden="true"></b-icon>
|
||||||
<span class="ml-2">{{ $t('navigation.admin_area') }}</span>
|
<span class="ml-2">{{ $t('navigation.admin_area') }}</span>
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
<b-nav-item class="font-weight-bold" @click="$emit('logout')" active-class="activeRoute">
|
<b-nav-item
|
||||||
|
class="font-weight-bold"
|
||||||
|
@click="$emit('logout')"
|
||||||
|
active-class="activeRoute"
|
||||||
|
data-test="logout-menu"
|
||||||
|
>
|
||||||
<b-img src="/img/svg/logout.svg" height="20" class="svg-icon" />
|
<b-img src="/img/svg/logout.svg" height="20" class="svg-icon" />
|
||||||
<span class="ml-2 text-205">{{ $t('navigation.logout') }}</span>
|
<span class="ml-2 text-205">{{ $t('navigation.logout') }}</span>
|
||||||
</b-nav-item>
|
</b-nav-item>
|
||||||
|
|||||||
@ -7,7 +7,8 @@
|
|||||||
"author": "Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>",
|
"author": "Ulf Gebhardt <ulf.gebhardt@webcraft-media.de>",
|
||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"release": "scripts/release.sh"
|
"release": "scripts/release.sh",
|
||||||
|
"installAll": "yarn && cd database && yarn && cd ../frontend && yarn && cd ../admin && yarn && cd ../backend && yarn && cd ../e2e-tests && yarn && cd ../federation && yarn && cd ../dht-node && yarn && cd .."
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"auto-changelog": "^2.4.0",
|
"auto-changelog": "^2.4.0",
|
||||||
|
|||||||
@ -7,6 +7,8 @@ FRONTEND_DIR="${PROJECT_DIR}/frontend/"
|
|||||||
BACKEND_DIR="${PROJECT_DIR}/backend/"
|
BACKEND_DIR="${PROJECT_DIR}/backend/"
|
||||||
DATABASE_DIR="${PROJECT_DIR}/database/"
|
DATABASE_DIR="${PROJECT_DIR}/database/"
|
||||||
ADMIN_DIR="${PROJECT_DIR}/admin/"
|
ADMIN_DIR="${PROJECT_DIR}/admin/"
|
||||||
|
DHTNODE_DIR="${PROJECT_DIR}/dht-node/"
|
||||||
|
FEDERATION_DIR="${PROJECT_DIR}/federation/"
|
||||||
|
|
||||||
# navigate to project directory
|
# navigate to project directory
|
||||||
cd ${PROJECT_DIR}
|
cd ${PROJECT_DIR}
|
||||||
@ -26,6 +28,10 @@ cd ${DATABASE_DIR}
|
|||||||
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
||||||
cd ${ADMIN_DIR}
|
cd ${ADMIN_DIR}
|
||||||
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
||||||
|
cd ${DHTNODE_DIR}
|
||||||
|
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
||||||
|
cd ${FEDERATION_DIR}
|
||||||
|
yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION}
|
||||||
|
|
||||||
# generate changelog
|
# generate changelog
|
||||||
cd ${PROJECT_DIR}
|
cd ${PROJECT_DIR}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user