diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index 03e0ec3..8f5509e 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -1,12 +1,12 @@ import { Request } from "express"; import * as jwt from "jsonwebtoken"; -import { Body, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; +import { Body, Delete, Get, JsonController, OnUndefined, Param, Post, QueryParam, Req, UseBefore } from 'routing-controllers'; import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi'; import { getConnectionManager, Repository } from 'typeorm'; import { config } from '../config'; import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError'; import { MailSendingError } from '../errors/MailErrors'; -import { RunnerEmailNeededError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors'; +import { RunnerEmailNeededError, RunnerHasDistanceDonationsError, RunnerNotFoundError, RunnerSelfserviceTimeoutError } from '../errors/RunnerErrors'; import { RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; import { ScanStationNotFoundError } from '../errors/ScanStationErrors'; import { JwtCreator } from '../jwtcreator'; @@ -23,6 +23,9 @@ import { ResponseScanStation } from '../models/responses/ResponseScanStation'; import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation'; import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; import { ResponseSelfServiceScan } from '../models/responses/ResponseSelfServiceScan'; +import { DonationController } from './DonationController'; +import { RunnerCardController } from './RunnerCardController'; +import { ScanController } from './ScanController'; @JsonController() export class RunnerSelfServiceController { @@ -43,11 +46,50 @@ export class RunnerSelfServiceController { @ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) - @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please contact support.' }) + @OpenAPI({ description: 'Lists all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) async get(@Param('jwt') token: string) { return (new ResponseSelfServiceRunner(await this.getRunner(token))); } + @Delete('/runners/me/:jwt') + @ResponseSchema(ResponseSelfServiceRunner) + @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) + @OnUndefined(RunnerNotFoundError) + @OpenAPI({ description: 'Deletes all information about yourself.
Please provide your runner jwt(that code we gave you during registration) for auth.
If you lost your jwt/personalized link please use the forgot endpoint.' }) + async remove(@Param('jwt') token: string, @QueryParam("force") force: boolean) { + const responseRunner = await this.getRunner(token); + let runner = await this.runnerRepository.findOne({ id: responseRunner.id }); + + if (!runner) { return null; } + if (!runner) { + throw new RunnerNotFoundError(); + } + + const runnerDonations = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["distanceDonations"] })).distanceDonations; + if (runnerDonations.length > 0 && !force) { + throw new RunnerHasDistanceDonationsError(); + } + const donationController = new DonationController(); + for (let donation of runnerDonations) { + await donationController.remove(donation.id, force); + } + + const runnerCards = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["cards"] })).cards; + const cardController = new RunnerCardController; + for (let card of runnerCards) { + await cardController.remove(card.id, force); + } + + const runnerScans = (await this.runnerRepository.findOne({ id: runner.id }, { relations: ["scans"] })).scans; + const scanController = new ScanController; + for (let scan of runnerScans) { + await scanController.remove(scan.id, force); + } + + await this.runnerRepository.delete(runner); + return new ResponseSelfServiceRunner(responseRunner); + } + @Get('/runners/me/:jwt/scans') @ResponseSchema(ResponseSelfServiceScan, { isArray: true }) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) diff --git a/src/tests/selfservice/selfservice_delete.spec.ts b/src/tests/selfservice/selfservice_delete.spec.ts new file mode 100644 index 0000000..9add148 --- /dev/null +++ b/src/tests/selfservice/selfservice_delete.spec.ts @@ -0,0 +1,66 @@ +import axios from 'axios'; +import { config } from '../../config'; +const base = "http://localhost:" + config.internal_port + +let access_token; +let axios_config; + +beforeAll(async () => { + jest.setTimeout(20000); + 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('delete selfservice runner invalid', () => { + let added_runner; + it('registering as citizen with minimal params should return 200', async () => { + const res = await axios.post(base + '/api/runners/register', { + "firstname": "string", + "lastname": "string", + "email": "user@example.com" + }, axios_config); + added_runner = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete with valid jwt should return 200', async () => { + const res = await axios.delete(base + '/api/runners/me/' + added_runner.token, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete with valid jwt but non-existant runner should return 200', async () => { + const res = await axios.delete(base + '/api/runners/me/' + added_runner.token, axios_config); + expect(res.status).toEqual(404); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete with invalid jwt should return 401', async () => { + const res = await axios.delete(base + '/api/runners/me/123.123', axios_config); + expect(res.status).toEqual(401); + expect(res.headers['content-type']).toContain("application/json"); + }); +}); +// --------------- +describe('delete selfservice runner valid', () => { + let added_runner; + it('registering as citizen with minimal params should return 200', async () => { + const res = await axios.post(base + '/api/runners/register', { + "firstname": "string", + "lastname": "string", + "email": "user@example.com" + }, axios_config); + added_runner = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + }); + it('delete with valid jwt should return 200', async () => { + const res = await axios.delete(base + '/api/runners/me/' + added_runner.token, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + delete added_runner.token; + expect(res.data).toEqual(added_runner); + }); +}); \ No newline at end of file