merge dev to main (#208)

Co-authored-by: Nicolai Ort <info@nicolai-ort.com>
Reviewed-on: #208
Co-authored-by: Philipp Dormann <philipp@philippdormann.de>
Co-committed-by: Philipp Dormann <philipp@philippdormann.de>
This commit is contained in:
Philipp Dormann 2023-11-06 17:18:48 +00:00 committed by Philipp Dormann
parent e98e7717aa
commit 5ef3b6eb97
Signed by: git.odit.services
GPG Key ID: 76E155504123332E
33 changed files with 8273 additions and 133 deletions

View File

@ -26,6 +26,13 @@ get:
path: odit-ci-bot path: odit-ci-bot
name: apikey name: apikey
---
kind: secret
name: npm_url
get:
path: odit-npm-cache
name: url
--- ---
kind: pipeline kind: pipeline
type: kubernetes type: kubernetes
@ -41,8 +48,12 @@ steps:
- name: run tests - name: run tests
image: registry.odit.services/hub/library/node:19.5.0-alpine3.16 image: registry.odit.services/hub/library/node:19.5.0-alpine3.16
commands: commands:
- yarn - npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
- yarn test:ci - pnpm i
- pnpm test:ci
environment:
NPM_REGISTRY_URL:
from_secret: npm_url
trigger: trigger:
event: event:
- pull_request - pull_request
@ -69,10 +80,8 @@ steps:
password: password:
from_secret: docker_password from_secret: docker_password
build_args: build_args:
- NPM_REGISTRY_DOMAIN: - NPM_REGISTRY_URL:
from_secret: npmjs_domain from_secret: npm_url
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
repo: lfk/backend repo: lfk/backend
tags: tags:
- dev - dev
@ -109,10 +118,8 @@ steps:
password: password:
from_secret: docker_password from_secret: docker_password
build_args: build_args:
- NPM_REGISTRY_DOMAIN: - NPM_REGISTRY_URL:
from_secret: npmjs_domain from_secret: npm_url
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
repo: lfk/backend repo: lfk/backend
tags: tags:
- latest - latest
@ -149,21 +156,13 @@ steps:
password: password:
from_secret: docker_password from_secret: docker_password
build_args: build_args:
- NPM_REGISTRY_DOMAIN: - NPM_REGISTRY_URL:
from_secret: npmjs_domain from_secret: npm_url
- NPM_REGISTRY_TOKEN:
from_secret: npmjs_token
repo: lfk/backend repo: lfk/backend
tags: tags:
- "${DRONE_TAG}" - "${DRONE_TAG}"
cache: true cache: true
registry: registry.odit.services registry: registry.odit.services
- name: trigger node lib build
image: idcooldi/drone-webhook
settings:
urls: https://ci.odit.services/api/repos/lfk/lfk-client-node/builds?SOURCE_TAG=${DRONE_TAG}
bearer:
from_secret: ci_token
- name: trigger js lib build - name: trigger js lib build
image: idcooldi/drone-webhook image: idcooldi/drone-webhook
settings: settings:

1
.gitignore vendored
View File

@ -136,4 +136,3 @@ build
lib lib
/oss-attribution /oss-attribution
*.tmp *.tmp
pnpm-lock.yaml

View File

@ -2,8 +2,174 @@
All notable changes to this project will be documented in this file. Dates are displayed in UTC. All notable changes to this project will be documented in this file. Dates are displayed in UTC.
#### [v1.1.3](https://git.odit.services/lfk/backend/compare/v1.1.2...v1.1.3)
- feat(orgs): Also resolve child-teams' distances and add them to org total [`8d94186`](https://git.odit.services/lfk/backend/commit/8d9418635d3e381c0f55a2521a3334ba497c169a)
- fix(orgs): Removed unused log [`f2832a2`](https://git.odit.services/lfk/backend/commit/f2832a2daecc7bc7bbee4d4fceeab8db194730cf)
#### [v1.1.2](https://git.odit.services/lfk/backend/compare/v1.1.1...v1.1.2)
> 10 May 2023
- 🚀Bumped version to v1.1.2 [`0d21596`](https://git.odit.services/lfk/backend/commit/0d21596e2b64a99258d4925ae2ad627d5cdbd984)
- feat(groups): Resolve the total group distance on group get single (aka get org and get team) [`245827e`](https://git.odit.services/lfk/backend/commit/245827e9c659cf76183dc33ab253becc22ddf032)
- chore(package): Formatting [`4608a36`](https://git.odit.services/lfk/backend/commit/4608a36df6b187520ca0c331b8dce615205257be)
#### [v1.1.1](https://git.odit.services/lfk/backend/compare/v1.1.0...v1.1.1)
> 19 April 2023
- feat(donors): Resolve donations with donors via pagination [`12a9ae2`](https://git.odit.services/lfk/backend/commit/12a9ae24933117acb3ff9815a7d72abca5eea7a7)
- 🚀Bumped version to v1.1.1 [`cb1305a`](https://git.odit.services/lfk/backend/commit/cb1305aa77c36aa9d7900f09e7413bc6d45f2c89)
#### [v1.1.0](https://git.odit.services/lfk/backend/compare/v1.0.1...v1.1.0)
> 19 April 2023
- feat(stats): Added donation count and donor count to stats [`6f39ac4`](https://git.odit.services/lfk/backend/commit/6f39ac42dafc2a589bbb2256b0417f3e774ae174)
- 🚀Bumped version to v1.1.0 [`b9fe9f1`](https://git.odit.services/lfk/backend/commit/b9fe9f1c24653b91255a6dbbdc32c30b1b411eeb)
- Added average donation per distance to stats [`fe59e3a`](https://git.odit.services/lfk/backend/commit/fe59e3a557903cf555d4c50098e935c49ca1fac4)
- Added hints [`b25b0db`](https://git.odit.services/lfk/backend/commit/b25b0db76071ef8d50cc60e950a399dc060a2a9f)
- Added calls to controller [`6ee5328`](https://git.odit.services/lfk/backend/commit/6ee5328dbc404603d19db3a5173ae4def560a9c9)
- Formatting [`42c23a5`](https://git.odit.services/lfk/backend/commit/42c23a5883dacda4e0147842d448b3ad35b197b1)
#### [v1.0.1](https://git.odit.services/lfk/backend/compare/v1.0.0...v1.0.1)
> 18 April 2023
- fix(pagination) page=0 resulted in false thx JS [`fcee390`](https://git.odit.services/lfk/backend/commit/fcee3909f4c4664115cc7ecb94f30e0dd8e78ce0)
- 🚀Bumped version to v1.0.1 [`301f334`](https://git.odit.services/lfk/backend/commit/301f33467489a8533bdac11fbd10efd1b791f5e3)
### [v1.0.0](https://git.odit.services/lfk/backend/compare/v0.15.4...v1.0.0)
> 18 April 2023
- 🚀Bumped version to v1.0.0 [`f0e20e4`](https://git.odit.services/lfk/backend/commit/f0e20e413014fe446c97754d2765cdad92c2cc3b)
- Merge pull request 'feature/205-pagination' (#206) from feature/205-pagination into dev [`80de188`](https://git.odit.services/lfk/backend/commit/80de188565523d642407612272432ef07672b890)
- Added pagination for runner orgs [`538622a`](https://git.odit.services/lfk/backend/commit/538622aa1841e27256f304e15b4204c2f6d24d76)
- RunnerTeam Pagination [`0fa663a`](https://git.odit.services/lfk/backend/commit/0fa663a34104d438dd8fc9ab02458fdf289329f8)
- users pagination [`244da61`](https://git.odit.services/lfk/backend/commit/244da618926377f58bb12dbbd89b7bb39d84596e)
- Track pagination [`2a72aea`](https://git.odit.services/lfk/backend/commit/2a72aea10ef940fbdd4a9e6137b22933fdec7734)
- usergroup pagination [`513d7f6`](https://git.odit.services/lfk/backend/commit/513d7f6fbaebe39beab6ec95e6e42eb10c62296d)
- statsclient pagination [`71ebce6`](https://git.odit.services/lfk/backend/commit/71ebce6f8eebf110bb973a53b91dd6a49e1def99)
- scanstation pagination [`f60025b`](https://git.odit.services/lfk/backend/commit/f60025b6de79b0f5f89995bf59260194f5de9af0)
- Get all pagination for permissions [`86a21db`](https://git.odit.services/lfk/backend/commit/86a21dbfa4b50d8e80c611ea6e3eabfc2b8ae365)
- Pagination for group contacts [`1e9e24d`](https://git.odit.services/lfk/backend/commit/1e9e24d99d75ce6dc846ff662e62c886646ea974)
- Added pagination for get all donors [`4493c0e`](https://git.odit.services/lfk/backend/commit/4493c0e3d9beebbf7f601b39e1a2579771b4d152)
- Added pagination for donations [`f5d48fc`](https://git.odit.services/lfk/backend/commit/f5d48fc638080c9333efe474d86f131794c809af)
- Added pagination for runnercards [`b35a2dd`](https://git.odit.services/lfk/backend/commit/b35a2dd2fab708253373b3326f11ab574be18371)
- Added pagination for runners [`d873674`](https://git.odit.services/lfk/backend/commit/d873674819e6cb33cf89da4f8fdc30a0b41707e4)
- Added pagination for get all scans [`37b2ac9`](https://git.odit.services/lfk/backend/commit/37b2ac974b2276efd13538c127ba5ddda2537fe3)
- Updated test for attribute [`2f305e1`](https://git.odit.services/lfk/backend/commit/2f305e127c75e9e6ff8e9fc0cfc10cc3db44759d)
- Formatting [`a28ffe0`](https://git.odit.services/lfk/backend/commit/a28ffe06e5f3f69e4af6fdf0c66c9a1dfda10cfa)
#### [v0.15.4](https://git.odit.services/lfk/backend/compare/v0.15.3...v0.15.4)
> 15 April 2023
- Fixed possible null [`0f0c3c7`](https://git.odit.services/lfk/backend/commit/0f0c3c7214f357d991518aafd015ffc4d387ce59)
- 🚀Bumped version to v0.15.4 [`81aed1d`](https://git.odit.services/lfk/backend/commit/81aed1de40166f4cefabdb478d7638017127b25c)
#### [v0.15.3](https://git.odit.services/lfk/backend/compare/v0.15.2...v0.15.3)
> 15 April 2023
- Faster stats (not including donations) [`b2ac70e`](https://git.odit.services/lfk/backend/commit/b2ac70e0aec1064e54a5043a104e7892984b2338)
- 🚀Bumped version to v0.15.3 [`3909ed3`](https://git.odit.services/lfk/backend/commit/3909ed34f739e9fee90828f16757c75da90bab0f)
#### [v0.15.2](https://git.odit.services/lfk/backend/compare/v0.15.1...v0.15.2)
> 15 April 2023
- 🚀Bumped version to v0.15.2 [`5f17e7f`](https://git.odit.services/lfk/backend/commit/5f17e7f783a7e8e2efc8f7dbbf2c98bcd1d80240)
- Don't resolve runner group and parten with get all card requests [`2d8f752`](https://git.odit.services/lfk/backend/commit/2d8f7528d98144832e7609f5aa6fac8de4723c4a)
- Resolve groups again for card generation [`a5a56a2`](https://git.odit.services/lfk/backend/commit/a5a56a263a01dbd911a799ab57084166e17b80ac)
#### [v0.15.1](https://git.odit.services/lfk/backend/compare/v0.15.0...v0.15.1)
> 15 April 2023
- 🚀Bumped version to v0.15.1 [`9581185`](https://git.odit.services/lfk/backend/commit/9581185b24039338e7f238ecdcc3881bb5203759)
- Faster trackscan creation by only loading the latest scan [`e9914e3`](https://git.odit.services/lfk/backend/commit/e9914e317b7fd78863cfd8549bad65da9292b7ca)
- Log batch time in mass scan script [`2905884`](https://git.odit.services/lfk/backend/commit/2905884c024d7f275b3ad2c2858a2f0911adb95b)
- Dont load cards with get all runners request [`702070d`](https://git.odit.services/lfk/backend/commit/702070da669cc605b93e6f5b62d712c28f079dd0)
#### [v0.15.0](https://git.odit.services/lfk/backend/compare/v0.14.6...v0.15.0)
> 15 April 2023
- Added test script for creating mass scans [`8007117`](https://git.odit.services/lfk/backend/commit/80071174342d87199fcbd981cd8c92300b0a51e4)
- 🚀Bumped version to v0.15.0 [`cc89ba8`](https://git.odit.services/lfk/backend/commit/cc89ba8afb3120569613a889baf962555612e95a)
- Get all scans speed improvement [`23fa78e`](https://git.odit.services/lfk/backend/commit/23fa78eb9dcc01ecc036347f6703aacc0d163d7d)
- More scan request optimizations [`7c4ff42`](https://git.odit.services/lfk/backend/commit/7c4ff42a3b3e7b186e16c85a97d9ecc854a32cb0)
#### [v0.14.6](https://git.odit.services/lfk/backend/compare/v0.14.5...v0.14.6)
> 15 April 2023
- 🚀Bumped version to v0.14.6 [`3b3e689`](https://git.odit.services/lfk/backend/commit/3b3e68900beca16cfff88dbef22540f77750d29b)
- Missing orm file [`3ff666f`](https://git.odit.services/lfk/backend/commit/3ff666fd3e84ac8cf41b30e9e17082b10548d55b)
#### [v0.14.5](https://git.odit.services/lfk/backend/compare/v0.14.4...v0.14.5)
> 15 April 2023
- 🚀Bumped version to v0.14.5 [`4e44350`](https://git.odit.services/lfk/backend/commit/4e4435010fd7095e3b9742e207cba1b68cd6da3b)
- Entrypoint fix [`de9af5a`](https://git.odit.services/lfk/backend/commit/de9af5a90907dcfc9bfb1d5a56420eed8bb59922)
- Fixed copy [`ac631f0`](https://git.odit.services/lfk/backend/commit/ac631f0af467446552478873b7b4802a9310f865)
#### [v0.14.4](https://git.odit.services/lfk/backend/compare/v0.14.3...v0.14.4)
> 15 April 2023
- Switched ci over to pnpm + cache [`6275aaa`](https://git.odit.services/lfk/backend/commit/6275aaa326f1c02c8dd42aa31608978408c44ab7)
- 🚀Bumped version to v0.14.4 [`6bbdd5b`](https://git.odit.services/lfk/backend/commit/6bbdd5bb04a1c38e4b3a150db24b76e9c96490dd)
- Back to ean13 based codes [`a8fc755`](https://git.odit.services/lfk/backend/commit/a8fc7558408b97da4b2c469ae5e73ab502b4fda0)
- install prod in first step [`d027439`](https://git.odit.services/lfk/backend/commit/d02743984dfea8057be3081bd3a32a8f67e610aa)
- Switched dockerfile to pnpm 8 with cache [`93d43b7`](https://git.odit.services/lfk/backend/commit/93d43b76843d7cb411f37fd2066c6a5364c05415)
- COPY by stage name [`a64f6c9`](https://git.odit.services/lfk/backend/commit/a64f6c9822af2b927e91b0b55f1f50176de30169)
- pinned pnpm version [`2a94bfa`](https://git.odit.services/lfk/backend/commit/2a94bfa6227d14f635b5fc2789b59c36d490937e)
- custom pnpm cache [`85dc344`](https://git.odit.services/lfk/backend/commit/85dc3444acc677ddd242f9f2543ce477fe427a7c)
- added missing ci env [`734c826`](https://git.odit.services/lfk/backend/commit/734c826face58dd5c3bb2607bda6e7f6d051012e)
- pinned pnpm to 8 [`27e74e8`](https://git.odit.services/lfk/backend/commit/27e74e824cd1e23d4d53c1a983a1668dd87f5d59)
- coherent baseimage [`b5c0a28`](https://git.odit.services/lfk/backend/commit/b5c0a288ac3c020f5d753c558aee160fea0bae14)
- bumped final pnpm version [`33b25c9`](https://git.odit.services/lfk/backend/commit/33b25c9743abb7cefb3538f08cc2f78a646905c8)
#### [v0.14.3](https://git.odit.services/lfk/backend/compare/v0.14.2...v0.14.3)
> 18 March 2023
- 🚀Bumped version to v0.14.3 [`16ce0a8`](https://git.odit.services/lfk/backend/commit/16ce0a848050b74c4b6dd93f17e5a6e9024cdb7d)
- Adjusted modulo for new fixed card length [`9a8d618`](https://git.odit.services/lfk/backend/commit/9a8d618ae4584640e8be1ce9fe4bddd2ef7a92ae)
#### [v0.14.2](https://git.odit.services/lfk/backend/compare/v0.14.1...v0.14.2)
> 18 March 2023
- 🚀Bumped version to v0.14.2 [`38da2d3`](https://git.odit.services/lfk/backend/commit/38da2d33187f4b24eef878642e153663ecd95de1)
- Back to modulo [`068deb4`](https://git.odit.services/lfk/backend/commit/068deb4960bd16decf99887ffbda7a7d3dd9ff0b)
#### [v0.14.1](https://git.odit.services/lfk/backend/compare/v0.14.0...v0.14.1)
> 18 March 2023
- 🚀Bumped version to v0.14.1 [`13f093b`](https://git.odit.services/lfk/backend/commit/13f093bb6138a498f93a05ef6dd812ae92f2676a)
- Switched from card prefix replacement via modulo to regex [`6289f30`](https://git.odit.services/lfk/backend/commit/6289f307400aacaa9cfe03f3024c1e0d5554d4f2)
#### [v0.14.0](https://git.odit.services/lfk/backend/compare/v0.13.3...v0.14.0)
> 15 March 2023
- 🚀Bumped version to v0.14.0 [`6ff764b`](https://git.odit.services/lfk/backend/commit/6ff764bc340ca25b3bdd62c6892259e228723973)
- Updated default length [`ea87cc7`](https://git.odit.services/lfk/backend/commit/ea87cc793b163bf0d4405a25bbe83fbc8e31c206)
- breaking(runnercards): shorter runnercard codes (padding to 12 was a bit tooo ambitious) [`ffee887`](https://git.odit.services/lfk/backend/commit/ffee887ddf6a71102ee39533d7cd504d1fd6698f)
- Removed sqlite journal [`92517e3`](https://git.odit.services/lfk/backend/commit/92517e365393f4baac3814f5668874b5752dc7c8)
#### [v0.13.3](https://git.odit.services/lfk/backend/compare/v0.13.2...v0.13.3) #### [v0.13.3](https://git.odit.services/lfk/backend/compare/v0.13.2...v0.13.3)
> 15 February 2023
- 🚀Bumped version to v0.13.3 [`3bac75e`](https://git.odit.services/lfk/backend/commit/3bac75e7ab9f16ecab1fbfa9915a7edb923883f6)
- Merge pull request 'feature/201-no_citizen-deletion' (#202) from feature/201-no_citizen-deletion into dev [`d05eddc`](https://git.odit.services/lfk/backend/commit/d05eddcae198427ce9a334096563b3aadcff2b56) - Merge pull request 'feature/201-no_citizen-deletion' (#202) from feature/201-no_citizen-deletion into dev [`d05eddc`](https://git.odit.services/lfk/backend/commit/d05eddcae198427ce9a334096563b3aadcff2b56)
- Updated tests [`d5c689d`](https://git.odit.services/lfk/backend/commit/d5c689d6937288df7dca14ce26fbbd4f46a8752a) - Updated tests [`d5c689d`](https://git.odit.services/lfk/backend/commit/d5c689d6937288df7dca14ce26fbbd4f46a8752a)
- Added delete check for citizen org [`8fedd4e`](https://git.odit.services/lfk/backend/commit/8fedd4ef3bdd48dc42abc1d53006eefc145175e3) - Added delete check for citizen org [`8fedd4e`](https://git.odit.services/lfk/backend/commit/8fedd4ef3bdd48dc42abc1d53006eefc145175e3)

View File

@ -1,15 +1,23 @@
# Typescript Build # Typescript Build
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 FROM registry.odit.services/hub/library/node:21.1.0-alpine3.18 as build
ARG NPM_REGISTRY_URL=https://registry.npmjs.org
WORKDIR /app WORKDIR /app
COPY package.json ./ COPY package.json ./
RUN npx pnpm@7.26.3 i RUN npm config set registry $NPM_REGISTRY_URL && npm i -g pnpm@8
RUN mkdir /pnpm && pnpm config set store-dir /pnpm && pnpm i
COPY tsconfig.json ormconfig.js ./ COPY tsconfig.json ormconfig.js ./
COPY src ./src COPY src ./src
RUN npm run build RUN pnpm run build \
&& rm -rf /app/node_modules \
&& pnpm i --production --prefer-offline
# final image # final image
FROM registry.odit.services/hub/library/node:19.5.0-alpine3.16 FROM registry.odit.services/hub/library/node:21.1.0-alpine3.18 as final
COPY package.json ormconfig.js ./ WORKDIR /app
RUN npx pnpm@7.26.3 i --prod COPY --from=build /app/package.json /app/package.json
COPY --from=0 /app/dist dist COPY --from=build /app/ormconfig.js /app/ormconfig.js
ENTRYPOINT ["node", "dist/app.js"] COPY --from=build /app/dist /app/dist
COPY --from=build /app/node_modules /app/node_modules
ENTRYPOINT ["node", "/app/dist/app.js"]

View File

@ -15,24 +15,24 @@ Backend Server
1. Rename the .env.example file to .env (you can adjust app port and other settings, if needed) 1. Rename the .env.example file to .env (you can adjust app port and other settings, if needed)
2. Install Dependencies 2. Install Dependencies
```bash ```bash
yarn pnpm i
``` ```
3. Start the server 3. Start the server
```bash ```bash
yarn dev pnpm dev
``` ```
### Run Tests ### Run Tests
```bash ```bash
# Run tests once (server has to run) # Run tests once (server has to run)
yarn test pnpm test
# Run test in watch mode (reruns on change) # Run test in watch mode (reruns on change)
yarn test:watch pnpm test:watch
# Run test in ci mode (automaticly starts the dev server) # Run test in ci mode (automaticly starts the dev server)
yarn test:ci pnpm test:ci
``` ```
### Use your own mail templates ### Use your own mail templates
@ -44,7 +44,7 @@ Currently the following templates exist:
### Generate Docs ### Generate Docs
```bash ```bash
yarn docs pnpm docs
``` ```
## ENV Vars ## ENV Vars
@ -90,5 +90,5 @@ yarn docs
* The dev tag of the docker image get's build from this * The dev tag of the docker image get's build from this
* Only push minor changes to this branch! * Only push minor changes to this branch!
* To merge a feature branch into this please create a pull request * To merge a feature branch into this please create a pull request
* feature/xyz: Feature branches - nameing scheme: `feature/issueid-title` * feature/xyz: Feature branches - naming scheme: `feature/issueid-title`
* bugfix/xyz: Branches for bugfixes - nameing scheme:`bugfix/issueid-title` * bugfix/xyz: Branches for bugfixes - naming scheme:`bugfix/issueid-title`

View File

@ -1,8 +1,11 @@
{ {
"name": "@odit/lfk-backend", "name": "@odit/lfk-backend",
"version": "0.13.3", "version": "1.1.3",
"main": "src/app.ts", "main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend", "repository": "https://git.odit.services/lfk/backend",
"engines": {
"pnpm": "8"
},
"author": { "author": {
"name": "ODIT.Services", "name": "ODIT.Services",
"email": "info@odit.services", "email": "info@odit.services",
@ -51,7 +54,7 @@
"validator": "13.5.2" "validator": "13.5.2"
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^7.6.0", "@faker-js/faker": "7.6.0",
"@odit/license-exporter": "0.0.9", "@odit/license-exporter": "0.0.9",
"@types/cors": "2.8.9", "@types/cors": "2.8.9",
"@types/csvtojson": "1.1.5", "@types/csvtojson": "1.1.5",
@ -60,7 +63,7 @@
"@types/jsonwebtoken": "8.5.0", "@types/jsonwebtoken": "8.5.0",
"@types/node": "14.14.22", "@types/node": "14.14.22",
"@types/uuid": "8.3.0", "@types/uuid": "8.3.0",
"auto-changelog": "^2.4.0", "auto-changelog": "2.4.0",
"cp-cli": "2.0.0", "cp-cli": "2.0.0",
"jest": "26.6.3", "jest": "26.6.3",
"nodemon": "2.0.7", "nodemon": "2.0.7",
@ -111,4 +114,4 @@
"docs/*" "docs/*"
] ]
} }
} }

7803
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { DonationIdsNotMatchingError, DonationNotFoundError } from '../errors/DonationErrors'; import { DonationIdsNotMatchingError, DonationNotFoundError } from '../errors/DonationErrors';
import { DonorNotFoundError } from '../errors/DonorErrors'; import { DonorNotFoundError } from '../errors/DonorErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerNotFoundError } from '../errors/RunnerErrors';
@ -36,9 +36,16 @@ export class DonationController {
@ResponseSchema(ResponseDonation, { isArray: true }) @ResponseSchema(ResponseDonation, { isArray: true })
@ResponseSchema(ResponseDistanceDonation, { isArray: true }) @ResponseSchema(ResponseDistanceDonation, { isArray: true })
@OpenAPI({ description: 'Lists all donations (fixed or distance based) from all donors. <br> This includes the donations\'s runner\'s distance ran(if distance donation).' }) @OpenAPI({ description: 'Lists all donations (fixed or distance based) from all donors. <br> This includes the donations\'s runner\'s distance ran(if distance donation).' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseDonations: ResponseDonation[] = new Array<ResponseDonation>(); let responseDonations: ResponseDonation[] = new Array<ResponseDonation>();
const donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] }); let donations: Array<Donation>;
if (page != undefined) {
donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'], skip: page * page_size, take: page_size });
} else {
donations = await this.donationRepository.find({ relations: ['runner', 'donor', 'runner.scans', 'runner.scans.track'] });
}
donations.forEach(donation => { donations.forEach(donation => {
responseDonations.push(donation.toResponse()); responseDonations.push(donation.toResponse());
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { DonorHasDonationsError, DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors'; import { DonorHasDonationsError, DonorIdsNotMatchingError, DonorNotFoundError } from '../errors/DonorErrors';
import { CreateDonor } from '../models/actions/create/CreateDonor'; import { CreateDonor } from '../models/actions/create/CreateDonor';
import { UpdateDonor } from '../models/actions/update/UpdateDonor'; import { UpdateDonor } from '../models/actions/update/UpdateDonor';
@ -25,9 +25,16 @@ export class DonorController {
@Authorized("DONOR:GET") @Authorized("DONOR:GET")
@ResponseSchema(ResponseDonor, { isArray: true }) @ResponseSchema(ResponseDonor, { isArray: true })
@OpenAPI({ description: 'Lists all donor. <br> This includes the donor\'s current donation amount.' }) @OpenAPI({ description: 'Lists all donor. <br> This includes the donor\'s current donation amount.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseDonors: ResponseDonor[] = new Array<ResponseDonor>(); let responseDonors: ResponseDonor[] = new Array<ResponseDonor>();
const donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] }); let donors: Array<Donor>;
if (page != undefined) {
donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'], skip: page * page_size, take: page_size });
} else {
donors = await this.donorRepository.find({ relations: ['donations', 'donations.runner', 'donations.runner.scans', 'donations.runner.scans.track'] });
}
donors.forEach(donor => { donors.forEach(donor => {
responseDonors.push(new ResponseDonor(donor)); responseDonors.push(new ResponseDonor(donor));
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnection, getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnection, getConnectionManager } from 'typeorm';
import { GroupContactIdsNotMatchingError, GroupContactNotFoundError } from '../errors/GroupContactErrors'; import { GroupContactIdsNotMatchingError, GroupContactNotFoundError } from '../errors/GroupContactErrors';
import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors';
import { CreateGroupContact } from '../models/actions/create/CreateGroupContact'; import { CreateGroupContact } from '../models/actions/create/CreateGroupContact';
@ -26,9 +26,16 @@ export class GroupContactController {
@Authorized("CONTACT:GET") @Authorized("CONTACT:GET")
@ResponseSchema(ResponseGroupContact, { isArray: true }) @ResponseSchema(ResponseGroupContact, { isArray: true })
@OpenAPI({ description: 'Lists all contacts. <br> This includes the contact\'s associated groups.' }) @OpenAPI({ description: 'Lists all contacts. <br> This includes the contact\'s associated groups.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseContacts: ResponseGroupContact[] = new Array<ResponseGroupContact>(); let responseContacts: ResponseGroupContact[] = new Array<ResponseGroupContact>();
const contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'] }); let contacts: Array<GroupContact>;
if (page != undefined) {
contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'], skip: page * page_size, take: page_size });
} else {
contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'] });
}
contacts.forEach(contact => { contacts.forEach(contact => {
responseContacts.push(contact.toResponse()); responseContacts.push(contact.toResponse());
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { PermissionIdsNotMatchingError, PermissionNeedsPrincipalError, PermissionNotFoundError } from '../errors/PermissionErrors'; import { PermissionIdsNotMatchingError, PermissionNeedsPrincipalError, PermissionNotFoundError } from '../errors/PermissionErrors';
import { PrincipalNotFoundError } from '../errors/PrincipalErrors'; import { PrincipalNotFoundError } from '../errors/PrincipalErrors';
import { CreatePermission } from '../models/actions/create/CreatePermission'; import { CreatePermission } from '../models/actions/create/CreatePermission';
@ -27,9 +27,16 @@ export class PermissionController {
@Authorized("PERMISSION:GET") @Authorized("PERMISSION:GET")
@ResponseSchema(ResponsePermission, { isArray: true }) @ResponseSchema(ResponsePermission, { isArray: true })
@OpenAPI({ description: 'Lists all permissions for all users and groups.' }) @OpenAPI({ description: 'Lists all permissions for all users and groups.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responsePermissions: ResponsePermission[] = new Array<ResponsePermission>(); let responsePermissions: ResponsePermission[] = new Array<ResponsePermission>();
const permissions = await this.permissionRepository.find({ relations: ['principal'] }); let permissions: Array<Permission>;
if (page != undefined) {
permissions = await this.permissionRepository.find({ relations: ['principal'], skip: page * page_size, take: page_size });
} else {
permissions = await this.permissionRepository.find({ relations: ['principal'] });
}
permissions.forEach(permission => { permissions.forEach(permission => {
responsePermissions.push(new ResponsePermission(permission)); responsePermissions.push(new ResponsePermission(permission));
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors'; import { RunnerCardHasScansError, RunnerCardIdsNotMatchingError, RunnerCardNotFoundError } from '../errors/RunnerCardErrors';
import { RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard'; import { CreateRunnerCard } from '../models/actions/create/CreateRunnerCard';
@ -26,9 +26,16 @@ export class RunnerCardController {
@Authorized("CARD:GET") @Authorized("CARD:GET")
@ResponseSchema(ResponseRunnerCard, { isArray: true }) @ResponseSchema(ResponseRunnerCard, { isArray: true })
@OpenAPI({ description: 'Lists all card.' }) @OpenAPI({ description: 'Lists all card.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>(); let responseCards: ResponseRunnerCard[] = new Array<ResponseRunnerCard>();
const cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'] }); let cards: Array<RunnerCard>;
if (page != undefined) {
cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'], skip: page * page_size, take: page_size });
} else {
cards = await this.cardRepository.find({ relations: ['runner', 'runner.group', 'runner.group.parentGroup'] });
}
cards.forEach(card => { cards.forEach(card => {
responseCards.push(new ResponseRunnerCard(card)); responseCards.push(new ResponseRunnerCard(card));
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { RunnerGroupNeededError, RunnerHasDistanceDonationsError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerGroupNeededError, RunnerHasDistanceDonationsError, RunnerIdsNotMatchingError, RunnerNotFoundError } from '../errors/RunnerErrors';
import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors'; import { RunnerGroupNotFoundError } from '../errors/RunnerGroupErrors';
import { CreateRunner } from '../models/actions/create/CreateRunner'; import { CreateRunner } from '../models/actions/create/CreateRunner';
@ -30,9 +30,16 @@ export class RunnerController {
@Authorized("RUNNER:GET") @Authorized("RUNNER:GET")
@ResponseSchema(ResponseRunner, { isArray: true }) @ResponseSchema(ResponseRunner, { isArray: true })
@OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' }) @OpenAPI({ description: 'Lists all runners from all teams/orgs. <br> This includes the runner\'s group and distance ran.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseRunners: ResponseRunner[] = new Array<ResponseRunner>(); let responseRunners: ResponseRunner[] = new Array<ResponseRunner>();
const runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards'] }); let runners: Array<Runner>;
if (page != undefined) {
runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track'], skip: page * page_size, take: page_size });
} else {
runners = await this.runnerRepository.find({ relations: ['scans', 'group', 'group.parentGroup', 'scans.track'] });
}
runners.forEach(runner => { runners.forEach(runner => {
responseRunners.push(new ResponseRunner(runner)); responseRunners.push(new ResponseRunner(runner));
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, BadRequestError, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, BadRequestError, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { RunnerOrganizationHasRunnersError, RunnerOrganizationHasTeamsError, RunnerOrganizationIdsNotMatchingError, RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; import { RunnerOrganizationHasRunnersError, RunnerOrganizationHasTeamsError, RunnerOrganizationIdsNotMatchingError, RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors';
import { CreateRunnerOrganization } from '../models/actions/create/CreateRunnerOrganization'; import { CreateRunnerOrganization } from '../models/actions/create/CreateRunnerOrganization';
import { UpdateRunnerOrganization } from '../models/actions/update/UpdateRunnerOrganization'; import { UpdateRunnerOrganization } from '../models/actions/update/UpdateRunnerOrganization';
@ -29,13 +29,20 @@ export class RunnerOrganizationController {
@Authorized("ORGANIZATION:GET") @Authorized("ORGANIZATION:GET")
@ResponseSchema(ResponseRunnerOrganization, { isArray: true }) @ResponseSchema(ResponseRunnerOrganization, { isArray: true })
@OpenAPI({ description: 'Lists all organizations. <br> This includes their address, contact and teams (if existing/associated).' }) @OpenAPI({ description: 'Lists all organizations. <br> This includes their address, contact and teams (if existing/associated).' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseTeams: ResponseRunnerOrganization[] = new Array<ResponseRunnerOrganization>(); let responseOrgs: ResponseRunnerOrganization[] = new Array<ResponseRunnerOrganization>();
const runners = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'] }); let orgs: Array<RunnerOrganization>;
runners.forEach(runner => {
responseTeams.push(new ResponseRunnerOrganization(runner)); if (page != undefined) {
orgs = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'], skip: page * page_size, take: page_size });
} else {
orgs = await this.runnerOrganizationRepository.find({ relations: ['contact', 'teams'] });
}
orgs.forEach(org => {
responseOrgs.push(new ResponseRunnerOrganization(org));
}); });
return responseTeams; return responseOrgs;
} }
@Get('/:id') @Get('/:id')
@ -45,7 +52,7 @@ export class RunnerOrganizationController {
@OnUndefined(RunnerOrganizationNotFoundError) @OnUndefined(RunnerOrganizationNotFoundError)
@OpenAPI({ description: 'Lists all information about the organization whose id got provided.' }) @OpenAPI({ description: 'Lists all information about the organization whose id got provided.' })
async getOne(@Param('id') id: number) { async getOne(@Param('id') id: number) {
let runnerOrg = await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['contact', 'teams'] }); let runnerOrg = await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['contact', 'teams', 'teams.runners', 'teams.runners.scans', 'teams.runners.scans.track', 'runners', 'runners.scans', 'runners.scans.track'] });
if (!runnerOrg) { throw new RunnerOrganizationNotFoundError(); } if (!runnerOrg) { throw new RunnerOrganizationNotFoundError(); }
return new ResponseRunnerOrganization(runnerOrg); return new ResponseRunnerOrganization(runnerOrg);
} }

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors'; import { RunnerTeamHasRunnersError, RunnerTeamIdsNotMatchingError, RunnerTeamNotFoundError } from '../errors/RunnerTeamErrors';
import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam'; import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam';
import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam'; import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam';
@ -27,11 +27,18 @@ export class RunnerTeamController {
@Authorized("TEAM:GET") @Authorized("TEAM:GET")
@ResponseSchema(ResponseRunnerTeam, { isArray: true }) @ResponseSchema(ResponseRunnerTeam, { isArray: true })
@OpenAPI({ description: 'Lists all teams. <br> This includes their parent organization and contact (if existing/associated).' }) @OpenAPI({ description: 'Lists all teams. <br> This includes their parent organization and contact (if existing/associated).' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseTeams: ResponseRunnerTeam[] = new Array<ResponseRunnerTeam>(); let responseTeams: ResponseRunnerTeam[] = new Array<ResponseRunnerTeam>();
const runners = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] }); let teams: Array<RunnerTeam>;
runners.forEach(runner => {
responseTeams.push(new ResponseRunnerTeam(runner)); if (page != undefined) {
teams = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'], skip: page * page_size, take: page_size });
} else {
teams = await this.runnerTeamRepository.find({ relations: ['parentGroup', 'contact'] });
}
teams.forEach(team => {
responseTeams.push(new ResponseRunnerTeam(team));
}); });
return responseTeams; return responseTeams;
} }
@ -43,7 +50,7 @@ export class RunnerTeamController {
@OnUndefined(RunnerTeamNotFoundError) @OnUndefined(RunnerTeamNotFoundError)
@OpenAPI({ description: 'Lists all information about the team whose id got provided.' }) @OpenAPI({ description: 'Lists all information about the team whose id got provided.' })
async getOne(@Param('id') id: number) { async getOne(@Param('id') id: number) {
let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact'] }); let runnerTeam = await this.runnerTeamRepository.findOne({ id: id }, { relations: ['parentGroup', 'contact', 'runners', 'runners.scans', 'runners.scans.track'] });
if (!runnerTeam) { throw new RunnerTeamNotFoundError(); } if (!runnerTeam) { throw new RunnerTeamNotFoundError(); }
return new ResponseRunnerTeam(runnerTeam); return new ResponseRunnerTeam(runnerTeam);
} }

View File

@ -1,7 +1,7 @@
import { Request } from "express"; import { Request } from "express";
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req, UseBefore } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam, Req, UseBefore } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { RunnerNotFoundError } from '../errors/RunnerErrors'; import { RunnerNotFoundError } from '../errors/RunnerErrors';
import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors'; import { ScanIdsNotMatchingError, ScanNotFoundError } from '../errors/ScanErrors';
import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; import { ScanStationNotFoundError } from '../errors/ScanStationErrors';
@ -34,9 +34,16 @@ export class ScanController {
@ResponseSchema(ResponseScan, { isArray: true }) @ResponseSchema(ResponseScan, { isArray: true })
@ResponseSchema(ResponseTrackScan, { isArray: true }) @ResponseSchema(ResponseTrackScan, { isArray: true })
@OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' }) @OpenAPI({ description: 'Lists all scans (normal or track) from all runners. <br> This includes the scan\'s runner\'s distance ran.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseScans: ResponseScan[] = new Array<ResponseScan>(); let responseScans: ResponseScan[] = new Array<ResponseScan>();
const scans = await this.scanRepository.find({ relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] }); let scans: Array<Scan>;
if (page != undefined) {
scans = await this.scanRepository.find({ relations: ['runner', 'track'], skip: page * page_size, take: page_size });
} else {
scans = await this.scanRepository.find({ relations: ['runner', 'track'] });
}
scans.forEach(scan => { scans.forEach(scan => {
responseScans.push(scan.toResponse()); responseScans.push(scan.toResponse());
}); });
@ -51,7 +58,7 @@ export class ScanController {
@OnUndefined(ScanNotFoundError) @OnUndefined(ScanNotFoundError)
@OpenAPI({ description: 'Lists all information about the scan whose id got provided. This includes the scan\'s runner\'s distance ran.' }) @OpenAPI({ description: 'Lists all information about the scan whose id got provided. This includes the scan\'s runner\'s distance ran.' })
async getOne(@Param('id') id: number) { async getOne(@Param('id') id: number) {
let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.scans', 'runner.group', 'runner.scans.track', 'card', 'station'] }) let scan = await this.scanRepository.findOne({ id: id }, { relations: ['runner', 'track', 'runner.group', 'card', 'station'] })
if (!scan) { throw new ScanNotFoundError(); } if (!scan) { throw new ScanNotFoundError(); }
return scan.toResponse(); return scan.toResponse();
} }

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { ScanStationHasScansError, ScanStationIdsNotMatchingError, ScanStationNotFoundError } from '../errors/ScanStationErrors'; import { ScanStationHasScansError, ScanStationIdsNotMatchingError, ScanStationNotFoundError } from '../errors/ScanStationErrors';
import { TrackNotFoundError } from '../errors/TrackErrors'; import { TrackNotFoundError } from '../errors/TrackErrors';
import { CreateScanStation } from '../models/actions/create/CreateScanStation'; import { CreateScanStation } from '../models/actions/create/CreateScanStation';
@ -26,9 +26,16 @@ export class ScanStationController {
@Authorized("STATION:GET") @Authorized("STATION:GET")
@ResponseSchema(ResponseScanStation, { isArray: true }) @ResponseSchema(ResponseScanStation, { isArray: true })
@OpenAPI({ description: 'Lists all stations. <br> This includes their associated tracks.' }) @OpenAPI({ description: 'Lists all stations. <br> This includes their associated tracks.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseStations: ResponseScanStation[] = new Array<ResponseScanStation>(); let responseStations: ResponseScanStation[] = new Array<ResponseScanStation>();
const stations = await this.stationRepository.find({ relations: ['track'] }); let stations: Array<ScanStation>;
if (page != undefined) {
stations = await this.stationRepository.find({ relations: ['track'], skip: page * page_size, take: page_size });
} else {
stations = await this.stationRepository.find({ relations: ['track'] });
}
stations.forEach(station => { stations.forEach(station => {
responseStations.push(station.toResponse()); responseStations.push(station.toResponse());
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { StatsClientNotFoundError } from '../errors/StatsClientErrors'; import { StatsClientNotFoundError } from '../errors/StatsClientErrors';
import { TrackNotFoundError } from "../errors/TrackErrors"; import { TrackNotFoundError } from "../errors/TrackErrors";
import { CreateStatsClient } from '../models/actions/create/CreateStatsClient'; import { CreateStatsClient } from '../models/actions/create/CreateStatsClient';
@ -24,9 +24,16 @@ export class StatsClientController {
@Authorized("STATSCLIENT:GET") @Authorized("STATSCLIENT:GET")
@ResponseSchema(ResponseStatsClient, { isArray: true }) @ResponseSchema(ResponseStatsClient, { isArray: true })
@OpenAPI({ description: 'Lists all stats clients. Please remember that the key can only be viewed on creation.' }) @OpenAPI({ description: 'Lists all stats clients. Please remember that the key can only be viewed on creation.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseClients: ResponseStatsClient[] = new Array<ResponseStatsClient>(); let responseClients: ResponseStatsClient[] = new Array<ResponseStatsClient>();
const clients = await this.clientRepository.find(); let clients: Array<StatsClient>;
if (page != undefined) {
clients = await this.clientRepository.find({ skip: page * page_size, take: page_size });
} else {
clients = await this.clientRepository.find();
}
clients.forEach(clients => { clients.forEach(clients => {
responseClients.push(new ResponseStatsClient(clients)); responseClients.push(new ResponseStatsClient(clients));
}); });

View File

@ -3,6 +3,7 @@ import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnection } from 'typeorm'; import { getConnection } from 'typeorm';
import StatsAuth from '../middlewares/StatsAuth'; import StatsAuth from '../middlewares/StatsAuth';
import { Donation } from '../models/entities/Donation'; import { Donation } from '../models/entities/Donation';
import { Donor } from '../models/entities/Donor';
import { Runner } from '../models/entities/Runner'; import { Runner } from '../models/entities/Runner';
import { RunnerOrganization } from '../models/entities/RunnerOrganization'; import { RunnerOrganization } from '../models/entities/RunnerOrganization';
import { RunnerTeam } from '../models/entities/RunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam';
@ -21,14 +22,26 @@ export class StatsController {
@ResponseSchema(ResponseStats) @ResponseSchema(ResponseStats)
@OpenAPI({ description: "A very basic stats endpoint providing basic counters for a dashboard or simmilar" }) @OpenAPI({ description: "A very basic stats endpoint providing basic counters for a dashboard or simmilar" })
async get() { async get() {
let connection = getConnection(); const connection = getConnection();
let runners = await connection.getRepository(Runner).find({ relations: ['scans', 'scans.track'] }); const runners = await connection.getRepository(Runner).count();
let teams = await connection.getRepository(RunnerTeam).find(); const teams = await connection.getRepository(RunnerTeam).count();
let orgs = await connection.getRepository(RunnerOrganization).find(); const orgs = await connection.getRepository(RunnerOrganization).count();
let users = await connection.getRepository(User).find(); const users = await connection.getRepository(User).count();
let scans = await connection.getRepository(Scan).find(); const scans = await connection.getRepository(Scan).count({ where: { valid: true } });
const distance_query = await connection.getRepository(Scan).createQueryBuilder('scan')
.leftJoinAndSelect("scan.track", "track").where("scan.valid = TRUE")
.select("SUM(track.distance)", "sum_track").addSelect("SUM(_distance)", "sum_distance")
.getRawOne();
let distace = parseInt(distance_query.sum_track)
if (distance_query.sum_distance) {
distace += parseInt(distance_query.sum_distance)
}
let donations = await connection.getRepository(Donation).find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] }); let donations = await connection.getRepository(Donation).find({ relations: ['runner', 'runner.scans', 'runner.scans.track'] });
return new ResponseStats(runners, teams, orgs, users, scans, donations) const donors = await connection.getRepository(Donor).count();
return new ResponseStats(runners, teams, orgs, users, scans, donations, distace, donors)
} }
@Get("/runners/distance") @Get("/runners/distance")

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { TrackHasScanStationsError, TrackIdsNotMatchingError, TrackLapTimeCantBeNegativeError, TrackNotFoundError } from "../errors/TrackErrors"; import { TrackHasScanStationsError, TrackIdsNotMatchingError, TrackLapTimeCantBeNegativeError, TrackNotFoundError } from "../errors/TrackErrors";
import { CreateTrack } from '../models/actions/create/CreateTrack'; import { CreateTrack } from '../models/actions/create/CreateTrack';
import { UpdateTrack } from '../models/actions/update/UpdateTrack'; import { UpdateTrack } from '../models/actions/update/UpdateTrack';
@ -25,9 +25,17 @@ export class TrackController {
@Authorized("TRACK:GET") @Authorized("TRACK:GET")
@ResponseSchema(ResponseTrack, { isArray: true }) @ResponseSchema(ResponseTrack, { isArray: true })
@OpenAPI({ description: 'Lists all tracks.' }) @OpenAPI({ description: 'Lists all tracks.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseTracks: ResponseTrack[] = new Array<ResponseTrack>(); let responseTracks: ResponseTrack[] = new Array<ResponseTrack>();
const tracks = await this.trackRepository.find(); let tracks: Array<Track>;
if (page != undefined) {
tracks = await this.trackRepository.find({ skip: page * page_size, take: page_size });
}
else {
tracks = await this.trackRepository.find();
}
tracks.forEach(track => { tracks.forEach(track => {
responseTracks.push(new ResponseTrack(track)); responseTracks.push(new ResponseTrack(track));
}); });

View File

@ -1,7 +1,7 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors'; import { PasswordMustContainLowercaseLetterError, PasswordMustContainNumberError, PasswordMustContainUppercaseLetterError, PasswordTooShortError, UserDeletionNotConfirmedError, UserIdsNotMatchingError, UserNotFoundError, UsernameContainsIllegalCharacterError } from '../errors/UserErrors';
import { UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { UserGroupNotFoundError } from '../errors/UserGroupErrors';
import { CreateUser } from '../models/actions/create/CreateUser'; import { CreateUser } from '../models/actions/create/CreateUser';
import { UpdateUser } from '../models/actions/update/UpdateUser'; import { UpdateUser } from '../models/actions/update/UpdateUser';
@ -28,9 +28,17 @@ export class UserController {
@Authorized("USER:GET") @Authorized("USER:GET")
@ResponseSchema(ResponseUser, { isArray: true }) @ResponseSchema(ResponseUser, { isArray: true })
@OpenAPI({ description: 'Lists all users. <br> This includes their groups and permissions granted to them.' }) @OpenAPI({ description: 'Lists all users. <br> This includes their groups and permissions granted to them.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseUsers: ResponseUser[] = new Array<ResponseUser>(); let responseUsers: ResponseUser[] = new Array<ResponseUser>();
const users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'] }); let users: Array<User>;
if (page != undefined) {
users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'], skip: page * page_size, take: page_size });
}
else {
users = await this.userRepository.find({ relations: ['permissions', 'groups', 'groups.permissions'] });
}
users.forEach(user => { users.forEach(user => {
responseUsers.push(new ResponseUser(user)); responseUsers.push(new ResponseUser(user));
}); });

View File

@ -1,6 +1,6 @@
import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm'; import { Repository, getConnectionManager } from 'typeorm';
import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors';
import { CreateUserGroup } from '../models/actions/create/CreateUserGroup'; import { CreateUserGroup } from '../models/actions/create/CreateUserGroup';
import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup'; import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup';
@ -27,9 +27,16 @@ export class UserGroupController {
@Authorized("USERGROUP:GET") @Authorized("USERGROUP:GET")
@ResponseSchema(ResponseUserGroup, { isArray: true }) @ResponseSchema(ResponseUserGroup, { isArray: true })
@OpenAPI({ description: 'Lists all groups. <br> The information provided might change while the project continues to evolve.' }) @OpenAPI({ description: 'Lists all groups. <br> The information provided might change while the project continues to evolve.' })
async getAll() { async getAll(@QueryParam("page", { required: false }) page: number, @QueryParam("page_size", { required: false }) page_size: number = 100) {
let responseGroups: ResponseUserGroup[] = new Array<ResponseUserGroup>(); let responseGroups: ResponseUserGroup[] = new Array<ResponseUserGroup>();
const groups = await this.userGroupsRepository.find({ relations: ['permissions'] }); let groups: Array<UserGroup>;
if (page != undefined) {
groups = await this.userGroupsRepository.find({ relations: ['permissions'], skip: page * page_size, take: page_size });
} else {
groups = await this.userGroupsRepository.find({ relations: ['permissions'] });
}
groups.forEach(group => { groups.forEach(group => {
responseGroups.push(group.toResponse()); responseGroups.push(group.toResponse());
}); });

View File

@ -58,11 +58,11 @@ export class CreateTrackScan {
*/ */
public async getCard(): Promise<RunnerCard> { public async getCard(): Promise<RunnerCard> {
const id = this.card % 200000000000; const id = this.card % 200000000000;
const track = await getConnection().getRepository(RunnerCard).findOne({ id: id }, { relations: ["runner"] }); const runnerCard = await getConnection().getRepository(RunnerCard).findOne({ id: id }, { relations: ["runner"] });
if (!track) { if (!runnerCard) {
throw new RunnerCardNotFoundError(); throw new RunnerCardNotFoundError();
} }
return track; return runnerCard;
} }
/** /**
@ -86,14 +86,13 @@ export class CreateTrackScan {
* @returns The validated scan with it's laptime set. * @returns The validated scan with it's laptime set.
*/ */
public async validateScan(scan: TrackScan): Promise<TrackScan> { public async validateScan(scan: TrackScan): Promise<TrackScan> {
const scans = await getConnection().getRepository(TrackScan).find({ where: { runner: scan.runner, valid: true }, relations: ["track"] }); const latestScan = await getConnection().getRepository(TrackScan).findOne({ where: { runner: scan.runner, valid: true }, relations: ["track"], order: { id: 'DESC' } });
if (scans.length == 0) { if (!latestScan) {
scan.lapTime = 0; scan.lapTime = 0;
scan.valid = true; scan.valid = true;
} }
else { else {
const newestScan = scans[scans.length - 1]; scan.lapTime = scan.timestamp - latestScan.timestamp;
scan.lapTime = scan.timestamp - newestScan.timestamp;
scan.valid = (scan.lapTime > scan.track.minimumLapTime); scan.valid = (scan.lapTime > scan.track.minimumLapTime);
} }
return scan; return scan;

View File

@ -52,13 +52,7 @@ export class RunnerCard {
* Generates a ean-13 compliant string for barcode generation. * Generates a ean-13 compliant string for barcode generation.
*/ */
public get code(): string { public get code(): string {
const multiply = [1, 3]; return this.paddedId
let total = 0;
this.paddedId.split('').forEach((letter, index) => {
total += parseInt(letter, 10) * multiply[index % 2];
});
const checkSum = (Math.ceil(total / 10) * 10) - total;
return this.paddedId + checkSum.toString();
} }
/** /**

View File

@ -53,7 +53,9 @@ export class ResponseDonation implements IResponse {
*/ */
public constructor(donation: Donation) { public constructor(donation: Donation) {
this.id = donation.id; this.id = donation.id;
this.donor = donation.donor.toResponse(); if (donation.donor) {
this.donor = donation.donor.toResponse();
}
this.amount = donation.amount; this.amount = donation.amount;
this.paidAmount = donation.paidAmount || 0; this.paidAmount = donation.paidAmount || 0;
if (this.paidAmount < this.amount) { if (this.paidAmount < this.amount) {

View File

@ -4,6 +4,7 @@ import {
import { Donor } from '../entities/Donor'; import { Donor } from '../entities/Donor';
import { ResponseObjectType } from '../enums/ResponseObjectType'; import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse'; import { IResponse } from './IResponse';
import { ResponseDonation } from './ResponseDonation';
import { ResponseParticipant } from './ResponseParticipant'; import { ResponseParticipant } from './ResponseParticipant';
/** /**
@ -34,6 +35,8 @@ export class ResponseDonor extends ResponseParticipant implements IResponse {
@IsInt() @IsInt()
paidDonationAmount: number; paidDonationAmount: number;
donations: Array<ResponseDonation>;
/** /**
* Creates a ResponseRunner object from a runner. * Creates a ResponseRunner object from a runner.
* @param runner The user the response shall be build for. * @param runner The user the response shall be build for.
@ -43,5 +46,11 @@ export class ResponseDonor extends ResponseParticipant implements IResponse {
this.receiptNeeded = donor.receiptNeeded; this.receiptNeeded = donor.receiptNeeded;
this.donationAmount = donor.donationAmount; this.donationAmount = donor.donationAmount;
this.paidDonationAmount = donor.paidDonationAmount; this.paidDonationAmount = donor.paidDonationAmount;
this.donations = new Array<ResponseDonation>();
if (donor.donations?.length > 0) {
for (const donation of donor.donations) {
this.donations.push(donation.toResponse())
}
}
} }
} }

View File

@ -1,4 +1,4 @@
import { IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator"; import { IsInt, IsNotEmpty, IsNumber, IsObject, IsOptional, IsString } from "class-validator";
import { RunnerGroup } from '../entities/RunnerGroup'; import { RunnerGroup } from '../entities/RunnerGroup';
import { ResponseObjectType } from '../enums/ResponseObjectType'; import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse'; import { IResponse } from './IResponse';
@ -36,6 +36,10 @@ export abstract class ResponseRunnerGroup implements IResponse {
@IsOptional() @IsOptional()
contact?: ResponseGroupContact; contact?: ResponseGroupContact;
@IsOptional()
@IsNumber()
total_distance: number
/** /**
* Creates a ResponseRunnerGroup object from a runnerGroup. * Creates a ResponseRunnerGroup object from a runnerGroup.
* @param group The runnerGroup the response shall be build for. * @param group The runnerGroup the response shall be build for.
@ -44,5 +48,6 @@ export abstract class ResponseRunnerGroup implements IResponse {
this.id = group.id; this.id = group.id;
this.name = group.name; this.name = group.name;
if (group.contact) { this.contact = group.contact.toResponse(); }; if (group.contact) { this.contact = group.contact.toResponse(); };
if (group.runners) { this.total_distance = group.runners.reduce((p, c) => p + c.distance, 0) }
} }
} }

View File

@ -67,6 +67,9 @@ export class ResponseRunnerOrganization extends ResponseRunnerGroup implements I
for (let team of org.teams) { for (let team of org.teams) {
this.teams.push(team.toResponse()); this.teams.push(team.toResponse());
} }
for (const team of this.teams) {
this.total_distance += team.total_distance;
}
} }
if (!org.key) { this.registrationEnabled = false; } if (!org.key) { this.registrationEnabled = false; }

View File

@ -2,11 +2,6 @@ import {
IsInt IsInt
} from "class-validator"; } from "class-validator";
import { Donation } from '../entities/Donation'; import { Donation } from '../entities/Donation';
import { Runner } from '../entities/Runner';
import { RunnerOrganization } from '../entities/RunnerOrganization';
import { RunnerTeam } from '../entities/RunnerTeam';
import { Scan } from '../entities/Scan';
import { User } from '../entities/User';
import { ResponseObjectType } from '../enums/ResponseObjectType'; import { ResponseObjectType } from '../enums/ResponseObjectType';
import { IResponse } from './IResponse'; import { IResponse } from './IResponse';
@ -63,12 +58,30 @@ export class ResponseStats implements IResponse {
@IsInt() @IsInt()
total_donation: number; total_donation: number;
/**
* The total donation count (cent).
*/
@IsInt()
total_donations: number;
/**
* The total donor count.
*/
@IsInt()
total_donors: number;
/** /**
* The average distance ran per runner. * The average distance ran per runner.
*/ */
@IsInt() @IsInt()
average_distance: number; average_distance: number;
/**
* The average donation per distance (cent).
*/
@IsInt()
average_donation: number;
/** /**
* Creates a new stats response containing some basic statistics for a dashboard or public display. * Creates a new stats response containing some basic statistics for a dashboard or public display.
* @param runners Array containing all runners - the following relations have to be resolved: scans, scans.track * @param runners Array containing all runners - the following relations have to be resolved: scans, scans.track
@ -78,14 +91,17 @@ export class ResponseStats implements IResponse {
* @param scans Array containing all scans - no relations have to be resolved. * @param scans Array containing all scans - no relations have to be resolved.
* @param donations Array containing all donations - the following relations have to be resolved: runner, runner.scans, runner.scans.track * @param donations Array containing all donations - the following relations have to be resolved: runner, runner.scans, runner.scans.track
*/ */
public constructor(runners: Runner[], teams: RunnerTeam[], orgs: RunnerOrganization[], users: User[], scans: Scan[], donations: Donation[]) { public constructor(runners: number, teams: number, orgs: number, users: number, scans: number, donations: Donation[], distance: number, donors: number) {
this.total_runners = runners.length; this.total_runners = runners;
this.total_teams = teams.length; this.total_teams = teams;
this.total_orgs = orgs.length; this.total_orgs = orgs;
this.total_users = users.length; this.total_users = users;
this.total_scans = scans.filter(scan => { scan.valid === true }).length; this.total_scans = scans;
this.total_distance = runners.reduce((sum, current) => sum + current.distance, 0); this.total_distance = distance;
this.total_donation = donations.reduce((sum, current) => sum + current.amount, 0); this.total_donation = donations.reduce((sum, current) => sum + current.amount, 0);
this.total_donations = donations.length;
this.average_donation = this.total_donation / this.total_donations
this.total_donors = donors;
this.average_distance = this.total_distance / this.total_runners; this.average_distance = this.total_distance / this.total_runners;
} }
} }

View File

@ -11,7 +11,7 @@ import { PermissionAction } from '../models/enums/PermissionAction';
import { PermissionTarget } from '../models/enums/PermissionTargets'; import { PermissionTarget } from '../models/enums/PermissionTargets';
/** /**
* Seeds a admin group with a demo user into the database for initial setup and auto recovery. * Seeds a admin group with a demo user into the database for initial setup and auto recovery.
* We know that the nameing isn't perfectly fitting. Feel free to change it. * We know that the naming isn't perfectly fitting. Feel free to change it.
*/ */
export default class SeedUsers implements Seeder { export default class SeedUsers implements Seeder {
public async run(factory: Factory, connection: Connection): Promise<any> { public async run(factory: Factory, connection: Connection): Promise<any> {

View File

@ -0,0 +1,19 @@
import axios from 'axios';
async function main() {
console.time("batches")
for (let i = 0; i < 100; i++) {
const batch = [];
for (let i = 0; i < 6; i++) {
batch.push(axios.post('http://localhost:4010/api/scans/trackscans', { card: 200000000001, station: 2 }, {
headers: {
Authorization: 'Bearer 10F2E64.BB4F6CC5-2148-4CCF-88B5-0AA85D0508A9'
}
}))
}
await Promise.all(batch)
console.timeLog("batches", `Finished batch ${i}`)
}
console.timeEnd("batches")
}
main();

View File

@ -66,6 +66,8 @@ describe('adding + getting scans', () => {
const res = await axios.get(base + '/api/scans/' + added_scan.id, axios_config); const res = await axios.get(base + '/api/scans/' + added_scan.id, axios_config);
expect(res.status).toEqual(200); expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json"); expect(res.headers['content-type']).toContain("application/json");
delete res.data.runner.distance;
delete added_scan.runner.distance;
expect(res.data).toEqual(added_scan); expect(res.data).toEqual(added_scan);
}); });
it('check if scans was added via the runner/scans endpoint.', async () => { it('check if scans was added via the runner/scans endpoint.', async () => {

Binary file not shown.