diff --git a/.drone.yml b/.drone.yml index ec2c8fd..204f6c3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -90,11 +90,20 @@ trigger: kind: pipeline type: docker name: build:latest +clone: + disable: true steps: + - name: clone + image: alpine/git + commands: + - git clone $DRONE_REMOTE_URL . + - git checkout dev + - git merge main + - git checkout main - name: build latest + depends_on: ["clone"] image: plugins/docker - depends_on: [clone] settings: username: from_secret: DOCKER_REGISTRY_USER @@ -104,6 +113,15 @@ steps: tags: - latest 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: branch: diff --git a/CHANGELOG.md b/CHANGELOG.md index 53b0dc0..e824ec5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,8 +2,32 @@ 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) +> 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) - 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) @@ -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] [`d1a0bed`](https://git.odit.services/lfk/backend/commit/d1a0bed00e01a0e9d8ba1165e3c6ca3dd910bd00) - 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) - 🚀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) diff --git a/package.json b/package.json index 3751c0a..20b9711 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odit/lfk-backend", - "version": "0.1.0", + "version": "0.1.1", "main": "src/app.ts", "repository": "https://git.odit.services/lfk/backend", "author": { diff --git a/src/app.ts b/src/app.ts index 44060da..6e814b3 100644 --- a/src/app.ts +++ b/src/app.ts @@ -5,10 +5,12 @@ import { config, e as errors } from './config'; import loaders from "./loaders/index"; import authchecker from "./middlewares/authchecker"; import { ErrorHandler } from './middlewares/ErrorHandler'; +import UserChecker from './middlewares/UserChecker'; const CONTROLLERS_FILE_EXTENSION = process.env.NODE_ENV === 'production' ? 'js' : 'ts'; const app = createExpressServer({ authorizationChecker: authchecker, + currentUserChecker: UserChecker, middlewares: [ErrorHandler], development: config.development, cors: true, diff --git a/src/controllers/MeController.ts b/src/controllers/MeController.ts new file mode 100644 index 0000000..08fdc51 --- /dev/null +++ b/src/controllers/MeController.ts @@ -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; + + /** + * 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.
You can't edit your own permissions or group memberships here - Please use the /api/users/:id enpoint instead.
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.
You have to confirm your decision by providing the ?force=true query param.
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); + } +} \ No newline at end of file diff --git a/src/controllers/UserController.ts b/src/controllers/UserController.ts index 846653f..0c5f0cb 100644 --- a/src/controllers/UserController.ts +++ b/src/controllers/UserController.ts @@ -1,7 +1,7 @@ import { Authorized, Body, Delete, Get, JsonController, OnUndefined, Param, Post, Put, QueryParam } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; 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 { CreateUser } from '../models/actions/create/CreateUser'; import { UpdateUser } from '../models/actions/update/UpdateUser'; @@ -105,9 +105,11 @@ export class UserController { @Authorized("USER:DELETE") @ResponseSchema(ResponseUser) @ResponseSchema(ResponseEmpty, { statusCode: 204 }) + @ResponseSchema(UserDeletionNotConfirmedError, { statusCode: 406 }) @OnUndefined(204) - @OpenAPI({ description: 'Delete the user whose id you provided.
If there are any permissions directly granted to the user they will get deleted as well.
If no user with this id exists it will just return 204(no content).' }) + @OpenAPI({ description: 'Delete the user whose id you provided.
You have to confirm your decision by providing the ?force=true query param.
If there are any permissions directly granted to the user they will get deleted as well.
If no user with this id exists it will just return 204(no content).' }) async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { + if (!force) { throw new UserDeletionNotConfirmedError; } let user = await this.userRepository.findOne({ id: id }); if (!user) { return null; } const responseUser = await this.userRepository.findOne({ id: id }, { relations: ['permissions', 'groups', 'groups.permissions'] });; diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts index 27f2e5f..ec68d67 100644 --- a/src/errors/AddressErrors.ts +++ b/src/errors/AddressErrors.ts @@ -1,24 +1,24 @@ -import { IsString } from 'class-validator'; -import { NotAcceptableError, NotFoundError } from 'routing-controllers'; - -/** - * Error to throw, when to provided address doesn't belong to the accepted types. - */ -export class AddressWrongTypeError extends NotAcceptableError { - @IsString() - name = "AddressWrongTypeError" - - @IsString() - 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. - */ -export class AddressNotFoundError extends NotFoundError { - @IsString() - name = "AddressNotFoundError" - - @IsString() - message = "The address you provided couldn't be located in the system. \n Please check your request." +import { IsString } from 'class-validator'; +import { NotAcceptableError, NotFoundError } from 'routing-controllers'; + +/** + * Error to throw, when to provided address doesn't belong to the accepted types. + */ +export class AddressWrongTypeError extends NotAcceptableError { + @IsString() + name = "AddressWrongTypeError" + + @IsString() + 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. + */ +export class AddressNotFoundError extends NotFoundError { + @IsString() + name = "AddressNotFoundError" + + @IsString() + message = "The address you provided couldn't be located in the system. \n Please check your request." } \ No newline at end of file diff --git a/src/errors/UserErrors.ts b/src/errors/UserErrors.ts index 5d2b659..ced02ed 100644 --- a/src/errors/UserErrors.ts +++ b/src/errors/UserErrors.ts @@ -59,4 +59,16 @@ export class UserIdsNotMatchingError extends NotAcceptableError { @IsString() 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." } \ No newline at end of file diff --git a/src/middlewares/UserChecker.ts b/src/middlewares/UserChecker.ts new file mode 100644 index 0000000..603fe6e --- /dev/null +++ b/src/middlewares/UserChecker.ts @@ -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 = 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 = 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; \ No newline at end of file diff --git a/src/models/actions/create/CreateAddress.ts b/src/models/actions/create/CreateAddress.ts index 0241301..77ce4a8 100644 --- a/src/models/actions/create/CreateAddress.ts +++ b/src/models/actions/create/CreateAddress.ts @@ -1,69 +1,69 @@ -import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; -import { config } from '../../../config'; -import { Address } from '../../entities/Address'; - -/** - * This classed is used to create a new Address entity from a json body (post request). - */ -export class CreateAddress { - /** - * The newaddress's description. - */ - @IsString() - @IsOptional() - description?: string; - - /** - * The new address's first line. - * Containing the street and house number. - */ - @IsString() - @IsNotEmpty() - address1: string; - - /** - * The new address's second line. - * Containing optional information. - */ - @IsString() - @IsOptional() - address2?: string; - - /** - * The new address's postal code. - * This will get checked against the postal code syntax for the configured country. - */ - @IsString() - @IsNotEmpty() - @IsPostalCode(config.postalcode_validation_countrycode) - postalcode: string; - - /** - * The new address's city. - */ - @IsString() - @IsNotEmpty() - city: string; - - /** - * The new address's country. - */ - @IsString() - @IsNotEmpty() - country: string; - - /** - * Creates a new Address entity from this. - */ - public async toEntity(): Promise
{ - let newAddress: Address = new Address(); - - newAddress.address1 = this.address1; - newAddress.address2 = this.address2; - newAddress.postalcode = this.postalcode; - newAddress.city = this.city; - newAddress.country = this.country; - - return newAddress; - } +import { IsNotEmpty, IsOptional, IsPostalCode, IsString } from 'class-validator'; +import { config } from '../../../config'; +import { Address } from '../../entities/Address'; + +/** + * This classed is used to create a new Address entity from a json body (post request). + */ +export class CreateAddress { + /** + * The newaddress's description. + */ + @IsString() + @IsOptional() + description?: string; + + /** + * The new address's first line. + * Containing the street and house number. + */ + @IsString() + @IsNotEmpty() + address1: string; + + /** + * The new address's second line. + * Containing optional information. + */ + @IsString() + @IsOptional() + address2?: string; + + /** + * The new address's postal code. + * This will get checked against the postal code syntax for the configured country. + */ + @IsString() + @IsNotEmpty() + @IsPostalCode(config.postalcode_validation_countrycode) + postalcode: string; + + /** + * The new address's city. + */ + @IsString() + @IsNotEmpty() + city: string; + + /** + * The new address's country. + */ + @IsString() + @IsNotEmpty() + country: string; + + /** + * Creates a new Address entity from this. + */ + public async toEntity(): Promise
{ + let newAddress: Address = new Address(); + + newAddress.address1 = this.address1; + newAddress.address2 = this.address2; + newAddress.postalcode = this.postalcode; + newAddress.city = this.city; + newAddress.country = this.country; + + return newAddress; + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateDonor.ts b/src/models/actions/create/CreateDonor.ts index 791461a..a207983 100644 --- a/src/models/actions/create/CreateDonor.ts +++ b/src/models/actions/create/CreateDonor.ts @@ -1,38 +1,38 @@ -import { IsBoolean, IsOptional } from 'class-validator'; -import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; -import { Donor } from '../../entities/Donor'; -import { CreateParticipant } from './CreateParticipant'; - -/** - * This classed is used to create a new Donor entity from a json body (post request). - */ -export class CreateDonor extends CreateParticipant { - - /** - * Does this donor need a receipt? - */ - @IsBoolean() - @IsOptional() - receiptNeeded?: boolean = false; - - /** - * Creates a new Donor entity from this. - */ - public async toEntity(): Promise { - let newDonor: Donor = new Donor(); - - newDonor.firstname = this.firstname; - newDonor.middlename = this.middlename; - newDonor.lastname = this.lastname; - newDonor.phone = this.phone; - newDonor.email = this.email; - newDonor.address = await this.getAddress(); - newDonor.receiptNeeded = this.receiptNeeded; - - if (this.receiptNeeded == true && this.address == null) { - throw new DonorReceiptAddressNeededError() - } - - return newDonor; - } +import { IsBoolean, IsOptional } from 'class-validator'; +import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; +import { Donor } from '../../entities/Donor'; +import { CreateParticipant } from './CreateParticipant'; + +/** + * This classed is used to create a new Donor entity from a json body (post request). + */ +export class CreateDonor extends CreateParticipant { + + /** + * Does this donor need a receipt? + */ + @IsBoolean() + @IsOptional() + receiptNeeded?: boolean = false; + + /** + * Creates a new Donor entity from this. + */ + public async toEntity(): Promise { + let newDonor: Donor = new Donor(); + + newDonor.firstname = this.firstname; + newDonor.middlename = this.middlename; + newDonor.lastname = this.lastname; + newDonor.phone = this.phone; + newDonor.email = this.email; + newDonor.address = await this.getAddress(); + newDonor.receiptNeeded = this.receiptNeeded; + + if (this.receiptNeeded == true && this.address == null) { + throw new DonorReceiptAddressNeededError() + } + + return newDonor; + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateGroupContact.ts b/src/models/actions/create/CreateGroupContact.ts index b9a9d41..2a52341 100644 --- a/src/models/actions/create/CreateGroupContact.ts +++ b/src/models/actions/create/CreateGroupContact.ts @@ -1,78 +1,78 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { config } from '../../../config'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; -import { Address } from '../../entities/Address'; -import { GroupContact } from '../../entities/GroupContact'; - -/** - * This classed is used to create a new Group entity from a json body (post request). - */ -export class CreateGroupContact { - /** - * The new contact's first name. - */ - @IsNotEmpty() - @IsString() - firstname: string; - - /** - * The new contact's middle name. - */ - @IsOptional() - @IsString() - middlename?: string; - - /** - * The new contact's last name. - */ - @IsNotEmpty() - @IsString() - lastname: string; - - /** - * The new contact's address's id. - */ - @IsInt() - @IsOptional() - address?: number; - - /** - * The contact's phone number. - * This will be validated against the configured country phone numer syntax (default: international). - */ - @IsOptional() - @IsPhoneNumber(config.phone_validation_countrycode) - phone?: string; - - /** - * The contact's email address. - */ - @IsOptional() - @IsEmail() - email?: string; - - /** - * Gets the new contact's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } - - /** - * Creates a new Address entity from this. - */ - public async toEntity(): Promise { - let contact: GroupContact = new GroupContact(); - contact.firstname = this.firstname; - contact.middlename = this.middlename; - contact.lastname = this.lastname; - contact.email = this.email; - contact.phone = this.phone; - contact.address = await this.getAddress(); - return null; - } +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { config } from '../../../config'; +import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { Address } from '../../entities/Address'; +import { GroupContact } from '../../entities/GroupContact'; + +/** + * This classed is used to create a new Group entity from a json body (post request). + */ +export class CreateGroupContact { + /** + * The new contact's first name. + */ + @IsNotEmpty() + @IsString() + firstname: string; + + /** + * The new contact's middle name. + */ + @IsOptional() + @IsString() + middlename?: string; + + /** + * The new contact's last name. + */ + @IsNotEmpty() + @IsString() + lastname: string; + + /** + * The new contact's address's id. + */ + @IsInt() + @IsOptional() + address?: number; + + /** + * The contact's phone number. + * This will be validated against the configured country phone numer syntax (default: international). + */ + @IsOptional() + @IsPhoneNumber(config.phone_validation_countrycode) + phone?: string; + + /** + * The contact's email address. + */ + @IsOptional() + @IsEmail() + email?: string; + + /** + * Gets the new contact's address by it's id. + */ + public async getAddress(): Promise
{ + if (!this.address) { return null; } + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; + } + + /** + * Creates a new Address entity from this. + */ + public async toEntity(): Promise { + let contact: GroupContact = new GroupContact(); + contact.firstname = this.firstname; + contact.middlename = this.middlename; + contact.lastname = this.lastname; + contact.email = this.email; + contact.phone = this.phone; + contact.address = await this.getAddress(); + return null; + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateParticipant.ts b/src/models/actions/create/CreateParticipant.ts index 6518666..753822c 100644 --- a/src/models/actions/create/CreateParticipant.ts +++ b/src/models/actions/create/CreateParticipant.ts @@ -1,65 +1,65 @@ -import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { config } from '../../../config'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; -import { Address } from '../../entities/Address'; - -/** - * This classed is used to create a new Participant entity from a json body (post request). - */ -export abstract class CreateParticipant { - /** - * The new participant's first name. - */ - @IsString() - @IsNotEmpty() - firstname: string; - - /** - * The new participant's middle name. - */ - @IsString() - @IsOptional() - middlename?: string; - - /** - * The new participant's last name. - */ - @IsString() - @IsNotEmpty() - lastname: string; - - /** - * The new participant's phone number. - * This will be validated against the configured country phone numer syntax (default: international). - */ - @IsString() - @IsOptional() - @IsPhoneNumber(config.phone_validation_countrycode) - phone?: string; - - /** - * The new participant's e-mail address. - */ - @IsString() - @IsOptional() - @IsEmail() - email?: string; - - /** - * The new participant's address's id. - */ - @IsInt() - @IsOptional() - address?: number; - - /** - * Gets the new participant's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } +import { IsEmail, IsInt, IsNotEmpty, IsOptional, IsPhoneNumber, IsString } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { config } from '../../../config'; +import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { Address } from '../../entities/Address'; + +/** + * This classed is used to create a new Participant entity from a json body (post request). + */ +export abstract class CreateParticipant { + /** + * The new participant's first name. + */ + @IsString() + @IsNotEmpty() + firstname: string; + + /** + * The new participant's middle name. + */ + @IsString() + @IsOptional() + middlename?: string; + + /** + * The new participant's last name. + */ + @IsString() + @IsNotEmpty() + lastname: string; + + /** + * The new participant's phone number. + * This will be validated against the configured country phone numer syntax (default: international). + */ + @IsString() + @IsOptional() + @IsPhoneNumber(config.phone_validation_countrycode) + phone?: string; + + /** + * The new participant's e-mail address. + */ + @IsString() + @IsOptional() + @IsEmail() + email?: string; + + /** + * The new participant's address's id. + */ + @IsInt() + @IsOptional() + address?: number; + + /** + * Gets the new participant's address by it's id. + */ + public async getAddress(): Promise
{ + if (!this.address) { return null; } + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateRunner.ts b/src/models/actions/create/CreateRunner.ts index 2286fdf..addfee2 100644 --- a/src/models/actions/create/CreateRunner.ts +++ b/src/models/actions/create/CreateRunner.ts @@ -1,53 +1,53 @@ -import { IsInt } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; -import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; -import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; -import { Runner } from '../../entities/Runner'; -import { RunnerGroup } from '../../entities/RunnerGroup'; -import { CreateParticipant } from './CreateParticipant'; - -/** - * This classed is used to create a new Runner entity from a json body (post request). - */ -export class CreateRunner extends CreateParticipant { - - /** - * The new runner's group's id. - */ - @IsInt() - group: number; - - /** - * Creates a new Runner entity from this. - */ - public async toEntity(): Promise { - let newRunner: Runner = new Runner(); - - newRunner.firstname = this.firstname; - newRunner.middlename = this.middlename; - newRunner.lastname = this.lastname; - newRunner.phone = this.phone; - newRunner.email = this.email; - newRunner.group = await this.getGroup(); - newRunner.address = await this.getAddress(); - - return newRunner; - } - - /** - * Gets the new runner's group by it's id. - */ - public async getGroup(): Promise { - if (this.group === undefined || this.group === null) { - throw new RunnerTeamNeedsParentError(); - } - if (!isNaN(this.group)) { - let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); - if (!group) { throw new RunnerGroupNotFoundError; } - return group; - } - - throw new RunnerOrganisationWrongTypeError; - } +import { IsInt } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; +import { RunnerOrganisationWrongTypeError } from '../../../errors/RunnerOrganisationErrors'; +import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; +import { Runner } from '../../entities/Runner'; +import { RunnerGroup } from '../../entities/RunnerGroup'; +import { CreateParticipant } from './CreateParticipant'; + +/** + * This classed is used to create a new Runner entity from a json body (post request). + */ +export class CreateRunner extends CreateParticipant { + + /** + * The new runner's group's id. + */ + @IsInt() + group: number; + + /** + * Creates a new Runner entity from this. + */ + public async toEntity(): Promise { + let newRunner: Runner = new Runner(); + + newRunner.firstname = this.firstname; + newRunner.middlename = this.middlename; + newRunner.lastname = this.lastname; + newRunner.phone = this.phone; + newRunner.email = this.email; + newRunner.group = await this.getGroup(); + newRunner.address = await this.getAddress(); + + return newRunner; + } + + /** + * Gets the new runner's group by it's id. + */ + public async getGroup(): Promise { + if (this.group === undefined || this.group === null) { + throw new RunnerTeamNeedsParentError(); + } + if (!isNaN(this.group)) { + let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); + if (!group) { throw new RunnerGroupNotFoundError; } + return group; + } + + throw new RunnerOrganisationWrongTypeError; + } } \ No newline at end of file diff --git a/src/models/actions/create/CreateRunnerOrganisation.ts b/src/models/actions/create/CreateRunnerOrganisation.ts index 1938eee..f1e6369 100644 --- a/src/models/actions/create/CreateRunnerOrganisation.ts +++ b/src/models/actions/create/CreateRunnerOrganisation.ts @@ -1,41 +1,41 @@ -import { IsInt, IsOptional } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; -import { Address } from '../../entities/Address'; -import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; -import { CreateRunnerGroup } from './CreateRunnerGroup'; - -/** - * This classed is used to create a new RunnerOrganisation entity from a json body (post request). - */ -export class CreateRunnerOrganisation extends CreateRunnerGroup { - /** - * The new organisation's address's id. - */ - @IsInt() - @IsOptional() - address?: number; - - /** - * Gets the org's address by it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } - - /** - * Creates a new RunnerOrganisation entity from this. - */ - public async toEntity(): Promise { - let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); - - newRunnerOrganisation.name = this.name; - newRunnerOrganisation.contact = await this.getContact(); - newRunnerOrganisation.address = await this.getAddress(); - - return newRunnerOrganisation; - } +import { IsInt, IsOptional } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { Address } from '../../entities/Address'; +import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; +import { CreateRunnerGroup } from './CreateRunnerGroup'; + +/** + * This classed is used to create a new RunnerOrganisation entity from a json body (post request). + */ +export class CreateRunnerOrganisation extends CreateRunnerGroup { + /** + * The new organisation's address's id. + */ + @IsInt() + @IsOptional() + address?: number; + + /** + * Gets the org's address by it's id. + */ + public async getAddress(): Promise
{ + if (!this.address) { return null; } + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; + } + + /** + * Creates a new RunnerOrganisation entity from this. + */ + public async toEntity(): Promise { + let newRunnerOrganisation: RunnerOrganisation = new RunnerOrganisation(); + + newRunnerOrganisation.name = this.name; + newRunnerOrganisation.contact = await this.getContact(); + newRunnerOrganisation.address = await this.getAddress(); + + return newRunnerOrganisation; + } } \ No newline at end of file diff --git a/src/models/actions/update/UpdateDonor.ts b/src/models/actions/update/UpdateDonor.ts index 8c77ff1..0e35bcf 100644 --- a/src/models/actions/update/UpdateDonor.ts +++ b/src/models/actions/update/UpdateDonor.ts @@ -1,44 +1,44 @@ -import { IsBoolean, IsInt, IsOptional } from 'class-validator'; -import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; -import { Donor } from '../../entities/Donor'; -import { CreateParticipant } from '../create/CreateParticipant'; - -/** - * This class is used to update a Donor entity (via put request). - */ -export class UpdateDonor extends CreateParticipant { - - /** - * 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). - */ - @IsInt() - id: number; - - /** - * Does the updated donor need a receipt? - */ - @IsBoolean() - @IsOptional() - receiptNeeded?: boolean; - - - /** - * Updates a provided Donor entity based on this. - */ - public async update(donor: Donor): Promise { - donor.firstname = this.firstname; - donor.middlename = this.middlename; - donor.lastname = this.lastname; - donor.phone = this.phone; - donor.email = this.email; - donor.receiptNeeded = this.receiptNeeded; - donor.address = await this.getAddress(); - - if (this.receiptNeeded == true && this.address == null) { - throw new DonorReceiptAddressNeededError() - } - - return donor; - } +import { IsBoolean, IsInt, IsOptional } from 'class-validator'; +import { DonorReceiptAddressNeededError } from '../../../errors/DonorErrors'; +import { Donor } from '../../entities/Donor'; +import { CreateParticipant } from '../create/CreateParticipant'; + +/** + * This class is used to update a Donor entity (via put request). + */ +export class UpdateDonor extends CreateParticipant { + + /** + * 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). + */ + @IsInt() + id: number; + + /** + * Does the updated donor need a receipt? + */ + @IsBoolean() + @IsOptional() + receiptNeeded?: boolean; + + + /** + * Updates a provided Donor entity based on this. + */ + public async update(donor: Donor): Promise { + donor.firstname = this.firstname; + donor.middlename = this.middlename; + donor.lastname = this.lastname; + donor.phone = this.phone; + donor.email = this.email; + donor.receiptNeeded = this.receiptNeeded; + donor.address = await this.getAddress(); + + if (this.receiptNeeded == true && this.address == null) { + throw new DonorReceiptAddressNeededError() + } + + return donor; + } } \ No newline at end of file diff --git a/src/models/actions/update/UpdateRunner.ts b/src/models/actions/update/UpdateRunner.ts index e9e323b..df98021 100644 --- a/src/models/actions/update/UpdateRunner.ts +++ b/src/models/actions/update/UpdateRunner.ts @@ -1,54 +1,54 @@ -import { IsInt, IsPositive } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; -import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; -import { Runner } from '../../entities/Runner'; -import { RunnerGroup } from '../../entities/RunnerGroup'; -import { CreateParticipant } from '../create/CreateParticipant'; - -/** - * This class is used to update a Runner entity (via put request). - */ -export class UpdateRunner extends CreateParticipant { - - /** - * 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). - */ - @IsInt() - id: number; - - /** - * The updated runner's group's id. - */ - @IsInt() - @IsPositive() - group: number; - - /** - * Updates a provided Runner entity based on this. - */ - public async update(runner: Runner): Promise { - runner.firstname = this.firstname; - runner.middlename = this.middlename; - runner.lastname = this.lastname; - runner.phone = this.phone; - runner.email = this.email; - runner.group = await this.getGroup(); - runner.address = await this.getAddress(); - - return runner; - } - - /** - * Loads the updated runner's group based on it's id. - */ - public async getGroup(): Promise { - if (this.group === undefined || this.group === null) { - throw new RunnerTeamNeedsParentError(); - } - let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); - if (!group) { throw new RunnerGroupNotFoundError; } - return group; - } +import { IsInt, IsPositive } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { RunnerGroupNotFoundError } from '../../../errors/RunnerGroupErrors'; +import { RunnerTeamNeedsParentError } from '../../../errors/RunnerTeamErrors'; +import { Runner } from '../../entities/Runner'; +import { RunnerGroup } from '../../entities/RunnerGroup'; +import { CreateParticipant } from '../create/CreateParticipant'; + +/** + * This class is used to update a Runner entity (via put request). + */ +export class UpdateRunner extends CreateParticipant { + + /** + * 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). + */ + @IsInt() + id: number; + + /** + * The updated runner's group's id. + */ + @IsInt() + @IsPositive() + group: number; + + /** + * Updates a provided Runner entity based on this. + */ + public async update(runner: Runner): Promise { + runner.firstname = this.firstname; + runner.middlename = this.middlename; + runner.lastname = this.lastname; + runner.phone = this.phone; + runner.email = this.email; + runner.group = await this.getGroup(); + runner.address = await this.getAddress(); + + return runner; + } + + /** + * Loads the updated runner's group based on it's id. + */ + public async getGroup(): Promise { + if (this.group === undefined || this.group === null) { + throw new RunnerTeamNeedsParentError(); + } + let group = await getConnectionManager().get().getRepository(RunnerGroup).findOne({ id: this.group }); + if (!group) { throw new RunnerGroupNotFoundError; } + return group; + } } \ No newline at end of file diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts index 20656ec..2fde7dc 100644 --- a/src/models/actions/update/UpdateRunnerOrganisation.ts +++ b/src/models/actions/update/UpdateRunnerOrganisation.ts @@ -1,48 +1,48 @@ -import { IsInt, IsOptional } from 'class-validator'; -import { getConnectionManager } from 'typeorm'; -import { AddressNotFoundError } from '../../../errors/AddressErrors'; -import { Address } from '../../entities/Address'; -import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; -import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; - -/** - * This class is used to update a RunnerOrganisation entity (via put request). - */ -export class UpdateRunnerOrganisation extends CreateRunnerGroup { - - /** - * 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). - */ - @IsInt() - id: number; - - /** - * The updated organisation's address's id. - */ - @IsInt() - @IsOptional() - address?: number; - - /** - * Loads the organisation's address based on it's id. - */ - public async getAddress(): Promise
{ - if (!this.address) { return null; } - let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); - if (!address) { throw new AddressNotFoundError; } - return address; - } - - /** - * Updates a provided RunnerOrganisation entity based on this. - */ - public async update(organisation: RunnerOrganisation): Promise { - - organisation.name = this.name; - organisation.contact = await this.getContact(); - organisation.address = await this.getAddress(); - - return organisation; - } +import { IsInt, IsOptional } from 'class-validator'; +import { getConnectionManager } from 'typeorm'; +import { AddressNotFoundError } from '../../../errors/AddressErrors'; +import { Address } from '../../entities/Address'; +import { RunnerOrganisation } from '../../entities/RunnerOrganisation'; +import { CreateRunnerGroup } from '../create/CreateRunnerGroup'; + +/** + * This class is used to update a RunnerOrganisation entity (via put request). + */ +export class UpdateRunnerOrganisation extends CreateRunnerGroup { + + /** + * 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). + */ + @IsInt() + id: number; + + /** + * The updated organisation's address's id. + */ + @IsInt() + @IsOptional() + address?: number; + + /** + * Loads the organisation's address based on it's id. + */ + public async getAddress(): Promise
{ + if (!this.address) { return null; } + let address = await getConnectionManager().get().getRepository(Address).findOne({ id: this.address }); + if (!address) { throw new AddressNotFoundError; } + return address; + } + + /** + * Updates a provided RunnerOrganisation entity based on this. + */ + public async update(organisation: RunnerOrganisation): Promise { + + organisation.name = this.name; + organisation.contact = await this.getContact(); + organisation.address = await this.getAddress(); + + return organisation; + } } \ No newline at end of file diff --git a/src/models/actions/update/UpdateUser.ts b/src/models/actions/update/UpdateUser.ts index c9dedcf..f130672 100644 --- a/src/models/actions/update/UpdateUser.ts +++ b/src/models/actions/update/UpdateUser.ts @@ -76,6 +76,7 @@ export class UpdateUser { * Should the user be enabled? */ @IsBoolean() + @IsOptional() enabled: boolean = true; /** diff --git a/src/models/entities/Address.ts b/src/models/entities/Address.ts index dbb7eb3..3728f43 100644 --- a/src/models/entities/Address.ts +++ b/src/models/entities/Address.ts @@ -1,90 +1,90 @@ -import { - IsInt, - IsNotEmpty, - IsOptional, - IsPostalCode, - IsString -} from "class-validator"; -import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; -import { config } from '../../config'; -import { IAddressUser } from './IAddressUser'; - -/** - * Defines the Address entity. - * Implemented this way to prevent any formatting differences. -*/ -@Entity() -export class Address { - /** - * Autogenerated unique id (primary key). - */ - @PrimaryGeneratedColumn() - @IsInt() - id: number; - - /** - * The address's description. - * Optional and mostly for UX. - */ - @Column({ nullable: true }) - @IsString() - @IsOptional() - description?: string; - - /** - * The address's first line. - * Containing the street and house number. - */ - @Column() - @IsString() - @IsNotEmpty() - address1: string; - - /** - * The address's second line. - * Containing optional information. - */ - @Column({ nullable: true }) - @IsString() - @IsOptional() - address2?: string; - - /** - * The address's postal code. - * This will get checked against the postal code syntax for the configured country. - */ - @Column() - @IsString() - @IsNotEmpty() - @IsPostalCode(config.postalcode_validation_countrycode) - postalcode: string; - - /** - * The address's city. - */ - @Column() - @IsString() - @IsNotEmpty() - city: string; - - /** - * The address's country. - */ - @Column() - @IsString() - @IsNotEmpty() - country: string; - - /** - * Used to link the address to participants. - */ - @OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true }) - addressUsers: IAddressUser[]; - - /** - * Turns this entity into it's response class. - */ - public toResponse() { - return new Error("NotImplemented"); - } -} +import { + IsInt, + IsNotEmpty, + IsOptional, + IsPostalCode, + IsString +} from "class-validator"; +import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; +import { config } from '../../config'; +import { IAddressUser } from './IAddressUser'; + +/** + * Defines the Address entity. + * Implemented this way to prevent any formatting differences. +*/ +@Entity() +export class Address { + /** + * Autogenerated unique id (primary key). + */ + @PrimaryGeneratedColumn() + @IsInt() + id: number; + + /** + * The address's description. + * Optional and mostly for UX. + */ + @Column({ nullable: true }) + @IsString() + @IsOptional() + description?: string; + + /** + * The address's first line. + * Containing the street and house number. + */ + @Column() + @IsString() + @IsNotEmpty() + address1: string; + + /** + * The address's second line. + * Containing optional information. + */ + @Column({ nullable: true }) + @IsString() + @IsOptional() + address2?: string; + + /** + * The address's postal code. + * This will get checked against the postal code syntax for the configured country. + */ + @Column() + @IsString() + @IsNotEmpty() + @IsPostalCode(config.postalcode_validation_countrycode) + postalcode: string; + + /** + * The address's city. + */ + @Column() + @IsString() + @IsNotEmpty() + city: string; + + /** + * The address's country. + */ + @Column() + @IsString() + @IsNotEmpty() + country: string; + + /** + * Used to link the address to participants. + */ + @OneToMany(() => IAddressUser, addressUser => addressUser.address, { nullable: true }) + addressUsers: IAddressUser[]; + + /** + * Turns this entity into it's response class. + */ + public toResponse() { + return new Error("NotImplemented"); + } +} diff --git a/src/models/entities/IAddressUser.ts b/src/models/entities/IAddressUser.ts index 3d8eaf9..f3b5d5a 100644 --- a/src/models/entities/IAddressUser.ts +++ b/src/models/entities/IAddressUser.ts @@ -1,20 +1,20 @@ -import { Entity, ManyToOne, PrimaryColumn } from 'typeorm'; -import { Address } from './Address'; - -/** - * The interface(tm) all entities using addresses have to implement. - * This is a abstract class, because apparently typeorm can't really work with interfaces :/ - */ -@Entity() -export abstract class IAddressUser { - @PrimaryColumn() - id: number; - - @ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) - address?: Address - - /** - * Turns this entity into it's response class. - */ - public abstract toResponse(); -} +import { Entity, ManyToOne, PrimaryColumn } from 'typeorm'; +import { Address } from './Address'; + +/** + * The interface(tm) all entities using addresses have to implement. + * This is a abstract class, because apparently typeorm can't really work with interfaces :/ + */ +@Entity() +export abstract class IAddressUser { + @PrimaryColumn() + id: number; + + @ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) + address?: Address + + /** + * Turns this entity into it's response class. + */ + public abstract toResponse(); +} diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts index ebae8fd..9aabe17 100644 --- a/src/models/entities/RunnerOrganisation.ts +++ b/src/models/entities/RunnerOrganisation.ts @@ -1,65 +1,65 @@ -import { IsInt, IsOptional } from "class-validator"; -import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; -import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation'; -import { Address } from './Address'; -import { IAddressUser } from './IAddressUser'; -import { Runner } from './Runner'; -import { RunnerGroup } from "./RunnerGroup"; -import { RunnerTeam } from "./RunnerTeam"; - -/** - * Defines the RunnerOrganisation entity. - * This usually is a school, club or company. -*/ -@ChildEntity() -export class RunnerOrganisation extends RunnerGroup implements IAddressUser { - - /** - * The organisations's address. - */ - @IsOptional() - @ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) - address?: Address; - - /** - * The organisation's teams. - * Used to link teams to a organisation. - */ - @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) - teams: RunnerTeam[]; - - /** - * Returns all runners associated with this organisation (directly or indirectly via teams). - */ - public get allRunners(): Runner[] { - let returnRunners: Runner[] = new Array(); - returnRunners.push(...this.runners); - for (let team of this.teams) { - returnRunners.push(...team.runners) - } - return returnRunners; - } - - /** - * Returns the total distance ran by this group's runners based on all their valid scans. - */ - @IsInt() - public get distance(): number { - 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. - */ - @IsInt() - public get distanceDonationAmount(): number { - return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0); - } - - /** - * Turns this entity into it's response class. - */ - public toResponse(): ResponseRunnerOrganisation { - return new ResponseRunnerOrganisation(this); - } +import { IsInt, IsOptional } from "class-validator"; +import { ChildEntity, ManyToOne, OneToMany } from "typeorm"; +import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation'; +import { Address } from './Address'; +import { IAddressUser } from './IAddressUser'; +import { Runner } from './Runner'; +import { RunnerGroup } from "./RunnerGroup"; +import { RunnerTeam } from "./RunnerTeam"; + +/** + * Defines the RunnerOrganisation entity. + * This usually is a school, club or company. +*/ +@ChildEntity() +export class RunnerOrganisation extends RunnerGroup implements IAddressUser { + + /** + * The organisations's address. + */ + @IsOptional() + @ManyToOne(() => Address, address => address.addressUsers, { nullable: true }) + address?: Address; + + /** + * The organisation's teams. + * Used to link teams to a organisation. + */ + @OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true }) + teams: RunnerTeam[]; + + /** + * Returns all runners associated with this organisation (directly or indirectly via teams). + */ + public get allRunners(): Runner[] { + let returnRunners: Runner[] = new Array(); + returnRunners.push(...this.runners); + for (let team of this.teams) { + returnRunners.push(...team.runners) + } + return returnRunners; + } + + /** + * Returns the total distance ran by this group's runners based on all their valid scans. + */ + @IsInt() + public get distance(): number { + 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. + */ + @IsInt() + public get distanceDonationAmount(): number { + return this.allRunners.reduce((sum, current) => sum + current.distanceDonationAmount, 0); + } + + /** + * Turns this entity into it's response class. + */ + public toResponse(): ResponseRunnerOrganisation { + return new ResponseRunnerOrganisation(this); + } } \ No newline at end of file diff --git a/src/models/entities/User.ts b/src/models/entities/User.ts index c9111ff..fc1b401 100644 --- a/src/models/entities/User.ts +++ b/src/models/entities/User.ts @@ -138,8 +138,10 @@ export class User extends Principal { if (!this.groups) { return returnPermissions; } for (let group of this.groups) { - for (let permission of group.permissions) { - returnPermissions.push(permission); + if (group.permissions) { + for (let permission of group.permissions) { + returnPermissions.push(permission); + } } } return returnPermissions; @@ -159,8 +161,10 @@ export class User extends Principal { if (!this.groups) { return returnPermissions; } for (let group of this.groups) { - for (let permission of group.permissions) { - returnPermissions.push(permission.toString()); + if (group.permissions) { + for (let permission of group.permissions) { + returnPermissions.push(permission.toString()); + } } } return Array.from(new Set(returnPermissions)); diff --git a/src/models/entities/UserGroup.ts b/src/models/entities/UserGroup.ts index 4d45a3f..cd7b06c 100644 --- a/src/models/entities/UserGroup.ts +++ b/src/models/entities/UserGroup.ts @@ -1,40 +1,40 @@ -import { - IsNotEmpty, - IsOptional, - IsString -} from "class-validator"; -import { ChildEntity, Column } from "typeorm"; -import { ResponsePrincipal } from '../responses/ResponsePrincipal'; -import { ResponseUserGroup } from '../responses/ResponseUserGroup'; -import { Principal } from './Principal'; - -/** - * Defines the UserGroup entity. - * This entity describes a group of users with a set of permissions. -*/ -@ChildEntity() -export class UserGroup extends Principal { - - /** - * The group's name - */ - @Column() - @IsNotEmpty() - @IsString() - name: string; - - /** - * The group's description - */ - @Column({ nullable: true }) - @IsOptional() - @IsString() - description?: string; - - /** - * Turns this entity into it's response class. - */ - public toResponse(): ResponsePrincipal { - return new ResponseUserGroup(this); - } +import { + IsNotEmpty, + IsOptional, + IsString +} from "class-validator"; +import { ChildEntity, Column } from "typeorm"; +import { ResponsePrincipal } from '../responses/ResponsePrincipal'; +import { ResponseUserGroup } from '../responses/ResponseUserGroup'; +import { Principal } from './Principal'; + +/** + * Defines the UserGroup entity. + * This entity describes a group of users with a set of permissions. +*/ +@ChildEntity() +export class UserGroup extends Principal { + + /** + * The group's name + */ + @Column() + @IsNotEmpty() + @IsString() + name: string; + + /** + * The group's description + */ + @Column({ nullable: true }) + @IsOptional() + @IsString() + description?: string; + + /** + * Turns this entity into it's response class. + */ + public toResponse(): ResponsePrincipal { + return new ResponseUserGroup(this); + } } \ No newline at end of file diff --git a/src/models/responses/ResponseParticipant.ts b/src/models/responses/ResponseParticipant.ts index 148bcff..135a837 100644 --- a/src/models/responses/ResponseParticipant.ts +++ b/src/models/responses/ResponseParticipant.ts @@ -1,56 +1,56 @@ -import { IsInt, IsString } from "class-validator"; -import { Participant } from '../entities/Participant'; - -/** - * Defines the participant response. -*/ -export abstract class ResponseParticipant { - /** - * The participant's id. - */ - @IsInt() - id: number; - - /** - * The participant's first name. - */ - @IsString() - firstname: string; - - /** - * The participant's middle name. - */ - @IsString() - middlename?: string; - - /** - * The participant's last name. - */ - @IsString() - lastname: string; - - /** - * The participant's phone number. - */ - @IsString() - phone?: string; - - /** - * The participant's e-mail address. - */ - @IsString() - email?: string; - - /** - * Creates a ResponseParticipant object from a participant. - * @param participant The participant the response shall be build for. - */ - public constructor(participant: Participant) { - this.id = participant.id; - this.firstname = participant.firstname; - this.middlename = participant.middlename; - this.lastname = participant.lastname; - this.phone = participant.phone; - this.email = participant.email; - } -} +import { IsInt, IsString } from "class-validator"; +import { Participant } from '../entities/Participant'; + +/** + * Defines the participant response. +*/ +export abstract class ResponseParticipant { + /** + * The participant's id. + */ + @IsInt() + id: number; + + /** + * The participant's first name. + */ + @IsString() + firstname: string; + + /** + * The participant's middle name. + */ + @IsString() + middlename?: string; + + /** + * The participant's last name. + */ + @IsString() + lastname: string; + + /** + * The participant's phone number. + */ + @IsString() + phone?: string; + + /** + * The participant's e-mail address. + */ + @IsString() + email?: string; + + /** + * Creates a ResponseParticipant object from a participant. + * @param participant The participant the response shall be build for. + */ + public constructor(participant: Participant) { + this.id = participant.id; + this.firstname = participant.firstname; + this.middlename = participant.middlename; + this.lastname = participant.lastname; + this.phone = participant.phone; + this.email = participant.email; + } +} diff --git a/src/models/responses/ResponseUser.ts b/src/models/responses/ResponseUser.ts index 526d537..884622c 100644 --- a/src/models/responses/ResponseUser.ts +++ b/src/models/responses/ResponseUser.ts @@ -1,97 +1,99 @@ -import { - IsArray, - IsBoolean, - - IsOptional, - IsString -} from "class-validator"; -import { User } from '../entities/User'; -import { UserGroup } from '../entities/UserGroup'; -import { ResponsePrincipal } from './ResponsePrincipal'; - -/** - * Defines the user response. -*/ -export class ResponseUser extends ResponsePrincipal { - /** - * The user's first name. - */ - @IsString() - firstname: string; - - /** - * The user's middle name. - */ - @IsString() - middlename?: string; - - /** - * The user's last name. - */ - @IsString() - lastname: string; - - /** - * The user's phone number. - */ - @IsString() - phone?: string; - - /** - * The user's e-mail address. - */ - @IsString() - email?: string; - - /** - * The user's username. - */ - @IsString() - username?: string; - - /** - * Is user enabled? - */ - @IsBoolean() - enabled: boolean = true; - - /** - * The user's profile pic (or rather a url pointing to it). - */ - @IsString() - profilePic: string; - - /** - * The groups that the user is a part of. - */ - @IsArray() - @IsOptional() - groups: UserGroup[]; - - /** - * The user's permissions. - * Directly granted or inherited converted to their string form and deduplicated. - */ - @IsArray() - @IsOptional() - permissions: string[]; - - /** - * Creates a ResponseUser object from a user. - * @param user The user the response shall be build for. - */ - public constructor(user: User) { - super(user); - this.firstname = user.firstname; - this.middlename = user.middlename; - this.lastname = user.lastname; - this.phone = user.phone; - this.email = user.email; - this.username = user.username; - this.enabled = user.enabled; - this.profilePic = user.profilePic; - this.groups = user.groups; - this.permissions = user.allPermissions; - this.groups.forEach(function (g) { delete g.permissions }); - } -} +import { + IsArray, + IsBoolean, + + IsOptional, + IsString +} from "class-validator"; +import { User } from '../entities/User'; +import { UserGroup } from '../entities/UserGroup'; +import { ResponsePrincipal } from './ResponsePrincipal'; + +/** + * Defines the user response. +*/ +export class ResponseUser extends ResponsePrincipal { + /** + * The user's first name. + */ + @IsString() + firstname: string; + + /** + * The user's middle name. + */ + @IsString() + middlename?: string; + + /** + * The user's last name. + */ + @IsString() + lastname: string; + + /** + * The user's phone number. + */ + @IsString() + phone?: string; + + /** + * The user's e-mail address. + */ + @IsString() + email?: string; + + /** + * The user's username. + */ + @IsString() + username?: string; + + /** + * Is user enabled? + */ + @IsBoolean() + enabled: boolean = true; + + /** + * The user's profile pic (or rather a url pointing to it). + */ + @IsString() + profilePic: string; + + /** + * The groups that the user is a part of. + */ + @IsArray() + @IsOptional() + groups: UserGroup[]; + + /** + * The user's permissions. + * Directly granted or inherited converted to their string form and deduplicated. + */ + @IsArray() + @IsOptional() + permissions: string[]; + + /** + * Creates a ResponseUser object from a user. + * @param user The user the response shall be build for. + */ + public constructor(user: User) { + super(user); + this.firstname = user.firstname; + this.middlename = user.middlename; + this.lastname = user.lastname; + this.phone = user.phone; + this.email = user.email; + this.username = user.username; + this.enabled = user.enabled; + this.profilePic = user.profilePic; + this.groups = user.groups; + this.permissions = user.allPermissions; + if (this.groups) { + this.groups.forEach(function (g) { delete g.permissions }); + } + } +} diff --git a/src/models/responses/ResponseUserGroup.ts b/src/models/responses/ResponseUserGroup.ts index 6dd428d..e0276f9 100644 --- a/src/models/responses/ResponseUserGroup.ts +++ b/src/models/responses/ResponseUserGroup.ts @@ -1,41 +1,41 @@ -import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator"; -import { Permission } from '../entities/Permission'; -import { UserGroup } from '../entities/UserGroup'; -import { ResponsePrincipal } from './ResponsePrincipal'; - -/** - * Defines the userGroup response. -*/ -export class ResponseUserGroup extends ResponsePrincipal { - /** - * The userGroup's name. - */ - @IsNotEmpty() - @IsString() - name: string; - - /** - * The userGroup's description. - */ - @IsOptional() - @IsString() - description?: string; - - /** - * The userGroup's permissions. - */ - @IsArray() - @IsOptional() - permissions: Permission[]; - - /** - * Creates a ResponseUserGroup object from a userGroup. - * @param group The userGroup the response shall be build for. - */ - public constructor(group: UserGroup) { - super(group); - this.name = group.name; - this.description = group.description; - this.permissions = group.permissions; - } -} +import { IsArray, IsNotEmpty, IsOptional, IsString } from "class-validator"; +import { Permission } from '../entities/Permission'; +import { UserGroup } from '../entities/UserGroup'; +import { ResponsePrincipal } from './ResponsePrincipal'; + +/** + * Defines the userGroup response. +*/ +export class ResponseUserGroup extends ResponsePrincipal { + /** + * The userGroup's name. + */ + @IsNotEmpty() + @IsString() + name: string; + + /** + * The userGroup's description. + */ + @IsOptional() + @IsString() + description?: string; + + /** + * The userGroup's permissions. + */ + @IsArray() + @IsOptional() + permissions: Permission[]; + + /** + * Creates a ResponseUserGroup object from a userGroup. + * @param group The userGroup the response shall be build for. + */ + public constructor(group: UserGroup) { + super(group); + this.name = group.name; + this.description = group.description; + this.permissions = group.permissions; + } +} diff --git a/src/tests/donors/donor_add.spec.ts b/src/tests/donors/donor_add.spec.ts index 954fd2e..30d84c9 100644 --- a/src/tests/donors/donor_add.spec.ts +++ b/src/tests/donors/donor_add.spec.ts @@ -1,94 +1,94 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -// --------------- -describe('POST /api/donors with errors', () => { - it('creating a new donor without any parameters should return 400', async () => { - const res1 = await axios.post(base + '/api/donors', null, axios_config); - expect(res1.status).toEqual(400); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new donor without a last name should return 400', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "middlename": "middle" - }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new donor with a invalid address should return 404', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "middlename": "middle", - "lastname": "last", - "address": 99999999999999999999999999 - }, axios_config); - expect(res2.status).toEqual(404); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new donor with a invalid phone number should return 400', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "middlename": "middle", - "lastname": "last", - "phone": "123" - }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new donor with a invalid mail address should return 400', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "string", - "middlename": "string", - "lastname": "string", - "phone": null, - "email": "123", - }, axios_config); - expect(res2.status).toEqual(400); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new donor without an address but with receiptNeeded=true 406', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "string", - "middlename": "string", - "lastname": "string", - "receiptNeeded": true - }, axios_config); - expect(res2.status).toEqual(406); - expect(res2.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('POST /api/donors working', () => { - it('creating a new donor with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "lastname": "last" - }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new donor with all non-relationship optional params should return 200', async () => { - const res3 = await axios.post(base + '/api/donors', { - "firstname": "first", - "middlename": "middle", - "lastname": "last", - "receiptNeeded": false - }, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +// --------------- +describe('POST /api/donors with errors', () => { + it('creating a new donor without any parameters should return 400', async () => { + const res1 = await axios.post(base + '/api/donors', null, axios_config); + expect(res1.status).toEqual(400); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new donor without a last name should return 400', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "middlename": "middle" + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new donor with a invalid address should return 404', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "middlename": "middle", + "lastname": "last", + "address": 99999999999999999999999999 + }, axios_config); + expect(res2.status).toEqual(404); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new donor with a invalid phone number should return 400', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "middlename": "middle", + "lastname": "last", + "phone": "123" + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new donor with a invalid mail address should return 400', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "string", + "middlename": "string", + "lastname": "string", + "phone": null, + "email": "123", + }, axios_config); + expect(res2.status).toEqual(400); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new donor without an address but with receiptNeeded=true 406', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "string", + "middlename": "string", + "lastname": "string", + "receiptNeeded": true + }, axios_config); + expect(res2.status).toEqual(406); + expect(res2.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('POST /api/donors working', () => { + it('creating a new donor with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "lastname": "last" + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new donor with all non-relationship optional params should return 200', async () => { + const res3 = await axios.post(base + '/api/donors', { + "firstname": "first", + "middlename": "middle", + "lastname": "last", + "receiptNeeded": false + }, axios_config); + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + }); }); \ No newline at end of file diff --git a/src/tests/donors/donor_update.spec.ts b/src/tests/donors/donor_update.spec.ts index 26f6eb6..d77824a 100644 --- a/src/tests/donors/donor_update.spec.ts +++ b/src/tests/donors/donor_update.spec.ts @@ -1,75 +1,75 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -describe('Update donor name after adding', () => { - let added_donor; - it('creating a new runner with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "lastname": "last" - }, axios_config); - added_donor = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('valid update should return 200', async () => { - let donor_copy = added_donor - donor_copy.firstname = "second" - const res3 = await axios.put(base + '/api/donors/' + added_donor.id, donor_copy, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - let updated_donor = res3.data - expect(updated_donor).toEqual(donor_copy); - }); -}); -// --------------- -describe('Update donor id after adding(should fail)', () => { - let added_donor; - it('creating a new donor with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "lastname": "last" - }, axios_config); - added_donor = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('invalid update should return 406', async () => { - added_donor.id++; - const res3 = await axios.put(base + '/api/donors/' + (added_donor.id - 1), added_donor, axios_config); - expect(res3.status).toEqual(406); - expect(res3.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('Update donor without address but receiptNeeded=true should fail', () => { - let added_donor; - it('creating a new donor with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/donors', { - "firstname": "first", - "lastname": "last", - }, axios_config); - added_donor = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('invalid update should return 406', async () => { - added_donor.receiptNeeded = true; - const res3 = await axios.put(base + '/api/donors/' + added_donor.id, added_donor, axios_config); - expect(res3.status).toEqual(406); - expect(res3.headers['content-type']).toContain("application/json") - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('Update donor name after adding', () => { + let added_donor; + it('creating a new runner with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_donor = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('valid update should return 200', async () => { + let donor_copy = added_donor + donor_copy.firstname = "second" + const res3 = await axios.put(base + '/api/donors/' + added_donor.id, donor_copy, axios_config); + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + let updated_donor = res3.data + expect(updated_donor).toEqual(donor_copy); + }); +}); +// --------------- +describe('Update donor id after adding(should fail)', () => { + let added_donor; + it('creating a new donor with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "lastname": "last" + }, axios_config); + added_donor = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('invalid update should return 406', async () => { + added_donor.id++; + const res3 = await axios.put(base + '/api/donors/' + (added_donor.id - 1), added_donor, axios_config); + expect(res3.status).toEqual(406); + expect(res3.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('Update donor without address but receiptNeeded=true should fail', () => { + let added_donor; + it('creating a new donor with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/donors', { + "firstname": "first", + "lastname": "last", + }, axios_config); + added_donor = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('invalid update should return 406', async () => { + added_donor.receiptNeeded = true; + const res3 = await axios.put(base + '/api/donors/' + added_donor.id, added_donor, axios_config); + expect(res3.status).toEqual(406); + expect(res3.headers['content-type']).toContain("application/json") + }); }); \ No newline at end of file diff --git a/src/tests/runnerOrgs/org_add.spec.ts b/src/tests/runnerOrgs/org_add.spec.ts index 6ee0606..e64a8a2 100644 --- a/src/tests/runnerOrgs/org_add.spec.ts +++ b/src/tests/runnerOrgs/org_add.spec.ts @@ -1,90 +1,90 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -describe('GET /api/organisations', () => { - it('basic get should return 200', async () => { - const res = await axios.get(base + '/api/organisations', axios_config); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('POST /api/organisations', () => { - it('creating a new org with just a name should return 200', async () => { - const res = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toContain("application/json") - }); - it('creating a new org with without a name should return 400', async () => { - const res = await axios.post(base + '/api/organisations', { - "name": null - }, axios_config); - expect(res.status).toEqual(400); - expect(res.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('adding + getting from all orgs', () => { - it('creating a new org with just a name should return 200', async () => { - const res = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toContain("application/json") - }); - it('check if org was added', async () => { - const res = await axios.get(base + '/api/organisations', axios_config); - expect(res.status).toEqual(200); - expect(res.headers['content-type']).toContain("application/json") - let added_org = res.data[res.data.length - 1] - delete added_org.id - expect(added_org).toEqual({ - "name": "test123", - "contact": null, - "address": null, - "teams": [] - }) - }); -}); -// --------------- -describe('adding + getting explicitly', () => { - let added_org_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - let added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('check if org was added', async () => { - const res2 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - let added_org2 = res2.data - added_org_id = added_org2.id; - delete added_org2.id - expect(added_org2).toEqual({ - "name": "test123", - "contact": null, - "address": null, - "teams": [] - }) - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('GET /api/organisations', () => { + it('basic get should return 200', async () => { + const res = await axios.get(base + '/api/organisations', axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('POST /api/organisations', () => { + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new org with without a name should return 400', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": null + }, axios_config); + expect(res.status).toEqual(400); + expect(res.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('adding + getting from all orgs', () => { + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('check if org was added', async () => { + const res = await axios.get(base + '/api/organisations', axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let added_org = res.data[res.data.length - 1] + delete added_org.id + expect(added_org).toEqual({ + "name": "test123", + "contact": null, + "address": null, + "teams": [] + }) + }); +}); +// --------------- +describe('adding + getting explicitly', () => { + let added_org_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + let added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('check if org was added', async () => { + const res2 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + let added_org2 = res2.data + added_org_id = added_org2.id; + delete added_org2.id + expect(added_org2).toEqual({ + "name": "test123", + "contact": null, + "address": null, + "teams": [] + }) + }); }); \ No newline at end of file diff --git a/src/tests/runnerOrgs/org_delete.spec.ts b/src/tests/runnerOrgs/org_delete.spec.ts index 08891f8..9c023e8 100644 --- a/src/tests/runnerOrgs/org_delete.spec.ts +++ b/src/tests/runnerOrgs/org_delete.spec.ts @@ -1,132 +1,132 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -// --------------- -describe('adding + deletion (non-existant)', () => { - it('delete', async () => { - const res2 = await axios.delete(base + '/api/organisations/0', axios_config); - expect(res2.status).toEqual(204); - }); -}); -// --------------- -describe('adding + deletion (successfull)', () => { - let added_org_id - let added_org - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('delete', async () => { - const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - let added_org2 = res2.data - added_org_id = added_org2.id; - delete added_org2.id - expect(added_org2).toEqual({ - "name": "test123", - "contact": null, - "address": null, - "teams": [] - }); - }); - it('check if org really was deleted', async () => { - const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); - expect(res3.status).toEqual(404); - expect(res3.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('adding + deletion with teams still existing (without force)', () => { - let added_org; - let added_org_id; - let added_team; - let added_team_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data; - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new team with a valid org should return 200', async () => { - const res2 = await axios.post(base + '/api/teams', { - "name": "test123", - "parentGroup": added_org_id - }, axios_config); - added_team = res2.data; - added_team_id = added_team.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('delete org - this should fail with a 406', async () => { - const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); - expect(res2.status).toEqual(406); - expect(res2.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('adding + deletion with teams still existing (with force)', () => { - let added_org; - let added_org_id; - let added_team; - let added_team_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data; - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new team with a valid org should return 200', async () => { - const res2 = await axios.post(base + '/api/teams', { - "name": "test123", - "parentGroup": added_org_id - }, axios_config); - added_team = res2.data; - added_team_id = added_team.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('delete', async () => { - const res2 = await axios.delete(base + '/api/organisations/' + added_org_id + '?force=true', axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - let added_org2 = res2.data - added_org_id = added_org2.id; - delete added_org2.id; - delete added_org2.teams; - expect(added_org2).toEqual({ - "name": "test123", - "contact": null, - "address": null - }); - }); - it('check if org really was deleted', async () => { - const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); - expect(res3.status).toEqual(404); - expect(res3.headers['content-type']).toContain("application/json") - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +// --------------- +describe('adding + deletion (non-existant)', () => { + it('delete', async () => { + const res2 = await axios.delete(base + '/api/organisations/0', axios_config); + expect(res2.status).toEqual(204); + }); +}); +// --------------- +describe('adding + deletion (successfull)', () => { + let added_org_id + let added_org + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('delete', async () => { + const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + let added_org2 = res2.data + added_org_id = added_org2.id; + delete added_org2.id + expect(added_org2).toEqual({ + "name": "test123", + "contact": null, + "address": null, + "teams": [] + }); + }); + it('check if org really was deleted', async () => { + const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); + expect(res3.status).toEqual(404); + expect(res3.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('adding + deletion with teams still existing (without force)', () => { + let added_org; + let added_org_id; + let added_team; + let added_team_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data; + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a valid org should return 200', async () => { + const res2 = await axios.post(base + '/api/teams', { + "name": "test123", + "parentGroup": added_org_id + }, axios_config); + added_team = res2.data; + added_team_id = added_team.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('delete org - this should fail with a 406', async () => { + const res2 = await axios.delete(base + '/api/organisations/' + added_org_id, axios_config); + expect(res2.status).toEqual(406); + expect(res2.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('adding + deletion with teams still existing (with force)', () => { + let added_org; + let added_org_id; + let added_team; + let added_team_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data; + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a valid org should return 200', async () => { + const res2 = await axios.post(base + '/api/teams', { + "name": "test123", + "parentGroup": added_org_id + }, axios_config); + added_team = res2.data; + added_team_id = added_team.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('delete', async () => { + const res2 = await axios.delete(base + '/api/organisations/' + added_org_id + '?force=true', axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + let added_org2 = res2.data + added_org_id = added_org2.id; + delete added_org2.id; + delete added_org2.teams; + expect(added_org2).toEqual({ + "name": "test123", + "contact": null, + "address": null + }); + }); + it('check if org really was deleted', async () => { + const res3 = await axios.get(base + '/api/organisations/' + added_org_id, axios_config); + expect(res3.status).toEqual(404); + expect(res3.headers['content-type']).toContain("application/json") + }); }); \ No newline at end of file diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts index e6a1055..96ab8c8 100644 --- a/src/tests/runnerOrgs/org_update.spec.ts +++ b/src/tests/runnerOrgs/org_update.spec.ts @@ -1,73 +1,73 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -// --------------- -describe('adding + updating name', () => { - let added_org_id - let added_org - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('update org', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { - "id": added_org_id, - "name": "testlelele", - "contact": null, - "address": null, - }, axios_config); - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - let added_org2 = res2.data - added_org_id = added_org2.id; - delete added_org2.id - expect(added_org2).toEqual({ - "name": "testlelele", - "contact": null, - "address": null, - "teams": [] - }) - }); -}); -// --------------- -describe('adding + try updating id (should return 406)', () => { - let added_org_id - let added_org - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('update org', async () => { - const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { - "id": added_org_id + 1, - "name": "testlelele", - "contact": null, - "address": null, - }, axios_config); - expect(res2.status).toEqual(406); - expect(res2.headers['content-type']).toContain("application/json") - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +// --------------- +describe('adding + updating name', () => { + let added_org_id + let added_org + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('update org', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id, + "name": "testlelele", + "contact": null, + "address": null, + }, axios_config); + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + let added_org2 = res2.data + added_org_id = added_org2.id; + delete added_org2.id + expect(added_org2).toEqual({ + "name": "testlelele", + "contact": null, + "address": null, + "teams": [] + }) + }); +}); +// --------------- +describe('adding + try updating id (should return 406)', () => { + let added_org_id + let added_org + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('update org', async () => { + const res2 = await axios.put(base + '/api/organisations/' + added_org_id, { + "id": added_org_id + 1, + "name": "testlelele", + "contact": null, + "address": null, + }, axios_config); + expect(res2.status).toEqual(406); + expect(res2.headers['content-type']).toContain("application/json") + }); }); \ No newline at end of file diff --git a/src/tests/runnerTeams/team_update.spec.ts b/src/tests/runnerTeams/team_update.spec.ts index 7acacd7..9ad8030 100644 --- a/src/tests/runnerTeams/team_update.spec.ts +++ b/src/tests/runnerTeams/team_update.spec.ts @@ -1,131 +1,131 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); -// --------------- -describe('adding + updating name', () => { - let added_org; - let added_org_id; - let added_team; - let added_team_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data; - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new team with a valid org should return 200', async () => { - const res2 = await axios.post(base + '/api/teams', { - "name": "test123", - "parentGroup": added_org_id - }, axios_config); - added_team = res2.data; - added_team_id = added_team.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('update name', async () => { - const res3 = await axios.put(base + '/api/teams/' + added_team_id, { - "id": added_team_id, - "name": "testlelele", - "contact": null, - "parentGroup": added_org.id - }, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - let updated_team = res3.data; - added_team.name = "testlelele"; - expect(updated_team).toEqual(added_team) - }); -}); -// --------------- -describe('adding + try updating id (should return 406)', () => { - let added_org; - let added_org_id; - let added_team; - let added_team_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data; - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new team with a valid org should return 200', async () => { - const res2 = await axios.post(base + '/api/teams', { - "name": "test123", - "parentGroup": added_org_id - }, axios_config); - added_team = res2.data; - added_team_id = added_team.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('update team', async () => { - added_team.id = added_team.id + 1; - added_team.parentGroup = added_team.parentGroup.id; - const res3 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); - expect(res3.status).toEqual(406); - expect(res3.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('add+update parent org (valid)', () => { - let added_org; - let added_org2; - let added_team; - let added_team_id - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new team with a valid org should return 200', async () => { - const res2 = await axios.post(base + '/api/teams', { - "name": "test123", - "parentGroup": added_org.id - }, axios_config); - added_team = res2.data; - added_team_id = added_team.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new org with just a name should return 200', async () => { - const res3 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org2 = res3.data; - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - }); - it('update team', async () => { - added_team.parentGroup = added_org2.id; - const res4 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); - let updated_team = res4.data; - expect(res4.status).toEqual(200); - expect(res4.headers['content-type']).toContain("application/json") - delete added_org2.address; - delete added_org2.contact; - delete added_org2.teams; - expect(updated_team.parentGroup).toEqual(added_org2) - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); +// --------------- +describe('adding + updating name', () => { + let added_org; + let added_org_id; + let added_team; + let added_team_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data; + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a valid org should return 200', async () => { + const res2 = await axios.post(base + '/api/teams', { + "name": "test123", + "parentGroup": added_org_id + }, axios_config); + added_team = res2.data; + added_team_id = added_team.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('update name', async () => { + const res3 = await axios.put(base + '/api/teams/' + added_team_id, { + "id": added_team_id, + "name": "testlelele", + "contact": null, + "parentGroup": added_org.id + }, axios_config); + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + let updated_team = res3.data; + added_team.name = "testlelele"; + expect(updated_team).toEqual(added_team) + }); +}); +// --------------- +describe('adding + try updating id (should return 406)', () => { + let added_org; + let added_org_id; + let added_team; + let added_team_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data; + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a valid org should return 200', async () => { + const res2 = await axios.post(base + '/api/teams', { + "name": "test123", + "parentGroup": added_org_id + }, axios_config); + added_team = res2.data; + added_team_id = added_team.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('update team', async () => { + added_team.id = added_team.id + 1; + added_team.parentGroup = added_team.parentGroup.id; + const res3 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); + expect(res3.status).toEqual(406); + expect(res3.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('add+update parent org (valid)', () => { + let added_org; + let added_org2; + let added_team; + let added_team_id + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new team with a valid org should return 200', async () => { + const res2 = await axios.post(base + '/api/teams', { + "name": "test123", + "parentGroup": added_org.id + }, axios_config); + added_team = res2.data; + added_team_id = added_team.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new org with just a name should return 200', async () => { + const res3 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org2 = res3.data; + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + }); + it('update team', async () => { + added_team.parentGroup = added_org2.id; + const res4 = await axios.put(base + '/api/teams/' + added_team_id, added_team, axios_config); + let updated_team = res4.data; + expect(res4.status).toEqual(200); + expect(res4.headers['content-type']).toContain("application/json") + delete added_org2.address; + delete added_org2.contact; + delete added_org2.teams; + expect(updated_team.parentGroup).toEqual(added_org2) + }); }); \ No newline at end of file diff --git a/src/tests/runners/runner_update.spec.ts b/src/tests/runners/runner_update.spec.ts index 39acc5f..0cba324 100644 --- a/src/tests/runners/runner_update.spec.ts +++ b/src/tests/runners/runner_update.spec.ts @@ -1,160 +1,160 @@ -import axios from 'axios'; -import { config } from '../../config'; -const base = "http://localhost:" + config.internal_port - -let access_token; -let axios_config; - -beforeAll(async () => { - const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); - access_token = res.data["access_token"]; - axios_config = { - headers: { "authorization": "Bearer " + access_token }, - validateStatus: undefined - }; -}); - -describe('Update runner name after adding', () => { - let added_org; - let added_runner; - let updated_runner; - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new runner with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/runners', { - "firstname": "first", - "lastname": "last", - "group": added_org.id - }, axios_config); - added_runner = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('valid update should return 200', async () => { - let runnercopy = added_runner - runnercopy.firstname = "second" - runnercopy.group = added_runner.group.id; - const res3 = await axios.put(base + '/api/runners/' + added_runner.id, runnercopy, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - updated_runner = res3.data; - delete added_org.address; - delete added_org.contact; - delete added_org.teams; - runnercopy.group = added_org; - expect(updated_runner).toEqual(runnercopy); - }); -}); -// --------------- -describe('Update runner group after adding', () => { - let added_org_id; - let added_org_2; - let added_runner; - let updated_runner; - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - let added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new runner with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/runners', { - "firstname": "first", - "lastname": "last", - "group": added_org_id - }, axios_config); - added_runner = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('creating a new org with just a name should return 200', async () => { - const res3 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org_2 = res3.data - delete added_org_2.address; - delete added_org_2.contact; - delete added_org_2.teams; - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - }); - it('valid group update should return 200', async () => { - added_runner.group = added_org_2.id; - const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - updated_runner = res3.data - expect(updated_runner.group).toEqual(added_org_2); - }); -}); -// --------------- -describe('Update runner id after adding(should fail)', () => { - let added_org_id; - let added_runner; - let added_runner_id; - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - let added_org = res1.data - added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new runner with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/runners', { - "firstname": "first", - "lastname": "last", - "group": added_org_id - }, axios_config); - added_runner = res2.data; - added_runner_id = added_runner.id; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('invalid update should return 406', async () => { - added_runner.id++; - added_runner.group = added_runner.group.id; - const res3 = await axios.put(base + '/api/runners/' + added_runner_id, added_runner, axios_config); - expect(res3.status).toEqual(406); - expect(res3.headers['content-type']).toContain("application/json") - }); -}); -// --------------- -describe('Update runner group with invalid group after adding', () => { - let added_org; - let added_runner; - it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organisations', { - "name": "test123" - }, axios_config); - added_org = res1.data - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") - }); - it('creating a new runner with only needed params should return 200', async () => { - const res2 = await axios.post(base + '/api/runners', { - "firstname": "first", - "lastname": "last", - "group": added_org.id - }, axios_config); - added_runner = res2.data; - expect(res2.status).toEqual(200); - expect(res2.headers['content-type']).toContain("application/json") - }); - it('invalid group update should return 404', async () => { - added_runner.group = 99999999999999999; - const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); - expect(res3.status).toEqual(404); - expect(res3.headers['content-type']).toContain("application/json") - }); +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + const res = await axios.post(base + '/api/auth/login', { username: "demo", password: "demo" }); + access_token = res.data["access_token"]; + axios_config = { + headers: { "authorization": "Bearer " + access_token }, + validateStatus: undefined + }; +}); + +describe('Update runner name after adding', () => { + let added_org; + let added_runner; + let updated_runner; + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new runner with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_org.id + }, axios_config); + added_runner = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('valid update should return 200', async () => { + let runnercopy = added_runner + runnercopy.firstname = "second" + runnercopy.group = added_runner.group.id; + const res3 = await axios.put(base + '/api/runners/' + added_runner.id, runnercopy, axios_config); + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + updated_runner = res3.data; + delete added_org.address; + delete added_org.contact; + delete added_org.teams; + runnercopy.group = added_org; + expect(updated_runner).toEqual(runnercopy); + }); +}); +// --------------- +describe('Update runner group after adding', () => { + let added_org_id; + let added_org_2; + let added_runner; + let updated_runner; + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + let added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new runner with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_org_id + }, axios_config); + added_runner = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('creating a new org with just a name should return 200', async () => { + const res3 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org_2 = res3.data + delete added_org_2.address; + delete added_org_2.contact; + delete added_org_2.teams; + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + }); + it('valid group update should return 200', async () => { + added_runner.group = added_org_2.id; + const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); + expect(res3.status).toEqual(200); + expect(res3.headers['content-type']).toContain("application/json") + updated_runner = res3.data + expect(updated_runner.group).toEqual(added_org_2); + }); +}); +// --------------- +describe('Update runner id after adding(should fail)', () => { + let added_org_id; + let added_runner; + let added_runner_id; + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + let added_org = res1.data + added_org_id = added_org.id; + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new runner with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_org_id + }, axios_config); + added_runner = res2.data; + added_runner_id = added_runner.id; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('invalid update should return 406', async () => { + added_runner.id++; + added_runner.group = added_runner.group.id; + const res3 = await axios.put(base + '/api/runners/' + added_runner_id, added_runner, axios_config); + expect(res3.status).toEqual(406); + expect(res3.headers['content-type']).toContain("application/json") + }); +}); +// --------------- +describe('Update runner group with invalid group after adding', () => { + let added_org; + let added_runner; + it('creating a new org with just a name should return 200', async () => { + const res1 = await axios.post(base + '/api/organisations', { + "name": "test123" + }, axios_config); + added_org = res1.data + expect(res1.status).toEqual(200); + expect(res1.headers['content-type']).toContain("application/json") + }); + it('creating a new runner with only needed params should return 200', async () => { + const res2 = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_org.id + }, axios_config); + added_runner = res2.data; + expect(res2.status).toEqual(200); + expect(res2.headers['content-type']).toContain("application/json") + }); + it('invalid group update should return 404', async () => { + added_runner.group = 99999999999999999; + const res3 = await axios.put(base + '/api/runners/' + added_runner.id, added_runner, axios_config); + expect(res3.status).toEqual(404); + expect(res3.headers['content-type']).toContain("application/json") + }); }); \ No newline at end of file