diff --git a/.github/file-filters.yml b/.github/file-filters.yml new file mode 100644 index 000000000..2ae1bf9b4 --- /dev/null +++ b/.github/file-filters.yml @@ -0,0 +1,43 @@ +# 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/**/*' \ No newline at end of file diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml new file mode 100644 index 000000000..d1dd2851a --- /dev/null +++ b/.github/workflows/e2e-test.yml @@ -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/ diff --git a/.github/workflows/test-admin-interface.yml b/.github/workflows/test-admin-interface.yml new file mode 100644 index 000000000..a163af4e9 --- /dev/null +++ b/.github/workflows/test-admin-interface.yml @@ -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 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 41a2c9fc2..1e5f0db02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,57 +3,6 @@ name: gradido test CI on: push 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 ############################################# ############################################################################## @@ -157,114 +106,6 @@ jobs: 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 ########################################################## ############################################################################## @@ -319,68 +160,6 @@ jobs: - name: Database | 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 #################################################### ############################################################################## @@ -415,20 +194,7 @@ jobs: - name: backend | docker-compose database run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database - name: backend Unit tests | test - run: | - 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: 81 - token: ${{ github.token }} + run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test ########################################################################## # DATABASE MIGRATION TEST UP + RESET ##################################### @@ -452,108 +218,3 @@ jobs: run: docker-compose -f docker-compose.yml run -T database yarn up - name: database | 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/ diff --git a/.github/workflows/test_dht-node.yml b/.github/workflows/test_dht-node.yml index 4ac475351..a57f09399 100644 --- a/.github/workflows/test_dht-node.yml +++ b/.github/workflows/test_dht-node.yml @@ -3,17 +3,38 @@ name: Gradido DHT Node 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 - 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 ##################################################### ############################################################################## build: 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 steps: - name: Checkout code uses: actions/checkout@v3 - - name: Build `test` image + - name: Build 'test' image run: | docker build --target test -t "gradido/dht-node:test" -f dht-node/Dockerfile . docker save "gradido/dht-node:test" > /tmp/dht-node.tar @@ -29,30 +50,24 @@ jobs: ############################################################################## lint: name: Lint - DHT Node + if: needs.files-changed.outputs.dht_node == 'true' + needs: files-changed runs-on: ubuntu-latest - needs: [build] steps: - name: Checkout code 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 - run: docker run --rm gradido/dht-node:test yarn run lint + run: cd dht-node && yarn && yarn run lint ############################################################################## # JOB: UNIT TEST ############################################################# ############################################################################## unit_test: 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 - needs: [build] steps: - name: Checkout code uses: actions/checkout@v3 @@ -83,16 +98,4 @@ jobs: #- name: Unit tests # run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test - name: Unit tests - run: | - 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 }} + run: docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/dht-node:test yarn run test diff --git a/.github/workflows/test_federation.yml b/.github/workflows/test_federation.yml index ab943eedd..fc29a1bf8 100644 --- a/.github/workflows/test_federation.yml +++ b/.github/workflows/test_federation.yml @@ -3,11 +3,32 @@ name: Gradido Federation 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 - 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 ##################################################### ############################################################################## build: 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 steps: - name: Checkout code @@ -29,30 +50,24 @@ jobs: ############################################################################## lint: name: Lint - Federation + if: needs.files-changed.outputs.federation == 'true' + needs: files-changed runs-on: ubuntu-latest - needs: [build] steps: - name: Checkout code 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 - run: docker run --rm gradido/federation:test yarn run lint + run: cd federation && yarn && yarn run lint ############################################################################## # JOB: UNIT TEST ############################################################# ############################################################################## unit_test: 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 - needs: [build] steps: - name: Checkout code uses: actions/checkout@v3 @@ -84,15 +99,4 @@ jobs: # run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test - name: Unit tests 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 - 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 }} + docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net --rm gradido/federation:test yarn run test diff --git a/.github/workflows/test_frontend.yml b/.github/workflows/test_frontend.yml new file mode 100644 index 000000000..6dd527079 --- /dev/null +++ b/.github/workflows/test_frontend.yml @@ -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 diff --git a/admin/jest.config.js b/admin/jest.config.js index 9233dd2e7..b6ec1dc80 100644 --- a/admin/jest.config.js +++ b/admin/jest.config.js @@ -1,11 +1,17 @@ module.exports = { verbose: true, + collectCoverage: true, collectCoverageFrom: [ 'src/**/*.{js,vue}', '!**/node_modules/**', '!src/assets/**', '!**/?(*.)+(spec|test).js?(x)', ], + coverageThreshold: { + global: { + lines: 97, + }, + }, moduleFileExtensions: [ 'js', // 'jsx', diff --git a/admin/package.json b/admin/package.json index 10399739d..3406c326a 100644 --- a/admin/package.json +++ b/admin/package.json @@ -14,7 +14,7 @@ "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", "stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'", - "test": "cross-env TZ=UTC jest --coverage", + "test": "cross-env TZ=UTC jest", "locales": "scripts/sort.sh" }, "dependencies": { diff --git a/admin/src/locales/en.json b/admin/src/locales/en.json index de7d3b871..f36944a63 100644 --- a/admin/src/locales/en.json +++ b/admin/src/locales/en.json @@ -39,7 +39,7 @@ }, "created": "Created for", "createdAt": "Created at", - "creation": "Amount", + "creation": "Creation", "creationList": "Creation list", "creation_form": { "creation_for": "Active Basic Income for", diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e index a5cdc4bfd..99d6c14d5 100644 --- a/backend/.env.test_e2e +++ b/backend/.env.test_e2e @@ -1,5 +1,5 @@ # Server -JWT_EXPIRES_IN=1m +JWT_EXPIRES_IN=2m # Email EMAIL=true diff --git a/backend/jest.config.js b/backend/jest.config.js index d6683d292..cd63f11d8 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -4,6 +4,11 @@ module.exports = { preset: 'ts-jest', collectCoverage: true, collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], + coverageThreshold: { + global: { + lines: 80, + }, + }, setupFiles: ['/test/testSetup.ts'], setupFilesAfterEnv: ['/test/extensions.ts'], modulePathIgnorePatterns: ['/build/'], diff --git a/backend/package.json b/backend/package.json index dab5e50af..22e75f4af 100644 --- a/backend/package.json +++ b/backend/package.json @@ -13,7 +13,7 @@ "start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js", "dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r tsconfig-paths/register src/index.ts", "lint": "eslint --max-warnings=0 --ext .js,.ts .", - "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", "klicktipp": "cross-env TZ=UTC NODE_ENV=development ts-node -r tsconfig-paths/register src/util/klicktipp.ts", "locales": "scripts/sort.sh" diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 27b51b47d..961d83219 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -10,7 +10,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0060-update_communities_table', + DB_VERSION: '0061-event_refactoring', DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info diff --git a/backend/src/event/Event.ts b/backend/src/event/Event.ts index 8e65d85f2..7ed75fd45 100644 --- a/backend/src/event/Event.ts +++ b/backend/src/event/Event.ts @@ -1,212 +1,217 @@ -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 { EventProtocolType } from './EventProtocolType' export const Event = ( type: EventProtocolType, - userId: number, - xUserId: number | null = null, - xCommunityId: number | null = null, - transactionId: number | null = null, - contributionId: number | null = null, + affectedUser: DbUser, + actingUser: DbUser, + involvedUser: DbUser | null = null, + involvedTransaction: DbTransaction | null = null, + involvedContribution: DbContribution | null = null, + involvedContributionMessage: DbContributionMessage | null = null, amount: Decimal | null = null, - messageId: number | null = null, ): DbEvent => { const event = new DbEvent() event.type = type - event.userId = userId - event.xUserId = xUserId - event.xCommunityId = xCommunityId - event.transactionId = transactionId - event.contributionId = contributionId + event.affectedUser = affectedUser + event.actingUser = actingUser + event.involvedUser = involvedUser + event.involvedTransaction = involvedTransaction + event.involvedContribution = involvedContribution + event.involvedContributionMessage = involvedContributionMessage event.amount = amount - event.messageId = messageId return event } export const EVENT_CONTRIBUTION_CREATE = async ( - userId: number, - contributionId: number, + user: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.CONTRIBUTION_CREATE, - userId, + user, + user, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_CONTRIBUTION_DELETE = async ( - userId: number, - contributionId: number, + user: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.CONTRIBUTION_DELETE, - userId, + user, + user, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_CONTRIBUTION_UPDATE = async ( - userId: number, - contributionId: number, + user: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.CONTRIBUTION_UPDATE, - userId, + user, + user, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_ADMIN_CONTRIBUTION_CREATE = async ( - userId: number, - contributionId: number, + user: DbUser, + moderator: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.ADMIN_CONTRIBUTION_CREATE, - userId, + user, + moderator, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_ADMIN_CONTRIBUTION_UPDATE = async ( - userId: number, - contributionId: number, + user: DbUser, + moderator: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, - userId, + user, + moderator, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_ADMIN_CONTRIBUTION_DELETE = async ( - userId: number, - contributionId: number, + user: DbUser, + moderator: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.ADMIN_CONTRIBUTION_DELETE, - userId, + user, + moderator, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_CONTRIBUTION_CONFIRM = async ( - userId: number, - contributionId: number, + user: DbUser, + moderator: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.CONTRIBUTION_CONFIRM, - userId, + user, + moderator, null, null, + contribution, null, - contributionId, amount, ).save() export const EVENT_ADMIN_CONTRIBUTION_DENY = async ( - userId: number, - xUserId: number, - contributionId: number, + user: DbUser, + moderator: DbUser, + contribution: DbContribution, amount: Decimal, ): Promise => Event( EventProtocolType.ADMIN_CONTRIBUTION_DENY, - userId, - xUserId, + user, + moderator, null, null, - contributionId, + contribution, + null, amount, ).save() export const EVENT_TRANSACTION_SEND = async ( - userId: number, - xUserId: number, - transactionId: number, + user: DbUser, + involvedUser: DbUser, + transaction: DbTransaction, amount: Decimal, ): Promise => Event( EventProtocolType.TRANSACTION_SEND, - userId, - xUserId, + user, + user, + involvedUser, + transaction, null, - transactionId, null, amount, ).save() export const EVENT_TRANSACTION_RECEIVE = async ( - userId: number, - xUserId: number, - transactionId: number, + user: DbUser, + involvedUser: DbUser, + transaction: DbTransaction, amount: Decimal, ): Promise => Event( EventProtocolType.TRANSACTION_RECEIVE, - userId, - xUserId, + user, + involvedUser, + involvedUser, + transaction, null, - transactionId, null, amount, ).save() -export const EVENT_LOGIN = async (userId: number): Promise => - Event(EventProtocolType.LOGIN, userId, null, null, null, null, null, null).save() +export const EVENT_LOGIN = async (user: DbUser): Promise => + Event(EventProtocolType.LOGIN, user, user).save() -export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async ( - userId: number, -): Promise => Event(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, userId).save() +export const EVENT_SEND_ACCOUNT_MULTIREGISTRATION_EMAIL = async (user: DbUser): Promise => + Event(EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, user, { id: 0 } as DbUser).save() -export const EVENT_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - Event(EventProtocolType.SEND_CONFIRMATION_EMAIL, userId).save() +export const EVENT_SEND_CONFIRMATION_EMAIL = async (user: DbUser): Promise => + Event(EventProtocolType.SEND_CONFIRMATION_EMAIL, user, user).save() -export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async (userId: number): Promise => - Event(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, userId).save() +export const EVENT_ADMIN_SEND_CONFIRMATION_EMAIL = async ( + user: DbUser, + moderator: DbUser, +): Promise => + Event(EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, user, moderator).save() -/* export const EVENT_REDEEM_REGISTER = async ( - userId: number, - transactionId: number | null = null, - contributionId: number | null = null, -): Promise => - Event( - EventProtocolType.REDEEM_REGISTER, - userId, - null, - null, - transactionId, - contributionId, - ).save() -*/ +export const EVENT_REGISTER = async (user: DbUser): Promise => + Event(EventProtocolType.REGISTER, user, user).save() -export const EVENT_REGISTER = async (userId: number): Promise => - Event(EventProtocolType.REGISTER, userId).save() - -export const EVENT_ACTIVATE_ACCOUNT = async (userId: number): Promise => - Event(EventProtocolType.ACTIVATE_ACCOUNT, userId).save() +export const EVENT_ACTIVATE_ACCOUNT = async (user: DbUser): Promise => + Event(EventProtocolType.ACTIVATE_ACCOUNT, user, user).save() diff --git a/backend/src/federation/client/1_0/FederationClient.ts b/backend/src/federation/client/1_0/FederationClient.ts index d4ed5960b..1e676a1a7 100644 --- a/backend/src/federation/client/1_0/FederationClient.ts +++ b/backend/src/federation/client/1_0/FederationClient.ts @@ -21,9 +21,13 @@ export async function requestGetPublicKey(dbCom: DbCommunity): Promise instance.url === url) + if (instance) { + return instance.client } - - return GraphQLGetClient.instance + const client = new GraphQLGetClient(url, { + method: 'GET', + jsonSerializer: { + parse: JSON.parse, + stringify: JSON.stringify, + }, + }) + GraphQLGetClient.instanceArray.push({ url, client } as ClientInstance) + return client } } diff --git a/backend/src/federation/validateCommunities.ts b/backend/src/federation/validateCommunities.ts index 157d83bb5..dfd46a3e9 100644 --- a/backend/src/federation/validateCommunities.ts +++ b/backend/src/federation/validateCommunities.ts @@ -42,17 +42,18 @@ export async function validateCommunities(): Promise { pubKey, `${dbCom.endPoint}/${dbCom.apiVersion}`, ) - if (pubKey && pubKey === dbCom.publicKey.toString('hex')) { + if (pubKey && pubKey === dbCom.publicKey.toString()) { logger.info(`Federation: matching publicKey: ${pubKey}`) await DbCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() }) 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) { if (!isLogError(err)) { logger.error(`Error:`, err) diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 70a5fdb5c..3b0d0e86f 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -46,7 +46,7 @@ import { userFactory } from '@/seeds/factory/user' import { creationFactory } from '@/seeds/factory/creation' import { creations } from '@/seeds/creation/index' import { peterLustig } from '@/seeds/users/peter-lustig' -import { EventProtocol } from '@entity/EventProtocol' +import { Event as DbEvent } from '@entity/Event' import { Contribution } from '@entity/Contribution' import { Transaction as DbTransaction } from '@entity/Transaction' import { User } from '@entity/User' @@ -279,12 +279,13 @@ describe('ContributionResolver', () => { }) it('stores the CONTRIBUTION_CREATE event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CREATE, + affectedUserId: bibi.id, + actingUserId: bibi.id, + involvedContributionId: pendingContribution.data.createContribution.id, 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_' }, }) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_UPDATE, + affectedUserId: bibi.id, + actingUserId: bibi.id, + involvedContributionId: pendingContribution.data.createContribution.id, 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 () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_CONTRIBUTION_DENY, - userId: bibi.id, - xUserId: admin.id, - contributionId: contributionToDeny.data.createContribution.id, + affectedUserId: bibi.id, + actingUserId: admin.id, + involvedContributionId: contributionToDeny.data.createContribution.id, amount: expect.decimalEqual(100), }), ) @@ -942,12 +944,13 @@ describe('ContributionResolver', () => { }) it('stores the CONTRIBUTION_DELETE event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_DELETE, - contributionId: contributionToDelete.data.createContribution.id, + affectedUserId: bibi.id, + actingUserId: bibi.id, + involvedContributionId: contributionToDelete.data.createContribution.id, amount: expect.decimalEqual(100), - userId: bibi.id, }), ) }) @@ -2031,10 +2034,11 @@ describe('ContributionResolver', () => { }) 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({ type: EventProtocolType.ADMIN_CONTRIBUTION_CREATE, - userId: admin.id, + affectedUserId: bibi.id, + actingUserId: admin.id, amount: expect.decimalEqual(200), }), ) @@ -2232,7 +2236,7 @@ describe('ContributionResolver', () => { mutate({ mutation: adminUpdateContribution, variables: { - id: creation ? creation.id : -1, + id: creation?.id, email: 'peter@lustig.de', amount: new Decimal(300), memo: 'Danke Peter!', @@ -2256,10 +2260,11 @@ describe('ContributionResolver', () => { }) 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({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, - userId: admin.id, + affectedUserId: creation?.userId, + actingUserId: admin.id, amount: 300, }), ) @@ -2273,7 +2278,7 @@ describe('ContributionResolver', () => { mutate({ mutation: adminUpdateContribution, variables: { - id: creation ? creation.id : -1, + id: creation?.id, email: 'peter@lustig.de', amount: new Decimal(200), memo: 'Das war leider zu Viel!', @@ -2297,10 +2302,11 @@ describe('ContributionResolver', () => { }) 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({ type: EventProtocolType.ADMIN_CONTRIBUTION_UPDATE, - userId: admin.id, + affectedUserId: creation?.userId, + actingUserId: admin.id, amount: expect.decimalEqual(200), }), ) @@ -2371,7 +2377,7 @@ describe('ContributionResolver', () => { mutate({ mutation: adminDeleteContribution, variables: { - id: creation ? creation.id : -1, + id: creation?.id, }, }), ).resolves.toEqual( @@ -2382,10 +2388,12 @@ describe('ContributionResolver', () => { }) 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({ type: EventProtocolType.ADMIN_CONTRIBUTION_DELETE, - userId: admin.id, + affectedUserId: creation?.userId, + actingUserId: admin.id, + involvedContributionId: creation?.id, amount: expect.decimalEqual(200), }), ) @@ -2538,7 +2546,7 @@ describe('ContributionResolver', () => { }) it('stores the CONTRIBUTION_CONFIRM event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.CONTRIBUTION_CONFIRM, }), @@ -2570,7 +2578,7 @@ describe('ContributionResolver', () => { }) 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({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, }), diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index 023a5ca1d..688380701 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -91,7 +91,7 @@ export class ContributionResolver { logger.trace('contribution to 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) } @@ -119,7 +119,7 @@ export class ContributionResolver { contribution.deletedAt = new Date() 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() return !!res @@ -247,7 +247,7 @@ export class ContributionResolver { contributionToUpdate.updatedAt = new Date() 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) } @@ -304,7 +304,7 @@ export class ContributionResolver { 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) } @@ -372,7 +372,12 @@ export class ContributionResolver { 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 } @@ -432,7 +437,12 @@ export class ContributionResolver { await contribution.save() 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({ firstName: user.firstName, @@ -545,7 +555,7 @@ export class ContributionResolver { await queryRunner.release() } - await EVENT_CONTRIBUTION_CONFIRM(user.id, contribution.id, contribution.amount) + await EVENT_CONTRIBUTION_CONFIRM(user, moderatorUser, contribution, contribution.amount) } finally { releaseLock() } @@ -605,9 +615,9 @@ export class ContributionResolver { const res = await contributionToUpdate.save() await EVENT_ADMIN_CONTRIBUTION_DENY( - contributionToUpdate.userId, - moderator.id, - contributionToUpdate.id, + user, + moderator, + contributionToUpdate, contributionToUpdate.amount, ) diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index c660a72da..6c1b4be7f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -18,7 +18,7 @@ import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' 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 { User } from '@entity/User' import { cleanDB, testEnvironment } from '@test/helpers' @@ -341,12 +341,13 @@ describe('send coins', () => { memo: 'unrepeatable memo', }) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.TRANSACTION_SEND, - userId: user[1].id, - transactionId: transaction[0].id, - xUserId: user[0].id, + affectedUserId: user[1].id, + actingUserId: user[1].id, + involvedUserId: user[0].id, + involvedTransactionId: transaction[0].id, }), ) }) @@ -358,12 +359,13 @@ describe('send coins', () => { memo: 'unrepeatable memo', }) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.TRANSACTION_RECEIVE, - userId: user[0].id, - transactionId: transaction[0].id, - xUserId: user[1].id, + affectedUserId: user[0].id, + actingUserId: user[1].id, + involvedUserId: user[1].id, + involvedTransactionId: transaction[0].id, }), ) }) diff --git a/backend/src/graphql/resolver/TransactionResolver.ts b/backend/src/graphql/resolver/TransactionResolver.ts index a11c5b377..5b10c098f 100644 --- a/backend/src/graphql/resolver/TransactionResolver.ts +++ b/backend/src/graphql/resolver/TransactionResolver.ts @@ -138,17 +138,12 @@ export const executeTransaction = async ( await queryRunner.commitTransaction() logger.info(`commit Transaction successful...`) - await EVENT_TRANSACTION_SEND( - transactionSend.userId, - transactionSend.linkedUserId, - transactionSend.id, - transactionSend.amount.mul(-1), - ) + await EVENT_TRANSACTION_SEND(sender, recipient, transactionSend, transactionSend.amount) await EVENT_TRANSACTION_RECEIVE( - transactionReceive.userId, - transactionReceive.linkedUserId, - transactionReceive.id, + recipient, + sender, + transactionReceive, transactionReceive.amount, ) } catch (e) { diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index a57346583..b91ed8709 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -40,7 +40,7 @@ import { transactionLinkFactory } from '@/seeds/factory/transactionLink' import { ContributionLink } from '@model/ContributionLink' import { TransactionLink } from '@entity/TransactionLink' import { EventProtocolType } from '@/event/EventProtocolType' -import { EventProtocol } from '@entity/EventProtocol' +import { Event as DbEvent } from '@entity/Event' import { validate as validateUUID, version as versionUUID } from 'uuid' import { peterLustig } from '@/seeds/users/peter-lustig' import { UserContact } from '@entity/UserContact' @@ -187,10 +187,11 @@ describe('UserResolver', () => { { email: 'peter@lustig.de' }, { relations: ['user'] }, ) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REGISTER, - userId: userConatct.user.id, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, }), ) }) @@ -216,10 +217,11 @@ describe('UserResolver', () => { }) 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({ type: EventProtocolType.SEND_CONFIRMATION_EMAIL, - userId: user[0].id, + affectedUserId: user[0].id, + actingUserId: user[0].id, }), ) }) @@ -261,10 +263,11 @@ describe('UserResolver', () => { { email: 'peter@lustig.de' }, { relations: ['user'] }, ) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.SEND_ACCOUNT_MULTIREGISTRATION_EMAIL, - userId: userConatct.user.id, + affectedUserId: userConatct.user.id, + actingUserId: 0, }), ) }) @@ -361,20 +364,22 @@ describe('UserResolver', () => { }) it('stores the ACTIVATE_ACCOUNT event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ACTIVATE_ACCOUNT, - userId: user[0].id, + affectedUserId: user[0].id, + actingUserId: user[0].id, }), ) }) it('stores the REDEEM_REGISTER event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, - userId: result.data.createUser.id, - contributionId: link.id, + affectedUserId: result.data.createUser.id, + actingUserId: result.data.createUser.id, + involvedContributionId: link.id, }), ) }) @@ -454,10 +459,12 @@ describe('UserResolver', () => { }) it('stores the REDEEM_REGISTER event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.REDEEM_REGISTER, - userId: newUser.data.createUser.id, + affectedUserId: newUser.data.createUser.id, + actingUserId: newUser.data.createUser.id, + involvedTransactionId: transactionLink.id, }), ) }) @@ -685,10 +692,11 @@ describe('UserResolver', () => { { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, - userId: userConatct.user.id, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, }), ) }) @@ -933,10 +941,11 @@ describe('UserResolver', () => { }) it('stores the LOGIN event in the database', async () => { - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.LOGIN, - userId: user[0].id, + affectedUserId: user[0].id, + actingUserId: user[0].id, }), ) }) @@ -1852,10 +1861,11 @@ describe('UserResolver', () => { { email: 'bibi@bloxberg.de' }, { relations: ['user'] }, ) - await expect(EventProtocol.find()).resolves.toContainEqual( + await expect(DbEvent.find()).resolves.toContainEqual( expect.objectContaining({ type: EventProtocolType.ADMIN_SEND_CONFIRMATION_EMAIL, - userId: userConatct.user.id, + affectedUserId: userConatct.user.id, + actingUserId: admin.id, }), ) }) diff --git a/backend/src/graphql/resolver/UserResolver.ts b/backend/src/graphql/resolver/UserResolver.ts index 078a29a8e..b3bf1e4ee 100644 --- a/backend/src/graphql/resolver/UserResolver.ts +++ b/backend/src/graphql/resolver/UserResolver.ts @@ -20,7 +20,9 @@ import { getConnection, getCustomRepository, IsNull, Not } from '@dbTools/typeor import { User as DbUser } from '@entity/User' import { UserContact as DbUserContact } from '@entity/UserContact' import { TransactionLink as DbTransactionLink } from '@entity/TransactionLink' +import { Transaction as DbTransaction } from '@entity/Transaction' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' +import { Contribution as DbContribution } from '@entity/Contribution' import { UserRepository } from '@repository/User' import { User } from '@model/User' @@ -182,7 +184,7 @@ export class UserResolver { value: encode(dbUser.gradidoID), }) - await EVENT_LOGIN(user.id) + await EVENT_LOGIN(dbUser) logger.info(`successful Login: ${JSON.stringify(user, null, 2)}`) return user } @@ -252,7 +254,7 @@ export class UserResolver { 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( `sendAccountMultiRegistrationEmail by ${firstName} ${lastName} to ${foundUser.firstName} ${foundUser.lastName} <${email}>`, @@ -270,7 +272,11 @@ export class UserResolver { const gradidoID = await newGradidoID() - const eventRegisterRedeem = Event(EventProtocolType.REDEEM_REGISTER, 0) + const eventRegisterRedeem = Event( + EventProtocolType.REDEEM_REGISTER, + { id: 0 } as DbUser, + { id: 0 } as DbUser, + ) let dbUser = new DbUser() dbUser.gradidoID = gradidoID dbUser.firstName = firstName @@ -287,14 +293,16 @@ export class UserResolver { logger.info('redeemCode found contributionLink', contributionLink) if (contributionLink) { dbUser.contributionLinkId = contributionLink.id - eventRegisterRedeem.contributionId = contributionLink.id + // TODO this is so wrong + eventRegisterRedeem.involvedContribution = { id: contributionLink.id } as DbContribution } } else { const transactionLink = await DbTransactionLink.findOne({ code: redeemCode }) logger.info('redeemCode found transactionLink', transactionLink) if (transactionLink) { dbUser.referrerId = transactionLink.userId - eventRegisterRedeem.transactionId = transactionLink.id + // TODO this is so wrong + eventRegisterRedeem.involvedTransaction = { id: transactionLink.id } as DbTransaction } } } @@ -333,7 +341,7 @@ export class UserResolver { }) logger.info(`sendAccountActivationEmail of ${firstName}.${lastName} to ${email}`) - await EVENT_SEND_CONFIRMATION_EMAIL(dbUser.id) + await EVENT_SEND_CONFIRMATION_EMAIL(dbUser) if (!emailSent) { logger.debug(`Account confirmation link: ${activationLink}`) @@ -350,10 +358,11 @@ export class UserResolver { logger.info('createUser() successful...') if (redeemCode) { - eventRegisterRedeem.userId = dbUser.id + eventRegisterRedeem.affectedUser = dbUser + eventRegisterRedeem.actingUser = dbUser await eventRegisterRedeem.save() } else { - await EVENT_REGISTER(dbUser.id) + await EVENT_REGISTER(dbUser) } return new User(dbUser) @@ -469,7 +478,7 @@ export class UserResolver { await queryRunner.commitTransaction() logger.info('User and UserContact data written successfully...') - await EVENT_ACTIVATE_ACCOUNT(user.id) + await EVENT_ACTIVATE_ACCOUNT(user) } catch (e) { await queryRunner.rollbackTransaction() throw new LogError('Error on writing User and User Contact data', e) @@ -779,9 +788,13 @@ export class UserResolver { return null } + // TODO this is an admin function - needs refactor @Authorized([RIGHTS.SEND_ACTIVATION_EMAIL]) @Mutation(() => Boolean) - async sendActivationEmail(@Arg('email') email: string): Promise { + async sendActivationEmail( + @Arg('email') email: string, + @Ctx() context: Context, + ): Promise { email = email.trim().toLowerCase() // const user = await dbUser.findOne({ id: emailContact.userId }) const user = await findUserByEmail(email) @@ -806,7 +819,7 @@ export class UserResolver { if (!emailSent) { logger.info(`Account confirmation link: ${activationLink}`) } else { - await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user.id) + await EVENT_ADMIN_SEND_CONFIRMATION_EMAIL(user, getUser(context)) } return true diff --git a/database/entity/0061-event_refactoring/Event.ts b/database/entity/0061-event_refactoring/Event.ts new file mode 100644 index 000000000..ab75c2d94 --- /dev/null +++ b/database/entity/0061-event_refactoring/Event.ts @@ -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 +} diff --git a/database/entity/Event.ts b/database/entity/Event.ts new file mode 100644 index 000000000..e53085f2f --- /dev/null +++ b/database/entity/Event.ts @@ -0,0 +1 @@ +export { Event } from './0061-event_refactoring/Event' diff --git a/database/entity/EventProtocol.ts b/database/entity/EventProtocol.ts deleted file mode 100644 index 6ebbd3d68..000000000 --- a/database/entity/EventProtocol.ts +++ /dev/null @@ -1 +0,0 @@ -export { EventProtocol } from './0050-add_messageId_to_event_protocol/EventProtocol' diff --git a/database/entity/index.ts b/database/entity/index.ts index a58afb816..2d9d04c3b 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -7,21 +7,21 @@ import { TransactionLink } from './TransactionLink' import { User } from './User' import { UserContact } from './UserContact' import { Contribution } from './Contribution' -import { EventProtocol } from './EventProtocol' +import { Event } from './Event' import { ContributionMessage } from './ContributionMessage' import { Community } from './Community' export const entities = [ + Community, Contribution, ContributionLink, + ContributionMessage, + Event, LoginElopageBuys, LoginEmailOptIn, Migration, Transaction, TransactionLink, User, - EventProtocol, - ContributionMessage, UserContact, - Community, ] diff --git a/database/migrations/0061-event_refactoring.ts b/database/migrations/0061-event_refactoring.ts new file mode 100644 index 000000000..019d3272b --- /dev/null +++ b/database/migrations/0061-event_refactoring.ts @@ -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>) { + 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>) { + 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`;') +} diff --git a/database/package.json b/database/package.json index 91bcce3f7..6dc8acf8c 100644 --- a/database/package.json +++ b/database/package.json @@ -16,9 +16,7 @@ "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_reset": "cross-env TZ=UTC ts-node src/index.ts reset", - "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" + "lint": "eslint --max-warnings=0 --ext .js,.ts ." }, "devDependencies": { "@types/faker": "^5.5.9", diff --git a/deployment/bare_metal/.env.dist b/deployment/bare_metal/.env.dist index b19bd8855..da745d705 100644 --- a/deployment/bare_metal/.env.dist +++ b/deployment/bare_metal/.env.dist @@ -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 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_CONFIG_VERSION=v1.2022-03-18 diff --git a/deployment/bare_metal/nginx/sites-available/gradido-federation.conf.template b/deployment/bare_metal/nginx/sites-available/gradido-federation.conf.template new file mode 100644 index 000000000..2192b7dbb --- /dev/null +++ b/deployment/bare_metal/nginx/sites-available/gradido-federation.conf.template @@ -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; + } diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template index aade0429b..ddb0724b0 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.ssl.template @@ -42,19 +42,19 @@ server { # Frontend (default) location / { - 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:3000; - proxy_redirect off; + 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:3000; + proxy_redirect off; access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; - } + } # Backend location /graphql { @@ -112,6 +112,9 @@ server { error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; } + # Federation + $FEDERATION_NGINX_CONF + # TODO this could be a performance optimization #location /vue { # alias /var/www/html/gradido/frontend/dist; diff --git a/deployment/bare_metal/nginx/sites-available/gradido.conf.template b/deployment/bare_metal/nginx/sites-available/gradido.conf.template index b4c7b3463..42a5a1851 100644 --- a/deployment/bare_metal/nginx/sites-available/gradido.conf.template +++ b/deployment/bare_metal/nginx/sites-available/gradido.conf.template @@ -27,19 +27,19 @@ server { # Frontend (default) location / { - 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:3000; - proxy_redirect off; + 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:3000; + proxy_redirect off; access_log $GRADIDO_LOG_PATH/nginx-access.frontend.log gradido_log; error_log $GRADIDO_LOG_PATH/nginx-error.frontend.log warn; - } + } # Backend location /graphql { @@ -98,6 +98,9 @@ server { error_log $GRADIDO_LOG_PATH/nginx-error.admin.log warn; } + # Federation + $FEDERATION_NGINX_CONF + # TODO this could be a performance optimization #location /vue { # alias /var/www/html/gradido/frontend/dist; diff --git a/deployment/bare_metal/start.sh b/deployment/bare_metal/start.sh index 89dc9d2f0..6c3c07766 100755 --- a/deployment/bare_metal/start.sh +++ b/deployment/bare_metal/start.sh @@ -59,8 +59,9 @@ ln -s /etc/nginx/sites-available/update-page.conf /etc/nginx/sites-enabled/ sudo /etc/init.d/nginx restart # stop all services -echo 'Stopping all Gradido services' >> $UPDATE_HTML -pm2 stop all +echo 'Stop and delete all Gradido services' >> $UPDATE_HTML +pm2 delete all +pm2 save # git BRANCH=${1:-master} @@ -73,12 +74,41 @@ git pull export BUILD_COMMIT="$(git rev-parse HEAD)" # 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 case "$NGINX_SSL" in true) TEMPLATE_FILE="gradido.conf.ssl.template" ;; *) TEMPLATE_FILE="gradido.conf.template" ;; 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 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/admin/.env $PROJECT_ROOT/admin/.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/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/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/federation/.env.template > $PROJECT_ROOT/federation/.env # Install & build database echo 'Updating database' >> $UPDATE_HTML @@ -124,7 +156,6 @@ if [ "$DEPLOY_SEED_DATA" = "true" ]; then fi # TODO maybe handle this differently? 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 save @@ -137,7 +168,6 @@ yarn install yarn build # TODO maybe handle this differently? 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 save @@ -150,7 +180,6 @@ yarn install yarn build # TODO maybe handle this differently? 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 save @@ -163,15 +192,49 @@ yarn install yarn build # TODO maybe handle this differently? export NODE_ENV=production -pm2 delete gradido-dht-node 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 save else - echo "=====================================================================" - echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..." - echo "=====================================================================" + echo "=====================================================================" >> $UPDATE_HTML + echo "WARNING: FEDERATION_DHT_TOPIC not configured. DHT-Node not started..." >> $UPDATE_HTML + echo "=====================================================================" >> $UPDATE_HTML fi -pm2 save + + +# 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 echo 'Configuring nginx to serve gradido again' >> $UPDATE_HTML diff --git a/dht-node/jest.config.js b/dht-node/jest.config.js index 10c87bddb..fa00ed868 100644 --- a/dht-node/jest.config.js +++ b/dht-node/jest.config.js @@ -4,6 +4,11 @@ module.exports = { preset: 'ts-jest', collectCoverage: true, collectCoverageFrom: ['src/**/*.ts', '!**/node_modules/**', '!src/seeds/**', '!build/**'], + coverageThreshold: { + global: { + lines: 80, + }, + }, setupFiles: ['/test/testSetup.ts'], setupFilesAfterEnv: [], modulePathIgnorePatterns: ['/build/'], diff --git a/dht-node/package.json b/dht-node/package.json index ac9b88968..15426e606 100644 --- a/dht-node/package.json +++ b/dht-node/package.json @@ -13,7 +13,7 @@ "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", "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": { "@hyperswarm/dht": "^6.4.4", diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 8210ddfcc..6ba9493ac 100644 --- a/dht-node/src/config/index.ts +++ b/dht-node/src/config/index.ts @@ -3,7 +3,7 @@ import dotenv from 'dotenv' dotenv.config() const constants = { - DB_VERSION: '0060-update_communities_table', + DB_VERSION: '0061-event_refactoring', LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info LOG_LEVEL: process.env.LOG_LEVEL || 'info', diff --git a/dht-node/src/dht_node/index.test.ts b/dht-node/src/dht_node/index.test.ts index b7e36c783..ac5b1b21a 100644 --- a/dht-node/src/dht_node/index.test.ts +++ b/dht-node/src/dht_node/index.test.ts @@ -719,11 +719,11 @@ describe('federation', () => { JSON.stringify([ { api: '1_0', - url: 'http://localhost:5001/api/', + url: 'http://localhost/api/', }, { api: '2_0', - url: 'http://localhost:5002/api/', + url: 'http://localhost/api/', }, ]), ), @@ -747,7 +747,7 @@ describe('federation', () => { foreign: true, publicKey: expect.any(Buffer), apiVersion: '1_0', - endPoint: 'http://localhost:5001/api/', + endPoint: 'http://localhost/api/', lastAnnouncedAt: expect.any(Date), createdAt: expect.any(Date), updatedAt: null, @@ -764,7 +764,7 @@ describe('federation', () => { foreign: true, publicKey: expect.any(Buffer), apiVersion: '2_0', - endPoint: 'http://localhost:5002/api/', + endPoint: 'http://localhost/api/', lastAnnouncedAt: expect.any(Date), createdAt: expect.any(Date), updatedAt: null, @@ -786,15 +786,15 @@ describe('federation', () => { JSON.stringify([ { api: '1_0', - url: 'http://localhost:5001/api/', + url: 'http://localhost/api/', }, { api: '1_1', - url: 'http://localhost:5002/api/', + url: 'http://localhost/api/', }, { api: '2_0', - url: 'http://localhost:5003/api/', + url: 'http://localhost/api/', }, ]), ), diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index 722a62af7..d101037ae 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -181,11 +181,9 @@ export const startDHT = async (topic: string): Promise => { async function writeHomeCommunityEnries(pubKey: any): Promise { const homeApiVersions: CommunityApi[] = Object.values(ApiVersionType).map(function (apiEnum) { - const port = - Number.parseInt(CONFIG.FEDERATION_COMMUNITY_API_PORT) + Number(apiEnum.replace('_', '')) const comApi: CommunityApi = { api: apiEnum, - url: CONFIG.FEDERATION_COMMUNITY_URL + ':' + port.toString() + '/api/', + url: CONFIG.FEDERATION_COMMUNITY_URL + '/api/', } return comApi }) diff --git a/e2e-tests/cypress.config.ts b/e2e-tests/cypress.config.ts index 9dff98a9b..7205ef9cb 100644 --- a/e2e-tests/cypress.config.ts +++ b/e2e-tests/cypress.config.ts @@ -56,7 +56,7 @@ export default defineConfig({ env: { backendURL: 'http://localhost:4000', 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) { email firstName @@ -69,7 +69,8 @@ export default defineConfig({ hasElopage publisherId isAdmin - creation + hideAmountGDD + hideAmountGDT __typename } }`, diff --git a/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature b/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature index b0f660709..50a29d320 100644 --- a/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature +++ b/e2e-tests/cypress/e2e/User.Authentication.ResetPassword.feature @@ -5,13 +5,13 @@ Feature: User Authentication - reset password # TODO for these pre-conditions utilize seeding or API check, if user exists in test system # Background: # Given the following "users" are in the database: - # | email | password | name | - # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | + # | email | password | name | + # | raeuber@hotzenplotz.de | Aa12345_ | Räuber Hotzenplotz | Scenario: Reset password from signin page successfully Given the user navigates to page "/login" 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 Then the user receives an e-mail containing the "password reset" link 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 submits the password form 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 - But the user submits the credentials "bibi@bloxberg.de" "12345Aa_" - And the user is logged in with username "Bibi Bloxberg" + But the user submits the credentials "raeuber@hotzenplotz.de" "12345Aa_" + And the user is logged in with username "Räuber Hotzenplotz" diff --git a/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature b/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature index aa853f6ff..b7aa6960c 100644 --- a/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature +++ b/e2e-tests/cypress/e2e/UserProfile.ChangePassword.feature @@ -5,14 +5,14 @@ Feature: User profile - change password Background: # TODO for these pre-conditions utilize seeding or API check, if user exists in test system # Given the following "users" are in the database: - # | email | password | name | - # | bibi@bloxberg.de | Aa12345_ | Bibi Bloxberg | | + # | email | password | name | + # | bob@baumeister.de | Aa12345_ | Bob der Baumeister | # 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 - Given the user navigates to page "/profile" + Given the user navigates to page "/settings" And the user opens the change password menu When the user fills the password form with: | Old password | Aa12345_ | @@ -21,7 +21,7 @@ Feature: User profile - change password And the user submits the password form And the user is presented a "success" message 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 - But the user submits the credentials "bibi@bloxberg.de" "12345Aa_" - And the user is logged in with username "Bibi Bloxberg" + But the user submits the credentials "bob@baumeister.de" "12345Aa_" + And the user is logged in with username "Bob der Baumeister" diff --git a/e2e-tests/cypress/e2e/models/SideNavMenu.ts b/e2e-tests/cypress/e2e/models/SideNavMenu.ts index ccd177b66..b52f35ed4 100644 --- a/e2e-tests/cypress/e2e/models/SideNavMenu.ts +++ b/e2e-tests/cypress/e2e/models/SideNavMenu.ts @@ -11,7 +11,7 @@ export class SideNavMenu { } logout() { - cy.get(this.logoutMenu).click() + cy.get('.main-sidebar').find(this.logoutMenu).click() return this } } diff --git a/federation/.env.dist b/federation/.env.dist new file mode 100644 index 000000000..42b3d05bf --- /dev/null +++ b/federation/.env.dist @@ -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 diff --git a/federation/.env.template b/federation/.env.template new file mode 100644 index 000000000..af6e8f627 --- /dev/null +++ b/federation/.env.template @@ -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 + diff --git a/federation/jest.config.js b/federation/jest.config.js index 742f35fbb..dff5ab169 100644 --- a/federation/jest.config.js +++ b/federation/jest.config.js @@ -9,6 +9,11 @@ module.exports = { '!src/seeds/**', '!build/**', ], + coverageThreshold: { + global: { + lines: 72, + }, + }, setupFiles: ['/test/testSetup.ts'], setupFilesAfterEnv: [], modulePathIgnorePatterns: ['/build/'], diff --git a/federation/package.json b/federation/package.json index 8a0153cc5..e7e067435 100644 --- a/federation/package.json +++ b/federation/package.json @@ -11,7 +11,7 @@ "build": "tsc --build", "clean": "tsc --build --clean", "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", "lint": "eslint --max-warnings=0 --ext .js,.ts ." }, diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index c8a841315..f792448d0 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -11,7 +11,7 @@ Decimal.set({ */ const constants = { - DB_VERSION: '0060-update_communities_table', + DB_VERSION: '0061-event_refactoring', // DECAY_START_TIME: new Date('2021-05-13 17:46:31-0000'), // GMT+0 LOG4JS_CONFIG: 'log4js-config.json', // default log level on production should be info @@ -24,7 +24,6 @@ const constants = { } const server = { - PORT: process.env.PORT || 5010, // JWT_SECRET: process.env.JWT_SECRET || 'secret123', // JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m', GRAPHIQL: process.env.GRAPHIQL === 'true' || false, @@ -40,21 +39,6 @@ const database = { TYPEORM_LOGGING_RELATIVE_PATH: 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 constants.CONFIG_VERSION.CURRENT = @@ -71,10 +55,8 @@ if ( } 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_PORT: process.env.FEDERATION_PORT || 5010, FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null, } diff --git a/federation/src/graphql/api/1_0/model/GetPublicKeyResult.ts b/federation/src/graphql/api/1_0/model/GetPublicKeyResult.ts new file mode 100644 index 000000000..696c96cfe --- /dev/null +++ b/federation/src/graphql/api/1_0/model/GetPublicKeyResult.ts @@ -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 +} diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts new file mode 100644 index 000000000..2e21af945 --- /dev/null +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts @@ -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 { + logger.info(`getPublicKey()...`) + const homeCom = await DbCommunity.findOneOrFail({ + foreign: false, + apiVersion: '1_0', + }) + logger.info(`getPublicKey()... with publicKey=${homeCom.publicKey}`) + return new GetPublicKeyResult(homeCom.publicKey.toString()) + } +} diff --git a/federation/src/index.ts b/federation/src/index.ts index 9096cb260..71c7545aa 100644 --- a/federation/src/index.ts +++ b/federation/src/index.ts @@ -20,7 +20,7 @@ async function main() { if (CONFIG.GRAPHIQL) { // eslint-disable-next-line no-console console.log( - `GraphIQL available at http://localhost:${CONFIG.FEDERATION_PORT}` + `GraphIQL available at ${CONFIG.FEDERATION_COMMUNITY_URL}/api/${CONFIG.FEDERATION_API}` ) } }) diff --git a/frontend/jest.config.js b/frontend/jest.config.js index a32330f3b..023fd64fd 100644 --- a/frontend/jest.config.js +++ b/frontend/jest.config.js @@ -1,13 +1,18 @@ module.exports = { verbose: true, + collectCoverage: true, collectCoverageFrom: ['src/**/*.{js,vue}', '!**/node_modules/**', '!**/?(*.)+(spec|test).js?(x)'], + coverageThreshold: { + global: { + lines: 95, + }, + }, moduleFileExtensions: [ 'js', // 'jsx', 'json', 'vue', ], - // coverageReporters: ['lcov', 'text'], moduleNameMapper: { '\\.(css|less)$': 'identity-obj-proxy', '\\.(scss)$': '/src/assets/mocks/styleMock.js', diff --git a/frontend/package.json b/frontend/package.json index 865ba060f..513b6b509 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,7 @@ "analyse-bundle": "yarn build && webpack-bundle-analyzer dist/webpack.stats.json", "lint": "eslint --max-warnings=0 --ext .js,.vue,.json .", "stylelint": "stylelint --max-warnings=0 '**/*.{scss,vue}'", - "test": "cross-env TZ=UTC jest --coverage", + "test": "cross-env TZ=UTC jest", "locales": "scripts/sort.sh" }, "dependencies": { diff --git a/frontend/src/components/Menu/Sidebar.spec.js b/frontend/src/components/Menu/Sidebar.spec.js index 321d73a3a..e6e4cfe2a 100644 --- a/frontend/src/components/Menu/Sidebar.spec.js +++ b/frontend/src/components/Menu/Sidebar.spec.js @@ -49,15 +49,15 @@ describe('Sidebar', () => { 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') }) - it('has nav-item "creation" in navbar', () => { + it('has nav-item "GDT" in navbar', () => { 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') }) }) @@ -68,13 +68,13 @@ describe('Sidebar', () => { 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( '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( 'navigation.logout', ) diff --git a/frontend/src/components/Menu/Sidebar.vue b/frontend/src/components/Menu/Sidebar.vue index 83adcb594..644fb37e0 100644 --- a/frontend/src/components/Menu/Sidebar.vue +++ b/frontend/src/components/Menu/Sidebar.vue @@ -48,7 +48,12 @@ {{ $t('navigation.admin_area') }} - + {{ $t('navigation.logout') }} diff --git a/scripts/release.sh b/scripts/release.sh index 80a54d949..df6ece9b6 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -7,6 +7,8 @@ FRONTEND_DIR="${PROJECT_DIR}/frontend/" BACKEND_DIR="${PROJECT_DIR}/backend/" DATABASE_DIR="${PROJECT_DIR}/database/" ADMIN_DIR="${PROJECT_DIR}/admin/" +DHTNODE_DIR="${PROJECT_DIR}/dht-node/" +FEDERATION_DIR="${PROJECT_DIR}/federation/" # navigate to project directory cd ${PROJECT_DIR} @@ -26,6 +28,10 @@ cd ${DATABASE_DIR} yarn version --no-git-tag-version --no-commit-hooks --no-commit --new-version ${VERSION} cd ${ADMIN_DIR} 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 cd ${PROJECT_DIR}