Merge pull request 'Alpha Release 0.1.1 - Hotfix release' (#106) from dev into main
All checks were successful
continuous-integration/drone/tag Build is passing
continuous-integration/drone/push Build is passing

Reviewed-on: #106
Reviewed-by: Philipp Dormann <philipp@philippdormann.de>
This commit is contained in:
Nicolai Ort 2021-01-16 20:32:39 +00:00
commit 7533c349ef
34 changed files with 1877 additions and 1667 deletions

View File

@ -90,11 +90,20 @@ trigger:
kind: pipeline kind: pipeline
type: docker type: docker
name: build:latest name: build:latest
clone:
disable: true
steps: steps:
- name: clone
image: alpine/git
commands:
- git clone $DRONE_REMOTE_URL .
- git checkout dev
- git merge main
- git checkout main
- name: build latest - name: build latest
depends_on: ["clone"]
image: plugins/docker image: plugins/docker
depends_on: [clone]
settings: settings:
username: username:
from_secret: DOCKER_REGISTRY_USER from_secret: DOCKER_REGISTRY_USER
@ -104,6 +113,15 @@ steps:
tags: tags:
- latest - latest
registry: registry.odit.services registry: registry.odit.services
- name: push merge to repo
depends_on: ["clone"]
image: appleboy/drone-git-push
settings:
branch: dev
commit: false
remote: git@git.odit.services:lfk/backend.git
ssh_key:
from_secret: GITLAB_SSHKEY
trigger: trigger:
branch: branch:

View File

@ -2,8 +2,32 @@
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.
#### [v0.1.1](https://git.odit.services/lfk/backend/compare/v0.1.0...v0.1.1)
- 🚀Bumped version to v0.1.1 [`9445c6f`](https://git.odit.services/lfk/backend/commit/9445c6f21e376329b9200664a44a94ba1f1dd463)
- 🧾New changelog file version [CI SKIP] [skip ci] [`1b9d296`](https://git.odit.services/lfk/backend/commit/1b9d2969ebdca4dca84898b1e8307be7b781b90b)
- Implemented the /me controller that allows a user to get and update themselves [`8ef5f90`](https://git.odit.services/lfk/backend/commit/8ef5f90abda97a73d5c5a7767a144ac3fb5288c1)
- Implemented a baisc user checker/getter [`f1db883`](https://git.odit.services/lfk/backend/commit/f1db8836092269966a7f54e69b1f20c171e81b21)
- Implemented getting own permissions [`4f6e816`](https://git.odit.services/lfk/backend/commit/4f6e81677c81c852e735407295c634b43b317479)
- Hotfix: Missing relation bug [`6e6979c`](https://git.odit.services/lfk/backend/commit/6e6979cfe3660056cff6b9eabc194852234ac0a6)
- Hotfix: Missing relation bug [`b167ba0`](https://git.odit.services/lfk/backend/commit/b167ba07f79709a2c3b33c5546c52659c42863f3)
- automaticly merge main into dev after building a latest image [`02efb9a`](https://git.odit.services/lfk/backend/commit/02efb9a8e55831ecce4109e17b2f07a56e491fd5)
- User deletion now requires confirmation [`6b7ecd3`](https://git.odit.services/lfk/backend/commit/6b7ecd3044c45b2eed46ee5010bed4dab4f02df9)
- 🧾New changelog file version [CI SKIP] [skip ci] [`3766899`](https://git.odit.services/lfk/backend/commit/3766899c8393545a89986a98dafd542edc4a1d39)
- 🧾New changelog file version [CI SKIP] [skip ci] [`6febb99`](https://git.odit.services/lfk/backend/commit/6febb994990b4cab7ee54b0368f74dd95664bfdf)
- 🧾New changelog file version [CI SKIP] [skip ci] [`de36a24`](https://git.odit.services/lfk/backend/commit/de36a24191a8cdc4ff6b23637ea9f91109b59bbb)
- Merge pull request 'User self-management feature/100-me_endpoints' (#103) from feature/100-me_endpoints into dev [`a6c7d54`](https://git.odit.services/lfk/backend/commit/a6c7d54fe72ffe23add926afa0be150a7a370099)
- Created barebones file for the userchecker [`e586a11`](https://git.odit.services/lfk/backend/commit/e586a11e2ad42af9c9bb5d2a47f48e3306fe49b2)
- Updated descriptions and responses [`fc7b8f4`](https://git.odit.services/lfk/backend/commit/fc7b8f4c16cef0e72b04f096d5a17d4144b5feb7)
- 🧾New changelog file version [CI SKIP] [skip ci] [`50b893f`](https://git.odit.services/lfk/backend/commit/50b893f5370902ccc40f8bb45ed160103400f529)
- Moved the me endpoints to /users/me [`f9834b5`](https://git.odit.services/lfk/backend/commit/f9834b5f4d80b11ee5f7773b339dd421341c6e7f)
- Moved optional param to being optional [`a334adf`](https://git.odit.services/lfk/backend/commit/a334adffc6d07c8ab340263123e00a96f21acecb)
#### [v0.1.0](https://git.odit.services/lfk/backend/compare/v0.0.12...v0.1.0) #### [v0.1.0](https://git.odit.services/lfk/backend/compare/v0.0.12...v0.1.0)
> 15 January 2021
- Merge pull request 'First feature version 0.1.0' (#102) from dev into main [`38b9a77`](https://git.odit.services/lfk/backend/commit/38b9a772cd2d1c1e6298ae449d07db7c555a00e9)
- Removed useless parts from functions and updated comments [`c05834f`](https://git.odit.services/lfk/backend/commit/c05834f2a13eb838efbf61be803e4e320561718e) - Removed useless parts from functions and updated comments [`c05834f`](https://git.odit.services/lfk/backend/commit/c05834f2a13eb838efbf61be803e4e320561718e)
- Switched tests over to the new id-only schema [`d88fb18`](https://git.odit.services/lfk/backend/commit/d88fb183198e66cadf5290c1ef7b7e4ccedad4f0) - Switched tests over to the new id-only schema [`d88fb18`](https://git.odit.services/lfk/backend/commit/d88fb183198e66cadf5290c1ef7b7e4ccedad4f0)
- 🧾New changelog file version [CI SKIP] [skip ci] [`0e119e4`](https://git.odit.services/lfk/backend/commit/0e119e48340cd0a602a08da727b480aa2fe5500c) - 🧾New changelog file version [CI SKIP] [skip ci] [`0e119e4`](https://git.odit.services/lfk/backend/commit/0e119e48340cd0a602a08da727b480aa2fe5500c)
@ -24,6 +48,7 @@ All notable changes to this project will be documented in this file. Dates are d
- 🧾New changelog file version [CI SKIP] [skip ci] [`dc6ad9c`](https://git.odit.services/lfk/backend/commit/dc6ad9cdd3d8f29ef9a15bf7ac61c7c55c57e9fb) - 🧾New changelog file version [CI SKIP] [skip ci] [`dc6ad9c`](https://git.odit.services/lfk/backend/commit/dc6ad9cdd3d8f29ef9a15bf7ac61c7c55c57e9fb)
- 🧾New changelog file version [CI SKIP] [skip ci] [`d1a0bed`](https://git.odit.services/lfk/backend/commit/d1a0bed00e01a0e9d8ba1165e3c6ca3dd910bd00) - 🧾New changelog file version [CI SKIP] [skip ci] [`d1a0bed`](https://git.odit.services/lfk/backend/commit/d1a0bed00e01a0e9d8ba1165e3c6ca3dd910bd00)
- Clarified comments [`1b799a6`](https://git.odit.services/lfk/backend/commit/1b799a697305791c3f67ac4a738c7287d1ac553e) - Clarified comments [`1b799a6`](https://git.odit.services/lfk/backend/commit/1b799a697305791c3f67ac4a738c7287d1ac553e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`6184304`](https://git.odit.services/lfk/backend/commit/618430433d03012c2cad5be6021cf1ea8fdf9624)
- 🧾New changelog file version [CI SKIP] [skip ci] [`8218a45`](https://git.odit.services/lfk/backend/commit/8218a452bdf7550ec1eed2b0045e94ea4ae91d31) - 🧾New changelog file version [CI SKIP] [skip ci] [`8218a45`](https://git.odit.services/lfk/backend/commit/8218a452bdf7550ec1eed2b0045e94ea4ae91d31)
- 🚀Bumped version to v0.1.0 [`80c5f9b`](https://git.odit.services/lfk/backend/commit/80c5f9b84de355b4408dcffd632589a9a0e4ad2e) - 🚀Bumped version to v0.1.0 [`80c5f9b`](https://git.odit.services/lfk/backend/commit/80c5f9b84de355b4408dcffd632589a9a0e4ad2e)
- 🧾New changelog file version [CI SKIP] [skip ci] [`79f46cb`](https://git.odit.services/lfk/backend/commit/79f46cb745e4cb4bdac7dbb6c6c2b8fdc9867592) - 🧾New changelog file version [CI SKIP] [skip ci] [`79f46cb`](https://git.odit.services/lfk/backend/commit/79f46cb745e4cb4bdac7dbb6c6c2b8fdc9867592)

View File

@ -1,6 +1,6 @@
{ {
"name": "@odit/lfk-backend", "name": "@odit/lfk-backend",
"version": "0.1.0", "version": "0.1.1",
"main": "src/app.ts", "main": "src/app.ts",
"repository": "https://git.odit.services/lfk/backend", "repository": "https://git.odit.services/lfk/backend",
"author": { "author": {

View File

@ -5,10 +5,12 @@ import { config, e as errors } from './config';
import loaders from "./loaders/index"; import loaders from "./loaders/index";
import authchecker from "./middlewares/authchecker"; import authchecker from "./middlewares/authchecker";
import { ErrorHandler } from './middlewares/ErrorHandler'; import { ErrorHandler } from './middlewares/ErrorHandler';
import UserChecker from './middlewares/UserChecker';
const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts'; const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts';
const app = createExpressServer({ const app = createExpressServer({
authorizationChecker: authchecker, authorizationChecker: authchecker,
currentUserChecker: UserChecker,
middlewares: [ErrorHandler], middlewares: [ErrorHandler],
development: config.development, development: config.development,
cors: true, cors: true,

View File

@ -0,0 +1,86 @@
import { Body, CurrentUser, Delete, Get, JsonController, OnUndefined, Put, QueryParam } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors';
import { UpdateUser } from '../models/actions/update/UpdateUser';
import { User } from '../models/entities/User';
import { ResponseUser } from '../models/responses/ResponseUser';
import { ResponseUserPermissions } from '../models/responses/ResponseUserPermissions';
import { PermissionController } from './PermissionController';
@JsonController('/users/me')
@OpenAPI({ security: [{ "AuthToken": [] }, { "RefreshTokenCookie": [] }] })
export class MeController {
private userRepository: Repository<User>;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.userRepository = getConnectionManager().get().getRepository(User);
}
@Get('/')
@ResponseSchema(ResponseUser)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@OnUndefined(UserNotFoundError)
@OpenAPI({ description: 'Lists all information about yourself.' })
async get(@CurrentUser() currentUser: User) {
let user = await this.userRepository.findOne({ id: currentUser.id }, { relations: ['permissions', 'groups', 'groups.permissions', 'permissions.principal', 'groups.permissions.principal'] })
if (!user) { throw new UserNotFoundError(); }
return new ResponseUser(user);
}
@Get('/')
@ResponseSchema(ResponseUserPermissions)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@OnUndefined(UserNotFoundError)
@OpenAPI({ description: 'Lists all permissions granted to the you sorted into directly granted and inherited as permission response objects.' })
async getPermissions(@CurrentUser() currentUser: User) {
let user = await this.userRepository.findOne({ id: currentUser.id }, { relations: ['permissions', 'groups', 'groups.permissions', 'permissions.principal', 'groups.permissions.principal'] })
if (!user) { throw new UserNotFoundError(); }
return new ResponseUserPermissions(user);
}
@Put('/')
@ResponseSchema(ResponseUser)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UserIdsNotMatchingError, { statusCode: 406 })
@ResponseSchema(UsernameContainsIllegalCharacterError, { statusCode: 406 })
@OpenAPI({ description: "Update the yourself. <br> You can't edit your own permissions or group memberships here - Please use the /api/users/:id enpoint instead. <br> Please remember that ids can't be changed." })
async put(@CurrentUser() currentUser: User, @Body({ validate: true }) updateUser: UpdateUser) {
let oldUser = await this.userRepository.findOne({ id: currentUser.id }, { relations: ['groups'] });
updateUser.groups = oldUser.groups.map(g => g.id);
if (!oldUser) {
throw new UserNotFoundError();
}
if (oldUser.id != updateUser.id) {
throw new UserIdsNotMatchingError();
}
await this.userRepository.save(await updateUser.update(oldUser));
return new ResponseUser(await this.userRepository.findOne({ id: currentUser.id }, { relations: ['permissions', 'groups', 'groups.permissions'] }));
}
@Delete('/')
@ResponseSchema(ResponseUser)
@ResponseSchema(UserNotFoundError, { statusCode: 404 })
@ResponseSchema(UserDeletionNotConfirmedError, { statusCode: 406 })
@OpenAPI({ description: 'Delete yourself. <br> You have to confirm your decision by providing the ?force=true query param. <br> If there are any permissions directly granted to you they will get deleted as well.' })
async remove(@CurrentUser() currentUser: User, @QueryParam("force") force: boolean) {
if (!force) { throw new UserDeletionNotConfirmedError; }
if (!currentUser) { return UserNotFoundError; }
const responseUser = await this.userRepository.findOne({ id: currentUser.id }, { relations: ['permissions', 'groups', 'groups.permissions'] });;
const permissionControler = new PermissionController();
for (let permission of responseUser.permissions) {
await permissionControler.remove(permission.id, true);
}
await this.userRepository.delete(currentUser);
return new ResponseUser(responseUser);
}
}

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 { getConnectionManager, Repository } from 'typeorm';
import { UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } from '../errors/UserErrors'; import { UserDeletionNotConfirmedError, UserIdsNotMatchingError, UsernameContainsIllegalCharacterError, UserNotFoundError } 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';
@ -105,9 +105,11 @@ export class UserController {
@Authorized("USER:DELETE") @Authorized("USER:DELETE")
@ResponseSchema(ResponseUser) @ResponseSchema(ResponseUser)
@ResponseSchema(ResponseEmpty, { statusCode: 204 }) @ResponseSchema(ResponseEmpty, { statusCode: 204 })
@ResponseSchema(UserDeletionNotConfirmedError, { statusCode: 406 })
@OnUndefined(204) @OnUndefined(204)
@OpenAPI({ description: 'Delete the user whose id you provided. <br> If there are any permissions directly granted to the user they will get deleted as well. <br> If no user with this id exists it will just return 204(no content).' }) @OpenAPI({ description: 'Delete the user whose id you provided. <br> You have to confirm your decision by providing the ?force=true query param. <br> If there are any permissions directly granted to the user they will get deleted as well. <br> If no user with this id exists it will just return 204(no content).' })
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
if (!force) { throw new UserDeletionNotConfirmedError; }
let user = await this.userRepository.findOne({ id: id }); let user = await this.userRepository.findOne({ id: id });
if (!user) { return null; } if (!user) { return null; }
const responseUser = await this.userRepository.findOne({ id: id }, { relations: ['permissions', 'groups', 'groups.permissions'] });; const responseUser = await this.userRepository.findOne({ id: id }, { relations: ['permissions', 'groups', 'groups.permissions'] });;

View File

@ -1,24 +1,24 @@
import { IsString } from 'class-validator'; import { IsString } from 'class-validator';
import { NotAcceptableError, NotFoundError } from 'routing-controllers'; import { NotAcceptableError, NotFoundError } from 'routing-controllers';
/** /**
* Error to throw, when to provided address doesn't belong to the accepted types. * Error to throw, when to provided address doesn't belong to the accepted types.
*/ */
export class AddressWrongTypeError extends NotAcceptableError { export class AddressWrongTypeError extends NotAcceptableError {
@IsString() @IsString()
name = "AddressWrongTypeError" name = "AddressWrongTypeError"
@IsString() @IsString()
message = "The address must be an existing address's id. \n You provided a object of another type." message = "The address must be an existing address's id. \n You provided a object of another type."
} }
/** /**
* Error to throw, when a non-existent address get's loaded. * Error to throw, when a non-existent address get's loaded.
*/ */
export class AddressNotFoundError extends NotFoundError { export class AddressNotFoundError extends NotFoundError {
@IsString() @IsString()
name = "AddressNotFoundError" name = "AddressNotFoundError"
@IsString() @IsString()
message = "The address you provided couldn't be located in the system. \n Please check your request." message = "The address you provided couldn't be located in the system. \n Please check your request."
} }

View File

@ -59,4 +59,16 @@ export class UserIdsNotMatchingError extends NotAcceptableError {
@IsString() @IsString()
message = "The ids don't match!! \n And if you wanted to change a user's id: This isn't allowed!" message = "The ids don't match!! \n And if you wanted to change a user's id: This isn't allowed!"
}
/**
* Error to throw when two users' ids don't match.
* Usually occurs when a user tries to change a user's id.
*/
export class UserDeletionNotConfirmedError extends NotAcceptableError {
@IsString()
name = "UserDeletionNotConfirmedError"
@IsString()
message = "You are trying to delete a user! \n If you're sure about doing this: provide the ?force=true query param."
} }

View File

@ -0,0 +1,58 @@
import cookie from "cookie";
import * as jwt from "jsonwebtoken";
import { Action } from 'routing-controllers';
import { getConnectionManager } from 'typeorm';
import { config } from '../config';
import { IllegalJWTError, UserDisabledError, UserNonexistantOrRefreshtokenInvalidError } from '../errors/AuthError';
import { JwtCreator, JwtUser } from '../jwtcreator';
import { User } from '../models/entities/User';
/**
* TODO:
*/
const UserChecker = async (action: Action) => {
let jwtPayload = undefined
try {
let provided_token = "" + action.request.headers["authorization"].replace("Bearer ", "");
jwtPayload = <any>jwt.verify(provided_token, config.jwt_secret);
jwtPayload = jwtPayload["userdetails"];
} catch (error) {
jwtPayload = await refresh(action);
}
const user = await getConnectionManager().get().getRepository(User).findOne({ id: jwtPayload["id"], refreshTokenCount: jwtPayload["refreshTokenCount"] })
if (!user) { throw new UserNonexistantOrRefreshtokenInvalidError() }
if (user.enabled == false) { throw new UserDisabledError(); }
return user;
};
/**
* Handles soft-refreshing of access-tokens.
* @param action Routing-Controllers action object that provides request and response objects among other stuff.
*/
const refresh = async (action: Action) => {
let refresh_token = undefined;
try {
refresh_token = cookie.parse(action.request.headers["cookie"])["lfk_backend__refresh_token"];
}
catch {
throw new IllegalJWTError();
}
let jwtPayload = undefined;
try {
jwtPayload = <any>jwt.verify(refresh_token, config.jwt_secret);
} catch (error) {
throw new IllegalJWTError();
}
const user = await getConnectionManager().get().getRepository(User).findOne({ id: jwtPayload["id"], refreshTokenCount: jwtPayload["refreshTokenCount"] }, { relations: ['permissions', 'groups', 'groups.permissions'] })
if (!user) { throw new UserNonexistantOrRefreshtokenInvalidError() }
if (user.enabled == false) { throw new UserDisabledError(); }
let newAccess = JwtCreator.createAccess(user);
action.response.header("authorization", "Bearer " + newAccess);
return await new JwtUser(user);
}
export default UserChecker;

View File

@ -1,69 +1,69 @@
import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator';
import { config } from '../../../config'; import { config } from '../../../config';
import { Address } from '../../entities/Address'; import { Address } from '../../entities/Address';
/** /**
* This classed is used to create a new Address entity from a json body (post request). * This classed is used to create a new Address entity from a json body (post request).
*/ */
export class CreateAddress { export class CreateAddress {
/** /**
* The newaddress's description. * The newaddress's description.
*/ */
@IsString() @IsString()
@IsOptional() @IsOptional()
description?: string; description?: string;
/** /**
* The new address's first line. * The new address's first line.
* Containing the street and house number. * Containing the street and house number.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
address1: string; address1: string;
/** /**
* The new address's second line. * The new address's second line.
* Containing optional information. * Containing optional information.
*/ */
@IsString() @IsString()
@IsOptional() @IsOptional()
address2?: string; address2?: string;
/** /**
* The new address's postal code. * The new address's postal code.
* This will get checked against the postal code syntax for the configured country. * This will get checked against the postal code syntax for the configured country.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@IsPostalCode(config.postalcode_validation_countrycode) @IsPostalCode(config.postalcode_validation_countrycode)
postalcode: string; postalcode: string;
/** /**
* The new address's city. * The new address's city.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
city: string; city: string;
/** /**
* The new address's country. * The new address's country.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
country: string; country: string;
/** /**
* Creates a new Address entity from this. * Creates a new Address entity from this.
*/ */
public async toEntity(): Promise<Address> { public async toEntity(): Promise<Address> {
let newAddress: Address = new Address(); let newAddress: Address = new Address();
newAddress.address1 = this.address1; newAddress.address1 = this.address1;
newAddress.address2 = this.address2; newAddress.address2 = this.address2;
newAddress.postalcode = this.postalcode; newAddress.postalcode = this.postalcode;
newAddress.city = this.city; newAddress.city = this.city;
newAddress.country = this.country; newAddress.country = this.country;
return newAddress; return newAddress;
} }
} }

View File

@ -1,38 +1,38 @@
import { IsBoolean, IsOptional } from 'class-validator'; import { IsBoolean, IsOptional } from 'class-validator';
import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors';
import { Donor } from '../../entities/Donor'; import { Donor } from '../../entities/Donor';
import { CreateParticipant } from './CreateParticipant'; import { CreateParticipant } from './CreateParticipant';
/** /**
* This classed is used to create a new Donor entity from a json body (post request). * This classed is used to create a new Donor entity from a json body (post request).
*/ */
export class CreateDonor extends CreateParticipant { export class CreateDonor extends CreateParticipant {
/** /**
* Does this donor need a receipt? * Does this donor need a receipt?
*/ */
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
receiptNeeded?: boolean = false; receiptNeeded?: boolean = false;
/** /**
* Creates a new Donor entity from this. * Creates a new Donor entity from this.
*/ */
public async toEntity(): Promise<Donor> { public async toEntity(): Promise<Donor> {
let newDonor: Donor = new Donor(); let newDonor: Donor = new Donor();
newDonor.firstname = this.firstname; newDonor.firstname = this.firstname;
newDonor.middlename = this.middlename; newDonor.middlename = this.middlename;
newDonor.lastname = this.lastname; newDonor.lastname = this.lastname;
newDonor.phone = this.phone; newDonor.phone = this.phone;
newDonor.email = this.email; newDonor.email = this.email;
newDonor.address = await this.getAddress(); newDonor.address = await this.getAddress();
newDonor.receiptNeeded = this.receiptNeeded; newDonor.receiptNeeded = this.receiptNeeded;
if (this.receiptNeeded == true && this.address == null) { if (this.receiptNeeded == true && this.address == null) {
throw new DonorReceiptAddressNeededError() throw new DonorReceiptAddressNeededError()
} }
return newDonor; return newDonor;
} }
} }

View File

@ -1,78 +1,78 @@
import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { config } from '../../../config'; import { config } from '../../../config';
import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { AddressNotFoundError } from '../../../errors/AddressErrors';
import { Address } from '../../entities/Address'; import { Address } from '../../entities/Address';
import { GroupContact } from '../../entities/GroupContact'; import { GroupContact } from '../../entities/GroupContact';
/** /**
* This classed is used to create a new Group entity from a json body (post request). * This classed is used to create a new Group entity from a json body (post request).
*/ */
export class CreateGroupContact { export class CreateGroupContact {
/** /**
* The new contact's first name. * The new contact's first name.
*/ */
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
firstname: string; firstname: string;
/** /**
* The new contact's middle name. * The new contact's middle name.
*/ */
@IsOptional() @IsOptional()
@IsString() @IsString()
middlename?: string; middlename?: string;
/** /**
* The new contact's last name. * The new contact's last name.
*/ */
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
lastname: string; lastname: string;
/** /**
* The new contact's address's id. * The new contact's address's id.
*/ */
@IsInt() @IsInt()
@IsOptional() @IsOptional()
address?: number; address?: number;
/** /**
* The contact's phone number. * The contact's phone number.
* This will be validated against the configured country phone numer syntax (default: international). * This will be validated against the configured country phone numer syntax (default: international).
*/ */
@IsOptional() @IsOptional()
@IsPhoneNumber(config.phone_validation_countrycode) @IsPhoneNumber(config.phone_validation_countrycode)
phone?: string; phone?: string;
/** /**
* The contact's email address. * The contact's email address.
*/ */
@IsOptional() @IsOptional()
@IsEmail() @IsEmail()
email?: string; email?: string;
/** /**
* Gets the new contact's address by it's id. * Gets the new contact's address by it's id.
*/ */
public async getAddress(): Promise<Address> { public async getAddress(): Promise<Address> {
if (!this.address) { return null; } if (!this.address) { return null; }
let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address });
if (!address) { throw new AddressNotFoundError; } if (!address) { throw new AddressNotFoundError; }
return address; return address;
} }
/** /**
* Creates a new Address entity from this. * Creates a new Address entity from this.
*/ */
public async toEntity(): Promise<GroupContact> { public async toEntity(): Promise<GroupContact> {
let contact: GroupContact = new GroupContact(); let contact: GroupContact = new GroupContact();
contact.firstname = this.firstname; contact.firstname = this.firstname;
contact.middlename = this.middlename; contact.middlename = this.middlename;
contact.lastname = this.lastname; contact.lastname = this.lastname;
contact.email = this.email; contact.email = this.email;
contact.phone = this.phone; contact.phone = this.phone;
contact.address = await this.getAddress(); contact.address = await this.getAddress();
return null; return null;
} }
} }

View File

@ -1,65 +1,65 @@
import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { config } from '../../../config'; import { config } from '../../../config';
import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { AddressNotFoundError } from '../../../errors/AddressErrors';
import { Address } from '../../entities/Address'; import { Address } from '../../entities/Address';
/** /**
* This classed is used to create a new Participant entity from a json body (post request). * This classed is used to create a new Participant entity from a json body (post request).
*/ */
export abstract class CreateParticipant { export abstract class CreateParticipant {
/** /**
* The new participant's first name. * The new participant's first name.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
firstname: string; firstname: string;
/** /**
* The new participant's middle name. * The new participant's middle name.
*/ */
@IsString() @IsString()
@IsOptional() @IsOptional()
middlename?: string; middlename?: string;
/** /**
* The new participant's last name. * The new participant's last name.
*/ */
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
lastname: string; lastname: string;
/** /**
* The new participant's phone number. * The new participant's phone number.
* This will be validated against the configured country phone numer syntax (default: international). * This will be validated against the configured country phone numer syntax (default: international).
*/ */
@IsString() @IsString()
@IsOptional() @IsOptional()
@IsPhoneNumber(config.phone_validation_countrycode) @IsPhoneNumber(config.phone_validation_countrycode)
phone?: string; phone?: string;
/** /**
* The new participant's e-mail address. * The new participant's e-mail address.
*/ */
@IsString() @IsString()
@IsOptional() @IsOptional()
@IsEmail() @IsEmail()
email?: string; email?: string;
/** /**
* The new participant's address's id. * The new participant's address's id.
*/ */
@IsInt() @IsInt()
@IsOptional() @IsOptional()
address?: number; address?: number;
/** /**
* Gets the new participant's address by it's id. * Gets the new participant's address by it's id.
*/ */
public async getAddress(): Promise<Address> { public async getAddress(): Promise<Address> {
if (!this.address) { return null; } if (!this.address) { return null; }
let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address });
if (!address) { throw new AddressNotFoundError; } if (!address) { throw new AddressNotFoundError; }
return address; return address;
} }
} }

View File

@ -1,53 +1,53 @@
import { IsInt } from 'class-validator'; import { IsInt } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors';
import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors';
import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors';
import { Runner } from '../../entities/Runner'; import { Runner } from '../../entities/Runner';
import { RunnerGroup } from '../../entities/RunnerGroup'; import { RunnerGroup } from '../../entities/RunnerGroup';
import { CreateParticipant } from './CreateParticipant'; import { CreateParticipant } from './CreateParticipant';
/** /**
* This classed is used to create a new Runner entity from a json body (post request). * This classed is used to create a new Runner entity from a json body (post request).
*/ */
export class CreateRunner extends CreateParticipant { export class CreateRunner extends CreateParticipant {
/** /**
* The new runner's group's id. * The new runner's group's id.
*/ */
@IsInt() @IsInt()
group: number; group: number;
/** /**
* Creates a new Runner entity from this. * Creates a new Runner entity from this.
*/ */
public async toEntity(): Promise<Runner> { public async toEntity(): Promise<Runner> {
let newRunner: Runner = new Runner(); let newRunner: Runner = new Runner();
newRunner.firstname = this.firstname; newRunner.firstname = this.firstname;
newRunner.middlename = this.middlename; newRunner.middlename = this.middlename;
newRunner.lastname = this.lastname; newRunner.lastname = this.lastname;
newRunner.phone = this.phone; newRunner.phone = this.phone;
newRunner.email = this.email; newRunner.email = this.email;
newRunner.group = await this.getGroup(); newRunner.group = await this.getGroup();
newRunner.address = await this.getAddress(); newRunner.address = await this.getAddress();
return newRunner; return newRunner;
} }
/** /**
* Gets the new runner's group by it's id. * Gets the new runner's group by it's id.
*/ */
public async getGroup(): Promise<RunnerGroup> { public async getGroup(): Promise<RunnerGroup> {
if (this.group === undefined || this.group === null) { if (this.group === undefined || this.group === null) {
throw new RunnerTeamNeedsParentError(); throw new RunnerTeamNeedsParentError();
} }
if (!isNaN(this.group)) { if (!isNaN(this.group)) {
let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group });
if (!group) { throw new RunnerGroupNotFoundError; } if (!group) { throw new RunnerGroupNotFoundError; }
return group; return group;
} }
throw new RunnerOrganisationWrongTypeError; throw new RunnerOrganisationWrongTypeError;
} }
} }

View File

@ -1,41 +1,41 @@
import { IsInt, IsOptional } from 'class-validator'; import { IsInt, IsOptional } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { AddressNotFoundError } from '../../../errors/AddressErrors';
import { Address } from '../../entities/Address'; import { Address } from '../../entities/Address';
import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; import { RunnerOrganisation } from '../../entities/RunnerOrganisation';
import { CreateRunnerGroup } from './CreateRunnerGroup'; import { CreateRunnerGroup } from './CreateRunnerGroup';
/** /**
* This classed is used to create a new RunnerOrganisation entity from a json body (post request). * This classed is used to create a new RunnerOrganisation entity from a json body (post request).
*/ */
export class CreateRunnerOrganisation extends CreateRunnerGroup { export class CreateRunnerOrganisation extends CreateRunnerGroup {
/** /**
* The new organisation's address's id. * The new organisation's address's id.
*/ */
@IsInt() @IsInt()
@IsOptional() @IsOptional()
address?: number; address?: number;
/** /**
* Gets the org's address by it's id. * Gets the org's address by it's id.
*/ */
public async getAddress(): Promise<Address> { public async getAddress(): Promise<Address> {
if (!this.address) { return null; } if (!this.address) { return null; }
let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address });
if (!address) { throw new AddressNotFoundError; } if (!address) { throw new AddressNotFoundError; }
return address; return address;
} }
/** /**
* Creates a new RunnerOrganisation entity from this. * Creates a new RunnerOrganisation entity from this.
*/ */
public async toEntity(): Promise<RunnerOrganisation> { public async toEntity(): Promise<RunnerOrganisation> {
let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation();
newRunnerOrganisation.name = this.name; newRunnerOrganisation.name = this.name;
newRunnerOrganisation.contact = await this.getContact(); newRunnerOrganisation.contact = await this.getContact();
newRunnerOrganisation.address = await this.getAddress(); newRunnerOrganisation.address = await this.getAddress();
return newRunnerOrganisation; return newRunnerOrganisation;
} }
} }

View File

@ -1,44 +1,44 @@
import { IsBoolean, IsInt, IsOptional } from 'class-validator'; import { IsBoolean, IsInt, IsOptional } from 'class-validator';
import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors';
import { Donor } from '../../entities/Donor'; import { Donor } from '../../entities/Donor';
import { CreateParticipant } from '../create/CreateParticipant'; import { CreateParticipant } from '../create/CreateParticipant';
/** /**
* This class is used to update a Donor entity (via put request). * This class is used to update a Donor entity (via put request).
*/ */
export class UpdateDonor extends CreateParticipant { export class UpdateDonor extends CreateParticipant {
/** /**
* The updated donor's id. * The updated donor's id.
* This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to).
*/ */
@IsInt() @IsInt()
id: number; id: number;
/** /**
* Does the updated donor need a receipt? * Does the updated donor need a receipt?
*/ */
@IsBoolean() @IsBoolean()
@IsOptional() @IsOptional()
receiptNeeded?: boolean; receiptNeeded?: boolean;
/** /**
* Updates a provided Donor entity based on this. * Updates a provided Donor entity based on this.
*/ */
public async update(donor: Donor): Promise<Donor> { public async update(donor: Donor): Promise<Donor> {
donor.firstname = this.firstname; donor.firstname = this.firstname;
donor.middlename = this.middlename; donor.middlename = this.middlename;
donor.lastname = this.lastname; donor.lastname = this.lastname;
donor.phone = this.phone; donor.phone = this.phone;
donor.email = this.email; donor.email = this.email;
donor.receiptNeeded = this.receiptNeeded; donor.receiptNeeded = this.receiptNeeded;
donor.address = await this.getAddress(); donor.address = await this.getAddress();
if (this.receiptNeeded == true && this.address == null) { if (this.receiptNeeded == true && this.address == null) {
throw new DonorReceiptAddressNeededError() throw new DonorReceiptAddressNeededError()
} }
return donor; return donor;
} }
} }

View File

@ -1,54 +1,54 @@
import { IsInt, IsPositive } from 'class-validator'; import { IsInt, IsPositive } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors';
import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors';
import { Runner } from '../../entities/Runner'; import { Runner } from '../../entities/Runner';
import { RunnerGroup } from '../../entities/RunnerGroup'; import { RunnerGroup } from '../../entities/RunnerGroup';
import { CreateParticipant } from '../create/CreateParticipant'; import { CreateParticipant } from '../create/CreateParticipant';
/** /**
* This class is used to update a Runner entity (via put request). * This class is used to update a Runner entity (via put request).
*/ */
export class UpdateRunner extends CreateParticipant { export class UpdateRunner extends CreateParticipant {
/** /**
* The updated runner's id. * The updated runner's id.
* This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to).
*/ */
@IsInt() @IsInt()
id: number; id: number;
/** /**
* The updated runner's group's id. * The updated runner's group's id.
*/ */
@IsInt() @IsInt()
@IsPositive() @IsPositive()
group: number; group: number;
/** /**
* Updates a provided Runner entity based on this. * Updates a provided Runner entity based on this.
*/ */
public async update(runner: Runner): Promise<Runner> { public async update(runner: Runner): Promise<Runner> {
runner.firstname = this.firstname; runner.firstname = this.firstname;
runner.middlename = this.middlename; runner.middlename = this.middlename;
runner.lastname = this.lastname; runner.lastname = this.lastname;
runner.phone = this.phone; runner.phone = this.phone;
runner.email = this.email; runner.email = this.email;
runner.group = await this.getGroup(); runner.group = await this.getGroup();
runner.address = await this.getAddress(); runner.address = await this.getAddress();
return runner; return runner;
} }
/** /**
* Loads the updated runner's group based on it's id. * Loads the updated runner's group based on it's id.
*/ */
public async getGroup(): Promise<RunnerGroup> { public async getGroup(): Promise<RunnerGroup> {
if (this.group === undefined || this.group === null) { if (this.group === undefined || this.group === null) {
throw new RunnerTeamNeedsParentError(); throw new RunnerTeamNeedsParentError();
} }
let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group });
if (!group) { throw new RunnerGroupNotFoundError; } if (!group) { throw new RunnerGroupNotFoundError; }
return group; return group;
} }
} }

View File

@ -1,48 +1,48 @@
import { IsInt, IsOptional } from 'class-validator'; import { IsInt, IsOptional } from 'class-validator';
import { getConnectionManager } from 'typeorm'; import { getConnectionManager } from 'typeorm';
import { AddressNotFoundError } from '../../../errors/AddressErrors'; import { AddressNotFoundError } from '../../../errors/AddressErrors';
import { Address } from '../../entities/Address'; import { Address } from '../../entities/Address';
import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; import { RunnerOrganisation } from '../../entities/RunnerOrganisation';
import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; import { CreateRunnerGroup } from '../create/CreateRunnerGroup';
/** /**
* This class is used to update a RunnerOrganisation entity (via put request). * This class is used to update a RunnerOrganisation entity (via put request).
*/ */
export class UpdateRunnerOrganisation extends CreateRunnerGroup { export class UpdateRunnerOrganisation extends CreateRunnerGroup {
/** /**
* The updated orgs's id. * The updated orgs's id.
* This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to). * This shouldn't have changed but it is here in case anyone ever wants to enable id changes (whyever they would want to).
*/ */
@IsInt() @IsInt()
id: number; id: number;
/** /**
* The updated organisation's address's id. * The updated organisation's address's id.
*/ */
@IsInt() @IsInt()
@IsOptional() @IsOptional()
address?: number; address?: number;
/** /**
* Loads the organisation's address based on it's id. * Loads the organisation's address based on it's id.
*/ */
public async getAddress(): Promise<Address> { public async getAddress(): Promise<Address> {
if (!this.address) { return null; } if (!this.address) { return null; }
let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address });
if (!address) { throw new AddressNotFoundError; } if (!address) { throw new AddressNotFoundError; }
return address; return address;
} }
/** /**
* Updates a provided RunnerOrganisation entity based on this. * Updates a provided RunnerOrganisation entity based on this.
*/ */
public async update(organisation: RunnerOrganisation): Promise<RunnerOrganisation> { public async update(organisation: RunnerOrganisation): Promise<RunnerOrganisation> {
organisation.name = this.name; organisation.name = this.name;
organisation.contact = await this.getContact(); organisation.contact = await this.getContact();
organisation.address = await this.getAddress(); organisation.address = await this.getAddress();
return organisation; return organisation;
} }
} }

View File

@ -76,6 +76,7 @@ export class UpdateUser {
* Should the user be enabled? * Should the user be enabled?
*/ */
@IsBoolean() @IsBoolean()
@IsOptional()
enabled: boolean = true; enabled: boolean = true;
/** /**

View File

@ -1,90 +1,90 @@
import { import {
IsInt, IsInt,
IsNotEmpty, IsNotEmpty,
IsOptional, IsOptional,
IsPostalCode, IsPostalCode,
IsString IsString
} from "class-validator"; } from "class-validator";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { config } from '../../config'; import { config } from '../../config';
import { IAddressUser } from './IAddressUser'; import { IAddressUser } from './IAddressUser';
/** /**
* Defines the Address entity. * Defines the Address entity.
* Implemented this way to prevent any formatting differences. * Implemented this way to prevent any formatting differences.
*/ */
@Entity() @Entity()
export class Address { export class Address {
/** /**
* Autogenerated unique id (primary key). * Autogenerated unique id (primary key).
*/ */
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
@IsInt() @IsInt()
id: number; id: number;
/** /**
* The address's description. * The address's description.
* Optional and mostly for UX. * Optional and mostly for UX.
*/ */
@Column({ nullable: true }) @Column({ nullable: true })
@IsString() @IsString()
@IsOptional() @IsOptional()
description?: string; description?: string;
/** /**
* The address's first line. * The address's first line.
* Containing the street and house number. * Containing the street and house number.
*/ */
@Column() @Column()
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
address1: string; address1: string;
/** /**
* The address's second line. * The address's second line.
* Containing optional information. * Containing optional information.
*/ */
@Column({ nullable: true }) @Column({ nullable: true })
@IsString() @IsString()
@IsOptional() @IsOptional()
address2?: string; address2?: string;
/** /**
* The address's postal code. * The address's postal code.
* This will get checked against the postal code syntax for the configured country. * This will get checked against the postal code syntax for the configured country.
*/ */
@Column() @Column()
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@IsPostalCode(config.postalcode_validation_countrycode) @IsPostalCode(config.postalcode_validation_countrycode)
postalcode: string; postalcode: string;
/** /**
* The address's city. * The address's city.
*/ */
@Column() @Column()
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
city: string; city: string;
/** /**
* The address's country. * The address's country.
*/ */
@Column() @Column()
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
country: string; country: string;
/** /**
* Used to link the address to participants. * Used to link the address to participants.
*/ */
@OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true }) @OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true })
addressUsers: IAddressUser[]; addressUsers: IAddressUser[];
/** /**
* Turns this entity into it's response class. * Turns this entity into it's response class.
*/ */
public toResponse() { public toResponse() {
return new Error("NotImplemented"); return new Error("NotImplemented");
} }
} }

View File

@ -1,20 +1,20 @@
import { Entity, ManyToOne, PrimaryColumn } from 'typeorm'; import { Entity, ManyToOne, PrimaryColumn } from 'typeorm';
import { Address } from './Address'; import { Address } from './Address';
/** /**
* The interface(tm) all entities using addresses have to implement. * The interface(tm) all entities using addresses have to implement.
* This is a abstract class, because apparently typeorm can't really work with interfaces :/ * This is a abstract class, because apparently typeorm can't really work with interfaces :/
*/ */
@Entity() @Entity()
export abstract class IAddressUser { export abstract class IAddressUser {
@PrimaryColumn() @PrimaryColumn()
id: number; id: number;
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) @ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
address?: Address address?: Address
/** /**
* Turns this entity into it's response class. * Turns this entity into it's response class.
*/ */
public abstract toResponse(); public abstract toResponse();
} }

View File

@ -1,65 +1,65 @@
import { IsInt, IsOptional } from "class-validator"; import { IsInt, IsOptional } from "class-validator";
import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; import { ChildEntity, ManyToOne, OneToMany } from "typeorm";
import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation'; import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation';
import { Address } from './Address'; import { Address } from './Address';
import { IAddressUser } from './IAddressUser'; import { IAddressUser } from './IAddressUser';
import { Runner } from './Runner'; import { Runner } from './Runner';
import { RunnerGroup } from "./RunnerGroup"; import { RunnerGroup } from "./RunnerGroup";
import { RunnerTeam } from "./RunnerTeam"; import { RunnerTeam } from "./RunnerTeam";
/** /**
* Defines the RunnerOrganisation entity. * Defines the RunnerOrganisation entity.
* This usually is a school, club or company. * This usually is a school, club or company.
*/ */
@ChildEntity() @ChildEntity()
export class RunnerOrganisation extends RunnerGroup implements IAddressUser { export class RunnerOrganisation extends RunnerGroup implements IAddressUser {
/** /**
* The organisations's address. * The organisations's address.
*/ */
@IsOptional() @IsOptional()
@ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) @ManyToOne(() => Address, address => address.addressUsers, { nullable: true })
address?: Address; address?: Address;
/** /**
* The organisation's teams. * The organisation's teams.
* Used to link teams to a organisation. * Used to link teams to a organisation.
*/ */
@OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true })
teams: RunnerTeam[]; teams: RunnerTeam[];
/** /**
* Returns all runners associated with this organisation (directly or indirectly via teams). * Returns all runners associated with this organisation (directly or indirectly via teams).
*/ */
public get allRunners(): Runner[] { public get allRunners(): Runner[] {
let returnRunners: Runner[] = new Array<Runner>(); let returnRunners: Runner[] = new Array<Runner>();
returnRunners.push(...this.runners); returnRunners.push(...this.runners);
for (let team of this.teams) { for (let team of this.teams) {
returnRunners.push(...team.runners) returnRunners.push(...team.runners)
} }
return returnRunners; return returnRunners;
} }
/** /**
* Returns the total distance ran by this group's runners based on all their valid scans. * Returns the total distance ran by this group's runners based on all their valid scans.
*/ */
@IsInt() @IsInt()
public get distance(): number { public get distance(): number {
return this.allRunners.reduce((sum, current) => sum + current.distance, 0); return this.allRunners.reduce((sum, current) => sum + current.distance, 0);
} }
/** /**
* Returns the total donations a runner has collected based on his linked donations and distance ran. * Returns the total donations a runner has collected based on his linked donations and distance ran.
*/ */
@IsInt() @IsInt()
public get distanceDonationAmount(): number { public get distanceDonationAmount(): number {
return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0); return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0);
} }
/** /**
* Turns this entity into it's response class. * Turns this entity into it's response class.
*/ */
public toResponse(): ResponseRunnerOrganisation { public toResponse(): ResponseRunnerOrganisation {
return new ResponseRunnerOrganisation(this); return new ResponseRunnerOrganisation(this);
} }
} }

View File

@ -138,8 +138,10 @@ export class User extends Principal {
if (!this.groups) { return returnPermissions; } if (!this.groups) { return returnPermissions; }
for (let group of this.groups) { for (let group of this.groups) {
for (let permission of group.permissions) { if (group.permissions) {
returnPermissions.push(permission); for (let permission of group.permissions) {
returnPermissions.push(permission);
}
} }
} }
return returnPermissions; return returnPermissions;
@ -159,8 +161,10 @@ export class User extends Principal {
if (!this.groups) { return returnPermissions; } if (!this.groups) { return returnPermissions; }
for (let group of this.groups) { for (let group of this.groups) {
for (let permission of group.permissions) { if (group.permissions) {
returnPermissions.push(permission.toString()); for (let permission of group.permissions) {
returnPermissions.push(permission.toString());
}
} }
} }
return Array.from(new Set(returnPermissions)); return Array.from(new Set(returnPermissions));

View File

@ -1,40 +1,40 @@
import { import {
IsNotEmpty, IsNotEmpty,
IsOptional, IsOptional,
IsString IsString
} from "class-validator"; } from "class-validator";
import { ChildEntity, Column } from "typeorm"; import { ChildEntity, Column } from "typeorm";
import { ResponsePrincipal } from '../responses/ResponsePrincipal'; import { ResponsePrincipal } from '../responses/ResponsePrincipal';
import { ResponseUserGroup } from '../responses/ResponseUserGroup'; import { ResponseUserGroup } from '../responses/ResponseUserGroup';
import { Principal } from './Principal'; import { Principal } from './Principal';
/** /**
* Defines the UserGroup entity. * Defines the UserGroup entity.
* This entity describes a group of users with a set of permissions. * This entity describes a group of users with a set of permissions.
*/ */
@ChildEntity() @ChildEntity()
export class UserGroup extends Principal { export class UserGroup extends Principal {
/** /**
* The group's name * The group's name
*/ */
@Column() @Column()
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
name: string; name: string;
/** /**
* The group's description * The group's description
*/ */
@Column({ nullable: true }) @Column({ nullable: true })
@IsOptional() @IsOptional()
@IsString() @IsString()
description?: string; description?: string;
/** /**
* Turns this entity into it's response class. * Turns this entity into it's response class.
*/ */
public toResponse(): ResponsePrincipal { public toResponse(): ResponsePrincipal {
return new ResponseUserGroup(this); return new ResponseUserGroup(this);
} }
} }

View File

@ -1,56 +1,56 @@
import { IsInt, IsString } from "class-validator"; import { IsInt, IsString } from "class-validator";
import { Participant } from '../entities/Participant'; import { Participant } from '../entities/Participant';
/** /**
* Defines the participant response. * Defines the participant response.
*/ */
export abstract class ResponseParticipant { export abstract class ResponseParticipant {
/** /**
* The participant's id. * The participant's id.
*/ */
@IsInt() @IsInt()
id: number; id: number;
/** /**
* The participant's first name. * The participant's first name.
*/ */
@IsString() @IsString()
firstname: string; firstname: string;
/** /**
* The participant's middle name. * The participant's middle name.
*/ */
@IsString() @IsString()
middlename?: string; middlename?: string;
/** /**
* The participant's last name. * The participant's last name.
*/ */
@IsString() @IsString()
lastname: string; lastname: string;
/** /**
* The participant's phone number. * The participant's phone number.
*/ */
@IsString() @IsString()
phone?: string; phone?: string;
/** /**
* The participant's e-mail address. * The participant's e-mail address.
*/ */
@IsString() @IsString()
email?: string; email?: string;
/** /**
* Creates a ResponseParticipant object from a participant. * Creates a ResponseParticipant object from a participant.
* @param participant The participant the response shall be build for. * @param participant The participant the response shall be build for.
*/ */
public constructor(participant: Participant) { public constructor(participant: Participant) {
this.id = participant.id; this.id = participant.id;
this.firstname = participant.firstname; this.firstname = participant.firstname;
this.middlename = participant.middlename; this.middlename = participant.middlename;
this.lastname = participant.lastname; this.lastname = participant.lastname;
this.phone = participant.phone; this.phone = participant.phone;
this.email = participant.email; this.email = participant.email;
} }
} }

View File

@ -1,97 +1,99 @@
import { import {
IsArray, IsArray,
IsBoolean, IsBoolean,
IsOptional, IsOptional,
IsString IsString
} from "class-validator"; } from "class-validator";
import { User } from '../entities/User'; import { User } from '../entities/User';
import { UserGroup } from '../entities/UserGroup'; import { UserGroup } from '../entities/UserGroup';
import { ResponsePrincipal } from './ResponsePrincipal'; import { ResponsePrincipal } from './ResponsePrincipal';
/** /**
* Defines the user response. * Defines the user response.
*/ */
export class ResponseUser extends ResponsePrincipal { export class ResponseUser extends ResponsePrincipal {
/** /**
* The user's first name. * The user's first name.
*/ */
@IsString() @IsString()
firstname: string; firstname: string;
/** /**
* The user's middle name. * The user's middle name.
*/ */
@IsString() @IsString()
middlename?: string; middlename?: string;
/** /**
* The user's last name. * The user's last name.
*/ */
@IsString() @IsString()
lastname: string; lastname: string;
/** /**
* The user's phone number. * The user's phone number.
*/ */
@IsString() @IsString()
phone?: string; phone?: string;
/** /**
* The user's e-mail address. * The user's e-mail address.
*/ */
@IsString() @IsString()
email?: string; email?: string;
/** /**
* The user's username. * The user's username.
*/ */
@IsString() @IsString()
username?: string; username?: string;
/** /**
* Is user enabled? * Is user enabled?
*/ */
@IsBoolean() @IsBoolean()
enabled: boolean = true; enabled: boolean = true;
/** /**
* The user's profile pic (or rather a url pointing to it). * The user's profile pic (or rather a url pointing to it).
*/ */
@IsString() @IsString()
profilePic: string; profilePic: string;
/** /**
* The groups that the user is a part of. * The groups that the user is a part of.
*/ */
@IsArray() @IsArray()
@IsOptional() @IsOptional()
groups: UserGroup[]; groups: UserGroup[];
/** /**
* The user's permissions. * The user's permissions.
* Directly granted or inherited converted to their string form and deduplicated. * Directly granted or inherited converted to their string form and deduplicated.
*/ */
@IsArray() @IsArray()
@IsOptional() @IsOptional()
permissions: string[]; permissions: string[];
/** /**
* Creates a ResponseUser object from a user. * Creates a ResponseUser object from a user.
* @param user The user the response shall be build for. * @param user The user the response shall be build for.
*/ */
public constructor(user: User) { public constructor(user: User) {
super(user); super(user);
this.firstname = user.firstname; this.firstname = user.firstname;
this.middlename = user.middlename; this.middlename = user.middlename;
this.lastname = user.lastname; this.lastname = user.lastname;
this.phone = user.phone; this.phone = user.phone;
this.email = user.email; this.email = user.email;
this.username = user.username; this.username = user.username;
this.enabled = user.enabled; this.enabled = user.enabled;
this.profilePic = user.profilePic; this.profilePic = user.profilePic;
this.groups = user.groups; this.groups = user.groups;
this.permissions = user.allPermissions; this.permissions = user.allPermissions;
this.groups.forEach(function (g) { delete g.permissions }); if (this.groups) {
} this.groups.forEach(function (g) { delete g.permissions });
} }
}
}

View File

@ -1,41 +1,41 @@
import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator"; import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator";
import { Permission } from '../entities/Permission'; import { Permission } from '../entities/Permission';
import { UserGroup } from '../entities/UserGroup'; import { UserGroup } from '../entities/UserGroup';
import { ResponsePrincipal } from './ResponsePrincipal'; import { ResponsePrincipal } from './ResponsePrincipal';
/** /**
* Defines the userGroup response. * Defines the userGroup response.
*/ */
export class ResponseUserGroup extends ResponsePrincipal { export class ResponseUserGroup extends ResponsePrincipal {
/** /**
* The userGroup's name. * The userGroup's name.
*/ */
@IsNotEmpty() @IsNotEmpty()
@IsString() @IsString()
name: string; name: string;
/** /**
* The userGroup's description. * The userGroup's description.
*/ */
@IsOptional() @IsOptional()
@IsString() @IsString()
description?: string; description?: string;
/** /**
* The userGroup's permissions. * The userGroup's permissions.
*/ */
@IsArray() @IsArray()
@IsOptional() @IsOptional()
permissions: Permission[]; permissions: Permission[];
/** /**
* Creates a ResponseUserGroup object from a userGroup. * Creates a ResponseUserGroup object from a userGroup.
* @param group The userGroup the response shall be build for. * @param group The userGroup the response shall be build for.
*/ */
public constructor(group: UserGroup) { public constructor(group: UserGroup) {
super(group); super(group);
this.name = group.name; this.name = group.name;
this.description = group.description; this.description = group.description;
this.permissions = group.permissions; this.permissions = group.permissions;
} }
} }

View File

@ -1,94 +1,94 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
// --------------- // ---------------
describe('POST /api/donors with errors', () => { describe('POST /api/donors with errors', () => {
it('creating a new donor without any parameters should return 400', async () => { it('creating a new donor without any parameters should return 400', async () => {
const res1 = await axios.post(base + '/api/donors', null, axios_config); const res1 = await axios.post(base + '/api/donors', null, axios_config);
expect(res1.status).toEqual(400); expect(res1.status).toEqual(400);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor without a last name should return 400', async () => { it('creating a new donor without a last name should return 400', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"middlename": "middle" "middlename": "middle"
}, axios_config); }, axios_config);
expect(res2.status).toEqual(400); expect(res2.status).toEqual(400);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor with a invalid address should return 404', async () => { it('creating a new donor with a invalid address should return 404', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"middlename": "middle", "middlename": "middle",
"lastname": "last", "lastname": "last",
"address": 99999999999999999999999999 "address": 99999999999999999999999999
}, axios_config); }, axios_config);
expect(res2.status).toEqual(404); expect(res2.status).toEqual(404);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor with a invalid phone number should return 400', async () => { it('creating a new donor with a invalid phone number should return 400', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"middlename": "middle", "middlename": "middle",
"lastname": "last", "lastname": "last",
"phone": "123" "phone": "123"
}, axios_config); }, axios_config);
expect(res2.status).toEqual(400); expect(res2.status).toEqual(400);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor with a invalid mail address should return 400', async () => { it('creating a new donor with a invalid mail address should return 400', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "string", "firstname": "string",
"middlename": "string", "middlename": "string",
"lastname": "string", "lastname": "string",
"phone": null, "phone": null,
"email": "123", "email": "123",
}, axios_config); }, axios_config);
expect(res2.status).toEqual(400); expect(res2.status).toEqual(400);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor without an address but with receiptNeeded=true 406', async () => { it('creating a new donor without an address but with receiptNeeded=true 406', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "string", "firstname": "string",
"middlename": "string", "middlename": "string",
"lastname": "string", "lastname": "string",
"receiptNeeded": true "receiptNeeded": true
}, axios_config); }, axios_config);
expect(res2.status).toEqual(406); expect(res2.status).toEqual(406);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('POST /api/donors working', () => { describe('POST /api/donors working', () => {
it('creating a new donor with only needed params should return 200', async () => { it('creating a new donor with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"lastname": "last" "lastname": "last"
}, axios_config); }, axios_config);
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new donor with all non-relationship optional params should return 200', async () => { it('creating a new donor with all non-relationship optional params should return 200', async () => {
const res3 = await axios.post(base + '/api/donors', { const res3 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"middlename": "middle", "middlename": "middle",
"lastname": "last", "lastname": "last",
"receiptNeeded": false "receiptNeeded": false
}, axios_config); }, axios_config);
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });

View File

@ -1,75 +1,75 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
describe('Update donor name after adding', () => { describe('Update donor name after adding', () => {
let added_donor; let added_donor;
it('creating a new runner with only needed params should return 200', async () => { it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"lastname": "last" "lastname": "last"
}, axios_config); }, axios_config);
added_donor = res2.data; added_donor = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('valid update should return 200', async () => { it('valid update should return 200', async () => {
let donor_copy = added_donor let donor_copy = added_donor
donor_copy.firstname = "second" donor_copy.firstname = "second"
const res3 = await axios.put(base + '/api/donors/' + added_donor.id, donor_copy, axios_config); const res3 = await axios.put(base + '/api/donors/' + added_donor.id, donor_copy, axios_config);
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
let updated_donor = res3.data let updated_donor = res3.data
expect(updated_donor).toEqual(donor_copy); expect(updated_donor).toEqual(donor_copy);
}); });
}); });
// --------------- // ---------------
describe('Update donor id after adding(should fail)', () => { describe('Update donor id after adding(should fail)', () => {
let added_donor; let added_donor;
it('creating a new donor with only needed params should return 200', async () => { it('creating a new donor with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"lastname": "last" "lastname": "last"
}, axios_config); }, axios_config);
added_donor = res2.data; added_donor = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('invalid update should return 406', async () => { it('invalid update should return 406', async () => {
added_donor.id++; added_donor.id++;
const res3 = await axios.put(base + '/api/donors/' + (added_donor.id - 1), added_donor, axios_config); const res3 = await axios.put(base + '/api/donors/' + (added_donor.id - 1), added_donor, axios_config);
expect(res3.status).toEqual(406); expect(res3.status).toEqual(406);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('Update donor without address but receiptNeeded=true should fail', () => { describe('Update donor without address but receiptNeeded=true should fail', () => {
let added_donor; let added_donor;
it('creating a new donor with only needed params should return 200', async () => { it('creating a new donor with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/donors', { const res2 = await axios.post(base + '/api/donors', {
"firstname": "first", "firstname": "first",
"lastname": "last", "lastname": "last",
}, axios_config); }, axios_config);
added_donor = res2.data; added_donor = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('invalid update should return 406', async () => { it('invalid update should return 406', async () => {
added_donor.receiptNeeded = true; added_donor.receiptNeeded = true;
const res3 = await axios.put(base + '/api/donors/' + added_donor.id, added_donor, axios_config); const res3 = await axios.put(base + '/api/donors/' + added_donor.id, added_donor, axios_config);
expect(res3.status).toEqual(406); expect(res3.status).toEqual(406);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });

View File

@ -1,90 +1,90 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
describe('GET /api/organisations', () => { describe('GET /api/organisations', () => {
it('basic get should return 200', async () => { it('basic get should return 200', async () => {
const res = await axios.get(base + '/api/organisations', axios_config); const res = await axios.get(base + '/api/organisations', 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")
}); });
}); });
// --------------- // ---------------
describe('POST /api/organisations', () => { describe('POST /api/organisations', () => {
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res = await axios.post(base + '/api/organisations', { const res = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, 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")
}); });
it('creating a new org with without a name should return 400', async () => { it('creating a new org with without a name should return 400', async () => {
const res = await axios.post(base + '/api/organisations', { const res = await axios.post(base + '/api/organisations', {
"name": null "name": null
}, axios_config); }, axios_config);
expect(res.status).toEqual(400); expect(res.status).toEqual(400);
expect(res.headers['content-type']).toContain("application/json") expect(res.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('adding + getting from all orgs', () => { describe('adding + getting from all orgs', () => {
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res = await axios.post(base + '/api/organisations', { const res = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, 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")
}); });
it('check if org was added', async () => { it('check if org was added', async () => {
const res = await axios.get(base + '/api/organisations', axios_config); const res = await axios.get(base + '/api/organisations', 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")
let added_org = res.data[res.data.length - 1] let added_org = res.data[res.data.length - 1]
delete added_org.id delete added_org.id
expect(added_org).toEqual({ expect(added_org).toEqual({
"name": "test123", "name": "test123",
"contact": null, "contact": null,
"address": null, "address": null,
"teams": [] "teams": []
}) })
}); });
}); });
// --------------- // ---------------
describe('adding + getting explicitly', () => { describe('adding + getting explicitly', () => {
let added_org_id let added_org_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
let added_org = res1.data let added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('check if org was added', async () => { it('check if org was added', async () => {
const res2 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); const res2 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config);
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
let added_org2 = res2.data let added_org2 = res2.data
added_org_id = added_org2.id; added_org_id = added_org2.id;
delete added_org2.id delete added_org2.id
expect(added_org2).toEqual({ expect(added_org2).toEqual({
"name": "test123", "name": "test123",
"contact": null, "contact": null,
"address": null, "address": null,
"teams": [] "teams": []
}) })
}); });
}); });

View File

@ -1,132 +1,132 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
// --------------- // ---------------
describe('adding + deletion (non-existant)', () => { describe('adding + deletion (non-existant)', () => {
it('delete', async () => { it('delete', async () => {
const res2 = await axios.delete(base + '/api/organisations/0', axios_config); const res2 = await axios.delete(base + '/api/organisations/0', axios_config);
expect(res2.status).toEqual(204); expect(res2.status).toEqual(204);
}); });
}); });
// --------------- // ---------------
describe('adding + deletion (successfull)', () => { describe('adding + deletion (successfull)', () => {
let added_org_id let added_org_id
let added_org let added_org
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('delete', async () => { it('delete', async () => {
const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config);
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
let added_org2 = res2.data let added_org2 = res2.data
added_org_id = added_org2.id; added_org_id = added_org2.id;
delete added_org2.id delete added_org2.id
expect(added_org2).toEqual({ expect(added_org2).toEqual({
"name": "test123", "name": "test123",
"contact": null, "contact": null,
"address": null, "address": null,
"teams": [] "teams": []
}); });
}); });
it('check if org really was deleted', async () => { it('check if org really was deleted', async () => {
const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config);
expect(res3.status).toEqual(404); expect(res3.status).toEqual(404);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('adding + deletion with teams still existing (without force)', () => { describe('adding + deletion with teams still existing (without force)', () => {
let added_org; let added_org;
let added_org_id; let added_org_id;
let added_team; let added_team;
let added_team_id let added_team_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data; added_org = res1.data;
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new team with a valid org should return 200', async () => { it('creating a new team with a valid org should return 200', async () => {
const res2 = await axios.post(base + '/api/teams', { const res2 = await axios.post(base + '/api/teams', {
"name": "test123", "name": "test123",
"parentGroup": added_org_id "parentGroup": added_org_id
}, axios_config); }, axios_config);
added_team = res2.data; added_team = res2.data;
added_team_id = added_team.id; added_team_id = added_team.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('delete org - this should fail with a 406', async () => { it('delete org - this should fail with a 406', async () => {
const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config);
expect(res2.status).toEqual(406); expect(res2.status).toEqual(406);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('adding + deletion with teams still existing (with force)', () => { describe('adding + deletion with teams still existing (with force)', () => {
let added_org; let added_org;
let added_org_id; let added_org_id;
let added_team; let added_team;
let added_team_id let added_team_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data; added_org = res1.data;
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new team with a valid org should return 200', async () => { it('creating a new team with a valid org should return 200', async () => {
const res2 = await axios.post(base + '/api/teams', { const res2 = await axios.post(base + '/api/teams', {
"name": "test123", "name": "test123",
"parentGroup": added_org_id "parentGroup": added_org_id
}, axios_config); }, axios_config);
added_team = res2.data; added_team = res2.data;
added_team_id = added_team.id; added_team_id = added_team.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('delete', async () => { it('delete', async () => {
const res2 = await axios.delete(base + '/api/organisations/' + added_org_id + '?force=true', axios_config); const res2 = await axios.delete(base + '/api/organisations/' + added_org_id + '?force=true', axios_config);
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
let added_org2 = res2.data let added_org2 = res2.data
added_org_id = added_org2.id; added_org_id = added_org2.id;
delete added_org2.id; delete added_org2.id;
delete added_org2.teams; delete added_org2.teams;
expect(added_org2).toEqual({ expect(added_org2).toEqual({
"name": "test123", "name": "test123",
"contact": null, "contact": null,
"address": null "address": null
}); });
}); });
it('check if org really was deleted', async () => { it('check if org really was deleted', async () => {
const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config);
expect(res3.status).toEqual(404); expect(res3.status).toEqual(404);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });

View File

@ -1,73 +1,73 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
// --------------- // ---------------
describe('adding + updating name', () => { describe('adding + updating name', () => {
let added_org_id let added_org_id
let added_org let added_org
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('update org', async () => { it('update org', async () => {
const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { const res2 = await axios.put(base + '/api/organisations/' + added_org_id, {
"id": added_org_id, "id": added_org_id,
"name": "testlelele", "name": "testlelele",
"contact": null, "contact": null,
"address": null, "address": null,
}, axios_config); }, axios_config);
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
let added_org2 = res2.data let added_org2 = res2.data
added_org_id = added_org2.id; added_org_id = added_org2.id;
delete added_org2.id delete added_org2.id
expect(added_org2).toEqual({ expect(added_org2).toEqual({
"name": "testlelele", "name": "testlelele",
"contact": null, "contact": null,
"address": null, "address": null,
"teams": [] "teams": []
}) })
}); });
}); });
// --------------- // ---------------
describe('adding + try updating id (should return 406)', () => { describe('adding + try updating id (should return 406)', () => {
let added_org_id let added_org_id
let added_org let added_org
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('update org', async () => { it('update org', async () => {
const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { const res2 = await axios.put(base + '/api/organisations/' + added_org_id, {
"id": added_org_id + 1, "id": added_org_id + 1,
"name": "testlelele", "name": "testlelele",
"contact": null, "contact": null,
"address": null, "address": null,
}, axios_config); }, axios_config);
expect(res2.status).toEqual(406); expect(res2.status).toEqual(406);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
}); });

View File

@ -1,131 +1,131 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
// --------------- // ---------------
describe('adding + updating name', () => { describe('adding + updating name', () => {
let added_org; let added_org;
let added_org_id; let added_org_id;
let added_team; let added_team;
let added_team_id let added_team_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data; added_org = res1.data;
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new team with a valid org should return 200', async () => { it('creating a new team with a valid org should return 200', async () => {
const res2 = await axios.post(base + '/api/teams', { const res2 = await axios.post(base + '/api/teams', {
"name": "test123", "name": "test123",
"parentGroup": added_org_id "parentGroup": added_org_id
}, axios_config); }, axios_config);
added_team = res2.data; added_team = res2.data;
added_team_id = added_team.id; added_team_id = added_team.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('update name', async () => { it('update name', async () => {
const res3 = await axios.put(base + '/api/teams/' + added_team_id, { const res3 = await axios.put(base + '/api/teams/' + added_team_id, {
"id": added_team_id, "id": added_team_id,
"name": "testlelele", "name": "testlelele",
"contact": null, "contact": null,
"parentGroup": added_org.id "parentGroup": added_org.id
}, axios_config); }, axios_config);
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
let updated_team = res3.data; let updated_team = res3.data;
added_team.name = "testlelele"; added_team.name = "testlelele";
expect(updated_team).toEqual(added_team) expect(updated_team).toEqual(added_team)
}); });
}); });
// --------------- // ---------------
describe('adding + try updating id (should return 406)', () => { describe('adding + try updating id (should return 406)', () => {
let added_org; let added_org;
let added_org_id; let added_org_id;
let added_team; let added_team;
let added_team_id let added_team_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data; added_org = res1.data;
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new team with a valid org should return 200', async () => { it('creating a new team with a valid org should return 200', async () => {
const res2 = await axios.post(base + '/api/teams', { const res2 = await axios.post(base + '/api/teams', {
"name": "test123", "name": "test123",
"parentGroup": added_org_id "parentGroup": added_org_id
}, axios_config); }, axios_config);
added_team = res2.data; added_team = res2.data;
added_team_id = added_team.id; added_team_id = added_team.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('update team', async () => { it('update team', async () => {
added_team.id = added_team.id + 1; added_team.id = added_team.id + 1;
added_team.parentGroup = added_team.parentGroup.id; added_team.parentGroup = added_team.parentGroup.id;
const res3 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); const res3 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config);
expect(res3.status).toEqual(406); expect(res3.status).toEqual(406);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('add+update parent org (valid)', () => { describe('add+update parent org (valid)', () => {
let added_org; let added_org;
let added_org2; let added_org2;
let added_team; let added_team;
let added_team_id let added_team_id
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data; added_org = res1.data;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new team with a valid org should return 200', async () => { it('creating a new team with a valid org should return 200', async () => {
const res2 = await axios.post(base + '/api/teams', { const res2 = await axios.post(base + '/api/teams', {
"name": "test123", "name": "test123",
"parentGroup": added_org.id "parentGroup": added_org.id
}, axios_config); }, axios_config);
added_team = res2.data; added_team = res2.data;
added_team_id = added_team.id; added_team_id = added_team.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res3 = await axios.post(base + '/api/organisations', { const res3 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org2 = res3.data; added_org2 = res3.data;
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
it('update team', async () => { it('update team', async () => {
added_team.parentGroup = added_org2.id; added_team.parentGroup = added_org2.id;
const res4 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); const res4 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config);
let updated_team = res4.data; let updated_team = res4.data;
expect(res4.status).toEqual(200); expect(res4.status).toEqual(200);
expect(res4.headers['content-type']).toContain("application/json") expect(res4.headers['content-type']).toContain("application/json")
delete added_org2.address; delete added_org2.address;
delete added_org2.contact; delete added_org2.contact;
delete added_org2.teams; delete added_org2.teams;
expect(updated_team.parentGroup).toEqual(added_org2) expect(updated_team.parentGroup).toEqual(added_org2)
}); });
}); });

View File

@ -1,160 +1,160 @@
import axios from 'axios'; import axios from 'axios';
import { config } from '../../config'; import { config } from '../../config';
const base = "http://localhost:" + config.internal_port const base = "http://localhost:" + config.internal_port
let access_token; let access_token;
let axios_config; let axios_config;
beforeAll(async () => { beforeAll(async () => {
const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" });
access_token = res.data["access_token"]; access_token = res.data["access_token"];
axios_config = { axios_config = {
headers: { "authorization": "Bearer " + access_token }, headers: { "authorization": "Bearer " + access_token },
validateStatus: undefined validateStatus: undefined
}; };
}); });
describe('Update runner name after adding', () => { describe('Update runner name after adding', () => {
let added_org; let added_org;
let added_runner; let added_runner;
let updated_runner; let updated_runner;
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data added_org = res1.data
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new runner with only needed params should return 200', async () => { it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', { const res2 = await axios.post(base + '/api/runners', {
"firstname": "first", "firstname": "first",
"lastname": "last", "lastname": "last",
"group": added_org.id "group": added_org.id
}, axios_config); }, axios_config);
added_runner = res2.data; added_runner = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('valid update should return 200', async () => { it('valid update should return 200', async () => {
let runnercopy = added_runner let runnercopy = added_runner
runnercopy.firstname = "second" runnercopy.firstname = "second"
runnercopy.group = added_runner.group.id; runnercopy.group = added_runner.group.id;
const res3 = await axios.put(base + '/api/runners/' + added_runner.id, runnercopy, axios_config); const res3 = await axios.put(base + '/api/runners/' + added_runner.id, runnercopy, axios_config);
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
updated_runner = res3.data; updated_runner = res3.data;
delete added_org.address; delete added_org.address;
delete added_org.contact; delete added_org.contact;
delete added_org.teams; delete added_org.teams;
runnercopy.group = added_org; runnercopy.group = added_org;
expect(updated_runner).toEqual(runnercopy); expect(updated_runner).toEqual(runnercopy);
}); });
}); });
// --------------- // ---------------
describe('Update runner group after adding', () => { describe('Update runner group after adding', () => {
let added_org_id; let added_org_id;
let added_org_2; let added_org_2;
let added_runner; let added_runner;
let updated_runner; let updated_runner;
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
let added_org = res1.data let added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new runner with only needed params should return 200', async () => { it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', { const res2 = await axios.post(base + '/api/runners', {
"firstname": "first", "firstname": "first",
"lastname": "last", "lastname": "last",
"group": added_org_id "group": added_org_id
}, axios_config); }, axios_config);
added_runner = res2.data; added_runner = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res3 = await axios.post(base + '/api/organisations', { const res3 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org_2 = res3.data added_org_2 = res3.data
delete added_org_2.address; delete added_org_2.address;
delete added_org_2.contact; delete added_org_2.contact;
delete added_org_2.teams; delete added_org_2.teams;
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
it('valid group update should return 200', async () => { it('valid group update should return 200', async () => {
added_runner.group = added_org_2.id; added_runner.group = added_org_2.id;
const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config);
expect(res3.status).toEqual(200); expect(res3.status).toEqual(200);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
updated_runner = res3.data updated_runner = res3.data
expect(updated_runner.group).toEqual(added_org_2); expect(updated_runner.group).toEqual(added_org_2);
}); });
}); });
// --------------- // ---------------
describe('Update runner id after adding(should fail)', () => { describe('Update runner id after adding(should fail)', () => {
let added_org_id; let added_org_id;
let added_runner; let added_runner;
let added_runner_id; let added_runner_id;
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
let added_org = res1.data let added_org = res1.data
added_org_id = added_org.id; added_org_id = added_org.id;
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new runner with only needed params should return 200', async () => { it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', { const res2 = await axios.post(base + '/api/runners', {
"firstname": "first", "firstname": "first",
"lastname": "last", "lastname": "last",
"group": added_org_id "group": added_org_id
}, axios_config); }, axios_config);
added_runner = res2.data; added_runner = res2.data;
added_runner_id = added_runner.id; added_runner_id = added_runner.id;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('invalid update should return 406', async () => { it('invalid update should return 406', async () => {
added_runner.id++; added_runner.id++;
added_runner.group = added_runner.group.id; added_runner.group = added_runner.group.id;
const res3 = await axios.put(base + '/api/runners/' + added_runner_id, added_runner, axios_config); const res3 = await axios.put(base + '/api/runners/' + added_runner_id, added_runner, axios_config);
expect(res3.status).toEqual(406); expect(res3.status).toEqual(406);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });
// --------------- // ---------------
describe('Update runner group with invalid group after adding', () => { describe('Update runner group with invalid group after adding', () => {
let added_org; let added_org;
let added_runner; let added_runner;
it('creating a new org with just a name should return 200', async () => { it('creating a new org with just a name should return 200', async () => {
const res1 = await axios.post(base + '/api/organisations', { const res1 = await axios.post(base + '/api/organisations', {
"name": "test123" "name": "test123"
}, axios_config); }, axios_config);
added_org = res1.data added_org = res1.data
expect(res1.status).toEqual(200); expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json") expect(res1.headers['content-type']).toContain("application/json")
}); });
it('creating a new runner with only needed params should return 200', async () => { it('creating a new runner with only needed params should return 200', async () => {
const res2 = await axios.post(base + '/api/runners', { const res2 = await axios.post(base + '/api/runners', {
"firstname": "first", "firstname": "first",
"lastname": "last", "lastname": "last",
"group": added_org.id "group": added_org.id
}, axios_config); }, axios_config);
added_runner = res2.data; added_runner = res2.data;
expect(res2.status).toEqual(200); expect(res2.status).toEqual(200);
expect(res2.headers['content-type']).toContain("application/json") expect(res2.headers['content-type']).toContain("application/json")
}); });
it('invalid group update should return 404', async () => { it('invalid group update should return 404', async () => {
added_runner.group = 99999999999999999; added_runner.group = 99999999999999999;
const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config);
expect(res3.status).toEqual(404); expect(res3.status).toEqual(404);
expect(res3.headers['content-type']).toContain("application/json") expect(res3.headers['content-type']).toContain("application/json")
}); });
}); });