diff --git a/.github/file-filters.yml b/.github/file-filters.yml index 80b7482d9..f0d38b75b 100644 --- a/.github/file-filters.yml +++ b/.github/file-filters.yml @@ -30,10 +30,13 @@ admin: &admin - 'admin/**/*' +backend: &backend + - 'backend/**/*' + dht_node: &dht_node - 'dht-node/**/*' -docker: &docker +docker-compose: &docker-compose - 'docker-compose.*' federation: &federation @@ -42,5 +45,8 @@ federation: &federation frontend: &frontend - 'frontend/**/*' +mariadb: &mariadb + - 'mariadb/**/*' + nginx: &nginx - 'nginx/**/*' \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 0fcedb4ce..000000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,195 +0,0 @@ -name: gradido test CI - -on: push - -jobs: - ############################################################################## - # JOB: DOCKER BUILD TEST BACKEND ############################################# - ############################################################################## - build_test_backend: - name: Docker Build Test - Backend - runs-on: ubuntu-latest - #needs: [nothing] - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # BACKEND ################################################################ - ########################################################################## - - name: Backend | Build `test` image - run: | - docker build -f ./backend/Dockerfile --target test -t "gradido/backend:test" . - docker save "gradido/backend:test" > /tmp/backend.tar - - name: Upload Artifact - uses: actions/upload-artifact@v3 - with: - name: docker-backend-test - path: /tmp/backend.tar - - ############################################################################## - # JOB: DOCKER BUILD TEST DATABASE UP ######################################### - ############################################################################## - build_test_database_up: - name: Docker Build Test - Database up - runs-on: ubuntu-latest - #needs: [nothing] - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # DATABASE UP ############################################################ - ########################################################################## - - name: Database | Build `test_up` image - run: | - docker build --target test_up -t "gradido/database:test_up" database/ - docker save "gradido/database:test_up" > /tmp/database_up.tar - - name: Upload Artifact - uses: actions/upload-artifact@v3 - with: - name: docker-database-test_up - path: /tmp/database_up.tar - - ############################################################################## - # JOB: DOCKER BUILD TEST MARIADB ############################################# - ############################################################################## - build_test_mariadb: - name: Docker Build Test - MariaDB - runs-on: ubuntu-latest - #needs: [nothing] - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # BUILD MARIADB DOCKER IMAGE ############################################# - ########################################################################## - - name: mariadb | Build `test` image - run: | - docker build --target mariadb_server -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./ - docker save "gradido/mariadb:test" > /tmp/mariadb.tar - - name: Upload Artifact - uses: actions/upload-artifact@v3 - with: - name: docker-mariadb-test - path: /tmp/mariadb.tar - - ############################################################################## - # JOB: LINT BACKEND ########################################################## - ############################################################################## - lint_backend: - name: Lint - Backend - runs-on: ubuntu-latest - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # LINT BACKEND ########################################################### - ########################################################################## - - name: backend | Lint - run: cd database && yarn && cd ../backend && yarn && yarn run lint - - ############################################################################## - # JOB: LOCALES BACKEND ####################################################### - ############################################################################## - locales_backend: - name: Locales - Backend - runs-on: ubuntu-latest - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # LOCALES BACKEND ##################################################### - ########################################################################## - - name: Backend | Locales - run: cd backend && yarn && yarn locales - - ############################################################################## - # JOB: LINT DATABASE UP ###################################################### - ############################################################################## - lint_database_up: - name: Lint - Database Up - runs-on: ubuntu-latest - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # LINT DATABASE ########################################################## - ########################################################################## - - name: Database | Lint - run: cd database && yarn && yarn run lint - - ############################################################################## - # JOB: UNIT TEST BACKEND #################################################### - ############################################################################## - unit_test_backend: - name: Unit tests - Backend - runs-on: ubuntu-latest - needs: [build_test_mariadb] - 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 - run: docker load < /tmp/mariadb.tar - ########################################################################## - # UNIT TESTS BACKEND ##################################################### - ########################################################################## - - name: backend | docker-compose mariadb - run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb - - name: Sleep for 30 seconds - run: sleep 30s - shell: bash - - 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 - - ########################################################################## - # DATABASE MIGRATION TEST UP + RESET ##################################### - ########################################################################## - database_migration_test: - name: Database Migration Test - Up + Reset - runs-on: ubuntu-latest - #needs: [nothing] - steps: - ########################################################################## - # CHECKOUT CODE ########################################################## - ########################################################################## - - name: Checkout code - uses: actions/checkout@v3 - ########################################################################## - # DOCKER COMPOSE DATABASE UP + RESET ##################################### - ########################################################################## - - name: database | docker-compose - run: docker-compose -f docker-compose.yml up --detach mariadb - - name: database | up - 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 diff --git a/.github/workflows/test-admin-interface.yml b/.github/workflows/test_admin_interface.yml similarity index 100% rename from .github/workflows/test-admin-interface.yml rename to .github/workflows/test_admin_interface.yml diff --git a/.github/workflows/test_backend.yml b/.github/workflows/test_backend.yml new file mode 100644 index 000000000..b517c5186 --- /dev/null +++ b/.github/workflows/test_backend.yml @@ -0,0 +1,81 @@ +name: Gradido Backend Test CI + +on: push + +jobs: + files-changed: + name: Detect File Changes - Backend + runs-on: ubuntu-latest + outputs: + backend: ${{ steps.changes.outputs.backend }} + database: ${{ steps.changes.outputs.database }} + docker-compose: ${{ steps.changes.outputs.docker-compose }} + mariadb: ${{ steps.changes.outputs.mariadb }} + 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.backend == 'true' + name: Docker Build Test - Backend + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Backend | Build 'test' image + run: docker build -f ./backend/Dockerfile --target test -t "gradido/backend:test" . + + unit_test: + if: needs.files-changed.outputs.backend == 'true' || needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' + name: Unit tests - Backend + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Backend | docker-compose mariadb + run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps mariadb + + - name: Sleep for 30 seconds + run: sleep 30s + shell: bash + + - 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 + run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test + + lint: + if: needs.files-changed.outputs.backend == 'true' + name: Lint - Backend + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Backend | Lint + run: cd database && yarn && cd ../backend && yarn && yarn run lint + + locales: + if: needs.files-changed.outputs.backend == 'true' + name: Locales - Backend + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Backend | Locales + run: cd backend && yarn && yarn locales \ No newline at end of file diff --git a/.github/workflows/test_database.yml b/.github/workflows/test_database.yml new file mode 100644 index 000000000..0444a0538 --- /dev/null +++ b/.github/workflows/test_database.yml @@ -0,0 +1,64 @@ +name: Gradido Database Test CI + +on: push + +jobs: + files-changed: + name: Detect File Changes - Database + runs-on: ubuntu-latest + outputs: + database: ${{ steps.changes.outputs.database }} + docker-compose: ${{ steps.changes.outputs.docker-compose }} + mariadb: ${{ steps.changes.outputs.mariadb }} + 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: + if: needs.files-changed.outputs.database == 'true' + name: Docker Build Test - Database up + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Database | Build 'test_up' image + run: docker build --target test_up -t "gradido/database:test_up" database/ + + database_migration_test: + if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' + name: Database Migration Test - Up + Reset + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Database | docker-compose + run: docker-compose -f docker-compose.yml up --detach mariadb + + - name: Database | up + 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 + + lint: + if: needs.files-changed.outputs.database == 'true' + name: Lint - Database Up + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Database | Lint + run: cd database && yarn && yarn run lint \ No newline at end of file diff --git a/.github/workflows/test_dht-node.yml b/.github/workflows/test_dht_node.yml similarity index 68% rename from .github/workflows/test_dht-node.yml rename to .github/workflows/test_dht_node.yml index a57f09399..b63d1fc0d 100644 --- a/.github/workflows/test_dht-node.yml +++ b/.github/workflows/test_dht_node.yml @@ -3,14 +3,13 @@ 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: + database: ${{ steps.changes.outputs.database }} dht_node: ${{ steps.changes.outputs.dht_node }} - docker: ${{ steps.changes.outputs.docker }} + docker-compose: ${{ steps.changes.outputs.docker-compose }} steps: - uses: actions/checkout@v3.3.0 @@ -22,12 +21,9 @@ jobs: 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' + if: needs.files-changed.outputs.dht_node == 'true' needs: files-changed runs-on: ubuntu-latest steps: @@ -45,9 +41,6 @@ jobs: name: docker-dht-node-test path: /tmp/dht-node.tar - ############################################################################## - # JOB: LINT ################################################################## - ############################################################################## lint: name: Lint - DHT Node if: needs.files-changed.outputs.dht_node == 'true' @@ -60,12 +53,9 @@ jobs: - name: 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' + if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.dht_node == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.mariadb == 'true' needs: [files-changed, build] runs-on: ubuntu-latest steps: diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/test_e2e.yml similarity index 100% rename from .github/workflows/e2e-test.yml rename to .github/workflows/test_e2e.yml diff --git a/.github/workflows/test_federation.yml b/.github/workflows/test_federation.yml index fc29a1bf8..92ccd95d2 100644 --- a/.github/workflows/test_federation.yml +++ b/.github/workflows/test_federation.yml @@ -3,13 +3,11 @@ 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 }} + docker-compose: ${{ steps.changes.outputs.docker-compose }} federation: ${{ steps.changes.outputs.federation }} steps: - uses: actions/checkout@v3.3.0 @@ -22,12 +20,9 @@ jobs: 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' + if: needs.files-changed.outputs.federation == 'true' needs: files-changed runs-on: ubuntu-latest steps: @@ -45,9 +40,6 @@ jobs: name: docker-federation-test path: /tmp/federation.tar - ############################################################################## - # JOB: LINT ################################################################## - ############################################################################## lint: name: Lint - Federation if: needs.files-changed.outputs.federation == 'true' @@ -60,12 +52,9 @@ jobs: - name: 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' + if: needs.files-changed.outputs.database == 'true' || needs.files-changed.outputs.docker-compose == 'true' || needs.files-changed.outputs.federation == 'true' || needs.files-changed.outputs.mariadb == 'true' needs: [files-changed, build] runs-on: ubuntu-latest steps: diff --git a/.github/workflows/test_mariadb.yml b/.github/workflows/test_mariadb.yml new file mode 100644 index 000000000..fe101bc48 --- /dev/null +++ b/.github/workflows/test_mariadb.yml @@ -0,0 +1,32 @@ +name: Gradido MariaDB Test CI + +on: push + +jobs: + files-changed: + name: Detect File Changes - MariaDB + runs-on: ubuntu-latest + outputs: + mariadb: ${{ steps.changes.outputs.mariadb }} + 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.mariadb == 'true' + name: Docker Build Test - MariaDB + needs: files-changed + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: MariaDB | Build 'test' image + run: docker build --target mariadb_server -t "gradido/mariadb:test" -f ./mariadb/Dockerfile ./ diff --git a/.github/workflows/test-nginx.yml b/.github/workflows/test_nginx.yml similarity index 100% rename from .github/workflows/test-nginx.yml rename to .github/workflows/test_nginx.yml diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js index 4c53a638f..d00b9199a 100644 --- a/backend/.eslintrc.js +++ b/backend/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { node: true, }, parser: '@typescript-eslint/parser', - plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'jest', 'import', 'n'], + plugins: ['prettier', '@typescript-eslint', 'type-graphql', 'import', 'n', 'promise'], extends: [ 'standard', 'eslint:recommended', @@ -33,12 +33,6 @@ module.exports = { htmlWhitespaceSensitivity: 'ignore', }, ], - // jest - 'jest/no-disabled-tests': 'error', - 'jest/no-focused-tests': 'error', - 'jest/no-identical-title': 'error', - 'jest/prefer-to-have-length': 'error', - 'jest/valid-expect': 'error', // import 'import/export': 'error', 'import/no-deprecated': 'error', @@ -142,6 +136,21 @@ module.exports = { 'n/prefer-global/url-search-params': 'error', 'n/prefer-promises/dns': 'error', 'n/prefer-promises/fs': 'error', + // promise + 'promise/catch-or-return': 'error', + 'promise/no-return-wrap': 'error', + 'promise/param-names': 'error', + 'promise/always-return': 'error', + 'promise/no-native': 'off', + 'promise/no-nesting': 'warn', + 'promise/no-promise-in-callback': 'warn', + 'promise/no-callback-in-promise': 'warn', + 'promise/avoid-new': 'warn', + 'promise/no-new-statics': 'error', + 'promise/no-return-in-finally': 'warn', + 'promise/valid-params': 'warn', + 'promise/prefer-await-to-callbacks': 'error', + 'promise/no-multiple-resolved': 'error', }, overrides: [ // only for ts files @@ -168,5 +177,18 @@ module.exports = { EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, }, }, + { + files: ['*.test.ts'], + plugins: ['jest'], + rules: { + 'jest/no-disabled-tests': 'error', + 'jest/no-focused-tests': 'error', + 'jest/no-identical-title': 'error', + 'jest/prefer-to-have-length': 'error', + 'jest/valid-expect': 'error', + '@typescript-eslint/unbound-method': 'off', + 'jest/unbound-method': 'error', + }, + }, ], } diff --git a/backend/.prettierrc.js b/backend/.prettierrc.js index bc1d767d7..8d38f2aac 100644 --- a/backend/.prettierrc.js +++ b/backend/.prettierrc.js @@ -1,9 +1,14 @@ module.exports = { - semi: false, printWidth: 100, - singleQuote: true, - trailingComma: "all", tabWidth: 2, + useTabs: false, + semi: false, + singleQuote: true, + quoteProps: "as-needed", + jsxSingleQuote: true, + trailingComma: "all", bracketSpacing: true, + bracketSameLine: false, + arrowParens: "always", endOfLine: "auto", }; diff --git a/backend/log4js-config.json b/backend/log4js-config.json index e595e7c52..160883eb3 100644 --- a/backend/log4js-config.json +++ b/backend/log4js-config.json @@ -8,7 +8,7 @@ "pattern": "yyyy-MM-dd", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" }, "keepFileExt" : true, "fileNameSep" : "_", @@ -21,7 +21,7 @@ "pattern": "yyyy-MM-dd", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" }, "keepFileExt" : true, "fileNameSep" : "_", @@ -34,7 +34,7 @@ "pattern": "yyyy-MM-dd", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" }, "keepFileExt" : true, "fileNameSep" : "_", @@ -47,7 +47,7 @@ "pattern": "yyyy-MM-dd", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" }, "keepFileExt" : true, "fileNameSep" : "_", @@ -60,7 +60,7 @@ "pattern": "yyyy-MM-dd", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m %s" }, "keepFileExt" : true, "fileNameSep" : "_", @@ -77,7 +77,7 @@ "type": "stdout", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" } }, "apolloOut": @@ -85,7 +85,7 @@ "type": "stdout", "layout": { - "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" + "type": "pattern", "pattern": "%d{ISO8601} %p %c [%X{user}] [%f : %l] - %m" } } }, diff --git a/backend/src/apis/HttpRequest.ts b/backend/src/apis/HttpRequest.ts index 063dfa202..f40d577bd 100644 --- a/backend/src/apis/HttpRequest.ts +++ b/backend/src/apis/HttpRequest.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ @@ -9,39 +10,35 @@ import { backendLogger as logger } from '@/server/logger' // eslint-disable-next-line @typescript-eslint/no-explicit-any export const apiPost = async (url: string, payload: unknown): Promise => { logger.trace('POST', url, payload) - return axios - .post(url, payload) - .then((result) => { - logger.trace('POST-Response', result) - if (result.status !== 200) { - throw new LogError('HTTP Status Error', result.status) - } - if (result.data.state !== 'success') { - throw new Error(result.data.msg) - } - return { success: true, data: result.data } - }) - .catch((error) => { - return { success: false, data: error.message } - }) + try { + const result = await axios.post(url, payload) + logger.trace('POST-Response', result) + if (result.status !== 200) { + throw new LogError('HTTP Status Error', result.status) + } + if (result.data.state !== 'success') { + throw new LogError(result.data.msg) + } + return { success: true, data: result.data } + } catch (error: any) { + return { success: false, data: error.message } + } } // eslint-disable-next-line @typescript-eslint/no-explicit-any export const apiGet = async (url: string): Promise => { logger.trace('GET: url=' + url) - return axios - .get(url) - .then((result) => { - logger.trace('GET-Response', result) - if (result.status !== 200) { - throw new LogError('HTTP Status Error', result.status) - } - if (!['success', 'warning'].includes(result.data.state)) { - throw new Error(result.data.msg) - } - return { success: true, data: result.data } - }) - .catch((error) => { - return { success: false, data: error.message } - }) + try { + const result = await axios.get(url) + logger.trace('GET-Response', result) + if (result.status !== 200) { + throw new LogError('HTTP Status Error', result.status) + } + if (!['success', 'warning'].includes(result.data.state)) { + throw new LogError(result.data.msg) + } + return { success: true, data: result.data } + } catch (error: any) { + return { success: false, data: error.message } + } } diff --git a/backend/src/apis/KlicktippController.ts b/backend/src/apis/KlicktippController.ts index 3f7136de2..a4b5b6be7 100644 --- a/backend/src/apis/KlicktippController.ts +++ b/backend/src/apis/KlicktippController.ts @@ -18,6 +18,7 @@ export const klicktippSignIn = async ( firstName?: string, lastName?: string, ): Promise => { + if (!CONFIG.KLICKTIPP) return true const fields = { fieldFirstName: firstName, fieldLastName: lastName, @@ -28,12 +29,14 @@ export const klicktippSignIn = async ( } export const signout = async (email: string, language: string): Promise => { + if (!CONFIG.KLICKTIPP) return true const apiKey = language === 'de' ? CONFIG.KLICKTIPP_APIKEY_DE : CONFIG.KLICKTIPP_APIKEY_EN const result = await klicktippConnector.signoff(apiKey, email) return result } export const unsubscribe = async (email: string): Promise => { + if (!CONFIG.KLICKTIPP) return true const isLogin = await loginKlicktippUser() if (isLogin) { return await klicktippConnector.unsubscribe(email) @@ -42,6 +45,7 @@ export const unsubscribe = async (email: string): Promise => { } export const getKlickTippUser = async (email: string): Promise => { + if (!CONFIG.KLICKTIPP) return true const isLogin = await loginKlicktippUser() if (isLogin) { const subscriberId = await klicktippConnector.subscriberSearch(email) @@ -52,14 +56,17 @@ export const getKlickTippUser = async (email: string): Promise => { } export const loginKlicktippUser = async (): Promise => { + if (!CONFIG.KLICKTIPP) return true return await klicktippConnector.login(CONFIG.KLICKTIPP_USER, CONFIG.KLICKTIPP_PASSWORD) } export const logoutKlicktippUser = async (): Promise => { + if (!CONFIG.KLICKTIPP) return true return await klicktippConnector.logout() } export const untagUser = async (email: string, tagId: string): Promise => { + if (!CONFIG.KLICKTIPP) return true const isLogin = await loginKlicktippUser() if (isLogin) { return await klicktippConnector.untag(email, tagId) @@ -68,6 +75,7 @@ export const untagUser = async (email: string, tagId: string): Promise } export const tagUser = async (email: string, tagIds: string): Promise => { + if (!CONFIG.KLICKTIPP) return true const isLogin = await loginKlicktippUser() if (isLogin) { return await klicktippConnector.tag(email, tagIds) @@ -76,9 +84,25 @@ export const tagUser = async (email: string, tagIds: string): Promise = } export const getKlicktippTagMap = async () => { + if (!CONFIG.KLICKTIPP) return true const isLogin = await loginKlicktippUser() if (isLogin) { return await klicktippConnector.tagIndex() } return '' } + +export const addFieldsToSubscriber = async ( + email: string, + fields: any = {}, + newemail = '', + newsmsnumber = '', +) => { + if (!CONFIG.KLICKTIPP) return true + const isLogin = await loginKlicktippUser() + if (isLogin) { + const subscriberId = await klicktippConnector.subscriberSearch(email) + return klicktippConnector.subscriberUpdate(subscriberId, fields, newemail, newsmsnumber) + } + return false +} diff --git a/backend/src/auth/RIGHTS.ts b/backend/src/auth/RIGHTS.ts index bcdb2f7ec..df4eed8a1 100644 --- a/backend/src/auth/RIGHTS.ts +++ b/backend/src/auth/RIGHTS.ts @@ -5,8 +5,6 @@ export enum RIGHTS { COMMUNITIES = 'COMMUNITIES', LIST_GDT_ENTRIES = 'LIST_GDT_ENTRIES', EXIST_PID = 'EXIST_PID', - GET_KLICKTIPP_USER = 'GET_KLICKTIPP_USER', - GET_KLICKTIPP_TAG_MAP = 'GET_KLICKTIPP_TAG_MAP', UNSUBSCRIBE_NEWSLETTER = 'UNSUBSCRIBE_NEWSLETTER', SUBSCRIBE_NEWSLETTER = 'SUBSCRIBE_NEWSLETTER', TRANSACTION_LIST = 'TRANSACTION_LIST', diff --git a/backend/src/auth/ROLES.ts b/backend/src/auth/ROLES.ts index df1ee0271..bc868a199 100644 --- a/backend/src/auth/ROLES.ts +++ b/backend/src/auth/ROLES.ts @@ -9,8 +9,6 @@ export const ROLE_USER = new Role('user', [ RIGHTS.BALANCE, RIGHTS.LIST_GDT_ENTRIES, RIGHTS.EXIST_PID, - RIGHTS.GET_KLICKTIPP_USER, - RIGHTS.GET_KLICKTIPP_TAG_MAP, RIGHTS.UNSUBSCRIBE_NEWSLETTER, RIGHTS.SUBSCRIBE_NEWSLETTER, RIGHTS.TRANSACTION_LIST, diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 0c36e4d5a..fc88011ea 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -12,7 +12,7 @@ Decimal.set({ }) const constants = { - DB_VERSION: '0064-event_rename', + DB_VERSION: '0065-refactor_communities_table', 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/emails/sendEmailTranslated.test.ts b/backend/src/emails/sendEmailTranslated.test.ts index 0e04db732..3f1dd1061 100644 --- a/backend/src/emails/sendEmailTranslated.test.ts +++ b/backend/src/emails/sendEmailTranslated.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/unbound-method */ import { createTransport } from 'nodemailer' import { logger, i18n } from '@test/testSetup' diff --git a/backend/src/emails/sendEmailVariants.test.ts b/backend/src/emails/sendEmailVariants.test.ts index fa83996cb..76e96f129 100644 --- a/backend/src/emails/sendEmailVariants.test.ts +++ b/backend/src/emails/sendEmailVariants.test.ts @@ -1,8 +1,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { Connection } from '@dbTools/typeorm' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { testEnvironment } from '@test/helpers' @@ -23,8 +24,12 @@ import { sendTransactionReceivedEmail, } from './sendEmailVariants' -let con: any -let testEnv: any +let con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment(logger, localization) diff --git a/backend/src/event/EVENT_NEWSLETTER_SUBSCRIBE.ts b/backend/src/event/EVENT_NEWSLETTER_SUBSCRIBE.ts new file mode 100644 index 000000000..717bb8296 --- /dev/null +++ b/backend/src/event/EVENT_NEWSLETTER_SUBSCRIBE.ts @@ -0,0 +1,8 @@ +import { Event as DbEvent } from '@entity/Event' +import { User as DbUser } from '@entity/User' + +import { Event } from './Event' +import { EventType } from './EventType' + +export const EVENT_NEWSLETTER_SUBSCRIBE = async (user: DbUser): Promise => + Event(EventType.NEWSLETTER_SUBSCRIBE, user, user).save() diff --git a/backend/src/event/EVENT_NEWSLETTER_UNSUBSCRIBE.ts b/backend/src/event/EVENT_NEWSLETTER_UNSUBSCRIBE.ts new file mode 100644 index 000000000..f8adc69d1 --- /dev/null +++ b/backend/src/event/EVENT_NEWSLETTER_UNSUBSCRIBE.ts @@ -0,0 +1,8 @@ +import { Event as DbEvent } from '@entity/Event' +import { User as DbUser } from '@entity/User' + +import { Event } from './Event' +import { EventType } from './EventType' + +export const EVENT_NEWSLETTER_UNSUBSCRIBE = async (user: DbUser): Promise => + Event(EventType.NEWSLETTER_UNSUBSCRIBE, user, user).save() diff --git a/backend/src/event/EventType.ts b/backend/src/event/EventType.ts index 959a848f5..b2b6f9322 100644 --- a/backend/src/event/EventType.ts +++ b/backend/src/event/EventType.ts @@ -21,6 +21,8 @@ export enum EventType { EMAIL_ADMIN_CONFIRMATION = 'EMAIL_ADMIN_CONFIRMATION', EMAIL_CONFIRMATION = 'EMAIL_CONFIRMATION', EMAIL_FORGOT_PASSWORD = 'EMAIL_FORGOT_PASSWORD', + NEWSLETTER_SUBSCRIBE = 'NEWSLETTER_SUBSCRIBE', + NEWSLETTER_UNSUBSCRIBE = 'NEWSLETTER_UNSUBSCRIBE', TRANSACTION_SEND = 'TRANSACTION_SEND', TRANSACTION_RECEIVE = 'TRANSACTION_RECEIVE', TRANSACTION_LINK_CREATE = 'TRANSACTION_LINK_CREATE', diff --git a/backend/src/event/Events.ts b/backend/src/event/Events.ts index d217cde28..4f10afbce 100644 --- a/backend/src/event/Events.ts +++ b/backend/src/event/Events.ts @@ -21,6 +21,8 @@ export { EVENT_EMAIL_ACCOUNT_MULTIREGISTRATION } from './EVENT_EMAIL_ACCOUNT_MUL export { EVENT_EMAIL_ADMIN_CONFIRMATION } from './EVENT_EMAIL_ADMIN_CONFIRMATION' export { EVENT_EMAIL_CONFIRMATION } from './EVENT_EMAIL_CONFIRMATION' export { EVENT_EMAIL_FORGOT_PASSWORD } from './EVENT_EMAIL_FORGOT_PASSWORD' +export { EVENT_NEWSLETTER_SUBSCRIBE } from './EVENT_NEWSLETTER_SUBSCRIBE' +export { EVENT_NEWSLETTER_UNSUBSCRIBE } from './EVENT_NEWSLETTER_UNSUBSCRIBE' export { EVENT_TRANSACTION_SEND } from './EVENT_TRANSACTION_SEND' export { EVENT_TRANSACTION_RECEIVE } from './EVENT_TRANSACTION_RECEIVE' export { EVENT_TRANSACTION_LINK_CREATE } from './EVENT_TRANSACTION_LINK_CREATE' diff --git a/backend/src/federation/client/1_0/FederationClient.ts b/backend/src/federation/client/1_0/FederationClient.ts index 13f05e761..743d17348 100644 --- a/backend/src/federation/client/1_0/FederationClient.ts +++ b/backend/src/federation/client/1_0/FederationClient.ts @@ -1,14 +1,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { gql } from 'graphql-request' import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' -export async function requestGetPublicKey(dbCom: DbCommunity): Promise { +export async function requestGetPublicKey( + dbCom: DbFederatedCommunity, +): Promise { let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' endpoint = `${endpoint}${dbCom.apiVersion}/` logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) diff --git a/backend/src/federation/client/1_1/FederationClient.ts b/backend/src/federation/client/1_1/FederationClient.ts index bda185fba..35c88bf3b 100644 --- a/backend/src/federation/client/1_1/FederationClient.ts +++ b/backend/src/federation/client/1_1/FederationClient.ts @@ -1,14 +1,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { gql } from 'graphql-request' import { GraphQLGetClient } from '@/federation/client/GraphQLGetClient' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' -export async function requestGetPublicKey(dbCom: DbCommunity): Promise { +export async function requestGetPublicKey( + dbCom: DbFederatedCommunity, +): Promise { let endpoint = dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/' endpoint = `${endpoint}${dbCom.apiVersion}/` logger.info(`requestGetPublicKey with endpoint='${endpoint}'...`) diff --git a/backend/src/federation/validateCommunities.test.ts b/backend/src/federation/validateCommunities.test.ts index d90664b63..ed4897e09 100644 --- a/backend/src/federation/validateCommunities.test.ts +++ b/backend/src/federation/validateCommunities.test.ts @@ -5,15 +5,21 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { Community as DbCommunity } from '@entity/Community' +import { Connection } from '@dbTools/typeorm' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { ApolloServerTestClient } from 'apollo-server-testing' import { testEnvironment, cleanDB } from '@test/helpers' import { logger } from '@test/testSetup' import { validateCommunities } from './validateCommunities' -let con: any -let testEnv: any +let con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment(logger) @@ -59,9 +65,9 @@ describe('validate Communities', () => { endPoint: 'http//localhost:5001/api/', lastAnnouncedAt: new Date(), } - await DbCommunity.createQueryBuilder() + await DbFederatedCommunity.createQueryBuilder() .insert() - .into(DbCommunity) + .into(DbFederatedCommunity) .values(variables1) .orUpdate({ conflict_target: ['id', 'publicKey', 'apiVersion'], @@ -90,9 +96,9 @@ describe('validate Communities', () => { endPoint: 'http//localhost:5001/api/', lastAnnouncedAt: new Date(), } - await DbCommunity.createQueryBuilder() + await DbFederatedCommunity.createQueryBuilder() .insert() - .into(DbCommunity) + .into(DbFederatedCommunity) .values(variables2) .orUpdate({ conflict_target: ['id', 'publicKey', 'apiVersion'], @@ -118,7 +124,7 @@ describe('validate Communities', () => { }) }) describe('with three Communities of api 1_0, 1_1 and 2_0', () => { - let dbCom: DbCommunity + let dbCom: DbFederatedCommunity beforeEach(async () => { const variables3 = { publicKey: Buffer.from('11111111111111111111111111111111'), @@ -126,16 +132,16 @@ describe('validate Communities', () => { endPoint: 'http//localhost:5001/api/', lastAnnouncedAt: new Date(), } - await DbCommunity.createQueryBuilder() + await DbFederatedCommunity.createQueryBuilder() .insert() - .into(DbCommunity) + .into(DbFederatedCommunity) .values(variables3) .orUpdate({ conflict_target: ['id', 'publicKey', 'apiVersion'], overwrite: ['end_point', 'last_announced_at'], }) .execute() - dbCom = await DbCommunity.findOneOrFail({ + dbCom = await DbFederatedCommunity.findOneOrFail({ where: { publicKey: variables3.publicKey, apiVersion: variables3.apiVersion }, }) jest.clearAllMocks() diff --git a/backend/src/federation/validateCommunities.ts b/backend/src/federation/validateCommunities.ts index 0e8c7cb12..b38f38ee9 100644 --- a/backend/src/federation/validateCommunities.ts +++ b/backend/src/federation/validateCommunities.ts @@ -1,5 +1,7 @@ +/** eslint-disable @typescript-eslint/no-unsafe-call */ +/** eslint-disable @typescript-eslint/no-unsafe-assignment */ import { IsNull } from '@dbTools/typeorm' -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { LogError } from '@/server/LogError' import { backendLogger as logger } from '@/server/logger' @@ -23,13 +25,14 @@ export function startValidateCommunities(timerInterval: number): void { } export async function validateCommunities(): Promise { - const dbCommunities: DbCommunity[] = await DbCommunity.createQueryBuilder() - .where({ foreign: true, verifiedAt: IsNull() }) - .orWhere('verified_at < last_announced_at') - .getMany() + const dbFederatedCommunities: DbFederatedCommunity[] = + await DbFederatedCommunity.createQueryBuilder() + .where({ foreign: true, verifiedAt: IsNull() }) + .orWhere('verified_at < last_announced_at') + .getMany() - logger.debug(`Federation: found ${dbCommunities.length} dbCommunities`) - for (const dbCom of dbCommunities) { + logger.debug(`Federation: found ${dbFederatedCommunities.length} dbCommunities`) + for (const dbCom of dbFederatedCommunities) { logger.debug('Federation: dbCom', dbCom) const apiValueStrings: string[] = Object.values(ApiVersionType) logger.debug(`suppported ApiVersions=`, apiValueStrings) @@ -46,7 +49,7 @@ export async function validateCommunities(): Promise { ) if (pubKey && pubKey === dbCom.publicKey.toString()) { logger.info(`Federation: matching publicKey: ${pubKey}`) - await DbCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() }) + await DbFederatedCommunity.update({ id: dbCom.id }, { verifiedAt: new Date() }) logger.debug(`Federation: updated dbCom: ${JSON.stringify(dbCom)}`) } else { logger.warn( @@ -74,7 +77,9 @@ function isLogError(err: unknown) { return err instanceof LogError } -async function invokeVersionedRequestGetPublicKey(dbCom: DbCommunity): Promise { +async function invokeVersionedRequestGetPublicKey( + dbCom: DbFederatedCommunity, +): Promise { switch (dbCom.apiVersion) { case ApiVersionType.V1_0: return v1_0_requestGetPublicKey(dbCom) diff --git a/backend/src/graphql/directive/isAuthorized.ts b/backend/src/graphql/directive/isAuthorized.ts index 117cff056..b8595a2bd 100644 --- a/backend/src/graphql/directive/isAuthorized.ts +++ b/backend/src/graphql/directive/isAuthorized.ts @@ -1,8 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ - import { User } from '@entity/User' import { AuthChecker } from 'type-graphql' @@ -10,9 +5,10 @@ import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS' import { decode, encode } from '@/auth/JWT' import { RIGHTS } from '@/auth/RIGHTS' import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '@/auth/ROLES' +import { Context } from '@/server/context' import { LogError } from '@/server/LogError' -export const isAuthorized: AuthChecker = async ({ context }, rights) => { +export const isAuthorized: AuthChecker = async ({ context }, rights) => { context.role = ROLE_UNAUTHORIZED // unauthorized user // is rights an inalienable right? @@ -47,7 +43,7 @@ export const isAuthorized: AuthChecker = async ({ context }, rights) => { } // check for correct rights - const missingRights = (rights).filter((right) => !context.role.hasRight(right)) + const missingRights = (rights).filter((right) => !context.role?.hasRight(right)) if (missingRights.length !== 0) { throw new LogError('401 Unauthorized') } diff --git a/backend/src/graphql/model/Community.ts b/backend/src/graphql/model/Community.ts index d79d40034..43e0a7108 100644 --- a/backend/src/graphql/model/Community.ts +++ b/backend/src/graphql/model/Community.ts @@ -6,14 +6,12 @@ export class Community { constructor(dbCom: DbCommunity) { this.id = dbCom.id this.foreign = dbCom.foreign - this.publicKey = dbCom.publicKey.toString() - this.url = - (dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') + dbCom.apiVersion - this.lastAnnouncedAt = dbCom.lastAnnouncedAt - this.verifiedAt = dbCom.verifiedAt - this.lastErrorAt = dbCom.lastErrorAt - this.createdAt = dbCom.createdAt - this.updatedAt = dbCom.updatedAt + this.name = dbCom.name + this.description = dbCom.description + this.url = dbCom.url + this.creationDate = dbCom.creationDate + this.uuid = dbCom.communityUuid + this.authenticatedAt = dbCom.authenticatedAt } @Field(() => Int) @@ -22,24 +20,21 @@ export class Community { @Field(() => Boolean) foreign: boolean - @Field(() => String) - publicKey: string + @Field(() => String, { nullable: true }) + name: string | null + + @Field(() => String, { nullable: true }) + description: string | null @Field(() => String) url: string @Field(() => Date, { nullable: true }) - lastAnnouncedAt: Date | null + creationDate: Date | null + + @Field(() => String, { nullable: true }) + uuid: string | null @Field(() => Date, { nullable: true }) - verifiedAt: Date | null - - @Field(() => Date, { nullable: true }) - lastErrorAt: Date | null - - @Field(() => Date, { nullable: true }) - createdAt: Date | null - - @Field(() => Date, { nullable: true }) - updatedAt: Date | null + authenticatedAt: Date | null } diff --git a/backend/src/graphql/model/FederatedCommunity.ts b/backend/src/graphql/model/FederatedCommunity.ts new file mode 100644 index 000000000..856a10d23 --- /dev/null +++ b/backend/src/graphql/model/FederatedCommunity.ts @@ -0,0 +1,45 @@ +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { ObjectType, Field, Int } from 'type-graphql' + +@ObjectType() +export class FederatedCommunity { + constructor(dbCom: DbFederatedCommunity) { + this.id = dbCom.id + this.foreign = dbCom.foreign + this.publicKey = dbCom.publicKey.toString() + this.url = + (dbCom.endPoint.endsWith('/') ? dbCom.endPoint : dbCom.endPoint + '/') + dbCom.apiVersion + this.lastAnnouncedAt = dbCom.lastAnnouncedAt + this.verifiedAt = dbCom.verifiedAt + this.lastErrorAt = dbCom.lastErrorAt + this.createdAt = dbCom.createdAt + this.updatedAt = dbCom.updatedAt + } + + @Field(() => Int) + id: number + + @Field(() => Boolean) + foreign: boolean + + @Field(() => String) + publicKey: string + + @Field(() => String) + url: string + + @Field(() => Date, { nullable: true }) + lastAnnouncedAt: Date | null + + @Field(() => Date, { nullable: true }) + verifiedAt: Date | null + + @Field(() => Date, { nullable: true }) + lastErrorAt: Date | null + + @Field(() => Date, { nullable: true }) + createdAt: Date | null + + @Field(() => Date, { nullable: true }) + updatedAt: Date | null +} diff --git a/backend/src/graphql/resolver/CommunityResolver.test.ts b/backend/src/graphql/resolver/CommunityResolver.test.ts index 26f4a3390..840544e51 100644 --- a/backend/src/graphql/resolver/CommunityResolver.test.ts +++ b/backend/src/graphql/resolver/CommunityResolver.test.ts @@ -5,23 +5,27 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -import { Community as DbCommunity } from '@entity/Community' +import { Connection } from '@dbTools/typeorm' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' +import { ApolloServerTestClient } from 'apollo-server-testing' import { testEnvironment } from '@test/helpers' import { getCommunities } from '@/seeds/graphql/queries' -let query: any - // to do: We need a setup for the tests that closes the connection -let con: any -let testEnv: any +let query: ApolloServerTestClient['query'], con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment() query = testEnv.query con = testEnv.con - await DbCommunity.clear() + await DbFederatedCommunity.clear() }) afterAll(async () => { @@ -30,12 +34,12 @@ afterAll(async () => { describe('CommunityResolver', () => { describe('getCommunities', () => { - let homeCom1: DbCommunity - let homeCom2: DbCommunity - let homeCom3: DbCommunity - let foreignCom1: DbCommunity - let foreignCom2: DbCommunity - let foreignCom3: DbCommunity + let homeCom1: DbFederatedCommunity + let homeCom2: DbFederatedCommunity + let homeCom3: DbFederatedCommunity + let foreignCom1: DbFederatedCommunity + let foreignCom2: DbFederatedCommunity + let foreignCom3: DbFederatedCommunity describe('with empty list', () => { it('returns no community entry', async () => { @@ -53,29 +57,29 @@ describe('CommunityResolver', () => { beforeEach(async () => { jest.clearAllMocks() - homeCom1 = DbCommunity.create() + homeCom1 = DbFederatedCommunity.create() homeCom1.foreign = false homeCom1.publicKey = Buffer.from('publicKey-HomeCommunity') homeCom1.apiVersion = '1_0' homeCom1.endPoint = 'http://localhost/api' homeCom1.createdAt = new Date() - await DbCommunity.insert(homeCom1) + await DbFederatedCommunity.insert(homeCom1) - homeCom2 = DbCommunity.create() + homeCom2 = DbFederatedCommunity.create() homeCom2.foreign = false homeCom2.publicKey = Buffer.from('publicKey-HomeCommunity') homeCom2.apiVersion = '1_1' homeCom2.endPoint = 'http://localhost/api' homeCom2.createdAt = new Date() - await DbCommunity.insert(homeCom2) + await DbFederatedCommunity.insert(homeCom2) - homeCom3 = DbCommunity.create() + homeCom3 = DbFederatedCommunity.create() homeCom3.foreign = false homeCom3.publicKey = Buffer.from('publicKey-HomeCommunity') homeCom3.apiVersion = '2_0' homeCom3.endPoint = 'http://localhost/api' homeCom3.createdAt = new Date() - await DbCommunity.insert(homeCom3) + await DbFederatedCommunity.insert(homeCom3) }) it('returns 3 home-community entries', async () => { @@ -125,29 +129,29 @@ describe('CommunityResolver', () => { beforeEach(async () => { jest.clearAllMocks() - foreignCom1 = DbCommunity.create() + foreignCom1 = DbFederatedCommunity.create() foreignCom1.foreign = true foreignCom1.publicKey = Buffer.from('publicKey-ForeignCommunity') foreignCom1.apiVersion = '1_0' foreignCom1.endPoint = 'http://remotehost/api' foreignCom1.createdAt = new Date() - await DbCommunity.insert(foreignCom1) + await DbFederatedCommunity.insert(foreignCom1) - foreignCom2 = DbCommunity.create() + foreignCom2 = DbFederatedCommunity.create() foreignCom2.foreign = true foreignCom2.publicKey = Buffer.from('publicKey-ForeignCommunity') foreignCom2.apiVersion = '1_1' foreignCom2.endPoint = 'http://remotehost/api' foreignCom2.createdAt = new Date() - await DbCommunity.insert(foreignCom2) + await DbFederatedCommunity.insert(foreignCom2) - foreignCom3 = DbCommunity.create() + foreignCom3 = DbFederatedCommunity.create() foreignCom3.foreign = true foreignCom3.publicKey = Buffer.from('publicKey-ForeignCommunity') foreignCom3.apiVersion = '1_2' foreignCom3.endPoint = 'http://remotehost/api' foreignCom3.createdAt = new Date() - await DbCommunity.insert(foreignCom3) + await DbFederatedCommunity.insert(foreignCom3) }) it('returns 3 home community and 3 foreign community entries', async () => { diff --git a/backend/src/graphql/resolver/CommunityResolver.ts b/backend/src/graphql/resolver/CommunityResolver.ts index 19a499a9b..4c6c8e785 100644 --- a/backend/src/graphql/resolver/CommunityResolver.ts +++ b/backend/src/graphql/resolver/CommunityResolver.ts @@ -1,22 +1,37 @@ import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { Resolver, Query, Authorized } from 'type-graphql' import { Community } from '@model/Community' +import { FederatedCommunity } from '@model/FederatedCommunity' import { RIGHTS } from '@/auth/RIGHTS' @Resolver() export class CommunityResolver { @Authorized([RIGHTS.COMMUNITIES]) - @Query(() => [Community]) - async getCommunities(): Promise { - const dbCommunities: DbCommunity[] = await DbCommunity.find({ + @Query(() => [FederatedCommunity]) + async getCommunities(): Promise { + const dbFederatedCommunities: DbFederatedCommunity[] = await DbFederatedCommunity.find({ order: { foreign: 'ASC', createdAt: 'DESC', lastAnnouncedAt: 'DESC', }, }) + return dbFederatedCommunities.map( + (dbCom: DbFederatedCommunity) => new FederatedCommunity(dbCom), + ) + } + + @Authorized([RIGHTS.COMMUNITIES]) + @Query(() => [Community]) + async getCommunitySelections(): Promise { + const dbCommunities: DbCommunity[] = await DbCommunity.find({ + order: { + name: 'ASC', + }, + }) return dbCommunities.map((dbCom: DbCommunity) => new Community(dbCom)) } } diff --git a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts index 908a38e9b..801e4560b 100644 --- a/backend/src/graphql/resolver/ContributionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionLinkResolver.test.ts @@ -1,12 +1,9 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ - +import { Connection } from '@dbTools/typeorm' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { Event as DbEvent } from '@entity/Event' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -25,8 +22,14 @@ import { listContributionLinks } from '@/seeds/graphql/queries' import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { peterLustig } from '@/seeds/users/peter-lustig' -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], + query: ApolloServerTestClient['query'], + con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment() diff --git a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts index 00b8f7bac..1b6b034c4 100644 --- a/backend/src/graphql/resolver/ContributionMessageResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionMessageResolver.test.ts @@ -1,12 +1,11 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { Event as DbEvent } from '@entity/Event' +import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' import { cleanDB, resetToken, testEnvironment } from '@test/helpers' @@ -36,8 +35,12 @@ jest.mock('@/emails/sendEmailVariants', () => { } }) -let mutate: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} let result: any beforeAll(async () => { diff --git a/backend/src/graphql/resolver/ContributionResolver.test.ts b/backend/src/graphql/resolver/ContributionResolver.test.ts index 54e2ab9d1..17c739fcb 100644 --- a/backend/src/graphql/resolver/ContributionResolver.test.ts +++ b/backend/src/graphql/resolver/ContributionResolver.test.ts @@ -1,23 +1,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/unbound-method */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { Contribution } from '@entity/Contribution' import { Event as DbEvent } from '@entity/Event' import { Transaction as DbTransaction } from '@entity/Transaction' import { User } from '@entity/User' import { UserInputError } from 'apollo-server-express' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' import { ContributionStatus } from '@enum/ContributionStatus' import { Order } from '@enum/Order' -import { ContributionListResult } from '@model/Contribution' -import { UnconfirmedContribution } from '@model/UnconfirmedContribution' import { cleanDB, resetToken, @@ -63,8 +58,14 @@ import { stephenHawking } from '@/seeds/users/stephen-hawking' jest.mock('@/emails/sendEmailVariants') -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], + query: ApolloServerTestClient['query'], + con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} let creation: Contribution | void let admin: User let pendingContribution: any @@ -166,7 +167,7 @@ describe('ContributionResolver', () => { describe('createContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: createContribution, variables: { amount: 100.0, memo: 'Test Contribution', creationDate: 'not-valid' }, }) @@ -191,7 +192,7 @@ describe('ContributionResolver', () => { it('throws error when memo length smaller than 5 chars', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: createContribution, variables: { amount: 100.0, @@ -210,7 +211,7 @@ describe('ContributionResolver', () => { it('throws error when memo length greater than 255 chars', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: createContribution, variables: { amount: 100.0, @@ -227,7 +228,7 @@ describe('ContributionResolver', () => { it('throws error when creationDate not-valid', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: createContribution, variables: { amount: 100.0, @@ -250,7 +251,7 @@ describe('ContributionResolver', () => { it('throws error when creationDate 3 month behind', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: createContribution, variables: { amount: 100.0, @@ -298,7 +299,7 @@ describe('ContributionResolver', () => { describe('updateContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: 1, @@ -327,7 +328,7 @@ describe('ContributionResolver', () => { it('throws error', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -348,7 +349,7 @@ describe('ContributionResolver', () => { it('throws error', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -400,7 +401,7 @@ describe('ContributionResolver', () => { it('throws an error', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -433,7 +434,7 @@ describe('ContributionResolver', () => { it('throws an error', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: adminUpdateContribution, variables: { id: pendingContribution.data.createContribution.id, @@ -512,7 +513,7 @@ describe('ContributionResolver', () => { it('throws an error', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -541,7 +542,7 @@ describe('ContributionResolver', () => { it('throws an error', async () => { jest.clearAllMocks() const date = new Date() - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -564,7 +565,7 @@ describe('ContributionResolver', () => { it('updates contribution', async () => { const { data: { updateContribution: contribution }, - }: { data: { updateContribution: UnconfirmedContribution } } = await mutate({ + } = await mutate({ mutation: updateContribution, variables: { contributionId: pendingContribution.data.createContribution.id, @@ -603,7 +604,7 @@ describe('ContributionResolver', () => { describe('denyContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: 1, @@ -626,7 +627,7 @@ describe('ContributionResolver', () => { }) it('returns an error', async () => { - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: 1, @@ -651,7 +652,7 @@ describe('ContributionResolver', () => { describe('wrong contribution id', () => { it('throws an error', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: -1, @@ -695,7 +696,7 @@ describe('ContributionResolver', () => { }, }) - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: contribution.data.createContribution.id, @@ -740,7 +741,7 @@ describe('ContributionResolver', () => { variables: { email: 'peter@lustig.de', password: 'Aa12345_' }, }) - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: contribution.data.createContribution.id, @@ -785,7 +786,7 @@ describe('ContributionResolver', () => { }, }) - const { errors: errorObjects }: { errors: GraphQLError[] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: denyContribution, variables: { id: contribution.data.createContribution.id, @@ -807,7 +808,7 @@ describe('ContributionResolver', () => { }) const { data: { denyContribution: isDenied }, - }: { data: { denyContribution: boolean } } = await mutate({ + } = await mutate({ mutation: denyContribution, variables: { id: contributionToDeny.data.createContribution.id, @@ -846,8 +847,8 @@ describe('ContributionResolver', () => { describe('deleteContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ - query: deleteContribution, + const { errors: errorObjects } = await mutate({ + mutation: deleteContribution, variables: { id: -1, }, @@ -871,7 +872,7 @@ describe('ContributionResolver', () => { describe('wrong contribution id', () => { it('returns an error', async () => { jest.clearAllMocks() - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: deleteContribution, variables: { id: -1, @@ -899,7 +900,7 @@ describe('ContributionResolver', () => { }) it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: deleteContribution, variables: { id: contributionToDelete.data.createContribution.id, @@ -935,7 +936,7 @@ describe('ContributionResolver', () => { it('deletes successfully', async () => { const { data: { deleteContribution: isDenied }, - }: { data: { deleteContribution: boolean } } = await mutate({ + } = await mutate({ mutation: deleteContribution, variables: { id: contributionToDelete.data.createContribution.id, @@ -974,7 +975,7 @@ describe('ContributionResolver', () => { mutation: login, variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, }) - const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + const { errors: errorObjects } = await mutate({ mutation: deleteContribution, variables: { id: contributionToConfirm.data.createContribution.id, @@ -998,7 +999,7 @@ describe('ContributionResolver', () => { describe('listContributions', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await query({ + const { errors: errorObjects } = await query({ query: listContributions, variables: { currentPage: 1, @@ -1026,7 +1027,7 @@ describe('ContributionResolver', () => { it('returns creations', async () => { const { data: { listContributions: contributionListResult }, - }: { data: { listContributions: ContributionListResult } } = await query({ + } = await query({ query: listContributions, variables: { currentPage: 1, @@ -1078,7 +1079,7 @@ describe('ContributionResolver', () => { it('returns only unconfirmed creations', async () => { const { data: { listContributions: contributionListResult }, - }: { data: { listContributions: ContributionListResult } } = await query({ + } = await query({ query: listContributions, variables: { currentPage: 1, @@ -1128,7 +1129,7 @@ describe('ContributionResolver', () => { describe('listAllContribution', () => { describe('unauthenticated', () => { it('returns an error', async () => { - const { errors: errorObjects }: { errors: [GraphQLError] } = await query({ + const { errors: errorObjects } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1154,7 +1155,7 @@ describe('ContributionResolver', () => { }) it('throws an error with "NOT_VALID" in statusFilter', async () => { - const { errors: errorObjects }: { errors: [GraphQLError | UserInputError] } = await query({ + const { errors: errorObjects } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1171,7 +1172,7 @@ describe('ContributionResolver', () => { }) it('throws an error with a null in statusFilter', async () => { - const { errors: errorObjects }: { errors: [Error] } = await query({ + const { errors: errorObjects } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1188,7 +1189,7 @@ describe('ContributionResolver', () => { }) it('throws an error with null and "NOT_VALID" in statusFilter', async () => { - const { errors: errorObjects }: { errors: [Error] } = await query({ + const { errors: errorObjects } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1210,7 +1211,7 @@ describe('ContributionResolver', () => { it('returns all contributions without statusFilter', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1274,7 +1275,7 @@ describe('ContributionResolver', () => { it('returns all contributions for statusFilter = null', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1339,7 +1340,7 @@ describe('ContributionResolver', () => { it('returns all contributions for statusFilter = []', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1404,7 +1405,7 @@ describe('ContributionResolver', () => { it('returns all CONFIRMED contributions', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1454,7 +1455,7 @@ describe('ContributionResolver', () => { it('returns all PENDING contributions', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1492,7 +1493,7 @@ describe('ContributionResolver', () => { it('returns all IN_PROGRESS Creation', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1530,7 +1531,7 @@ describe('ContributionResolver', () => { it('returns all DENIED Creation', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1574,7 +1575,7 @@ describe('ContributionResolver', () => { it('does not return any DELETED Creation', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -1593,7 +1594,7 @@ describe('ContributionResolver', () => { it('returns all CONFIRMED and PENDING Creation', async () => { const { data: { listAllContributions: contributionListObject }, - }: { data: { listAllContributions: ContributionListResult } } = await query({ + } = await query({ query: listAllContributions, variables: { currentPage: 1, @@ -2676,7 +2677,7 @@ describe('ContributionResolver', () => { it('returns 17 creations in total', async () => { const { data: { adminListContributions: contributionListObject }, - }: { data: { adminListContributions: ContributionListResult } } = await query({ + } = await query({ query: adminListContributions, }) expect(contributionListObject.contributionList).toHaveLength(17) @@ -2843,7 +2844,7 @@ describe('ContributionResolver', () => { it('returns two pending creations with page size set to 2', async () => { const { data: { adminListContributions: contributionListObject }, - }: { data: { adminListContributions: ContributionListResult } } = await query({ + } = await query({ query: adminListContributions, variables: { currentPage: 1, diff --git a/backend/src/graphql/resolver/ContributionResolver.ts b/backend/src/graphql/resolver/ContributionResolver.ts index f6ce30e52..1884fecc4 100644 --- a/backend/src/graphql/resolver/ContributionResolver.ts +++ b/backend/src/graphql/resolver/ContributionResolver.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ import { IsNull, getConnection } from '@dbTools/typeorm' import { Contribution as DbContribution } from '@entity/Contribution' import { ContributionMessage } from '@entity/ContributionMessage' @@ -229,11 +228,11 @@ export class ContributionResolver { contributionMessage.createdAt = contributionToUpdate.updatedAt ? contributionToUpdate.updatedAt : contributionToUpdate.createdAt - const changeMessage = `${contributionToUpdate.contributionDate} + const changeMessage = `${contributionToUpdate.contributionDate.toString()} --- ${contributionToUpdate.memo} --- - ${contributionToUpdate.amount}` + ${contributionToUpdate.amount.toString()}` contributionMessage.message = changeMessage contributionMessage.isModerator = false contributionMessage.userId = user.id @@ -259,7 +258,7 @@ export class ContributionResolver { @Ctx() context: Context, ): Promise { logger.info( - `adminCreateContribution(email=${email}, amount=${amount}, memo=${memo}, creationDate=${creationDate})`, + `adminCreateContribution(email=${email}, amount=${amount.toString()}, memo=${memo}, creationDate=${creationDate})`, ) const clientTimezoneOffset = getClientTimezoneOffset(context) if (!isValidDateString(creationDate)) { diff --git a/backend/src/graphql/resolver/EmailOptinCodes.test.ts b/backend/src/graphql/resolver/EmailOptinCodes.test.ts index 442c63c00..640faad17 100644 --- a/backend/src/graphql/resolver/EmailOptinCodes.test.ts +++ b/backend/src/graphql/resolver/EmailOptinCodes.test.ts @@ -1,10 +1,8 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - +import { Connection } from '@dbTools/typeorm' import { User as DbUser } from '@entity/User' +import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' import { testEnvironment, cleanDB } from '@test/helpers' @@ -13,8 +11,14 @@ import { CONFIG } from '@/config' import { createUser, setPassword, forgotPassword } from '@/seeds/graphql/mutations' import { queryOptIn } from '@/seeds/graphql/queries' -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], + query: ApolloServerTestClient['query'], + con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} CONFIG.EMAIL_CODE_VALID_TIME = 1440 CONFIG.EMAIL_CODE_REQUEST_TIME = 10 diff --git a/backend/src/graphql/resolver/KlicktippResolver.test.ts b/backend/src/graphql/resolver/KlicktippResolver.test.ts new file mode 100644 index 000000000..6a2250bc9 --- /dev/null +++ b/backend/src/graphql/resolver/KlicktippResolver.test.ts @@ -0,0 +1,138 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +import { Event as DbEvent } from '@entity/Event' +import { UserContact } from '@entity/UserContact' +import { GraphQLError } from 'graphql' + +import { cleanDB, resetToken, testEnvironment } from '@test/helpers' +import { logger, i18n as localization } from '@test/testSetup' + +import { EventType } from '@/event/Events' +import { userFactory } from '@/seeds/factory/user' +import { login, subscribeNewsletter, unsubscribeNewsletter } from '@/seeds/graphql/mutations' +import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' + +let testEnv: any, mutate: any, con: any + +beforeAll(async () => { + testEnv = await testEnvironment(logger, localization) + mutate = testEnv.mutate + con = testEnv.con + await cleanDB() +}) + +afterAll(async () => { + await cleanDB() + await con.close() +}) + +describe('KlicktippResolver', () => { + beforeAll(async () => { + await userFactory(testEnv, bibiBloxberg) + }) + + afterAll(async () => { + await cleanDB() + }) + + describe('subscribeNewsletter', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + mutation: subscribeNewsletter, + }) + + expect(errorObjects).toEqual([new GraphQLError('401 Unauthorized')]) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(() => { + resetToken() + }) + + it('calls API', async () => { + const { + data: { subscribeNewsletter: isSubscribed }, + }: { data: { subscribeNewsletter: boolean } } = await mutate({ + mutation: subscribeNewsletter, + }) + + expect(isSubscribed).toEqual(true) + }) + + it('stores the NEWSLETTER_SUBSCRIBE event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.NEWSLETTER_SUBSCRIBE, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, + }), + ) + }) + }) + }) + + describe('unsubscribeNewsletter', () => { + describe('unauthenticated', () => { + it('returns an error', async () => { + const { errors: errorObjects }: { errors: [GraphQLError] } = await mutate({ + mutation: unsubscribeNewsletter, + }) + + expect(errorObjects).toEqual([new GraphQLError('401 Unauthorized')]) + }) + }) + + describe('authenticated', () => { + beforeAll(async () => { + await mutate({ + mutation: login, + variables: { email: 'bibi@bloxberg.de', password: 'Aa12345_' }, + }) + }) + + afterAll(() => { + resetToken() + }) + + it('calls API', async () => { + const { + data: { unsubscribeNewsletter: isUnsubscribed }, + }: { data: { unsubscribeNewsletter: boolean } } = await mutate({ + mutation: unsubscribeNewsletter, + }) + + expect(isUnsubscribed).toEqual(true) + }) + + it('stores the NEWSLETTER_UNSUBSCRIBE event in the database', async () => { + const userConatct = await UserContact.findOneOrFail( + { email: 'bibi@bloxberg.de' }, + { relations: ['user'] }, + ) + await expect(DbEvent.find()).resolves.toContainEqual( + expect.objectContaining({ + type: EventType.NEWSLETTER_UNSUBSCRIBE, + affectedUserId: userConatct.user.id, + actingUserId: userConatct.user.id, + }), + ) + }) + }) + }) +}) diff --git a/backend/src/graphql/resolver/KlicktippResolver.ts b/backend/src/graphql/resolver/KlicktippResolver.ts index 31bde0581..6875abcc5 100644 --- a/backend/src/graphql/resolver/KlicktippResolver.ts +++ b/backend/src/graphql/resolver/KlicktippResolver.ts @@ -1,33 +1,17 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -import { Resolver, Query, Authorized, Arg, Mutation, Ctx } from 'type-graphql' +import { Resolver, Authorized, Mutation, Ctx } from 'type-graphql' -import { - getKlickTippUser, - getKlicktippTagMap, - unsubscribe, - klicktippSignIn, -} from '@/apis/KlicktippController' +import { unsubscribe, klicktippSignIn } from '@/apis/KlicktippController' import { RIGHTS } from '@/auth/RIGHTS' +import { EVENT_NEWSLETTER_SUBSCRIBE, EVENT_NEWSLETTER_UNSUBSCRIBE } from '@/event/Events' import { Context, getUser } from '@/server/context' @Resolver() export class KlicktippResolver { - @Authorized([RIGHTS.GET_KLICKTIPP_USER]) - @Query(() => String) - async getKlicktippUser(@Arg('email') email: string): Promise { - return await getKlickTippUser(email) - } - - @Authorized([RIGHTS.GET_KLICKTIPP_TAG_MAP]) - @Query(() => String) - async getKlicktippTagMap(): Promise { - return await getKlicktippTagMap() - } - @Authorized([RIGHTS.UNSUBSCRIBE_NEWSLETTER]) @Mutation(() => Boolean) async unsubscribeNewsletter(@Ctx() context: Context): Promise { const user = getUser(context) + await EVENT_NEWSLETTER_UNSUBSCRIBE(user) return unsubscribe(user.emailContact.email) } @@ -35,6 +19,7 @@ export class KlicktippResolver { @Mutation(() => Boolean) async subscribeNewsletter(@Ctx() context: Context): Promise { const user = getUser(context) + await EVENT_NEWSLETTER_SUBSCRIBE(user) return klicktippSignIn(user.emailContact.email, user.language) } } diff --git a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts index cec7a1952..04df0dc86 100644 --- a/backend/src/graphql/resolver/TransactionLinkResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionLinkResolver.test.ts @@ -1,16 +1,13 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/unbound-method */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { ContributionLink as DbContributionLink } from '@entity/ContributionLink' import { Event as DbEvent } from '@entity/Event' import { Transaction } from '@entity/Transaction' import { User } from '@entity/User' import { UserContact } from '@entity/UserContact' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -45,8 +42,14 @@ import { transactionLinkCode } from './TransactionLinkResolver' jest.mock('@/util/TRANSACTIONS_LOCK') TRANSACTIONS_LOCK.acquire = jest.fn().mockResolvedValue(jest.fn()) -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], + query: ApolloServerTestClient['query'], + con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} let user: User diff --git a/backend/src/graphql/resolver/TransactionResolver.test.ts b/backend/src/graphql/resolver/TransactionResolver.test.ts index d7bb380e5..1a2f04838 100644 --- a/backend/src/graphql/resolver/TransactionResolver.test.ts +++ b/backend/src/graphql/resolver/TransactionResolver.test.ts @@ -1,13 +1,11 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/unbound-method */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { Event as DbEvent } from '@entity/Event' import { Transaction } from '@entity/Transaction' import { User } from '@entity/User' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { GraphQLError } from 'graphql' @@ -27,13 +25,16 @@ import { garrickOllivander } from '@/seeds/users/garrick-ollivander' import { peterLustig } from '@/seeds/users/peter-lustig' import { stephenHawking } from '@/seeds/users/stephen-hawking' -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment(logger) mutate = testEnv.mutate - query = testEnv.query con = testEnv.con await cleanDB() }) @@ -274,7 +275,7 @@ describe('send coins', () => { }) // login as admin - await query({ mutation: login, variables: peterData }) + await mutate({ mutation: login, variables: peterData }) // confirm the contribution await mutate({ @@ -283,7 +284,7 @@ describe('send coins', () => { }) // login as bob again - await query({ mutation: login, variables: bobData }) + await mutate({ mutation: login, variables: bobData }) }) afterAll(async () => { diff --git a/backend/src/graphql/resolver/UserResolver.test.ts b/backend/src/graphql/resolver/UserResolver.test.ts index 77b08f0d6..d60ba7771 100644 --- a/backend/src/graphql/resolver/UserResolver.test.ts +++ b/backend/src/graphql/resolver/UserResolver.test.ts @@ -2,14 +2,14 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/unbound-method */ /* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { Event as DbEvent } from '@entity/Event' import { TransactionLink } from '@entity/TransactionLink' import { User } from '@entity/User' import { UserContact } from '@entity/UserContact' +import { ApolloServerTestClient } from 'apollo-server-testing' import { GraphQLError } from 'graphql' import { v4 as uuidv4, validate as validateUUID, version as versionUUID } from 'uuid' @@ -88,8 +88,14 @@ jest.mock('@/apis/KlicktippController', () => { let admin: User let user: User -let mutate: any, query: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], + query: ApolloServerTestClient['query'], + con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment(logger, localization) @@ -236,7 +242,7 @@ describe('UserResolver', () => { }) describe('user already exists', () => { - let mutation: User + let mutation: any beforeAll(async () => { mutation = await mutate({ mutation: createUser, variables }) }) @@ -638,7 +644,7 @@ describe('UserResolver', () => { publisherId: 1234, } - let result: User + let result: any afterAll(async () => { await cleanDB() diff --git a/backend/src/graphql/resolver/semaphore.test.ts b/backend/src/graphql/resolver/semaphore.test.ts index f4d87f467..eb4052e1f 100644 --- a/backend/src/graphql/resolver/semaphore.test.ts +++ b/backend/src/graphql/resolver/semaphore.test.ts @@ -1,9 +1,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' +import { ApolloServerTestClient } from 'apollo-server-testing' import { Decimal } from 'decimal.js-light' import { cleanDB, testEnvironment, contributionDateFormatter } from '@test/helpers' @@ -23,8 +22,12 @@ import { bibiBloxberg } from '@/seeds/users/bibi-bloxberg' import { bobBaumeister } from '@/seeds/users/bob-baumeister' import { peterLustig } from '@/seeds/users/peter-lustig' -let mutate: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment() diff --git a/backend/src/graphql/resolver/util/creations.test.ts b/backend/src/graphql/resolver/util/creations.test.ts index 0b05099c8..25d8a45c2 100644 --- a/backend/src/graphql/resolver/util/creations.test.ts +++ b/backend/src/graphql/resolver/util/creations.test.ts @@ -1,11 +1,7 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { Connection } from '@dbTools/typeorm' import { Contribution } from '@entity/Contribution' import { User } from '@entity/User' +import { ApolloServerTestClient } from 'apollo-server-testing' import { testEnvironment, cleanDB, contributionDateFormatter } from '@test/helpers' @@ -16,8 +12,12 @@ import { peterLustig } from '@/seeds/users/peter-lustig' import { getUserCreation } from './creations' -let mutate: any, con: any -let testEnv: any +let mutate: ApolloServerTestClient['mutate'], con: Connection +let testEnv: { + mutate: ApolloServerTestClient['mutate'] + query: ApolloServerTestClient['query'] + con: Connection +} beforeAll(async () => { testEnv = await testEnvironment() diff --git a/backend/src/index.ts b/backend/src/index.ts index b522e1ff5..86f78326d 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { CONFIG } from './config' import { startValidateCommunities } from './federation/validateCommunities' import { createServer } from './server/createServer' diff --git a/backend/src/middleware/klicktippMiddleware.ts b/backend/src/middleware/klicktippMiddleware.ts index c5d6dd0ff..4c5f8db4f 100644 --- a/backend/src/middleware/klicktippMiddleware.ts +++ b/backend/src/middleware/klicktippMiddleware.ts @@ -7,8 +7,7 @@ import { MiddlewareFn } from 'type-graphql' import { KlickTipp } from '@model/KlickTipp' -import { /* klicktippSignIn, */ getKlickTippUser } from '@/apis/KlicktippController' -import { CONFIG } from '@/config' +import { getKlickTippUser } from '@/apis/KlicktippController' import { klickTippLogger as logger } from '@/server/logger' // export const klicktippRegistrationMiddleware: MiddlewareFn = async ( @@ -32,15 +31,13 @@ export const klicktippNewsletterStateMiddleware: MiddlewareFn = async ( // eslint-disable-next-line n/callback-return const result = await next() let klickTipp = new KlickTipp({ status: 'Unsubscribed' }) - if (CONFIG.KLICKTIPP) { - try { - const klickTippUser = await getKlickTippUser(result.email) - if (klickTippUser) { - klickTipp = new KlickTipp(klickTippUser) - } - } catch (err) { - logger.error(`There is no user for (email='${result.email}') ${err}`) + try { + const klickTippUser = await getKlickTippUser(result.email) + if (klickTippUser) { + klickTipp = new KlickTipp(klickTippUser) } + } catch (err) { + logger.error(`There is no user for (email='${result.email}') ${err}`) } result.klickTipp = klickTipp return result diff --git a/backend/src/seeds/factory/creation.ts b/backend/src/seeds/factory/creation.ts index 6a3aaa3e7..5b4c56c57 100644 --- a/backend/src/seeds/factory/creation.ts +++ b/backend/src/seeds/factory/creation.ts @@ -2,9 +2,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/unbound-method */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - import { Contribution } from '@entity/Contribution' import { Transaction } from '@entity/Transaction' import { ApolloServerTestClient } from 'apollo-server-testing' @@ -12,7 +9,6 @@ import { ApolloServerTestClient } from 'apollo-server-testing' import { findUserByEmail } from '@/graphql/resolver/UserResolver' import { CreationInterface } from '@/seeds/creation/CreationInterface' import { login, createContribution, confirmContribution } from '@/seeds/graphql/mutations' -// import CONFIG from '@/config/index' export const nMonthsBefore = (date: Date, months = 1): string => { return new Date(date.getFullYear(), date.getMonth() - months, 1).toISOString() diff --git a/backend/src/seeds/graphql/mutations.ts b/backend/src/seeds/graphql/mutations.ts index b0716ff74..67a01977f 100644 --- a/backend/src/seeds/graphql/mutations.ts +++ b/backend/src/seeds/graphql/mutations.ts @@ -1,14 +1,14 @@ import { gql } from 'graphql-tag' export const subscribeNewsletter = gql` - mutation ($email: String!, $language: String!) { - subscribeNewsletter(email: $email, language: $language) + mutation { + subscribeNewsletter } ` export const unsubscribeNewsletter = gql` - mutation ($email: String!) { - unsubscribeNewsletter(email: $email) + mutation { + unsubscribeNewsletter } ` diff --git a/backend/src/seeds/index.ts b/backend/src/seeds/index.ts index 8ca42fc79..c2533765e 100644 --- a/backend/src/seeds/index.ts +++ b/backend/src/seeds/index.ts @@ -1,10 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - import { entities } from '@entity/index' import { createTestClient } from 'apollo-server-testing' import { name, internet, datatype } from 'faker' @@ -43,10 +36,12 @@ export const cleanDB = async () => { } } -const resetEntity = async (entity: any) => { +const [entityTypes] = entities + +const resetEntity = async (entity: typeof entityTypes) => { const items = await entity.find({ withDeleted: true }) if (items.length > 0) { - const ids = items.map((i: any) => i.id) + const ids = items.map((i) => i.id) await entity.delete(ids) } } diff --git a/backend/src/server/LogError.test.ts b/backend/src/server/LogError.test.ts index b013a31ce..115567a8b 100644 --- a/backend/src/server/LogError.test.ts +++ b/backend/src/server/LogError.test.ts @@ -1,5 +1,4 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/unbound-method */ import { logger } from '@test/testSetup' import { LogError } from './LogError' diff --git a/backend/src/server/context.ts b/backend/src/server/context.ts index a52c979e3..c7e59365b 100644 --- a/backend/src/server/context.ts +++ b/backend/src/server/context.ts @@ -13,6 +13,7 @@ export interface Context { role?: Role user?: dbUser clientTimezoneOffset?: number + gradidoID?: string // hack to use less DB calls for Balance Resolver lastTransaction?: dbTransaction transactionCount?: number diff --git a/backend/src/server/plugins.ts b/backend/src/server/plugins.ts index cb0e4e443..1da062b83 100644 --- a/backend/src/server/plugins.ts +++ b/backend/src/server/plugins.ts @@ -5,7 +5,6 @@ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ - import clonedeep from 'lodash.clonedeep' const setHeadersPlugin = { diff --git a/backend/test/helpers.ts b/backend/test/helpers.ts index b43dc2d96..d42db959f 100644 --- a/backend/test/helpers.ts +++ b/backend/test/helpers.ts @@ -1,12 +1,4 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/unbound-method */ -/* eslint-disable @typescript-eslint/no-explicit-any */ -/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ - import { entities } from '@entity/index' import { createTestClient } from 'apollo-server-testing' @@ -15,6 +7,7 @@ import { createServer } from '@/server/createServer' import { i18n, logger } from './testSetup' export const headerPushMock = jest.fn((t) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access context.token = t.value }) @@ -34,7 +27,7 @@ export const cleanDB = async () => { } } -export const testEnvironment = async (testLogger: any = logger, testI18n: any = i18n) => { +export const testEnvironment = async (testLogger = logger, testI18n = i18n) => { const server = await createServer(context, testLogger, testI18n) const con = server.con const testClient = createTestClient(server.apollo) @@ -43,10 +36,12 @@ export const testEnvironment = async (testLogger: any = logger, testI18n: any = return { mutate, query, con } } -export const resetEntity = async (entity: any) => { +const [entityTypes] = entities + +export const resetEntity = async (entity: typeof entityTypes) => { const items = await entity.find({ withDeleted: true }) if (items.length > 0) { - const ids = items.map((i: any) => i.id) + const ids = items.map((i) => i.id) await entity.delete(ids) } } diff --git a/backend/test/testSetup.ts b/backend/test/testSetup.ts index 12bd25d64..a61556798 100644 --- a/backend/test/testSetup.ts +++ b/backend/test/testSetup.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ import { CONFIG } from '@/config' import { i18n } from '@/server/localization' import { backendLogger as logger } from '@/server/logger' @@ -10,7 +8,7 @@ CONFIG.EMAIL_TEST_MODUS = false jest.setTimeout(1000000) jest.mock('@/server/logger', () => { - const originalModule = jest.requireActual('@/server/logger') + const originalModule = jest.requireActual('@/server/logger') return { __esModule: true, ...originalModule, @@ -27,7 +25,7 @@ jest.mock('@/server/logger', () => { }) jest.mock('@/server/localization', () => { - const originalModule = jest.requireActual('@/server/localization') + const originalModule = jest.requireActual('@/server/localization') return { __esModule: true, ...originalModule, diff --git a/database/entity/0065-refactor_communities_table/Community.ts b/database/entity/0065-refactor_communities_table/Community.ts new file mode 100644 index 000000000..5857634a6 --- /dev/null +++ b/database/entity/0065-refactor_communities_table/Community.ts @@ -0,0 +1,60 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm' + +@Entity('communities') +export class Community extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'foreign', type: 'bool', nullable: false, default: true }) + foreign: boolean + + @Column({ name: 'url', length: 255, nullable: false }) + url: string + + @Column({ name: 'public_key', type: 'binary', length: 64, nullable: false }) + publicKey: Buffer + + @Column({ + name: 'community_uuid', + type: 'char', + length: 36, + nullable: true, + collation: 'utf8mb4_unicode_ci', + }) + communityUuid: string | null + + @Column({ name: 'authenticated_at', type: 'datetime', nullable: true }) + authenticatedAt: Date | null + + @Column({ name: 'name', type: 'varchar', length: 40, nullable: true }) + name: string | null + + @Column({ name: 'description', type: 'varchar', length: 255, nullable: true }) + description: string | null + + @CreateDateColumn({ name: 'creation_date', type: 'datetime', nullable: true }) + creationDate: Date | null + + @CreateDateColumn({ + name: 'created_at', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP(3)', + nullable: false, + }) + createdAt: Date + + @UpdateDateColumn({ + name: 'updated_at', + type: 'datetime', + onUpdate: 'CURRENT_TIMESTAMP(3)', + nullable: true, + }) + updatedAt: Date | null +} diff --git a/database/entity/0065-refactor_communities_table/FederatedCommunity.ts b/database/entity/0065-refactor_communities_table/FederatedCommunity.ts new file mode 100644 index 000000000..0adbf4612 --- /dev/null +++ b/database/entity/0065-refactor_communities_table/FederatedCommunity.ts @@ -0,0 +1,51 @@ +import { + BaseEntity, + Entity, + PrimaryGeneratedColumn, + Column, + CreateDateColumn, + UpdateDateColumn, +} from 'typeorm' + +@Entity('federated_communities') +export class FederatedCommunity extends BaseEntity { + @PrimaryGeneratedColumn('increment', { unsigned: true }) + id: number + + @Column({ name: 'foreign', type: 'bool', nullable: false, default: true }) + foreign: boolean + + @Column({ name: 'public_key', type: 'binary', length: 64, default: null, nullable: true }) + publicKey: Buffer + + @Column({ name: 'api_version', length: 10, nullable: false }) + apiVersion: string + + @Column({ name: 'end_point', length: 255, nullable: false }) + endPoint: string + + @Column({ name: 'last_announced_at', type: 'datetime', nullable: true }) + lastAnnouncedAt: Date | null + + @Column({ name: 'verified_at', type: 'datetime', nullable: true }) + verifiedAt: Date | null + + @Column({ name: 'last_error_at', type: 'datetime', nullable: true }) + lastErrorAt: Date | null + + @CreateDateColumn({ + name: 'created_at', + type: 'datetime', + default: () => 'CURRENT_TIMESTAMP(3)', + nullable: false, + }) + createdAt: Date + + @UpdateDateColumn({ + name: 'updated_at', + type: 'datetime', + onUpdate: 'CURRENT_TIMESTAMP(3)', + nullable: true, + }) + updatedAt: Date | null +} diff --git a/database/entity/Community.ts b/database/entity/Community.ts index 80e5ace30..ee08323b6 100644 --- a/database/entity/Community.ts +++ b/database/entity/Community.ts @@ -1 +1 @@ -export { Community } from './0060-update_communities_table/Community' +export { Community } from './0065-refactor_communities_table/Community' diff --git a/database/entity/FederatedCommunity.ts b/database/entity/FederatedCommunity.ts new file mode 100644 index 000000000..cacaaff9c --- /dev/null +++ b/database/entity/FederatedCommunity.ts @@ -0,0 +1 @@ +export { FederatedCommunity } from './0065-refactor_communities_table/FederatedCommunity' diff --git a/database/entity/index.ts b/database/entity/index.ts index 2d9d04c3b..d44029754 100644 --- a/database/entity/index.ts +++ b/database/entity/index.ts @@ -10,6 +10,7 @@ import { Contribution } from './Contribution' import { Event } from './Event' import { ContributionMessage } from './ContributionMessage' import { Community } from './Community' +import { FederatedCommunity } from './FederatedCommunity' export const entities = [ Community, @@ -17,6 +18,7 @@ export const entities = [ ContributionLink, ContributionMessage, Event, + FederatedCommunity, LoginElopageBuys, LoginEmailOptIn, Migration, diff --git a/database/migrations/0065-refactor_communities_table.ts b/database/migrations/0065-refactor_communities_table.ts new file mode 100644 index 000000000..06f5b3990 --- /dev/null +++ b/database/migrations/0065-refactor_communities_table.ts @@ -0,0 +1,36 @@ +/* MIGRATION TO CREATE THE FEDERATION COMMUNITY TABLES + * + * This migration creates the `community` and 'communityfederation' tables in the `apollo` database (`gradido_community`). + */ + +/* 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 communities TO federated_communities;`) + await queryFn(` + CREATE TABLE communities ( + \`id\` int unsigned NOT NULL AUTO_INCREMENT, + \`foreign\` tinyint(4) NOT NULL DEFAULT 1, + \`url\` varchar(255) NOT NULL, + \`public_key\` binary(64) NOT NULL, + \`community_uuid\` char(36) NULL, + \`authenticated_at\` datetime(3) NULL, + \`name\` varchar(40) NULL, + \`description\` varchar(255) NULL, + \`creation_date\` datetime(3) NULL, + \`created_at\` datetime(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + \`updated_at\` datetime(3), + PRIMARY KEY (id), + UNIQUE KEY url_key (url), + UNIQUE KEY uuid_key (community_uuid), + UNIQUE KEY public_key_key (public_key) + ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + `) +} + +export async function downgrade(queryFn: (query: string, values?: any[]) => Promise>) { + // write downgrade logic as parameter of queryFn + await queryFn(`DROP TABLE communities;`) + await queryFn(`RENAME TABLE federated_communities TO communities;`) +} diff --git a/deployment/bare_metal/setup.md b/deployment/bare_metal/setup.md index 627aa4a2d..9cbc4e2e3 100644 --- a/deployment/bare_metal/setup.md +++ b/deployment/bare_metal/setup.md @@ -198,11 +198,9 @@ Follow the commands in `./install.sh` as installation pattern. ## Define Cronjob To Compensate Yarn Output In `/tmp` -`yarn` creates output in `/tmp` directory, which must be deleted regularly and will be done per Cron-Job. +`yarn` creates output in `/tmp` directory. This output is generated whenever `yarn start` is called. This is especially problematic on staging systems where instable versions are automatically deployed which can lead to an ever restarting, hence generating a lot of yarn output. -### On `stage1` - -An hourly job is necessary on `stage1` by setting the following job in the `crontab` for the `gradido` user. +To solve this you can install the following hourly cron using `crontab` as `gradido` user. Run: @@ -213,24 +211,11 @@ crontab -e This opens the crontab in edit-mode and insert the following entry: ```bash -0 * * * * find /tmp -name "yarn--*" -cmin +60 -exec rm -r {} \; > /dev/null +0 * * * * find /tmp -name "yarn--*" -exec rm -r {} \; > /dev/null + ``` -### On `stage2` - -A daily job is necessary on `stage2` by setting the following job in the `crontab` for the `gradido` user. - -Run: - -```bash -crontab -e -``` - -This opens the `crontab` in edit-mode and insert the following entry: - -```bash -0 4 * * * find /tmp -name "yarn--*" -ctime +1 -exec rm -r {} \; > /dev/null -``` +For production systems this is not needed by default since the yarn output is deleted when `start.sh` is executed. If the service runs stable and does not restart frequently, the yarn output to the tmp folder scales with the amount of services running. ## Define Cronjob To start backup script automatically diff --git a/dht-node/src/config/index.ts b/dht-node/src/config/index.ts index 078c0fbf3..5c4676337 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: '0064-event_rename', + DB_VERSION: '0065-refactor_communities_table', 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 ac5b1b21a..e76e6ac9f 100644 --- a/dht-node/src/dht_node/index.test.ts +++ b/dht-node/src/dht_node/index.test.ts @@ -5,7 +5,7 @@ import { startDHT } from './index' import DHT from '@hyperswarm/dht' import CONFIG from '@/config' import { logger } from '@test/testSetup' -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { testEnvironment, cleanDB } from '@test/helpers' CONFIG.FEDERATION_DHT_SEED = '64ebcb0e3ad547848fef4197c6e2332f' @@ -261,7 +261,7 @@ describe('federation', () => { describe('with receiving wrong but tolerated property data', () => { let jsonArray: any[] - let result: DbCommunity[] = [] + let result: DbFederatedCommunity[] = [] beforeAll(async () => { jest.clearAllMocks() jsonArray = [ @@ -277,7 +277,7 @@ describe('federation', () => { }, ] await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) - result = await DbCommunity.find({ foreign: true }) + result = await DbFederatedCommunity.find({ foreign: true }) }) afterAll(async () => { @@ -523,7 +523,7 @@ describe('federation', () => { describe('with receiving data of exact max allowed properties length', () => { let jsonArray: any[] - let result: DbCommunity[] = [] + let result: DbFederatedCommunity[] = [] beforeAll(async () => { jest.clearAllMocks() jsonArray = [ @@ -538,7 +538,7 @@ describe('federation', () => { { api: 'toolong api', url: 'some valid url' }, ] await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) - result = await DbCommunity.find({ foreign: true }) + result = await DbFederatedCommunity.find({ foreign: true }) }) afterAll(async () => { @@ -570,7 +570,7 @@ describe('federation', () => { describe('with receiving data of exact max allowed buffer length', () => { let jsonArray: any[] - let result: DbCommunity[] = [] + let result: DbFederatedCommunity[] = [] beforeAll(async () => { jest.clearAllMocks() jsonArray = [ @@ -592,7 +592,7 @@ describe('federation', () => { }, ] await socketEventMocks.data(Buffer.from(JSON.stringify(jsonArray))) - result = await DbCommunity.find({ foreign: true }) + result = await DbFederatedCommunity.find({ foreign: true }) }) afterAll(async () => { @@ -711,7 +711,7 @@ describe('federation', () => { }) describe('with proper data', () => { - let result: DbCommunity[] = [] + let result: DbFederatedCommunity[] = [] beforeAll(async () => { jest.clearAllMocks() await socketEventMocks.data( @@ -728,7 +728,7 @@ describe('federation', () => { ]), ), ) - result = await DbCommunity.find({ foreign: true }) + result = await DbFederatedCommunity.find({ foreign: true }) }) afterAll(async () => { diff --git a/dht-node/src/dht_node/index.ts b/dht-node/src/dht_node/index.ts index d101037ae..0db7a28c2 100644 --- a/dht-node/src/dht_node/index.ts +++ b/dht-node/src/dht_node/index.ts @@ -3,7 +3,7 @@ import DHT from '@hyperswarm/dht' import { logger } from '@/server/logger' import CONFIG from '@/config' -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' const KEY_SECRET_SEEDBYTES = 32 const getSeed = (): Buffer | null => @@ -31,7 +31,7 @@ export const startDHT = async (topic: string): Promise => { logger.info(`keyPairDHT: publicKey=${keyPair.publicKey.toString('hex')}`) logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`) - const ownApiVersions = await writeHomeCommunityEnries(keyPair.publicKey) + const ownApiVersions = await writeFederatedHomeCommunityEnries(keyPair.publicKey) logger.info(`ApiList: ${JSON.stringify(ownApiVersions)}`) const node = new DHT({ keyPair }) @@ -92,9 +92,9 @@ export const startDHT = async (topic: string): Promise => { } logger.debug(`upsert with variables=${JSON.stringify(variables)}`) // this will NOT update the updatedAt column, to distingue between a normal update and the last announcement - await DbCommunity.createQueryBuilder() + await DbFederatedCommunity.createQueryBuilder() .insert() - .into(DbCommunity) + .into(DbFederatedCommunity) .values(variables) .orUpdate({ conflict_target: ['id', 'publicKey', 'apiVersion'], @@ -179,7 +179,7 @@ export const startDHT = async (topic: string): Promise => { } } -async function writeHomeCommunityEnries(pubKey: any): Promise { +async function writeFederatedHomeCommunityEnries(pubKey: any): Promise { const homeApiVersions: CommunityApi[] = Object.values(ApiVersionType).map(function (apiEnum) { const comApi: CommunityApi = { api: apiEnum, @@ -189,17 +189,17 @@ async function writeHomeCommunityEnries(pubKey: any): Promise { }) try { // first remove privious existing homeCommunity entries - DbCommunity.createQueryBuilder().delete().where({ foreign: false }).execute() + DbFederatedCommunity.createQueryBuilder().delete().where({ foreign: false }).execute() homeApiVersions.forEach(async function (homeApi) { - const homeCom = new DbCommunity() + const homeCom = new DbFederatedCommunity() homeCom.foreign = false homeCom.apiVersion = homeApi.api homeCom.endPoint = homeApi.url homeCom.publicKey = pubKey.toString('hex') // this will NOT update the updatedAt column, to distingue between a normal update and the last announcement - await DbCommunity.insert(homeCom) + await DbFederatedCommunity.insert(homeCom) logger.info(`federation home-community inserted successfully: ${JSON.stringify(homeCom)}`) }) } catch (err) { diff --git a/docu/Concepts/TechnicalRequirements/Federation.md b/docu/Concepts/TechnicalRequirements/Federation.md index 8bbd74a3e..84c4965af 100644 --- a/docu/Concepts/TechnicalRequirements/Federation.md +++ b/docu/Concepts/TechnicalRequirements/Federation.md @@ -50,6 +50,52 @@ Before starting in describing the details of the federation handshake, some prer With the federation additional data tables/entities have to be created. +##### 1st Draft + +The following diagramms shows the first draft of the possible database-model base on the migration 0063-event_link_fields.ts with 3 steps of migration to reach the required entities. All three diagramms are not exhaustive and are still a base for discussions: + +![img](./image/classdiagramm_x-community-readyness_step1.svg) + +In the first step the current communities table will be renamed to communities_federation. A new table communities is created. Because of the dynamic in the communities_federation data during dht-federation the relation between both entities will be on the collumn communities.communities_federation_public_key. This relation will allow to read a community-entry including its relation to the multi federation entries per api-version with the public key as identifier. + +![img](./image/classdiagramm_x-community-readyness_step2.svg) + +The 2nd step is an introduction of the entity accounts between the users and the transactions table. This will cause a separation of the transactions from the users, to avoid possible conflicts or dependencies between local users of the community and remote users of foreign users, who will be part of x-communitiy-transactions. + +![img](./image/classdiagramm_x-community-readyness_step3.svg) + +The 3rd step will introduce an additional foreign-users and a users_favorites table. A foreign_user could be stored in the existing users-table, but he will not need all the attributes of a home-user, especially he will never gets an AGE-account in this community. The user_favorites entity is designed to buildup the relations between users and foreign_users or in general between all users. This is simply a first idea for a future discussion. + +##### 2nd Draft + +After team discussion in architecture meeting a second vision draft for database migration is shown in the following picture. Only the concerned tables of the database migration are presented. The three elliptical surroundings shows the different steps, which should be done in separate issues. The model, table namings and columns are not exhaustive and are still a base for further discussions. + +![img](./image/class-diagramm_vision-draft2.svg) + +**The first step** with renaming the current `communities` table in `communities_federation` and creating a new `communities` table is not changed. More details about motivation and arguments are described above. + +**The second step** is changed to migrate the `users `table by creating a new `users_settings` table and shift the most existing attributes from `users `table to it. The criterium for shifting a column to `user_settings` is to keep only those columns in the `users `table, which will be used for "home-users" and "foreign-users". A foreign-user at this point of time is a user of an other community, who is involved in a x-community-transaction as `linked_user`. He will not have the possibility to login to the home-community, because after a x-community-tx only the attributes of the `users `table will be exchanged during the handshake of transaction processing of both communities. Even the `transactions `table will be ready for x-community-tx with this `users `and `user_settings` migration, because it contains a reference to both participants of the transaction. For easier displaying and because of historical reasons it will be a good idea to add the columns `linked_user `and `user `(not shown in diagramm) to the `transactions `table with type varchar(512) to store the valid firstname and lastname of the participants at transaction-date. If one of the participants will change his names, there will be no migration of the transaction data necessary and a transaction-list will present always the currect names even over a long distance. + +**The third step** contains a migration for handling accounts and user_correlations. With the new table `gdd_accounts `a new entity will be introduced to support the different types of gradido accounts AGE, GMW and AUF. The second new table `user_correlations `is to buildup the different correlations a user will have: + +* user to user correlation like favorites, trustee, etc +* user to account correlation for cashier of a GMW and AUF community account, trustee of children or seniors, etc. + +The previous idea to replace the `user_id `with an `account_id` in the `transactions `table will be not necessary with this draft, because it will not cause a benefit and saves a lot refactoring efforts. + +##### 3rd Draft + +After further discussions about the database-model and the necessary migration steps the team decided to integrate an additional migration step for X-Community-Transaction. The decision base on keeping the goal focus on implementation of the x-community sendCoins feature as soon as possible. + +![img](./image/class-diagramm_vision-draft3.svg) + +The additional migration step 2 will simply concentrated on the `transactions `table by adding all necessary columns to handle a *x-community-tx* without a previous migration of the `users `table, shown as step 3 in the picture above. + +In concequence of these additional columns in the `transactions `table the database-model will be denormalized by containing redundanten columns. But this migration step will reduce the necessary efforts to reach the *x-community sendCoins* feature as soon as possible. On the other side it offers more possibilities to ensure data consitency, because of internal data checks in conjunction with the redundant columns. + +The feature *x-community-tx* per *sendCoins* will create several challenges, because there is no technical transaction bracket, which will ensure consistent data in both community databases after all write access are finished. The most favorite concept about handling a x-community-transaction and the upcoming handshake to ensure valid send- and receive-transaction entries in both databases is to follow the two-phase-commit protocol. To avoid blocking the transactions table or user dependent transactions-entries during the two-phase-commit processing the idea of pending_transactions is born. This additional pending_transactions table contains the same columns than the transactions table plus one column named `x_transactions_state`. + + ##### Community-Entity Create the new *Community* table to store attributes of the own community. This table is used more like a frame for own community data in the future like the list of federated foreign communities, own users, own futher accounts like AUF- and Welfare-account and the profile data of the own community: diff --git a/docu/Concepts/TechnicalRequirements/graphics/classdiagramm_x-community-readyness.drawio b/docu/Concepts/TechnicalRequirements/graphics/classdiagramm_x-community-readyness.drawio new file mode 100644 index 000000000..c618971f8 --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/graphics/classdiagramm_x-community-readyness.drawio @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.png b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.png new file mode 100644 index 000000000..cd12f1fdd Binary files /dev/null and b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.png differ diff --git a/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.svg b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.svg new file mode 100644 index 000000000..a3a6fd0b4 --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft2.svg @@ -0,0 +1 @@ +
X-Community-Readyness
Step 3
X-Community-Read...
X-Community-Readyness
Step 1
X-Community-Read...
X-Community-Readyness
Step 2
X-Community-Read...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.png b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.png new file mode 100644 index 000000000..b6643fd32 Binary files /dev/null and b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.png differ diff --git a/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.svg b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.svg new file mode 100644 index 000000000..acf9ebc0d --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/image/class-diagramm_vision-draft3.svg @@ -0,0 +1 @@ +
X-Community-Readyness
Step 4
X-Community-Read...
X-Community-Readyness
Step 1
X-Community-Read...
X-Community-Readyness
Step 3
X-Community-Read...
X-Community-Readyness
Step 2
X-Community-Read...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/image/classdiagramm_communities-communities_federation.png b/docu/Concepts/TechnicalRequirements/image/classdiagramm_communities-communities_federation.png new file mode 100644 index 000000000..a794f9e75 Binary files /dev/null and b/docu/Concepts/TechnicalRequirements/image/classdiagramm_communities-communities_federation.png differ diff --git a/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step1.svg b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step1.svg new file mode 100644 index 000000000..28bf0d314 --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step1.svg @@ -0,0 +1 @@ +
X-Community-Readyness
Step 1
X-Community-Read...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step2.svg b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step2.svg new file mode 100644 index 000000000..41300d046 --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step2.svg @@ -0,0 +1 @@ +
X-Community-Readyness
Step 2
X-Community-Read...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step3.svg b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step3.svg new file mode 100644 index 000000000..382c9d728 --- /dev/null +++ b/docu/Concepts/TechnicalRequirements/image/classdiagramm_x-community-readyness_step3.svg @@ -0,0 +1 @@ +
X-Community-Readyness
Step 3
X-Community-Read...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/federation/Dockerfile b/federation/Dockerfile index 959252d29..81b95010e 100644 --- a/federation/Dockerfile +++ b/federation/Dockerfile @@ -86,7 +86,7 @@ RUN cd ../database && yarn run build FROM build as test # Run command -CMD /bin/sh -c "yarn run start" +CMD /bin/sh -c "yarn run dev" ################################################################################## # PRODUCTION (Does contain only "binary"- and static-files to reduce image size) # diff --git a/federation/src/config/index.ts b/federation/src/config/index.ts index ce0c5a9a5..70a155d63 100644 --- a/federation/src/config/index.ts +++ b/federation/src/config/index.ts @@ -11,7 +11,7 @@ Decimal.set({ */ const constants = { - DB_VERSION: '0064-event_rename', + DB_VERSION: '0065-refactor_communities_table', // 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/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts index 20e6c8228..18d2a7599 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.test.ts @@ -2,7 +2,7 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ import { createTestClient } from 'apollo-server-testing' import createServer from '@/server/createServer' -import { Community as DbCommunity } from '@entity/Community' +import { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' let query: any @@ -13,7 +13,7 @@ beforeAll(async () => { const server = await createServer() con = server.con query = createTestClient(server.apollo).query - DbCommunity.clear() + DbFederatedCommunity.clear() }) afterAll(async () => { @@ -32,12 +32,12 @@ describe('PublicKeyResolver', () => { describe('getPublicKey', () => { beforeEach(async () => { - const homeCom = new DbCommunity() + const homeCom = new DbFederatedCommunity() homeCom.foreign = false homeCom.apiVersion = '1_0' homeCom.endPoint = 'endpoint-url' homeCom.publicKey = Buffer.from('homeCommunity-publicKey') - await DbCommunity.insert(homeCom) + await DbFederatedCommunity.insert(homeCom) }) it('returns homeCommunity-publicKey', async () => { diff --git a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts index f96f20c58..0145324fc 100644 --- a/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts +++ b/federation/src/graphql/api/1_0/resolver/PublicKeyResolver.ts @@ -1,7 +1,7 @@ // 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 { FederatedCommunity as DbFederatedCommunity } from '@entity/FederatedCommunity' import { GetPublicKeyResult } from '../model/GetPublicKeyResult' @Resolver() @@ -10,7 +10,7 @@ export class PublicKeyResolver { @Query(() => GetPublicKeyResult) async getPublicKey(): Promise { logger.debug(`getPublicKey() via apiVersion=1_0 ...`) - const homeCom = await DbCommunity.findOneOrFail({ + const homeCom = await DbFederatedCommunity.findOneOrFail({ foreign: false, apiVersion: '1_0', }) diff --git a/frontend/package.json b/frontend/package.json index 8515ca209..f07284da6 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -44,7 +44,7 @@ "graphql": "^15.5.1", "identity-obj-proxy": "^3.0.0", "jest": "^26.6.3", - "jest-canvas-mock": "^2.3.1", + "jest-canvas-mock": "^2.5.0", "jwt-decode": "^3.1.2", "portal-vue": "^2.1.7", "prettier": "^2.2.1", diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 9e8fdd81d..7cc8e5fe5 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -8657,10 +8657,10 @@ javascript-stringify@^1.6.0: resolved "https://registry.yarnpkg.com/javascript-stringify/-/javascript-stringify-1.6.0.tgz#142d111f3a6e3dae8f4a9afd77d45855b5a9cce3" integrity sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM= -jest-canvas-mock@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.3.1.tgz#9535d14bc18ccf1493be36ac37dd349928387826" - integrity sha512-5FnSZPrX3Q2ZfsbYNE3wqKR3+XorN8qFzDzB5o0golWgt6EOX1+emBnpOc9IAQ+NXFj8Nzm3h7ZdE/9H0ylBcg== +jest-canvas-mock@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/jest-canvas-mock/-/jest-canvas-mock-2.5.0.tgz#3e60f87f77ddfa273cf8e7e4ea5f86fa827c7117" + integrity sha512-s2bmY2f22WPMzhB2YA93kiyf7CAfWAnV/sFfY9s48IVOrGmwui1eSFluDPesq1M+7tSC1hJAit6mzO0ZNXvVBA== dependencies: cssfontparser "^1.2.1" moo-color "^1.0.2" diff --git a/mariadb/Dockerfile b/mariadb/Dockerfile index 07d2ba368..d3d5937d0 100644 --- a/mariadb/Dockerfile +++ b/mariadb/Dockerfile @@ -2,8 +2,3 @@ # mariadb server ######################################################################################################### FROM mariadb/server:10.5 as mariadb_server - -# ENV DOCKER_WORKDIR="/docker-entrypoint-initdb.d" - -# RUN mkdir -p ${DOCKER_WORKDIR} -# WORKDIR ${DOCKER_WORKDIR}