mirror of
https://github.com/IT4Change/gradido.git
synced 2025-12-13 07:45:54 +00:00
Merge branch 'master' into 2708-button-for-chat-with-moderator-incorrect-fetch
This commit is contained in:
commit
05ad93a36e
158
.github/workflows/test.yml
vendored
158
.github/workflows/test.yml
vendored
@ -163,7 +163,6 @@ jobs:
|
|||||||
locales_frontend:
|
locales_frontend:
|
||||||
name: Locales - Frontend
|
name: Locales - Frontend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_frontend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -171,20 +170,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Frontend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/frontend.tar
|
|
||||||
##########################################################################
|
|
||||||
# LOCALES FRONTEND #######################################################
|
# LOCALES FRONTEND #######################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Frontend | Locales
|
- name: Frontend | Locales
|
||||||
run: docker run --rm gradido/frontend:test yarn run locales
|
run: cd frontend && yarn && yarn run locales
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT FRONTEND #########################################################
|
# JOB: LINT FRONTEND #########################################################
|
||||||
@ -192,7 +181,6 @@ jobs:
|
|||||||
lint_frontend:
|
lint_frontend:
|
||||||
name: Lint - Frontend
|
name: Lint - Frontend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_frontend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -200,20 +188,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Frontend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/frontend.tar
|
|
||||||
##########################################################################
|
|
||||||
# LINT FRONTEND ##########################################################
|
# LINT FRONTEND ##########################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Frontend | Lint
|
- name: Frontend | Lint
|
||||||
run: docker run --rm gradido/frontend:test yarn run lint
|
run: cd frontend && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: STYLELINT FRONTEND ####################################################
|
# JOB: STYLELINT FRONTEND ####################################################
|
||||||
@ -221,7 +199,6 @@ jobs:
|
|||||||
stylelint_frontend:
|
stylelint_frontend:
|
||||||
name: Stylelint - Frontend
|
name: Stylelint - Frontend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_frontend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -229,20 +206,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Frontend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/frontend.tar
|
|
||||||
##########################################################################
|
|
||||||
# STYLELINT FRONTEND #####################################################
|
# STYLELINT FRONTEND #####################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Frontend | Stylelint
|
- name: Frontend | Stylelint
|
||||||
run: docker run --rm gradido/frontend:test yarn run stylelint
|
run: cd frontend && yarn && yarn run stylelint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT ADMIN INTERFACE ##################################################
|
# JOB: LINT ADMIN INTERFACE ##################################################
|
||||||
@ -250,7 +217,6 @@ jobs:
|
|||||||
lint_admin:
|
lint_admin:
|
||||||
name: Lint - Admin Interface
|
name: Lint - Admin Interface
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_admin]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -258,20 +224,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Admin Interface)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/admin.tar
|
|
||||||
##########################################################################
|
|
||||||
# LINT ADMIN INTERFACE ###################################################
|
# LINT ADMIN INTERFACE ###################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Admin Interface | Lint
|
- name: Admin Interface | Lint
|
||||||
run: docker run --rm gradido/admin:test yarn run lint
|
run: cd admin && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: STYLELINT ADMIN INTERFACE #############################################
|
# JOB: STYLELINT ADMIN INTERFACE #############################################
|
||||||
@ -279,7 +235,6 @@ jobs:
|
|||||||
stylelint_admin:
|
stylelint_admin:
|
||||||
name: Stylelint - Admin Interface
|
name: Stylelint - Admin Interface
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_admin]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -287,20 +242,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Admin Interface)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/admin.tar
|
|
||||||
##########################################################################
|
|
||||||
# STYLELINT ADMIN INTERFACE ##############################################
|
# STYLELINT ADMIN INTERFACE ##############################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Admin Interface | Stylelint
|
- name: Admin Interface | Stylelint
|
||||||
run: docker run --rm gradido/admin:test yarn run stylelint
|
run: cd admin && yarn && yarn run stylelint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LOCALES ADMIN #########################################################
|
# JOB: LOCALES ADMIN #########################################################
|
||||||
@ -308,7 +253,6 @@ jobs:
|
|||||||
locales_admin:
|
locales_admin:
|
||||||
name: Locales - Admin Interface
|
name: Locales - Admin Interface
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_admin]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -316,20 +260,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Admin Interface)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/admin.tar
|
|
||||||
##########################################################################
|
|
||||||
# LOCALES FRONTEND #######################################################
|
# LOCALES FRONTEND #######################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: admin | Locales
|
- name: Admin | Locales
|
||||||
run: docker run --rm gradido/admin:test yarn run locales
|
run: cd admin && yarn && yarn run locales
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LINT BACKEND ##########################################################
|
# JOB: LINT BACKEND ##########################################################
|
||||||
@ -337,7 +271,6 @@ jobs:
|
|||||||
lint_backend:
|
lint_backend:
|
||||||
name: Lint - Backend
|
name: Lint - Backend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_backend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -345,20 +278,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Backend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-backend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/backend.tar
|
|
||||||
##########################################################################
|
|
||||||
# LINT BACKEND ###########################################################
|
# LINT BACKEND ###########################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: backend | Lint
|
- name: backend | Lint
|
||||||
run: docker run --rm gradido/backend:test yarn run lint
|
run: cd backend && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: LOCALES BACKEND #######################################################
|
# JOB: LOCALES BACKEND #######################################################
|
||||||
@ -366,7 +289,6 @@ jobs:
|
|||||||
locales_backend:
|
locales_backend:
|
||||||
name: Locales - Backend
|
name: Locales - Backend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_backend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -385,7 +307,6 @@ jobs:
|
|||||||
lint_database_up:
|
lint_database_up:
|
||||||
name: Lint - Database Up
|
name: Lint - Database Up
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_database_up]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -393,20 +314,10 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGE ##################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Backend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-database-test_up
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/database_up.tar
|
|
||||||
##########################################################################
|
|
||||||
# LINT DATABASE ##########################################################
|
# LINT DATABASE ##########################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: database | Lint
|
- name: Database | Lint
|
||||||
run: docker run --rm gradido/database:test_up yarn run lint
|
run: cd database && yarn && yarn run lint
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
# JOB: UNIT TEST FRONTEND ###################################################
|
# JOB: UNIT TEST FRONTEND ###################################################
|
||||||
@ -414,7 +325,6 @@ jobs:
|
|||||||
unit_test_frontend:
|
unit_test_frontend:
|
||||||
name: Unit tests - Frontend
|
name: Unit tests - Frontend
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_frontend]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -422,30 +332,12 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGES #################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Frontend)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-frontend-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/frontend.tar
|
|
||||||
##########################################################################
|
|
||||||
# UNIT TESTS FRONTEND ####################################################
|
# UNIT TESTS FRONTEND ####################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: frontend | Unit tests
|
- name: Frontend | Unit tests
|
||||||
run: |
|
run: |
|
||||||
docker run --env NODE_ENV=test -v ~/coverage:/app/coverage --rm gradido/frontend:test yarn run test
|
cd frontend && yarn && yarn run test
|
||||||
cp -r ~/coverage ./coverage
|
cp -r ./coverage ../
|
||||||
##########################################################################
|
|
||||||
# COVERAGE REPORT FRONTEND ###############################################
|
|
||||||
##########################################################################
|
|
||||||
#- name: frontend | Coverage report
|
|
||||||
# uses: romeovs/lcov-reporter-action@v0.2.21
|
|
||||||
# with:
|
|
||||||
# github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
||||||
# lcov-file: ./coverage/lcov.info
|
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# COVERAGE CHECK FRONTEND ################################################
|
# COVERAGE CHECK FRONTEND ################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -454,7 +346,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
report_name: Coverage Frontend
|
report_name: Coverage Frontend
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./frontend/coverage/lcov.info
|
||||||
min_coverage: 95
|
min_coverage: 95
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
@ -464,7 +356,6 @@ jobs:
|
|||||||
unit_test_admin:
|
unit_test_admin:
|
||||||
name: Unit tests - Admin Interface
|
name: Unit tests - Admin Interface
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
needs: [build_test_admin]
|
|
||||||
steps:
|
steps:
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# CHECKOUT CODE ##########################################################
|
# CHECKOUT CODE ##########################################################
|
||||||
@ -472,22 +363,12 @@ jobs:
|
|||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# DOWNLOAD DOCKER IMAGES #################################################
|
|
||||||
##########################################################################
|
|
||||||
- name: Download Docker Image (Admin Interface)
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: docker-admin-test
|
|
||||||
path: /tmp
|
|
||||||
- name: Load Docker Image
|
|
||||||
run: docker load < /tmp/admin.tar
|
|
||||||
##########################################################################
|
|
||||||
# UNIT TESTS ADMIN INTERFACE #############################################
|
# UNIT TESTS ADMIN INTERFACE #############################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
- name: Admin Interface | Unit tests
|
- name: Admin Interface | Unit tests
|
||||||
run: |
|
run: |
|
||||||
docker run -v ~/coverage:/app/coverage --rm gradido/admin:test yarn run test
|
cd admin && yarn && yarn run test
|
||||||
cp -r ~/coverage ./coverage
|
cp -r ./coverage ../
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# COVERAGE CHECK ADMIN INTERFACE #########################################
|
# COVERAGE CHECK ADMIN INTERFACE #########################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
@ -496,7 +377,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
report_name: Coverage Admin Interface
|
report_name: Coverage Admin Interface
|
||||||
type: lcov
|
type: lcov
|
||||||
result_path: ./coverage/lcov.info
|
result_path: ./admin/coverage/lcov.info
|
||||||
min_coverage: 97
|
min_coverage: 97
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
|
|
||||||
@ -534,8 +415,9 @@ jobs:
|
|||||||
- name: backend | docker-compose database
|
- name: backend | docker-compose database
|
||||||
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
||||||
- name: backend Unit tests | test
|
- name: backend Unit tests | test
|
||||||
run: cd database && yarn && yarn build && cd ../backend && yarn && yarn test
|
run: |
|
||||||
# run: docker-compose -f docker-compose.yml -f docker-compose.test.yml exec -T backend yarn test
|
cd database && yarn && yarn build && cd ../backend && yarn && yarn test
|
||||||
|
cp -r ./coverage ../
|
||||||
##########################################################################
|
##########################################################################
|
||||||
# COVERAGE CHECK BACKEND #################################################
|
# COVERAGE CHECK BACKEND #################################################
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|||||||
98
.github/workflows/test_federation.yml
vendored
Normal file
98
.github/workflows/test_federation.yml
vendored
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
name: gradido test_federation CI
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
##############################################################################
|
||||||
|
# JOB: DOCKER BUILD TEST #####################################################
|
||||||
|
##############################################################################
|
||||||
|
build:
|
||||||
|
name: Docker Build Test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Build `test` image
|
||||||
|
run: |
|
||||||
|
docker build --target test -t "gradido/federation:test" -f federation/Dockerfile .
|
||||||
|
docker save "gradido/federation:test" > /tmp/federation.tar
|
||||||
|
|
||||||
|
- name: Upload Artifact
|
||||||
|
uses: actions/upload-artifact@v3
|
||||||
|
with:
|
||||||
|
name: docker-federation-test
|
||||||
|
path: /tmp/federation.tar
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# JOB: LINT ##################################################################
|
||||||
|
##############################################################################
|
||||||
|
lint:
|
||||||
|
name: Lint
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Download Docker Image
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: docker-federation-test
|
||||||
|
path: /tmp
|
||||||
|
- name: Load Docker Image
|
||||||
|
run: docker load < /tmp/federation.tar
|
||||||
|
|
||||||
|
- name: Lint
|
||||||
|
run: docker run --rm gradido/federation:test yarn run lint
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
# JOB: UNIT TEST #############################################################
|
||||||
|
##############################################################################
|
||||||
|
unit_test:
|
||||||
|
name: Unit tests
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [build]
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Download Docker Image
|
||||||
|
uses: actions/download-artifact@v3
|
||||||
|
with:
|
||||||
|
name: docker-federation-test
|
||||||
|
path: /tmp
|
||||||
|
|
||||||
|
- name: Load Docker Image
|
||||||
|
run: docker load < /tmp/federation.tar
|
||||||
|
|
||||||
|
- name: 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: docker-compose database
|
||||||
|
run: docker-compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps database
|
||||||
|
|
||||||
|
- name: Sleep for 30 seconds
|
||||||
|
run: sleep 30s
|
||||||
|
shell: bash
|
||||||
|
|
||||||
|
#- name: Unit tests
|
||||||
|
# run: cd database && yarn && yarn build && cd ../dht-node && yarn && yarn test
|
||||||
|
- name: Unit tests
|
||||||
|
run: |
|
||||||
|
docker run --env NODE_ENV=test --env DB_HOST=mariadb --network gradido_internal-net -v ~/coverage:/app/coverage --rm gradido/federation:test yarn run test
|
||||||
|
cp -r ~/coverage ./coverage
|
||||||
|
|
||||||
|
- name: Coverage check
|
||||||
|
uses: webcraftmedia/coverage-check-action@master
|
||||||
|
with:
|
||||||
|
report_name: Coverage federation
|
||||||
|
type: lcov
|
||||||
|
#result_path: ./federation/coverage/lcov.info
|
||||||
|
result_path: ./coverage/lcov.info
|
||||||
|
min_coverage: 72
|
||||||
|
token: ${{ github.token }}
|
||||||
@ -1,9 +1,10 @@
|
|||||||
import jwt from 'jsonwebtoken'
|
import jwt from 'jsonwebtoken'
|
||||||
import CONFIG from '@/config/'
|
import CONFIG from '@/config/'
|
||||||
import { CustomJwtPayload } from './CustomJwtPayload'
|
import { CustomJwtPayload } from './CustomJwtPayload'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
export const decode = (token: string): CustomJwtPayload | null => {
|
export const decode = (token: string): CustomJwtPayload | null => {
|
||||||
if (!token) throw new Error('401 Unauthorized')
|
if (!token) throw new LogError('401 Unauthorized')
|
||||||
try {
|
try {
|
||||||
return <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
|
return <CustomJwtPayload>jwt.verify(token, CONFIG.JWT_SECRET)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import { ROLE_UNAUTHORIZED, ROLE_USER, ROLE_ADMIN } from '@/auth/ROLES'
|
|||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
|
import { INALIENABLE_RIGHTS } from '@/auth/INALIENABLE_RIGHTS'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
||||||
context.role = ROLE_UNAUTHORIZED // unauthorized user
|
context.role = ROLE_UNAUTHORIZED // unauthorized user
|
||||||
@ -17,13 +18,13 @@ const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
|||||||
|
|
||||||
// Do we have a token?
|
// Do we have a token?
|
||||||
if (!context.token) {
|
if (!context.token) {
|
||||||
throw new Error('401 Unauthorized')
|
throw new LogError('401 Unauthorized')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decode the token
|
// Decode the token
|
||||||
const decoded = decode(context.token)
|
const decoded = decode(context.token)
|
||||||
if (!decoded) {
|
if (!decoded) {
|
||||||
throw new Error('403.13 - Client certificate revoked')
|
throw new LogError('403.13 - Client certificate revoked')
|
||||||
}
|
}
|
||||||
// Set context gradidoID
|
// Set context gradidoID
|
||||||
context.gradidoID = decoded.gradidoID
|
context.gradidoID = decoded.gradidoID
|
||||||
@ -39,13 +40,13 @@ const isAuthorized: AuthChecker<any> = async ({ context }, rights) => {
|
|||||||
context.role = user.isAdmin ? ROLE_ADMIN : ROLE_USER
|
context.role = user.isAdmin ? ROLE_ADMIN : ROLE_USER
|
||||||
} catch {
|
} catch {
|
||||||
// in case the database query fails (user deleted)
|
// in case the database query fails (user deleted)
|
||||||
throw new Error('401 Unauthorized')
|
throw new LogError('401 Unauthorized')
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for correct rights
|
// check for correct rights
|
||||||
const missingRights = (<RIGHTS[]>rights).filter((right) => !context.role.hasRight(right))
|
const missingRights = (<RIGHTS[]>rights).filter((right) => !context.role.hasRight(right))
|
||||||
if (missingRights.length !== 0) {
|
if (missingRights.length !== 0) {
|
||||||
throw new Error('401 Unauthorized')
|
throw new LogError('401 Unauthorized')
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new header token
|
// set new header token
|
||||||
|
|||||||
@ -257,17 +257,13 @@ describe('Contribution Links', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [
|
errors: [new GraphQLError('A Start-Date must be set')],
|
||||||
new GraphQLError('Start-Date is not initialized. A Start-Date must be set!'),
|
|
||||||
],
|
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith('A Start-Date must be set')
|
||||||
'Start-Date is not initialized. A Start-Date must be set!',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an error if missing endDate', async () => {
|
it('returns an error if missing endDate', async () => {
|
||||||
@ -282,15 +278,13 @@ describe('Contribution Links', () => {
|
|||||||
}),
|
}),
|
||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [new GraphQLError('End-Date is not initialized. An End-Date must be set!')],
|
errors: [new GraphQLError('An End-Date must be set')],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith('An End-Date must be set')
|
||||||
'End-Date is not initialized. An End-Date must be set!',
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('returns an error if endDate is before startDate', async () => {
|
it('returns an error if endDate is before startDate', async () => {
|
||||||
@ -307,7 +301,7 @@ describe('Contribution Links', () => {
|
|||||||
).resolves.toEqual(
|
).resolves.toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
errors: [
|
errors: [
|
||||||
new GraphQLError(`The value of validFrom must before or equals the validTo!`),
|
new GraphQLError(`The value of validFrom must before or equals the validTo`),
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
@ -315,7 +309,7 @@ describe('Contribution Links', () => {
|
|||||||
|
|
||||||
it('logs the error thrown', () => {
|
it('logs the error thrown', () => {
|
||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
`The value of validFrom must before or equals the validTo!`,
|
`The value of validFrom must before or equals the validTo`,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -33,10 +33,14 @@ export class ContributionMessageResolver {
|
|||||||
try {
|
try {
|
||||||
const contribution = await DbContribution.findOne({ id: contributionId })
|
const contribution = await DbContribution.findOne({ id: contributionId })
|
||||||
if (!contribution) {
|
if (!contribution) {
|
||||||
throw new Error('Contribution not found')
|
throw new LogError('Contribution not found', contributionId)
|
||||||
}
|
}
|
||||||
if (contribution.userId !== user.id) {
|
if (contribution.userId !== user.id) {
|
||||||
throw new Error('Can not send message to contribution of another user')
|
throw new LogError(
|
||||||
|
'Can not send message to contribution of another user',
|
||||||
|
contribution.userId,
|
||||||
|
user.id,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
contributionMessage.contributionId = contributionId
|
contributionMessage.contributionId = contributionId
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
import { Arg, Args, Authorized, Ctx, Int, Mutation, Query, Resolver } from 'type-graphql'
|
||||||
import { FindOperator, IsNull, In, getConnection } from '@dbTools/typeorm'
|
import { FindOperator, IsNull, getConnection } from '@dbTools/typeorm'
|
||||||
|
|
||||||
import { Contribution as DbContribution } from '@entity/Contribution'
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
import { ContributionMessage } from '@entity/ContributionMessage'
|
import { ContributionMessage } from '@entity/ContributionMessage'
|
||||||
@ -30,12 +30,11 @@ import { backendLogger as logger } from '@/server/logger'
|
|||||||
import {
|
import {
|
||||||
getCreationDates,
|
getCreationDates,
|
||||||
getUserCreation,
|
getUserCreation,
|
||||||
getUserCreations,
|
|
||||||
validateContribution,
|
validateContribution,
|
||||||
updateCreations,
|
updateCreations,
|
||||||
isValidDateString,
|
isValidDateString,
|
||||||
} from './util/creations'
|
} from './util/creations'
|
||||||
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS, FULL_CREATION_AVAILABLE } from './const/const'
|
import { MEMO_MAX_CHARS, MEMO_MIN_CHARS } from './const/const'
|
||||||
import {
|
import {
|
||||||
EVENT_CONTRIBUTION_CREATE,
|
EVENT_CONTRIBUTION_CREATE,
|
||||||
EVENT_CONTRIBUTION_DELETE,
|
EVENT_CONTRIBUTION_DELETE,
|
||||||
@ -56,6 +55,7 @@ import { TRANSACTIONS_LOCK } from '@/util/TRANSACTIONS_LOCK'
|
|||||||
import LogError from '@/server/LogError'
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
import { getLastTransaction } from './util/getLastTransaction'
|
import { getLastTransaction } from './util/getLastTransaction'
|
||||||
|
import { findContributions } from './util/findContributions'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class ContributionResolver {
|
export class ContributionResolver {
|
||||||
@ -168,25 +168,14 @@ export class ContributionResolver {
|
|||||||
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
||||||
statusFilter?: ContributionStatus[],
|
statusFilter?: ContributionStatus[],
|
||||||
): Promise<ContributionListResult> {
|
): Promise<ContributionListResult> {
|
||||||
const where: {
|
const [dbContributions, count] = await findContributions(
|
||||||
contributionStatus?: FindOperator<string> | null
|
order,
|
||||||
} = {}
|
currentPage,
|
||||||
|
pageSize,
|
||||||
|
false,
|
||||||
|
statusFilter,
|
||||||
|
)
|
||||||
|
|
||||||
if (statusFilter && statusFilter.length) {
|
|
||||||
where.contributionStatus = In(statusFilter)
|
|
||||||
}
|
|
||||||
|
|
||||||
const [dbContributions, count] = await getConnection()
|
|
||||||
.createQueryBuilder()
|
|
||||||
.select('c')
|
|
||||||
.from(DbContribution, 'c')
|
|
||||||
.innerJoinAndSelect('c.user', 'u')
|
|
||||||
.leftJoinAndSelect('c.messages', 'm')
|
|
||||||
.where(where)
|
|
||||||
.orderBy('c.createdAt', order)
|
|
||||||
.limit(pageSize)
|
|
||||||
.offset((currentPage - 1) * pageSize)
|
|
||||||
.getManyAndCount()
|
|
||||||
return new ContributionListResult(
|
return new ContributionListResult(
|
||||||
count,
|
count,
|
||||||
dbContributions.map((contribution) => new Contribution(contribution, contribution.user)),
|
dbContributions.map((contribution) => new Contribution(contribution, contribution.user)),
|
||||||
@ -425,40 +414,25 @@ export class ContributionResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS])
|
@Authorized([RIGHTS.LIST_UNCONFIRMED_CONTRIBUTIONS])
|
||||||
@Query(() => [UnconfirmedContribution])
|
@Query(() => ContributionListResult) // [UnconfirmedContribution]
|
||||||
async listUnconfirmedContributions(@Ctx() context: Context): Promise<UnconfirmedContribution[]> {
|
async adminListAllContributions(
|
||||||
const clientTimezoneOffset = getClientTimezoneOffset(context)
|
@Args()
|
||||||
const contributions = await getConnection()
|
{ currentPage = 1, pageSize = 3, order = Order.DESC }: Paginated,
|
||||||
.createQueryBuilder()
|
@Arg('statusFilter', () => [ContributionStatus], { nullable: true })
|
||||||
.select('c')
|
statusFilter?: ContributionStatus[],
|
||||||
.from(DbContribution, 'c')
|
): Promise<ContributionListResult> {
|
||||||
.leftJoinAndSelect('c.messages', 'm')
|
const [dbContributions, count] = await findContributions(
|
||||||
.where({ confirmedAt: IsNull() })
|
order,
|
||||||
.andWhere({ deniedAt: IsNull() })
|
currentPage,
|
||||||
.getMany()
|
pageSize,
|
||||||
|
true,
|
||||||
if (contributions.length === 0) {
|
statusFilter,
|
||||||
return []
|
)
|
||||||
}
|
|
||||||
|
return new ContributionListResult(
|
||||||
const userIds = contributions.map((p) => p.userId)
|
count,
|
||||||
const userCreations = await getUserCreations(userIds, clientTimezoneOffset)
|
dbContributions.map((contribution) => new Contribution(contribution, contribution.user)),
|
||||||
const users = await DbUser.find({
|
|
||||||
where: { id: In(userIds) },
|
|
||||||
withDeleted: true,
|
|
||||||
relations: ['emailContact'],
|
|
||||||
})
|
|
||||||
|
|
||||||
return contributions.map((contribution) => {
|
|
||||||
const user = users.find((u) => u.id === contribution.userId)
|
|
||||||
const creation = userCreations.find((c) => c.id === contribution.userId)
|
|
||||||
|
|
||||||
return new UnconfirmedContribution(
|
|
||||||
contribution,
|
|
||||||
user,
|
|
||||||
creation ? creation.creations : FULL_CREATION_AVAILABLE,
|
|
||||||
)
|
)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
|
@Authorized([RIGHTS.ADMIN_DELETE_CONTRIBUTION])
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import { Context, getUser } from '@/server/context'
|
|||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import { apiGet, apiPost } from '@/apis/HttpRequest'
|
import { apiGet, apiPost } from '@/apis/HttpRequest'
|
||||||
import { RIGHTS } from '@/auth/RIGHTS'
|
import { RIGHTS } from '@/auth/RIGHTS'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
export class GdtResolver {
|
export class GdtResolver {
|
||||||
@ -25,11 +26,11 @@ export class GdtResolver {
|
|||||||
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.emailContact.email}/${currentPage}/${pageSize}/${order}`,
|
`${CONFIG.GDT_API_URL}/GdtEntries/listPerEmailApi/${userEntity.emailContact.email}/${currentPage}/${pageSize}/${order}`,
|
||||||
)
|
)
|
||||||
if (!resultGDT.success) {
|
if (!resultGDT.success) {
|
||||||
throw new Error(resultGDT.data)
|
throw new LogError(resultGDT.data)
|
||||||
}
|
}
|
||||||
return new GdtEntryList(resultGDT.data)
|
return new GdtEntryList(resultGDT.data)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error('GDT Server is not reachable.')
|
throw new LogError('GDT Server is not reachable')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ export class GdtResolver {
|
|||||||
email: user.emailContact.email,
|
email: user.emailContact.email,
|
||||||
})
|
})
|
||||||
if (!resultGDTSum.success) {
|
if (!resultGDTSum.success) {
|
||||||
throw new Error('Call not successful')
|
throw new LogError('Call not successful')
|
||||||
}
|
}
|
||||||
return Number(resultGDTSum.data.sum) || 0
|
return Number(resultGDTSum.data.sum) || 0
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -59,7 +60,7 @@ export class GdtResolver {
|
|||||||
// load user
|
// load user
|
||||||
const resultPID = await apiGet(`${CONFIG.GDT_API_URL}/publishers/checkPidApi/${pid}`)
|
const resultPID = await apiGet(`${CONFIG.GDT_API_URL}/publishers/checkPidApi/${pid}`)
|
||||||
if (!resultPID.success) {
|
if (!resultPID.success) {
|
||||||
throw new Error(resultPID.data)
|
throw new LogError(resultPID.data)
|
||||||
}
|
}
|
||||||
return resultPID.data.pid
|
return resultPID.data.pid
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,6 +53,21 @@ afterAll(async () => {
|
|||||||
|
|
||||||
describe('TransactionLinkResolver', () => {
|
describe('TransactionLinkResolver', () => {
|
||||||
describe('createTransactionLink', () => {
|
describe('createTransactionLink', () => {
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('throws an error', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
resetToken()
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: createTransactionLink, variables: { amount: 0, memo: 'Test' } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await mutate({
|
await mutate({
|
||||||
mutation: login,
|
mutation: login,
|
||||||
@ -114,6 +129,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
expect(logger.error).toBeCalledWith('User has not enough GDD', expect.any(Number))
|
expect(logger.error).toBeCalledWith('User has not enough GDD', expect.any(Number))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('redeemTransactionLink', () => {
|
describe('redeemTransactionLink', () => {
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
@ -121,6 +137,21 @@ describe('TransactionLinkResolver', () => {
|
|||||||
resetToken()
|
resetToken()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('unauthenticated', () => {
|
||||||
|
it('throws an error', async () => {
|
||||||
|
jest.clearAllMocks()
|
||||||
|
resetToken()
|
||||||
|
await expect(
|
||||||
|
mutate({ mutation: redeemTransactionLink, variables: { code: 'CL-123456' } }),
|
||||||
|
).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
errors: [new GraphQLError('401 Unauthorized')],
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('authenticated', () => {
|
||||||
describe('contributionLink', () => {
|
describe('contributionLink', () => {
|
||||||
describe('input not valid', () => {
|
describe('input not valid', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
@ -362,7 +393,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
expect(logger.error).toBeCalledWith(
|
expect(logger.error).toBeCalledWith(
|
||||||
'Creation from contribution link was not successful',
|
'Creation from contribution link was not successful',
|
||||||
new Error(
|
new Error(
|
||||||
'The amount (5 GDD) to be created exceeds the amount (0 GDD) still available for this month.',
|
'The amount to be created exceeds the amount still available for this month',
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@ -478,6 +509,7 @@ describe('TransactionLinkResolver', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('listTransactionLinksAdmin', () => {
|
describe('listTransactionLinksAdmin', () => {
|
||||||
const variables = {
|
const variables = {
|
||||||
|
|||||||
@ -86,8 +86,8 @@ export class TransactionLinkResolver {
|
|||||||
transactionLink.code = transactionLinkCode(createdDate)
|
transactionLink.code = transactionLinkCode(createdDate)
|
||||||
transactionLink.createdAt = createdDate
|
transactionLink.createdAt = createdDate
|
||||||
transactionLink.validUntil = validUntil
|
transactionLink.validUntil = validUntil
|
||||||
await DbTransactionLink.save(transactionLink).catch(() => {
|
await DbTransactionLink.save(transactionLink).catch((e) => {
|
||||||
throw new Error('Unable to save transaction link')
|
throw new LogError('Unable to save transaction link', e)
|
||||||
})
|
})
|
||||||
|
|
||||||
return new TransactionLink(transactionLink, new User(user))
|
return new TransactionLink(transactionLink, new User(user))
|
||||||
@ -103,19 +103,23 @@ export class TransactionLinkResolver {
|
|||||||
|
|
||||||
const transactionLink = await DbTransactionLink.findOne({ id })
|
const transactionLink = await DbTransactionLink.findOne({ id })
|
||||||
if (!transactionLink) {
|
if (!transactionLink) {
|
||||||
throw new Error('Transaction Link not found!')
|
throw new LogError('Transaction link not found', id)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionLink.userId !== user.id) {
|
if (transactionLink.userId !== user.id) {
|
||||||
throw new Error('Transaction Link cannot be deleted!')
|
throw new LogError(
|
||||||
|
'Transaction link cannot be deleted by another user',
|
||||||
|
transactionLink.userId,
|
||||||
|
user.id,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionLink.redeemedBy) {
|
if (transactionLink.redeemedBy) {
|
||||||
throw new Error('Transaction Link already redeemed!')
|
throw new LogError('Transaction link already redeemed', transactionLink.redeemedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
await transactionLink.softRemove().catch(() => {
|
await transactionLink.softRemove().catch((e) => {
|
||||||
throw new Error('Transaction Link could not be deleted!')
|
throw new LogError('Transaction link could not be deleted', e)
|
||||||
})
|
})
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@ -312,18 +316,18 @@ export class TransactionLinkResolver {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (user.id === linkedUser.id) {
|
if (user.id === linkedUser.id) {
|
||||||
throw new Error('Cannot redeem own transaction link.')
|
throw new LogError('Cannot redeem own transaction link', user.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: The now check should be done within the semaphore lock,
|
// TODO: The now check should be done within the semaphore lock,
|
||||||
// since the program might wait a while till it is ready to proceed
|
// since the program might wait a while till it is ready to proceed
|
||||||
// writing the transaction.
|
// writing the transaction.
|
||||||
if (transactionLink.validUntil.getTime() < now.getTime()) {
|
if (transactionLink.validUntil.getTime() < now.getTime()) {
|
||||||
throw new Error('Transaction Link is not valid anymore.')
|
throw new LogError('Transaction link is not valid anymore', transactionLink.validUntil)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (transactionLink.redeemedBy) {
|
if (transactionLink.redeemedBy) {
|
||||||
throw new Error('Transaction Link already redeemed.')
|
throw new LogError('Transaction link already redeemed', transactionLink.redeemedBy)
|
||||||
}
|
}
|
||||||
|
|
||||||
await executeTransaction(
|
await executeTransaction(
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import LogError from '@/server/LogError'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import { getConnection } from '@dbTools/typeorm'
|
import { getConnection } from '@dbTools/typeorm'
|
||||||
import { Contribution } from '@entity/Contribution'
|
import { Contribution } from '@entity/Contribution'
|
||||||
@ -19,19 +20,14 @@ export const validateContribution = (
|
|||||||
const index = getCreationIndex(creationDate.getMonth(), timezoneOffset)
|
const index = getCreationIndex(creationDate.getMonth(), timezoneOffset)
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
logger.error(
|
throw new LogError('No information for available creations for the given date', creationDate)
|
||||||
'No information for available creations with the given creationDate=',
|
|
||||||
creationDate.toString(),
|
|
||||||
)
|
|
||||||
throw new Error('No information for available creations for the given date')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount.greaterThan(creations[index].toString())) {
|
if (amount.greaterThan(creations[index].toString())) {
|
||||||
logger.error(
|
throw new LogError(
|
||||||
`The amount (${amount} GDD) to be created exceeds the amount (${creations[index]} GDD) still available for this month.`,
|
'The amount to be created exceeds the amount still available for this month',
|
||||||
)
|
amount,
|
||||||
throw new Error(
|
creations[index],
|
||||||
`The amount (${amount} GDD) to be created exceeds the amount (${creations[index]} GDD) still available for this month.`,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -126,19 +122,16 @@ export const isStartEndDateValid = (
|
|||||||
endDate: string | null | undefined,
|
endDate: string | null | undefined,
|
||||||
): void => {
|
): void => {
|
||||||
if (!startDate) {
|
if (!startDate) {
|
||||||
logger.error('Start-Date is not initialized. A Start-Date must be set!')
|
throw new LogError('A Start-Date must be set')
|
||||||
throw new Error('Start-Date is not initialized. A Start-Date must be set!')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!endDate) {
|
if (!endDate) {
|
||||||
logger.error('End-Date is not initialized. An End-Date must be set!')
|
throw new LogError('An End-Date must be set')
|
||||||
throw new Error('End-Date is not initialized. An End-Date must be set!')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if endDate is before startDate
|
// check if endDate is before startDate
|
||||||
if (new Date(endDate).getTime() - new Date(startDate).getTime() < 0) {
|
if (new Date(endDate).getTime() - new Date(startDate).getTime() < 0) {
|
||||||
logger.error(`The value of validFrom must before or equals the validTo!`)
|
throw new LogError(`The value of validFrom must before or equals the validTo`)
|
||||||
throw new Error(`The value of validFrom must before or equals the validTo!`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +143,7 @@ export const updateCreations = (
|
|||||||
const index = getCreationIndex(contribution.contributionDate.getMonth(), timezoneOffset)
|
const index = getCreationIndex(contribution.contributionDate.getMonth(), timezoneOffset)
|
||||||
|
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
throw new Error('You cannot create GDD for a month older than the last three months.')
|
throw new LogError('You cannot create GDD for a month older than the last three months')
|
||||||
}
|
}
|
||||||
creations[index] = creations[index].plus(contribution.amount.toString())
|
creations[index] = creations[index].plus(contribution.amount.toString())
|
||||||
return creations
|
return creations
|
||||||
|
|||||||
24
backend/src/graphql/resolver/util/findContributions.ts
Normal file
24
backend/src/graphql/resolver/util/findContributions.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { ContributionStatus } from '@enum/ContributionStatus'
|
||||||
|
import { Order } from '@enum/Order'
|
||||||
|
import { Contribution as DbContribution } from '@entity/Contribution'
|
||||||
|
import { In } from '@dbTools/typeorm'
|
||||||
|
|
||||||
|
export const findContributions = async (
|
||||||
|
order: Order,
|
||||||
|
currentPage: number,
|
||||||
|
pageSize: number,
|
||||||
|
withDeleted: boolean,
|
||||||
|
statusFilter?: ContributionStatus[],
|
||||||
|
): Promise<[DbContribution[], number]> =>
|
||||||
|
DbContribution.findAndCount({
|
||||||
|
where: {
|
||||||
|
...(statusFilter && statusFilter.length && { contributionStatus: In(statusFilter) }),
|
||||||
|
},
|
||||||
|
withDeleted: withDeleted,
|
||||||
|
order: {
|
||||||
|
createdAt: order,
|
||||||
|
},
|
||||||
|
relations: ['user'],
|
||||||
|
skip: (currentPage - 1) * pageSize,
|
||||||
|
take: pageSize,
|
||||||
|
})
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
import { backendLogger as logger } from '@/server/logger'
|
import { backendLogger as logger } from '@/server/logger'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
import { PasswordEncryptionType } from '@enum/PasswordEncryptionType'
|
||||||
@ -16,11 +17,10 @@ export const SecretKeyCryptographyCreateKey = (salt: string, password: string):
|
|||||||
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
|
const configLoginAppSecret = Buffer.from(CONFIG.LOGIN_APP_SECRET, 'hex')
|
||||||
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
|
const configLoginServerKey = Buffer.from(CONFIG.LOGIN_SERVER_KEY, 'hex')
|
||||||
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) {
|
if (configLoginServerKey.length !== sodium.crypto_shorthash_KEYBYTES) {
|
||||||
logger.error(
|
throw new LogError(
|
||||||
`ServerKey has an invalid size. The size must be ${sodium.crypto_shorthash_KEYBYTES} bytes.`,
|
'ServerKey has an invalid size',
|
||||||
)
|
configLoginServerKey.length,
|
||||||
throw new Error(
|
sodium.crypto_shorthash_KEYBYTES,
|
||||||
`ServerKey has an invalid size. The size must be ${sodium.crypto_shorthash_KEYBYTES} bytes.`,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,20 +52,13 @@ export const SecretKeyCryptographyCreateKey = (salt: string, password: string):
|
|||||||
|
|
||||||
export const getUserCryptographicSalt = (dbUser: User): string => {
|
export const getUserCryptographicSalt = (dbUser: User): string => {
|
||||||
switch (dbUser.passwordEncryptionType) {
|
switch (dbUser.passwordEncryptionType) {
|
||||||
case PasswordEncryptionType.NO_PASSWORD: {
|
case PasswordEncryptionType.NO_PASSWORD:
|
||||||
logger.error('Password not set for user ' + dbUser.id)
|
throw new LogError('User has no password set', dbUser.id)
|
||||||
throw new Error('Password not set for user ' + dbUser.id) // user has no password
|
case PasswordEncryptionType.EMAIL:
|
||||||
}
|
|
||||||
case PasswordEncryptionType.EMAIL: {
|
|
||||||
return dbUser.emailContact.email
|
return dbUser.emailContact.email
|
||||||
break
|
case PasswordEncryptionType.GRADIDO_ID:
|
||||||
}
|
|
||||||
case PasswordEncryptionType.GRADIDO_ID: {
|
|
||||||
return dbUser.gradidoID
|
return dbUser.gradidoID
|
||||||
break
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
logger.error(`Unknown password encryption type: ${dbUser.passwordEncryptionType}`)
|
throw new LogError('Unknown password encryption type', dbUser.passwordEncryptionType)
|
||||||
throw new Error(`Unknown password encryption type: ${dbUser.passwordEncryptionType}`)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ export const nMonthsBefore = (date: Date, months = 1): string => {
|
|||||||
export const creationFactory = async (
|
export const creationFactory = async (
|
||||||
client: ApolloServerTestClient,
|
client: ApolloServerTestClient,
|
||||||
creation: CreationInterface,
|
creation: CreationInterface,
|
||||||
): Promise<Contribution | void> => {
|
): Promise<Contribution> => {
|
||||||
const { mutate } = client
|
const { mutate } = client
|
||||||
await mutate({ mutation: login, variables: { email: creation.email, password: 'Aa12345_' } })
|
await mutate({ mutation: login, variables: { email: creation.email, password: 'Aa12345_' } })
|
||||||
|
|
||||||
@ -51,6 +51,7 @@ export const creationFactory = async (
|
|||||||
await confirmedContribution.save()
|
await confirmedContribution.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return confirmedContribution
|
||||||
} else {
|
} else {
|
||||||
return contribution
|
return contribution
|
||||||
}
|
}
|
||||||
|
|||||||
@ -272,6 +272,12 @@ export const deleteContribution = gql`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const denyContribution = gql`
|
||||||
|
mutation ($id: Int!) {
|
||||||
|
denyContribution(id: $id)
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const createContributionMessage = gql`
|
export const createContributionMessage = gql`
|
||||||
mutation ($contributionId: Float!, $message: String!) {
|
mutation ($contributionId: Float!, $message: String!) {
|
||||||
createContributionMessage(contributionId: $contributionId, message: $message) {
|
createContributionMessage(contributionId: $contributionId, message: $message) {
|
||||||
|
|||||||
@ -166,6 +166,15 @@ export const listContributions = gql`
|
|||||||
id
|
id
|
||||||
amount
|
amount
|
||||||
memo
|
memo
|
||||||
|
createdAt
|
||||||
|
contributionDate
|
||||||
|
confirmedAt
|
||||||
|
confirmedBy
|
||||||
|
deletedAt
|
||||||
|
state
|
||||||
|
messagesCount
|
||||||
|
deniedAt
|
||||||
|
deniedBy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,18 +204,35 @@ query ($currentPage: Int = 1, $pageSize: Int = 5, $order: Order = DESC, $statusF
|
|||||||
`
|
`
|
||||||
// from admin interface
|
// from admin interface
|
||||||
|
|
||||||
export const listUnconfirmedContributions = gql`
|
export const adminListAllContributions = gql`
|
||||||
query {
|
query (
|
||||||
listUnconfirmedContributions {
|
$currentPage: Int = 1
|
||||||
|
$pageSize: Int = 25
|
||||||
|
$order: Order = DESC
|
||||||
|
$statusFilter: [ContributionStatus!]
|
||||||
|
) {
|
||||||
|
adminListAllContributions(
|
||||||
|
currentPage: $currentPage
|
||||||
|
pageSize: $pageSize
|
||||||
|
order: $order
|
||||||
|
statusFilter: $statusFilter
|
||||||
|
) {
|
||||||
|
contributionCount
|
||||||
|
contributionList {
|
||||||
id
|
id
|
||||||
firstName
|
firstName
|
||||||
lastName
|
lastName
|
||||||
email
|
|
||||||
amount
|
amount
|
||||||
memo
|
memo
|
||||||
date
|
createdAt
|
||||||
moderator
|
confirmedAt
|
||||||
creation
|
confirmedBy
|
||||||
|
contributionDate
|
||||||
|
state
|
||||||
|
messagesCount
|
||||||
|
deniedAt
|
||||||
|
deniedBy
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import { User as dbUser } from '@entity/User'
|
|||||||
import { Transaction as dbTransaction } from '@entity/Transaction'
|
import { Transaction as dbTransaction } from '@entity/Transaction'
|
||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import { ExpressContext } from 'apollo-server-express'
|
import { ExpressContext } from 'apollo-server-express'
|
||||||
|
import LogError from './LogError'
|
||||||
|
|
||||||
export interface Context {
|
export interface Context {
|
||||||
token: string | null
|
token: string | null
|
||||||
@ -35,7 +36,7 @@ const context = (args: ExpressContext): Context => {
|
|||||||
|
|
||||||
export const getUser = (context: Context): dbUser => {
|
export const getUser = (context: Context): dbUser => {
|
||||||
if (context.user) return context.user
|
if (context.user) return context.user
|
||||||
throw new Error('No user given in context!')
|
throw new LogError('No user given in context')
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getClientTimezoneOffset = (context: Context): number => {
|
export const getClientTimezoneOffset = (context: Context): number => {
|
||||||
@ -45,7 +46,7 @@ export const getClientTimezoneOffset = (context: Context): number => {
|
|||||||
) {
|
) {
|
||||||
return context.clientTimezoneOffset
|
return context.clientTimezoneOffset
|
||||||
}
|
}
|
||||||
throw new Error('No valid client time zone offset in context!')
|
throw new LogError('No valid client time zone offset in context')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default context
|
export default context
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import Decimal from 'decimal.js-light'
|
import Decimal from 'decimal.js-light'
|
||||||
import CONFIG from '@/config'
|
import CONFIG from '@/config'
|
||||||
import { Decay } from '@model/Decay'
|
import { Decay } from '@model/Decay'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
// TODO: externalize all those definitions and functions into an external decay library
|
// TODO: externalize all those definitions and functions into an external decay library
|
||||||
|
|
||||||
@ -22,7 +23,7 @@ function calculateDecay(
|
|||||||
const startBlockMs = startBlock.getTime()
|
const startBlockMs = startBlock.getTime()
|
||||||
|
|
||||||
if (toMs < fromMs) {
|
if (toMs < fromMs) {
|
||||||
throw new Error('to < from, reverse decay calculation is invalid')
|
throw new LogError('calculateDecay: to < from, reverse decay calculation is invalid')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize with no decay
|
// Initialize with no decay
|
||||||
|
|||||||
@ -1,11 +1,12 @@
|
|||||||
import connection from '@/typeorm/connection'
|
import connection from '@/typeorm/connection'
|
||||||
import { getKlickTippUser } from '@/apis/KlicktippController'
|
import { getKlickTippUser } from '@/apis/KlicktippController'
|
||||||
import { User } from '@entity/User'
|
import { User } from '@entity/User'
|
||||||
|
import LogError from '@/server/LogError'
|
||||||
|
|
||||||
export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
export async function retrieveNotRegisteredEmails(): Promise<string[]> {
|
||||||
const con = await connection()
|
const con = await connection()
|
||||||
if (!con) {
|
if (!con) {
|
||||||
throw new Error('No connection to database')
|
throw new LogError('No connection to database')
|
||||||
}
|
}
|
||||||
const users = await User.find({ relations: ['emailContact'] })
|
const users = await User.find({ relations: ['emailContact'] })
|
||||||
const notRegisteredUser = []
|
const notRegisteredUser = []
|
||||||
|
|||||||
@ -32,15 +32,6 @@ export const startDHT = async (topic: string): Promise<void> => {
|
|||||||
logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`)
|
logger.debug(`keyPairDHT: secretKey=${keyPair.secretKey.toString('hex')}`)
|
||||||
|
|
||||||
const ownApiVersions = writeHomeCommunityEnries(keyPair.publicKey)
|
const ownApiVersions = writeHomeCommunityEnries(keyPair.publicKey)
|
||||||
/*
|
|
||||||
const ownApiVersions = Object.values(ApiVersionType).map(function (apiEnum) {
|
|
||||||
const comApi: CommunityApi = {
|
|
||||||
api: apiEnum,
|
|
||||||
url: CONFIG.FEDERATION_COMMUNITY_URL,
|
|
||||||
}
|
|
||||||
return comApi
|
|
||||||
})
|
|
||||||
*/
|
|
||||||
logger.debug(`ApiList: ${JSON.stringify(ownApiVersions)}`)
|
logger.debug(`ApiList: ${JSON.stringify(ownApiVersions)}`)
|
||||||
|
|
||||||
const node = new DHT({ keyPair })
|
const node = new DHT({ keyPair })
|
||||||
@ -216,6 +207,5 @@ async function writeHomeCommunityEnries(pubKey: any): Promise<CommunityApi[]> {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new Error(`Federation: Error writing HomeCommunity-Entries: ${err}`)
|
throw new Error(`Federation: Error writing HomeCommunity-Entries: ${err}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return homeApiVersions
|
return homeApiVersions
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,6 +84,29 @@ services:
|
|||||||
- ./dht-node:/app
|
- ./dht-node:/app
|
||||||
- ./database:/database
|
- ./database:/database
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
# FEDERATION ###########################################
|
||||||
|
########################################################
|
||||||
|
federation:
|
||||||
|
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||||
|
image: gradido/federation:local-development
|
||||||
|
build:
|
||||||
|
target: development
|
||||||
|
networks:
|
||||||
|
- external-net
|
||||||
|
- internal-net
|
||||||
|
environment:
|
||||||
|
- NODE_ENV="development"
|
||||||
|
volumes:
|
||||||
|
# This makes sure the docker container has its own node modules.
|
||||||
|
# Therefore it is possible to have a different node version on the host machine
|
||||||
|
- federation_node_modules:/app/node_modules
|
||||||
|
- federation_database_node_modules:/database/node_modules
|
||||||
|
- federation_database_build:/database/build
|
||||||
|
# bind the local folder to the docker to allow live reload
|
||||||
|
- ./federation:/app
|
||||||
|
- ./database:/database
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# DATABASE ##############################################
|
# DATABASE ##############################################
|
||||||
########################################################
|
########################################################
|
||||||
@ -155,5 +178,8 @@ volumes:
|
|||||||
dht_node_modules:
|
dht_node_modules:
|
||||||
dht_database_node_modules:
|
dht_database_node_modules:
|
||||||
dht_database_build:
|
dht_database_build:
|
||||||
|
federation_node_modules:
|
||||||
|
federation_database_node_modules:
|
||||||
|
federation_database_build:
|
||||||
database_node_modules:
|
database_node_modules:
|
||||||
database_build:
|
database_build:
|
||||||
@ -36,6 +36,21 @@ services:
|
|||||||
- NODE_ENV="test"
|
- NODE_ENV="test"
|
||||||
- DB_HOST=mariadb
|
- DB_HOST=mariadb
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
# FEDERATION ###########################################
|
||||||
|
########################################################
|
||||||
|
federation:
|
||||||
|
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||||
|
image: gradido/federation:test
|
||||||
|
build:
|
||||||
|
target: test
|
||||||
|
networks:
|
||||||
|
- external-net
|
||||||
|
- internal-net
|
||||||
|
environment:
|
||||||
|
- NODE_ENV="test"
|
||||||
|
- DB_HOST=mariadb
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# DATABASE #############################################
|
# DATABASE #############################################
|
||||||
########################################################
|
########################################################
|
||||||
|
|||||||
@ -147,6 +147,42 @@ services:
|
|||||||
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||||
- ./logs/dht-node:/logs/dht-node
|
- ./logs/dht-node:/logs/dht-node
|
||||||
|
|
||||||
|
########################################################
|
||||||
|
# FEDERATION ###########################################
|
||||||
|
########################################################
|
||||||
|
federation:
|
||||||
|
# name the image so that it cannot be found in a DockerHub repository, otherwise it will not be built locally from the 'dockerfile' but pulled from there
|
||||||
|
image: gradido/federation:local-production
|
||||||
|
build:
|
||||||
|
# since we have to include the entities from ./database we cannot define the context as ./federation
|
||||||
|
# this might blow build image size to the moon ?!
|
||||||
|
context: ./
|
||||||
|
dockerfile: ./federation/Dockerfile
|
||||||
|
target: production
|
||||||
|
networks:
|
||||||
|
- internal-net
|
||||||
|
- external-net
|
||||||
|
ports:
|
||||||
|
- 5010:5010
|
||||||
|
depends_on:
|
||||||
|
- mariadb
|
||||||
|
restart: always
|
||||||
|
environment:
|
||||||
|
# Envs used in Dockerfile
|
||||||
|
# - DOCKER_WORKDIR="/app"
|
||||||
|
- PORT=5010
|
||||||
|
- BUILD_DATE
|
||||||
|
- BUILD_VERSION
|
||||||
|
- BUILD_COMMIT
|
||||||
|
- NODE_ENV="production"
|
||||||
|
- DB_HOST=mariadb
|
||||||
|
# Application only envs
|
||||||
|
#env_file:
|
||||||
|
# - ./frontend/.env
|
||||||
|
volumes:
|
||||||
|
# <host_machine_directory>:<container_directory> – mirror bidirectional path in local context with path in Docker container
|
||||||
|
- ./logs/federation:/logs/federation
|
||||||
|
|
||||||
########################################################
|
########################################################
|
||||||
# DATABASE #############################################
|
# DATABASE #############################################
|
||||||
########################################################
|
########################################################
|
||||||
|
|||||||
116
federation/Dockerfile
Normal file
116
federation/Dockerfile
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
##################################################################################
|
||||||
|
# BASE ###########################################################################
|
||||||
|
##################################################################################
|
||||||
|
FROM node:18.7.0-alpine3.16 as base
|
||||||
|
|
||||||
|
# ENVs (available in production aswell, can be overwritten by commandline or env file)
|
||||||
|
## DOCKER_WORKDIR would be a classical ARG, but that is not multi layer persistent - shame
|
||||||
|
ENV DOCKER_WORKDIR="/app"
|
||||||
|
## We Cannot do `$(date -u +'%Y-%m-%dT%H:%M:%SZ')` here so we use unix timestamp=0
|
||||||
|
ENV BUILD_DATE="1970-01-01T00:00:00.00Z"
|
||||||
|
## We cannot do $(npm run version).${BUILD_NUMBER} here so we default to 0.0.0.0
|
||||||
|
ENV BUILD_VERSION="0.0.0.0"
|
||||||
|
## We cannot do `$(git rev-parse --short HEAD)` here so we default to 0000000
|
||||||
|
ENV BUILD_COMMIT="0000000"
|
||||||
|
## SET NODE_ENV
|
||||||
|
ENV NODE_ENV="production"
|
||||||
|
## App relevant Envs
|
||||||
|
ENV PORT="5010"
|
||||||
|
# ENV PORT="${env.FEDERATION_PORT}"
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL org.label-schema.build-date="${BUILD_DATE}"
|
||||||
|
LABEL org.label-schema.name="gradido:federation"
|
||||||
|
LABEL org.label-schema.description="Gradido GraphQL Federation"
|
||||||
|
LABEL org.label-schema.usage="https://github.com/gradido/gradido/blob/master/README.md"
|
||||||
|
LABEL org.label-schema.url="https://gradido.net"
|
||||||
|
LABEL org.label-schema.vcs-url="https://github.com/gradido/gradido/tree/master/federation"
|
||||||
|
LABEL org.label-schema.vcs-ref="${BUILD_COMMIT}"
|
||||||
|
LABEL org.label-schema.vendor="Gradido Community"
|
||||||
|
LABEL org.label-schema.version="${BUILD_VERSION}"
|
||||||
|
LABEL org.label-schema.schema-version="1.0"
|
||||||
|
LABEL maintainer="support@gradido.net"
|
||||||
|
|
||||||
|
# Install Additional Software
|
||||||
|
## install: git
|
||||||
|
#RUN apk --no-cache add git
|
||||||
|
|
||||||
|
# Settings
|
||||||
|
## Expose Container Port
|
||||||
|
EXPOSE ${PORT}
|
||||||
|
|
||||||
|
## Workdir
|
||||||
|
RUN mkdir -p ${DOCKER_WORKDIR}
|
||||||
|
WORKDIR ${DOCKER_WORKDIR}
|
||||||
|
|
||||||
|
RUN mkdir -p /database
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# DEVELOPMENT (Connected to the local environment, to reload on demand) ##########
|
||||||
|
##################################################################################
|
||||||
|
FROM base as development
|
||||||
|
|
||||||
|
# We don't need to copy or build anything since we gonna bind to the
|
||||||
|
# local filesystem which will need a rebuild anyway
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
# (for development we need to execute yarn install since the
|
||||||
|
# node_modules are on another volume and need updating)
|
||||||
|
CMD /bin/sh -c "cd /database && yarn install && yarn build && cd /app && yarn install && yarn run dev"
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# BUILD (Does contain all files and is therefore bloated) ########################
|
||||||
|
##################################################################################
|
||||||
|
FROM base as build
|
||||||
|
|
||||||
|
# Copy everything from federation
|
||||||
|
COPY ./federation/ ./
|
||||||
|
# Copy everything from database
|
||||||
|
COPY ./database/ ../database/
|
||||||
|
|
||||||
|
# yarn install federation
|
||||||
|
RUN yarn install --production=false --frozen-lockfile --non-interactive
|
||||||
|
|
||||||
|
# yarn install database
|
||||||
|
RUN cd ../database && yarn install --production=false --frozen-lockfile --non-interactive
|
||||||
|
|
||||||
|
# yarn build
|
||||||
|
RUN yarn run build
|
||||||
|
|
||||||
|
# yarn build database
|
||||||
|
RUN cd ../database && yarn run build
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# TEST ###########################################################################
|
||||||
|
##################################################################################
|
||||||
|
FROM build as test
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
CMD /bin/sh -c "yarn run start"
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
# PRODUCTION (Does contain only "binary"- and static-files to reduce image size) #
|
||||||
|
##################################################################################
|
||||||
|
FROM base as production
|
||||||
|
|
||||||
|
# Copy "binary"-files from build image
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/build ./build
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/../database/build ../database/build
|
||||||
|
# We also copy the node_modules express and serve-static for the run script
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/node_modules ./node_modules
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/../database/node_modules ../database/node_modules
|
||||||
|
|
||||||
|
# Copy static files
|
||||||
|
# COPY --from=build ${DOCKER_WORKDIR}/public ./public
|
||||||
|
# Copy package.json for script definitions (lock file should not be needed)
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/package.json ./package.json
|
||||||
|
# Copy tsconfig.json to provide alias path definitions
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/tsconfig.json ./tsconfig.json
|
||||||
|
# Copy log4js-config.json to provide log configuration
|
||||||
|
COPY --from=build ${DOCKER_WORKDIR}/log4js-config.json ./log4js-config.json
|
||||||
|
|
||||||
|
# Copy run scripts run/
|
||||||
|
# COPY --from=build ${DOCKER_WORKDIR}/run ./run
|
||||||
|
|
||||||
|
# Run command
|
||||||
|
CMD /bin/sh -c "yarn run start"
|
||||||
32
federation/jest.config.js
Normal file
32
federation/jest.config.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||||
|
module.exports = {
|
||||||
|
verbose: true,
|
||||||
|
preset: 'ts-jest',
|
||||||
|
collectCoverage: true,
|
||||||
|
collectCoverageFrom: [
|
||||||
|
'src/**/*.ts',
|
||||||
|
'!**/node_modules/**',
|
||||||
|
'!src/seeds/**',
|
||||||
|
'!build/**',
|
||||||
|
],
|
||||||
|
setupFiles: ['<rootDir>/test/testSetup.ts'],
|
||||||
|
setupFilesAfterEnv: [],
|
||||||
|
modulePathIgnorePatterns: ['<rootDir>/build/'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'@/(.*)': '<rootDir>/src/$1',
|
||||||
|
'@arg/(.*)': '<rootDir>/src/graphql/arg/$1',
|
||||||
|
'@enum/(.*)': '<rootDir>/src/graphql/enum/$1',
|
||||||
|
'@model/(.*)': '<rootDir>/src/graphql/model/$1',
|
||||||
|
'@union/(.*)': '<rootDir>/src/graphql/union/$1',
|
||||||
|
'@repository/(.*)': '<rootDir>/src/typeorm/repository/$1',
|
||||||
|
'@test/(.*)': '<rootDir>/test/$1',
|
||||||
|
'@entity/(.*)':
|
||||||
|
process.env.NODE_ENV === 'development'
|
||||||
|
? '<rootDir>/../database/entity/$1'
|
||||||
|
: '<rootDir>/../database/build/entity/$1',
|
||||||
|
'@dbTools/(.*)':
|
||||||
|
process.env.NODE_ENV === 'development'
|
||||||
|
? '<rootDir>/../database/src/$1'
|
||||||
|
: '<rootDir>/../database/build/src/$1',
|
||||||
|
},
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@
|
|||||||
"build": "tsc --build",
|
"build": "tsc --build",
|
||||||
"clean": "tsc --build --clean",
|
"clean": "tsc --build --clean",
|
||||||
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
"start": "cross-env TZ=UTC TS_NODE_BASEURL=./build node -r tsconfig-paths/register build/src/index.js",
|
||||||
|
"test": "cross-env TZ=UTC NODE_ENV=development jest --runInBand --coverage --forceExit --detectOpenHandles",
|
||||||
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
|
"dev": "cross-env TZ=UTC nodemon -w src --ext ts --exec ts-node -r dotenv/config -r tsconfig-paths/register src/index.ts",
|
||||||
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
"lint": "eslint --max-warnings=0 --ext .js,.ts ."
|
||||||
},
|
},
|
||||||
@ -26,15 +27,16 @@
|
|||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"log4js": "^6.7.1",
|
"log4js": "^6.7.1",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13",
|
||||||
"ts-node": "^10.9.1",
|
|
||||||
"tsconfig-paths": "^4.1.1",
|
|
||||||
"type-graphql": "^1.1.1"
|
"type-graphql": "^1.1.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
"@types/express": "4.17.12",
|
||||||
"@typescript-eslint/parser": "^4.28.0",
|
"@types/jest": "27.0.2",
|
||||||
"@types/lodash.clonedeep": "^4.5.6",
|
"@types/lodash.clonedeep": "^4.5.6",
|
||||||
"@types/node": "^16.10.3",
|
"@types/node": "^16.10.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
||||||
|
"@typescript-eslint/parser": "^4.28.0",
|
||||||
|
"apollo-server-testing": "2.25.2",
|
||||||
"eslint": "^7.29.0",
|
"eslint": "^7.29.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-config-prettier": "^8.3.0",
|
||||||
"eslint-config-standard": "^16.0.3",
|
"eslint-config-standard": "^16.0.3",
|
||||||
@ -42,8 +44,15 @@
|
|||||||
"eslint-plugin-node": "^11.1.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"eslint-plugin-prettier": "^3.4.0",
|
||||||
"eslint-plugin-promise": "^5.1.0",
|
"eslint-plugin-promise": "^5.1.0",
|
||||||
|
"jest": "27.2.4",
|
||||||
|
"ts-jest": "27.0.5",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"tsconfig-paths": "^4.1.1",
|
||||||
|
"nodemon": "^2.0.7",
|
||||||
"prettier": "^2.3.1",
|
"prettier": "^2.3.1",
|
||||||
"typescript": "^4.3.4",
|
"typescript": "^4.3.4"
|
||||||
"nodemon": "^2.0.7"
|
},
|
||||||
|
"nodemonConfig": {
|
||||||
|
"ignore": ["**/*.test.ts"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -24,7 +24,7 @@ const constants = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const server = {
|
const server = {
|
||||||
PORT: process.env.PORT || 5000,
|
PORT: process.env.PORT || 5010,
|
||||||
// JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
// JWT_SECRET: process.env.JWT_SECRET || 'secret123',
|
||||||
// JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m',
|
// JWT_EXPIRES_IN: process.env.JWT_EXPIRES_IN || '10m',
|
||||||
GRAPHIQL: process.env.GRAPHIQL === 'true' || false,
|
GRAPHIQL: process.env.GRAPHIQL === 'true' || false,
|
||||||
@ -73,7 +73,7 @@ if (
|
|||||||
const federation = {
|
const federation = {
|
||||||
// FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || null,
|
// FEDERATION_DHT_TOPIC: process.env.FEDERATION_DHT_TOPIC || null,
|
||||||
// FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
// FEDERATION_DHT_SEED: process.env.FEDERATION_DHT_SEED || null,
|
||||||
FEDERATION_PORT: process.env.FEDERATION_PORT || 5000,
|
FEDERATION_PORT: process.env.FEDERATION_PORT || 5010,
|
||||||
FEDERATION_API: process.env.FEDERATION_API || '1_0',
|
FEDERATION_API: process.env.FEDERATION_API || '1_0',
|
||||||
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null,
|
FEDERATION_COMMUNITY_URL: process.env.FEDERATION_COMMUNITY_URL || null,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
import { Query, Resolver } from 'type-graphql'
|
|
||||||
import { federationLogger as logger } from '@/server/logger'
|
|
||||||
import { GetTestApiResult } from '../../GetTestApiResult'
|
|
||||||
|
|
||||||
@Resolver()
|
|
||||||
export class Test2Resolver {
|
|
||||||
@Query(() => GetTestApiResult)
|
|
||||||
async test2(): Promise<GetTestApiResult> {
|
|
||||||
logger.info(`test api 2 1_0`)
|
|
||||||
return new GetTestApiResult('1_0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
41
federation/src/graphql/api/1_0/resolver/TestResolver.test.ts
Normal file
41
federation/src/graphql/api/1_0/resolver/TestResolver.test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
|
import createServer from '@/server/createServer'
|
||||||
|
|
||||||
|
let query: any
|
||||||
|
|
||||||
|
// to do: We need a setup for the tests that closes the connection
|
||||||
|
let con: any
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const server = await createServer()
|
||||||
|
con = server.con
|
||||||
|
query = createTestClient(server.apollo).query
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('TestResolver', () => {
|
||||||
|
const getTestQuery = `
|
||||||
|
query {
|
||||||
|
test {
|
||||||
|
api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
describe('getTestApi', () => {
|
||||||
|
it('returns 1_0', async () => {
|
||||||
|
await expect(query({ query: getTestQuery })).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
test: {
|
||||||
|
api: '1_0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { Field, ObjectType, Query, Resolver } from 'type-graphql'
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { Query, Resolver } from 'type-graphql'
|
||||||
import { federationLogger as logger } from '@/server/logger'
|
import { federationLogger as logger } from '@/server/logger'
|
||||||
import { GetTestApiResult } from '../../GetTestApiResult'
|
import { GetTestApiResult } from '../../GetTestApiResult'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export class TestResolver {
|
export class TestResolver {
|
||||||
@Query(() => GetTestApiResult)
|
@Query(() => GetTestApiResult)
|
||||||
async test(): Promise<GetTestApiResult> {
|
async test(): Promise<GetTestApiResult> {
|
||||||
|
|||||||
44
federation/src/graphql/api/1_1/resolver/TestResolver.test.ts
Normal file
44
federation/src/graphql/api/1_1/resolver/TestResolver.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
|
import createServer from '@/server/createServer'
|
||||||
|
import CONFIG from '@/config'
|
||||||
|
|
||||||
|
CONFIG.FEDERATION_API = '1_1'
|
||||||
|
|
||||||
|
let query: any
|
||||||
|
|
||||||
|
// to do: We need a setup for the tests that closes the connection
|
||||||
|
let con: any
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const server = await createServer()
|
||||||
|
con = server.con
|
||||||
|
query = createTestClient(server.apollo).query
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('TestResolver', () => {
|
||||||
|
const getTestQuery = `
|
||||||
|
query {
|
||||||
|
test {
|
||||||
|
api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
describe('getTestApi', () => {
|
||||||
|
it('returns 1_1', async () => {
|
||||||
|
await expect(query({ query: getTestQuery })).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
test: {
|
||||||
|
api: '1_1',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { Field, ObjectType, Query, Resolver } from 'type-graphql'
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { Query, Resolver } from 'type-graphql'
|
||||||
import { federationLogger as logger } from '@/server/logger'
|
import { federationLogger as logger } from '@/server/logger'
|
||||||
import { GetTestApiResult } from '../../GetTestApiResult'
|
import { GetTestApiResult } from '../../GetTestApiResult'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export class TestResolver {
|
export class TestResolver {
|
||||||
@Query(() => GetTestApiResult)
|
@Query(() => GetTestApiResult)
|
||||||
async test(): Promise<GetTestApiResult> {
|
async test(): Promise<GetTestApiResult> {
|
||||||
|
|||||||
44
federation/src/graphql/api/2_0/resolver/TestResolver.test.ts
Normal file
44
federation/src/graphql/api/2_0/resolver/TestResolver.test.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||||
|
import { createTestClient } from 'apollo-server-testing'
|
||||||
|
import createServer from '@/server/createServer'
|
||||||
|
import CONFIG from '@/config'
|
||||||
|
|
||||||
|
CONFIG.FEDERATION_API = '2_0'
|
||||||
|
|
||||||
|
let query: any
|
||||||
|
|
||||||
|
// to do: We need a setup for the tests that closes the connection
|
||||||
|
let con: any
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const server = await createServer()
|
||||||
|
con = server.con
|
||||||
|
query = createTestClient(server.apollo).query
|
||||||
|
})
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await con.close()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('TestResolver', () => {
|
||||||
|
const getTestQuery = `
|
||||||
|
query {
|
||||||
|
test {
|
||||||
|
api
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
describe('getTestApi', () => {
|
||||||
|
it('returns 2_0', async () => {
|
||||||
|
await expect(query({ query: getTestQuery })).resolves.toMatchObject({
|
||||||
|
data: {
|
||||||
|
test: {
|
||||||
|
api: '2_0',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -1,8 +1,10 @@
|
|||||||
import { Field, ObjectType, Query, Resolver } from 'type-graphql'
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
import { Query, Resolver } from 'type-graphql'
|
||||||
import { federationLogger as logger } from '@/server/logger'
|
import { federationLogger as logger } from '@/server/logger'
|
||||||
import { GetTestApiResult } from '../../GetTestApiResult'
|
import { GetTestApiResult } from '../../GetTestApiResult'
|
||||||
|
|
||||||
@Resolver()
|
@Resolver()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export class TestResolver {
|
export class TestResolver {
|
||||||
@Query(() => GetTestApiResult)
|
@Query(() => GetTestApiResult)
|
||||||
async test(): Promise<GetTestApiResult> {
|
async test(): Promise<GetTestApiResult> {
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
import { Field, ObjectType } from 'type-graphql'
|
import { Field, ObjectType } from 'type-graphql'
|
||||||
|
|
||||||
@ObjectType()
|
@ObjectType()
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
export class GetTestApiResult {
|
export class GetTestApiResult {
|
||||||
constructor(apiVersion: string) {
|
constructor(apiVersion: string) {
|
||||||
this.api = apiVersion
|
this.api = apiVersion
|
||||||
|
|||||||
22
federation/test/testSetup.ts
Normal file
22
federation/test/testSetup.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { federationLogger as logger } from '@/server/logger'
|
||||||
|
|
||||||
|
jest.setTimeout(1000000)
|
||||||
|
|
||||||
|
jest.mock('@/server/logger', () => {
|
||||||
|
const originalModule = jest.requireActual('@/server/logger')
|
||||||
|
return {
|
||||||
|
__esModule: true,
|
||||||
|
...originalModule,
|
||||||
|
backendLogger: {
|
||||||
|
addContext: jest.fn(),
|
||||||
|
trace: jest.fn(),
|
||||||
|
debug: jest.fn(),
|
||||||
|
warn: jest.fn(),
|
||||||
|
info: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
fatal: jest.fn(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export { logger }
|
||||||
@ -52,14 +52,14 @@
|
|||||||
// "@enum/*": ["src/graphql/enum/*"],
|
// "@enum/*": ["src/graphql/enum/*"],
|
||||||
// "@model/*": ["src/graphql/model/*"],
|
// "@model/*": ["src/graphql/model/*"],
|
||||||
"@repository/*": ["src/typeorm/repository/*"],
|
"@repository/*": ["src/typeorm/repository/*"],
|
||||||
// "@test/*": ["test/*"],
|
"@test/*": ["test/*"],
|
||||||
/* external */
|
/* external */
|
||||||
"@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"],
|
"@typeorm/*": ["../backend/src/typeorm/*", "../../backend/src/typeorm/*"],
|
||||||
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
|
"@dbTools/*": ["../database/src/*", "../../database/build/src/*"],
|
||||||
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
|
"@entity/*": ["../database/entity/*", "../../database/build/entity/*"]
|
||||||
},
|
},
|
||||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||||
"typeRoots": ["src/dht_node/@types", "node_modules/@types"], /* List of folders to include type definitions from. */
|
"typeRoots": ["node_modules/@types"], /* List of folders to include type definitions from. */
|
||||||
// "types": [], /* Type declaration files to be included in compilation. */
|
// "types": [], /* Type declaration files to be included in compilation. */
|
||||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||||
|
|||||||
2197
federation/yarn.lock
2197
federation/yarn.lock
File diff suppressed because it is too large
Load Diff
BIN
frontend/public/img/brand/gradido-logo.png
Normal file
BIN
frontend/public/img/brand/gradido-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@ -3,16 +3,16 @@
|
|||||||
<b-navbar :toggleable="false" class="pr-4">
|
<b-navbar :toggleable="false" class="pr-4">
|
||||||
<b-navbar-brand class="d-none d-lg-block">
|
<b-navbar-brand class="d-none d-lg-block">
|
||||||
<b-img
|
<b-img
|
||||||
class="imgLogo position-absolute ml--3 mt-lg--2 mt-3 p-2 zindex1000"
|
class="position-absolute ml--3 mt-lg--2 mt-3 p-2 zindex1000"
|
||||||
:src="logo"
|
:src="logo"
|
||||||
width="200"
|
width="200"
|
||||||
alt="..."
|
alt="Logo"
|
||||||
/>
|
/>
|
||||||
<b-img
|
<b-img
|
||||||
class="imgLogoBack mt--3 ml--3"
|
class="mt--3 ml--3"
|
||||||
src="/img/template/gradido_background_header.png"
|
:src="background_header"
|
||||||
width="230"
|
width="230"
|
||||||
alt="start background image"
|
alt="Background Image"
|
||||||
></b-img>
|
></b-img>
|
||||||
</b-navbar-brand>
|
</b-navbar-brand>
|
||||||
<b-img class="sheet-img position-absolute d-block d-lg-none zindex1000" :src="sheet"></b-img>
|
<b-img class="sheet-img position-absolute d-block d-lg-none zindex1000" :src="sheet"></b-img>
|
||||||
@ -35,7 +35,8 @@ export default {
|
|||||||
mixins: [authLinks],
|
mixins: [authLinks],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
logo: '/img/brand/green.png',
|
background_header: '/img/template/gradido_background_header.png',
|
||||||
|
logo: '/img/brand/gradido-logo.png',
|
||||||
sheet: '/img/template/Blaetter.png',
|
sheet: '/img/template/Blaetter.png',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -4,10 +4,10 @@
|
|||||||
<b-navbar toggleable="lg" class="pr-4">
|
<b-navbar toggleable="lg" class="pr-4">
|
||||||
<b-navbar-brand>
|
<b-navbar-brand>
|
||||||
<b-img
|
<b-img
|
||||||
class="imgLogo mt-lg--2 mt-3 mb-3 d-none d-lg-block zindex10"
|
class="mt-lg--2 mt-3 mb-3 d-none d-lg-block zindex10"
|
||||||
:src="logo"
|
:src="logo"
|
||||||
width=""
|
width="200"
|
||||||
alt="..."
|
alt="Logo"
|
||||||
/>
|
/>
|
||||||
<div v-b-toggle.sidebar-mobile variant="link" class="d-block d-lg-none">
|
<div v-b-toggle.sidebar-mobile variant="link" class="d-block d-lg-none">
|
||||||
<span class="navbar-toggler-icon h2"></span>
|
<span class="navbar-toggler-icon h2"></span>
|
||||||
@ -60,7 +60,7 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
logo: '/img/brand/green.png',
|
logo: '/img/brand/gradido-logo.png',
|
||||||
sheet: '/img/template/Blaetter.png',
|
sheet: '/img/template/Blaetter.png',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2,19 +2,19 @@
|
|||||||
<div class="nav-community container">
|
<div class="nav-community container">
|
||||||
<b-row class="nav-row">
|
<b-row class="nav-row">
|
||||||
<b-col cols="12" lg="4" md="4" class="px-0">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="/community#edit">
|
<b-btn to="contribute" active-class="btn-active" block variant="link">
|
||||||
<b-icon icon="pencil" class="mr-2" />
|
<b-icon icon="pencil" class="mr-2" />
|
||||||
{{ $t('community.submitContribution') }}
|
{{ $t('community.submitContribution') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="12" lg="4" md="4" class="px-0">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="/community#my">
|
<b-btn to="contributions" active-class="btn-active" block variant="link">
|
||||||
<b-icon icon="person" class="mr-2" />
|
<b-icon icon="person" class="mr-2" />
|
||||||
{{ $t('community.myContributions') }}
|
{{ $t('community.myContributions') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
</b-col>
|
</b-col>
|
||||||
<b-col cols="12" lg="4" md="4" class="px-0">
|
<b-col cols="12" lg="4" md="4" class="px-0">
|
||||||
<b-btn active-class="btn-active" block variant="link" to="/community#all">
|
<b-btn to="community" active-class="btn-active" block variant="link">
|
||||||
<b-icon icon="people" class="mr-2" />
|
<b-icon icon="people" class="mr-2" />
|
||||||
{{ $t('community.community') }}
|
{{ $t('community.community') }}
|
||||||
</b-btn>
|
</b-btn>
|
||||||
|
|||||||
@ -1,76 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="contribution-info d-none d-lg-block">
|
<div class="contribution-info d-none d-lg-block">
|
||||||
<div v-if="hash === '#my'">
|
<slot :name="$route.params.tab" />
|
||||||
<h4 class="alert-heading">{{ $t('community.myContributions') }}</h4>
|
|
||||||
<p>
|
|
||||||
{{ $t('contribution.alert.myContributionNoteList') }}
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
|
||||||
{{ $t('contribution.alert.pending') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="question-square" variant="warning"></b-icon>
|
|
||||||
{{ $t('contribution.alert.in_progress') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="check" variant="success"></b-icon>
|
|
||||||
{{ $t('contribution.alert.confirm') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="x-circle" variant="warning"></b-icon>
|
|
||||||
{{ $t('contribution.alert.denied') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="trash" variant="danger"></b-icon>
|
|
||||||
{{ $t('contribution.alert.deleted') }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div v-if="hash === '#all'" show fade variant="secondary" class="text-dark">
|
|
||||||
<h4 class="alert-heading">{{ $t('navigation.community') }}</h4>
|
|
||||||
<p>
|
|
||||||
{{ $t('contribution.alert.communityNoteList') }}
|
|
||||||
</p>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
|
||||||
{{ $t('contribution.alert.pending') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="question-square" variant="warning"></b-icon>
|
|
||||||
{{ $t('contribution.alert.in_progress') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="check" variant="success"></b-icon>
|
|
||||||
{{ $t('contribution.alert.confirm') }}
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<b-icon icon="x-circle" variant="warning"></b-icon>
|
|
||||||
{{ $t('contribution.alert.denied') }}
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div v-if="hash === '#edit'" show fade variant="secondary" class="text-dark">
|
|
||||||
<div>
|
|
||||||
<h3>{{ $t('contribution.formText.yourContribution') }}</h3>
|
|
||||||
{{ $t('contribution.formText.bringYourTalentsTo') }}
|
|
||||||
|
|
||||||
<div class="my-3">
|
|
||||||
<b>{{ $t('contribution.formText.describeYourCommunity') }}</b>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'ContributionInfo',
|
name: 'ContributionInfo',
|
||||||
computed: {
|
|
||||||
hash() {
|
|
||||||
return this.$route.hash
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -121,11 +121,7 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
<!-- Right Side Mobil -->
|
<!-- Right Side Mobil -->
|
||||||
<b-col class="d-block d-lg-none">
|
<b-col class="d-block d-lg-none">
|
||||||
<right-side
|
<right-side>
|
||||||
:transactions="transactions"
|
|
||||||
:transactionCount="transactionCount"
|
|
||||||
:transactionLinkCount="transactionLinkCount"
|
|
||||||
>
|
|
||||||
<template #transactions>
|
<template #transactions>
|
||||||
<last-transactions
|
<last-transactions
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
@ -135,7 +131,7 @@
|
|||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #community>
|
<template #community>
|
||||||
<contribution-info />
|
<community-template />
|
||||||
</template>
|
</template>
|
||||||
<template #empty />
|
<template #empty />
|
||||||
</right-side>
|
</right-side>
|
||||||
@ -162,11 +158,7 @@
|
|||||||
</b-col>
|
</b-col>
|
||||||
<!-- RightSide Desktop -->
|
<!-- RightSide Desktop -->
|
||||||
<b-col cols="3" class="d-none d-lg-block">
|
<b-col cols="3" class="d-none d-lg-block">
|
||||||
<right-side
|
<right-side>
|
||||||
:transactions="transactions"
|
|
||||||
:transactionCount="transactionCount"
|
|
||||||
:transactionLinkCount="transactionLinkCount"
|
|
||||||
>
|
|
||||||
<template #transactions>
|
<template #transactions>
|
||||||
<last-transactions
|
<last-transactions
|
||||||
:transactions="transactions"
|
:transactions="transactions"
|
||||||
@ -175,10 +167,10 @@
|
|||||||
@set-tunneled-email="setTunneledEmail"
|
@set-tunneled-email="setTunneledEmail"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<template #community>
|
|
||||||
<contribution-info />
|
|
||||||
</template>
|
|
||||||
<template #empty />
|
<template #empty />
|
||||||
|
<template #community>
|
||||||
|
<community-template />
|
||||||
|
</template>
|
||||||
</right-side>
|
</right-side>
|
||||||
</b-col>
|
</b-col>
|
||||||
</b-row>
|
</b-row>
|
||||||
@ -194,6 +186,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import ContentHeader from '@/layouts/templates/ContentHeader.vue'
|
import ContentHeader from '@/layouts/templates/ContentHeader.vue'
|
||||||
|
import CommunityTemplate from '@/layouts/templates/CommunityTemplate.vue'
|
||||||
import Breadcrumb from '@/components/Breadcrumb/breadcrumb.vue'
|
import Breadcrumb from '@/components/Breadcrumb/breadcrumb.vue'
|
||||||
import RightSide from '@/layouts/templates/RightSide.vue'
|
import RightSide from '@/layouts/templates/RightSide.vue'
|
||||||
import SkeletonOverview from '@/components/skeleton/Overview.vue'
|
import SkeletonOverview from '@/components/skeleton/Overview.vue'
|
||||||
@ -211,7 +204,6 @@ import GdtAmount from '@/components/Template/ContentHeader/GdtAmount.vue'
|
|||||||
import CommunityMember from '@/components/Template/ContentHeader/CommunityMember.vue'
|
import CommunityMember from '@/components/Template/ContentHeader/CommunityMember.vue'
|
||||||
import NavCommunity from '@/components/Template/ContentHeader/NavCommunity.vue'
|
import NavCommunity from '@/components/Template/ContentHeader/NavCommunity.vue'
|
||||||
import LastTransactions from '@/components/Template/RightSide/LastTransactions.vue'
|
import LastTransactions from '@/components/Template/RightSide/LastTransactions.vue'
|
||||||
import ContributionInfo from '@/components/Template/RightSide/ContributionInfo.vue'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DashboardLayout',
|
name: 'DashboardLayout',
|
||||||
@ -231,7 +223,7 @@ export default {
|
|||||||
CommunityMember,
|
CommunityMember,
|
||||||
NavCommunity,
|
NavCommunity,
|
||||||
LastTransactions,
|
LastTransactions,
|
||||||
ContributionInfo,
|
CommunityTemplate,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import ContributionInfo from './ContributionInfo'
|
import CommunityTemplate from './CommunityTemplate'
|
||||||
|
|
||||||
const localVue = global.localVue
|
const localVue = global.localVue
|
||||||
|
|
||||||
@ -10,15 +10,17 @@ const mocks = {
|
|||||||
$t: jest.fn((t) => t),
|
$t: jest.fn((t) => t),
|
||||||
$d: jest.fn((d) => d),
|
$d: jest.fn((d) => d),
|
||||||
$route: {
|
$route: {
|
||||||
hash: '',
|
params: {
|
||||||
|
tab: 'contribute',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('ContributionInfo', () => {
|
describe('CommunityTemplate', () => {
|
||||||
let wrapper
|
let wrapper
|
||||||
|
|
||||||
const Wrapper = () => {
|
const Wrapper = () => {
|
||||||
return mount(ContributionInfo, { localVue, mocks })
|
return mount(CommunityTemplate, { localVue, mocks })
|
||||||
}
|
}
|
||||||
describe('mount', () => {
|
describe('mount', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@ -29,9 +31,9 @@ describe('ContributionInfo', () => {
|
|||||||
expect(wrapper.findComponent({ name: 'ContributionInfo' }).exists()).toBe(true)
|
expect(wrapper.findComponent({ name: 'ContributionInfo' }).exists()).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mounted with hash #my', () => {
|
describe('mounted with parameter contributions', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.$route.hash = '#my'
|
mocks.$route.params.tab = 'contributions'
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a header related to "my contribitions"', () => {
|
it('has a header related to "my contribitions"', () => {
|
||||||
@ -59,9 +61,9 @@ describe('ContributionInfo', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mounted with hash #all', () => {
|
describe('mounted with parameter community', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.$route.hash = '#all'
|
mocks.$route.params.tab = 'community'
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a header related to "the community"', () => {
|
it('has a header related to "the community"', () => {
|
||||||
@ -89,9 +91,9 @@ describe('ContributionInfo', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('mounted with hash #edit', () => {
|
describe('mounted with parameter contribute', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mocks.$route.hash = '#edit'
|
mocks.$route.params.tab = 'contribute'
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has a header related to "the community"', () => {
|
it('has a header related to "the community"', () => {
|
||||||
82
frontend/src/layouts/templates/CommunityTemplate.vue
Normal file
82
frontend/src/layouts/templates/CommunityTemplate.vue
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
<template>
|
||||||
|
<contribution-info>
|
||||||
|
<template #contribute>
|
||||||
|
<div show fade variant="secondary" class="text-dark">
|
||||||
|
<div>
|
||||||
|
<h3>{{ $t('contribution.formText.yourContribution') }}</h3>
|
||||||
|
{{ $t('contribution.formText.bringYourTalentsTo') }}
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<b>{{ $t('contribution.formText.describeYourCommunity') }}</b>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #contributions>
|
||||||
|
<div show fade variant="secondary" class="text-dark">
|
||||||
|
<h4 class="alert-heading">{{ $t('community.myContributions') }}</h4>
|
||||||
|
<p>
|
||||||
|
{{ $t('contribution.alert.myContributionNoteList') }}
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
||||||
|
{{ $t('contribution.alert.pending') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="question-square" variant="warning"></b-icon>
|
||||||
|
{{ $t('contribution.alert.in_progress') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="check" variant="success"></b-icon>
|
||||||
|
{{ $t('contribution.alert.confirm') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="x-circle" variant="warning"></b-icon>
|
||||||
|
{{ $t('contribution.alert.denied') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="trash" variant="danger"></b-icon>
|
||||||
|
{{ $t('contribution.alert.deleted') }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #community>
|
||||||
|
<div show fade variant="secondary" class="text-dark">
|
||||||
|
<h4 class="alert-heading">{{ $t('navigation.community') }}</h4>
|
||||||
|
<p>
|
||||||
|
{{ $t('contribution.alert.communityNoteList') }}
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="bell-fill" variant="primary"></b-icon>
|
||||||
|
{{ $t('contribution.alert.pending') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="question-square" variant="warning"></b-icon>
|
||||||
|
{{ $t('contribution.alert.in_progress') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="check" variant="success"></b-icon>
|
||||||
|
{{ $t('contribution.alert.confirm') }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<b-icon icon="x-circle" variant="warning"></b-icon>
|
||||||
|
{{ $t('contribution.alert.denied') }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</contribution-info>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import ContributionInfo from '@/components/Template/RightSide/ContributionInfo.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'CommunityTemplate',
|
||||||
|
components: {
|
||||||
|
ContributionInfo,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@ -9,7 +9,7 @@ export default {
|
|||||||
name: 'ContentHeader',
|
name: 'ContentHeader',
|
||||||
computed: {
|
computed: {
|
||||||
path() {
|
path() {
|
||||||
return this.$route.path.replace(/^\//, '')
|
return this.$route.path.replace(/^\/(.+?)(\/.+)?$/, '$1')
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ export default {
|
|||||||
name: 'RightSide',
|
name: 'RightSide',
|
||||||
computed: {
|
computed: {
|
||||||
name() {
|
name() {
|
||||||
switch (this.$route.path.replace(/^\//, '')) {
|
switch (this.$route.path.replace(/^\/(.+?)(\/.+)?$/, '$1')) {
|
||||||
case 'settings':
|
case 'settings':
|
||||||
return 'empty'
|
return 'empty'
|
||||||
case 'community':
|
case 'community':
|
||||||
|
|||||||
@ -215,7 +215,9 @@ describe('Community', () => {
|
|||||||
push: routerPushMock,
|
push: routerPushMock,
|
||||||
},
|
},
|
||||||
$route: {
|
$route: {
|
||||||
hash: '#edit',
|
params: {
|
||||||
|
tab: 'contribute',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,7 +262,11 @@ describe('Community', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('check for correct tabIndex if state is "IN_PROGRESS" or not', () => {
|
it('check for correct tabIndex if state is "IN_PROGRESS" or not', () => {
|
||||||
expect(routerPushMock).toBeCalledWith({ path: '/community#my' })
|
expect(routerPushMock).toBeCalledWith({ params: { tab: 'contributions' } })
|
||||||
|
})
|
||||||
|
|
||||||
|
it('sets tab index to 1', () => {
|
||||||
|
expect(wrapper.vm.tabIndex).toBe(1)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('toasts an info', () => {
|
it('toasts an info', () => {
|
||||||
@ -268,16 +274,6 @@ describe('Community', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('API calls after creation', () => {
|
|
||||||
it('has a DIV .community-page', () => {
|
|
||||||
expect(wrapper.find('div.community-page').exists()).toBe(true)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('emits update transactions', () => {
|
|
||||||
expect(wrapper.emitted('update-transactions')).toEqual([[0]])
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('save contrubtion', () => {
|
describe('save contrubtion', () => {
|
||||||
describe('with error', () => {
|
describe('with error', () => {
|
||||||
const now = new Date().toISOString()
|
const now = new Date().toISOString()
|
||||||
@ -491,6 +487,10 @@ describe('Community', () => {
|
|||||||
it('sets tab index back to 0', () => {
|
it('sets tab index back to 0', () => {
|
||||||
expect(wrapper.vm.tabIndex).toBe(0)
|
expect(wrapper.vm.tabIndex).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('pushes contribute parameter to router', () => {
|
||||||
|
expect(routerPushMock).toBeCalledWith({ params: { tab: 'contribute' } })
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('update list all contributions', () => {
|
describe('update list all contributions', () => {
|
||||||
|
|||||||
@ -64,6 +64,8 @@ import ContributionList from '@/components/Contributions/ContributionList.vue'
|
|||||||
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
import { createContribution, updateContribution, deleteContribution } from '@/graphql/mutations'
|
||||||
import { listContributions, listAllContributions, openCreations } from '@/graphql/queries'
|
import { listContributions, listAllContributions, openCreations } from '@/graphql/queries'
|
||||||
|
|
||||||
|
const COMMUNITY_TABS = ['contribute', 'contributions', 'community']
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Community',
|
name: 'Community',
|
||||||
components: {
|
components: {
|
||||||
@ -73,8 +75,6 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hashLink: '',
|
|
||||||
tabLinkHashes: ['#edit', '#my', '#all'],
|
|
||||||
tabIndex: 0,
|
tabIndex: 0,
|
||||||
items: [],
|
items: [],
|
||||||
itemsAll: [],
|
itemsAll: [],
|
||||||
@ -97,10 +97,7 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$nextTick(() => {
|
this.updateTabIndex()
|
||||||
this.tabIndex = this.tabLinkHashes.findIndex((hashLink) => hashLink === this.$route.hash)
|
|
||||||
this.hashLink = this.$route.hash
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
apollo: {
|
apollo: {
|
||||||
OpenCreations: {
|
OpenCreations: {
|
||||||
@ -153,9 +150,8 @@ export default {
|
|||||||
this.items = listContributions.contributionList
|
this.items = listContributions.contributionList
|
||||||
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
|
if (this.items.find((item) => item.state === 'IN_PROGRESS')) {
|
||||||
this.tabIndex = 1
|
this.tabIndex = 1
|
||||||
if (this.$route.hash !== '#my') {
|
if (this.$route.params.tab !== 'contributions')
|
||||||
this.$router.push({ path: '/community#my' })
|
this.$router.push({ params: { tab: 'contributions' } })
|
||||||
}
|
|
||||||
this.toastInfo(this.$t('contribution.alert.answerQuestionToast'))
|
this.toastInfo(this.$t('contribution.alert.answerQuestionToast'))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -165,21 +161,8 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
$route(to, from) {
|
'$route.params.tab'() {
|
||||||
this.tabIndex = this.tabLinkHashes.findIndex((hashLink) => hashLink === to.hash)
|
this.updateTabIndex()
|
||||||
this.hashLink = to.hash
|
|
||||||
this.closeAllOpenCollapse()
|
|
||||||
},
|
|
||||||
tabIndex(num) {
|
|
||||||
if (num !== 0) {
|
|
||||||
this.form = {
|
|
||||||
id: null,
|
|
||||||
date: new Date(),
|
|
||||||
memo: '',
|
|
||||||
hours: 0,
|
|
||||||
amount: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -211,6 +194,11 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
updateTabIndex() {
|
||||||
|
const index = COMMUNITY_TABS.indexOf(this.$route.params.tab)
|
||||||
|
this.tabIndex = index > -1 ? index : 0
|
||||||
|
this.closeAllOpenCollapse()
|
||||||
|
},
|
||||||
closeAllOpenCollapse() {
|
closeAllOpenCollapse() {
|
||||||
this.$el.querySelectorAll('.collapse.show').forEach((value) => {
|
this.$el.querySelectorAll('.collapse.show').forEach((value) => {
|
||||||
this.$root.$emit('bv::toggle::collapse', value.id)
|
this.$root.$emit('bv::toggle::collapse', value.id)
|
||||||
@ -294,8 +282,8 @@ export default {
|
|||||||
this.form.amount = item.amount
|
this.form.amount = item.amount
|
||||||
this.form.hours = item.amount / 20
|
this.form.hours = item.amount / 20
|
||||||
this.updateAmount = item.amount
|
this.updateAmount = item.amount
|
||||||
this.$router.push({ path: '#edit' })
|
|
||||||
this.tabIndex = 0
|
this.tabIndex = 0
|
||||||
|
this.$router.push({ params: { tab: 'contribute' } })
|
||||||
},
|
},
|
||||||
updateTransactions(pagination) {
|
updateTransactions(pagination) {
|
||||||
this.$emit('update-transactions', pagination)
|
this.$emit('update-transactions', pagination)
|
||||||
@ -304,11 +292,6 @@ export default {
|
|||||||
this.items.find((item) => item.id === id).state = 'PENDING'
|
this.items.find((item) => item.id === id).state = 'PENDING'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
|
||||||
this.updateTransactions(0)
|
|
||||||
this.tabIndex = 0
|
|
||||||
this.$router.push({ path: '/community#edit' })
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|||||||
@ -45,7 +45,6 @@ export default {
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
img: '/img/brand/green.png',
|
|
||||||
linkData: {
|
linkData: {
|
||||||
__typename: 'TransactionLink',
|
__typename: 'TransactionLink',
|
||||||
amount: '123.45',
|
amount: '123.45',
|
||||||
|
|||||||
@ -49,8 +49,8 @@ describe('router', () => {
|
|||||||
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
|
expect(routes.find((r) => r.path === '/').redirect()).toEqual({ path: '/login' })
|
||||||
})
|
})
|
||||||
|
|
||||||
it('has sixteen routes defined', () => {
|
it('has 19 routes defined', () => {
|
||||||
expect(routes).toHaveLength(18)
|
expect(routes).toHaveLength(19)
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('overview', () => {
|
describe('overview', () => {
|
||||||
@ -75,7 +75,19 @@ describe('router', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('community', () => {
|
describe('community without tab parameter', () => {
|
||||||
|
it('requires authorization', () => {
|
||||||
|
expect(routes.find((r) => r.path === '/community').meta.requiresAuth).toBeTruthy()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('redirects to contribute tab', async () => {
|
||||||
|
expect(routes.find((r) => r.path === '/community').redirect()).toEqual({
|
||||||
|
path: '/community/contribute',
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('community with tab parameter', () => {
|
||||||
it('requires authorization', () => {
|
it('requires authorization', () => {
|
||||||
expect(routes.find((r) => r.path === '/community').meta.requiresAuth).toBeTruthy()
|
expect(routes.find((r) => r.path === '/community').meta.requiresAuth).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -58,6 +58,17 @@ const routes = [
|
|||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
pageTitle: 'community',
|
pageTitle: 'community',
|
||||||
},
|
},
|
||||||
|
redirect: (to) => {
|
||||||
|
return { path: '/community/contribute' }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/community/:tab',
|
||||||
|
component: () => import('@/pages/Community.vue'),
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
pageTitle: 'community',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/information',
|
path: '/information',
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user