diff --git a/src/controllers/GroupContactController.ts b/src/controllers/GroupContactController.ts
index 3d6f83e..f6f6364 100644
--- a/src/controllers/GroupContactController.ts
+++ b/src/controllers/GroupContactController.ts
@@ -28,7 +28,7 @@ export class GroupContactController {
@OpenAPI({ description: 'Lists all contacts.
This includes the contact\'s associated groups.' })
async getAll() {
let responseContacts: ResponseGroupContact[] = new Array();
- const contacts = await this.contactRepository.find({ relations: ['groups'] });
+ const contacts = await this.contactRepository.find({ relations: ['groups', 'groups.parentGroup'] });
contacts.forEach(contact => {
responseContacts.push(contact.toResponse());
});
@@ -42,7 +42,7 @@ export class GroupContactController {
@OnUndefined(GroupContactNotFoundError)
@OpenAPI({ description: 'Lists all information about the contact whose id got provided.
This includes the contact\'s associated groups.' })
async getOne(@Param('id') id: number) {
- let contact = await this.contactRepository.findOne({ id: id }, { relations: ['groups'] })
+ let contact = await this.contactRepository.findOne({ id: id }, { relations: ['groups', 'groups.parentGroup'] })
if (!contact) { throw new GroupContactNotFoundError(); }
return contact.toResponse();
}
@@ -61,7 +61,7 @@ export class GroupContactController {
}
contact = await this.contactRepository.save(contact)
- return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse();
+ return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups', 'groups.parentGroup'] })).toResponse();
}
@Put('/:id')
@@ -83,7 +83,7 @@ export class GroupContactController {
}
await this.contactRepository.save(await contact.update(oldContact));
- return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups'] })).toResponse();
+ return (await this.contactRepository.findOne({ id: contact.id }, { relations: ['groups', 'groups.parentGroup'] })).toResponse();
}
@Delete('/:id')
@@ -95,7 +95,7 @@ export class GroupContactController {
async remove(@Param("id") id: number, @QueryParam("force") force: boolean) {
let contact = await this.contactRepository.findOne({ id: id });
if (!contact) { return null; }
- const responseContact = await this.contactRepository.findOne(contact, { relations: ['groups'] });
+ const responseContact = await this.contactRepository.findOne(contact, { relations: ['groups', 'groups.parentGroup'] });
for (let group of responseContact.groups) {
group.contact = null;
await getConnection().getRepository(RunnerGroup).save(group);
diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts
index b2d2539..07789ee 100644
--- a/src/controllers/RunnerSelfServiceController.ts
+++ b/src/controllers/RunnerSelfServiceController.ts
@@ -1,23 +1,31 @@
import * as jwt from "jsonwebtoken";
-import { Get, JsonController, OnUndefined, Param } from 'routing-controllers';
+import { Body, Get, JsonController, OnUndefined, Param, Post } from 'routing-controllers';
import { OpenAPI, ResponseSchema } from 'routing-controllers-openapi';
import { getConnectionManager, Repository } from 'typeorm';
import { config } from '../config';
-import { InvalidCredentialsError } from '../errors/AuthError';
-import { RunnerNotFoundError } from '../errors/RunnerErrors';
+import { InvalidCredentialsError, JwtNotProvidedError } from '../errors/AuthError';
+import { RunnerEmailNeededError, RunnerNotFoundError } from '../errors/RunnerErrors';
+import { RunnerOrganisationNotFoundError } from '../errors/RunnerOrganisationErrors';
+import { JwtCreator } from '../jwtcreator';
+import { CreateSelfServiceCitizenRunner } from '../models/actions/create/CreateSelfServiceCitizenRunner';
+import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServiceRunner';
import { Runner } from '../models/entities/Runner';
+import { RunnerGroup } from '../models/entities/RunnerGroup';
+import { RunnerOrganisation } from '../models/entities/RunnerOrganisation';
import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner';
@JsonController('/runners')
export class RunnerSelfServiceController {
private runnerRepository: Repository;
+ private orgRepository: Repository;
/**
* Gets the repository of this controller's model/entity.
*/
constructor() {
this.runnerRepository = getConnectionManager().get().getRepository(Runner);
+ this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganisation);
}
@Get('/me/:jwt')
@@ -29,11 +37,40 @@ export class RunnerSelfServiceController {
return (new ResponseSelfServiceRunner(await this.getRunner(token)));
}
+ @Post('/register')
+ @ResponseSchema(ResponseSelfServiceRunner)
+ @ResponseSchema(RunnerEmailNeededError, { statusCode: 406 })
+ @OpenAPI({ description: 'Create a new selfservice runner in the citizen org.
This endpoint shoud be used to allow "everyday citizen" to register themselves.
You have to provide a mail address, b/c the future we\'ll implement email verification.' })
+ async registerRunner(@Body({ validate: true }) createRunner: CreateSelfServiceCitizenRunner) {
+ let runner = await createRunner.toEntity();
+
+ runner = await this.runnerRepository.save(runner);
+ let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }));
+ response.token = JwtCreator.createSelfService(runner);
+ return response;
+ }
+
+ @Post('/register/:token')
+ @ResponseSchema(ResponseSelfServiceRunner)
+ @ResponseSchema(RunnerOrganisationNotFoundError, { statusCode: 404 })
+ @OpenAPI({ description: 'Create a new selfservice runner in a provided org.
The orgs get provided and authorized via api tokens that can be optained via the /organisations endpoint.' })
+ async registerOrganisationRunner(@Param('token') token: string, @Body({ validate: true }) createRunner: CreateSelfServiceRunner) {
+ const org = await this.getOrgansisation(token);
+
+ let runner = await createRunner.toEntity(org);
+ runner = await this.runnerRepository.save(runner);
+
+ let response = new ResponseSelfServiceRunner(await this.runnerRepository.findOne(runner, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] }));
+ response.token = JwtCreator.createSelfService(runner);
+ return response;
+ }
+
/**
* Get's a runner by a provided jwt token.
* @param token The runner jwt provided by the runner to identitfy themselves.
*/
private async getRunner(token: string): Promise {
+ if (token == "") { throw new JwtNotProvidedError(); }
let jwtPayload = undefined
try {
jwtPayload = jwt.verify(token, config.jwt_secret);
@@ -41,9 +78,21 @@ export class RunnerSelfServiceController {
throw new InvalidCredentialsError();
}
- const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
+ const runner = await this.runnerRepository.findOne({ id: jwtPayload["id"] }, { relations: ['scans', 'group', 'group.parentGroup', 'scans.track', 'cards', 'distanceDonations', 'distanceDonations.donor', 'distanceDonations.runner', 'distanceDonations.runner.scans', 'distanceDonations.runner.scans.track'] });
if (!runner) { throw new RunnerNotFoundError() }
return runner;
}
+ /**
+ * Get's a runner org by a provided registration api key.
+ * @param token The organisation's registration api token.
+ */
+ private async getOrgansisation(token: string): Promise {
+ token = Buffer.from(token, 'base64').toString('utf8');
+
+ const organisation = await this.orgRepository.findOne({ key: token });
+ if (!organisation) { throw new RunnerOrganisationNotFoundError; }
+
+ return organisation;
+ }
}
\ No newline at end of file
diff --git a/src/errors/RunnerErrors.ts b/src/errors/RunnerErrors.ts
index 4dad85f..12621f0 100644
--- a/src/errors/RunnerErrors.ts
+++ b/src/errors/RunnerErrors.ts
@@ -35,6 +35,17 @@ export class RunnerGroupNeededError extends NotAcceptableError {
message = "Runner's need to be part of one group (team or organisation)! \n You provided neither."
}
+/**
+ * Error to throw when a citizen runner has no mail-address.
+ */
+export class RunnerEmailNeededError extends NotAcceptableError {
+ @IsString()
+ name = "RunnerEmailNeededError"
+
+ @IsString()
+ message = "Citizenrunners have to provide an email address for verification and contacting."
+}
+
/**
* Error to throw when a runner still has distance donations associated.
*/
diff --git a/src/jwtcreator.ts b/src/jwtcreator.ts
index 0b8ff7e..15b2d13 100644
--- a/src/jwtcreator.ts
+++ b/src/jwtcreator.ts
@@ -1,6 +1,7 @@
import { IsBoolean, IsEmail, IsInt, IsNotEmpty, IsOptional, IsString, IsUUID } from 'class-validator';
import * as jsonwebtoken from "jsonwebtoken";
import { config } from './config';
+import { Runner } from './models/entities/Runner';
import { User } from './models/entities/User';
/**
@@ -34,6 +35,19 @@ export class JwtCreator {
}, config.jwt_secret)
}
+ /**
+ * Creates a new selfservice token for a given runner.
+ * @param runner Runner entity that the access token shall be created for.
+ * @param expiry_timestamp Timestamp for the token expiry. Will be set about 9999 years if none provided.
+ */
+ public static createSelfService(runner: Runner, expiry_timestamp?: number) {
+ if (!expiry_timestamp) { expiry_timestamp = Math.floor(Date.now() / 1000) + 36000 * 60 * 24 * 365 * 9999; }
+ return jsonwebtoken.sign({
+ id: runner.id,
+ exp: expiry_timestamp
+ }, config.jwt_secret)
+ }
+
/**
* Creates a new password reset token for a given user.
* The token is valid for 15 minutes or 1 use - whatever comes first.
diff --git a/src/models/actions/create/CreateRunnerOrganisation.ts b/src/models/actions/create/CreateRunnerOrganisation.ts
index e99d851..58b406c 100644
--- a/src/models/actions/create/CreateRunnerOrganisation.ts
+++ b/src/models/actions/create/CreateRunnerOrganisation.ts
@@ -1,8 +1,10 @@
-import { IsObject, IsOptional } from 'class-validator';
+import { IsBoolean, IsObject, IsOptional } from 'class-validator';
+import * as uuid from 'uuid';
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).
*/
@@ -14,6 +16,13 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup {
@IsObject()
address?: Address;
+ /**
+ * Is registration enabled for the new organisation?
+ */
+ @IsOptional()
+ @IsBoolean()
+ registrationEnabled?: boolean = false;
+
/**
* Creates a new RunnerOrganisation entity from this.
*/
@@ -25,6 +34,10 @@ export class CreateRunnerOrganisation extends CreateRunnerGroup {
newRunnerOrganisation.address = this.address;
Address.validate(newRunnerOrganisation.address);
+ if (this.registrationEnabled) {
+ newRunnerOrganisation.key = uuid.v4().toUpperCase();
+ }
+
return newRunnerOrganisation;
}
}
\ No newline at end of file
diff --git a/src/models/actions/create/CreateSelfServiceCitizenRunner.ts b/src/models/actions/create/CreateSelfServiceCitizenRunner.ts
new file mode 100644
index 0000000..1b9514b
--- /dev/null
+++ b/src/models/actions/create/CreateSelfServiceCitizenRunner.ts
@@ -0,0 +1,52 @@
+import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
+import { getConnection } from 'typeorm';
+import { RunnerEmailNeededError } from '../../../errors/RunnerErrors';
+import { Address } from '../../entities/Address';
+import { Runner } from '../../entities/Runner';
+import { RunnerOrganisation } from '../../entities/RunnerOrganisation';
+import { CreateParticipant } from './CreateParticipant';
+
+/**
+ * This classed is used to create a new Runner entity from a json body (post request).
+ */
+export class CreateSelfServiceCitizenRunner extends CreateParticipant {
+
+ /**
+ * The new runners's e-mail address.
+ * Must be provided for email-verification to work.
+ */
+ @IsString()
+ @IsNotEmpty()
+ @IsEmail()
+ email: string;
+
+ /**
+ * 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;
+
+ if (!newRunner.email) {
+ throw new RunnerEmailNeededError();
+ }
+
+ newRunner.group = await this.getGroup();
+ newRunner.address = this.address;
+ Address.validate(newRunner.address);
+
+ return newRunner;
+ }
+
+ /**
+ * Gets the new runner's group by it's id.
+ */
+ public async getGroup(): Promise {
+ return await getConnection().getRepository(RunnerOrganisation).findOne({ id: 1 });
+ }
+}
\ No newline at end of file
diff --git a/src/models/actions/create/CreateSelfServiceRunner.ts b/src/models/actions/create/CreateSelfServiceRunner.ts
new file mode 100644
index 0000000..953db43
--- /dev/null
+++ b/src/models/actions/create/CreateSelfServiceRunner.ts
@@ -0,0 +1,55 @@
+import { IsInt, IsOptional } from 'class-validator';
+import { getConnection } from 'typeorm';
+import { RunnerTeamNotFoundError } from '../../../errors/RunnerTeamErrors';
+import { Address } from '../../entities/Address';
+import { Runner } from '../../entities/Runner';
+import { RunnerGroup } from '../../entities/RunnerGroup';
+import { RunnerTeam } from '../../entities/RunnerTeam';
+import { CreateParticipant } from './CreateParticipant';
+
+/**
+ * This classed is used to create a new Runner entity from a json body (post request).
+ */
+export class CreateSelfServiceRunner extends CreateParticipant {
+
+ /**
+ * The new runner's team's id.
+ * The team has to be a part of the runner's org.
+ * The team property may get ignored.
+ * If no team get's provided the runner's group will be their org.
+ */
+ @IsInt()
+ @IsOptional()
+ team?: number;
+
+ /**
+ * Creates a new Runner entity from this.
+ */
+ public async toEntity(group: RunnerGroup): 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(group);
+ newRunner.address = this.address;
+ Address.validate(newRunner.address);
+
+ return newRunner;
+ }
+
+ /**
+ * Gets the new runner's group by it's id.
+ */
+ public async getGroup(group: RunnerGroup): Promise {
+ if (!this.team) {
+ return group;
+ }
+ const team = await getConnection().getRepository(RunnerTeam).findOne({ id: this.team }, { relations: ["parentGroup"] });
+ if (!team) { throw new RunnerTeamNotFoundError(); }
+ if (team.parentGroup.id != group.id) { throw new RunnerTeamNotFoundError(); }
+ return team;
+ }
+}
\ No newline at end of file
diff --git a/src/models/actions/update/UpdateRunnerOrganisation.ts b/src/models/actions/update/UpdateRunnerOrganisation.ts
index b34bc8f..ade982c 100644
--- a/src/models/actions/update/UpdateRunnerOrganisation.ts
+++ b/src/models/actions/update/UpdateRunnerOrganisation.ts
@@ -1,4 +1,5 @@
-import { IsInt, IsObject, IsOptional } from 'class-validator';
+import { IsBoolean, IsInt, IsObject, IsOptional } from 'class-validator';
+import * as uuid from 'uuid';
import { Address } from '../../entities/Address';
import { RunnerOrganisation } from '../../entities/RunnerOrganisation';
import { CreateRunnerGroup } from '../create/CreateRunnerGroup';
@@ -22,6 +23,13 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup {
@IsObject()
address?: Address;
+ /**
+ * Is registration enabled for the updated organisation?
+ */
+ @IsOptional()
+ @IsBoolean()
+ registrationEnabled?: boolean = false;
+
/**
* Updates a provided RunnerOrganisation entity based on this.
*/
@@ -33,6 +41,13 @@ export class UpdateRunnerOrganisation extends CreateRunnerGroup {
else { organisation.address = this.address; }
Address.validate(organisation.address);
+ if (this.registrationEnabled && !organisation.key) {
+ organisation.key = uuid.v4().toUpperCase();
+ }
+ else {
+ organisation.key = null;
+ }
+
return organisation;
}
}
\ No newline at end of file
diff --git a/src/models/entities/RunnerOrganisation.ts b/src/models/entities/RunnerOrganisation.ts
index e5f3330..e137cdf 100644
--- a/src/models/entities/RunnerOrganisation.ts
+++ b/src/models/entities/RunnerOrganisation.ts
@@ -1,4 +1,4 @@
-import { IsInt, IsOptional } from "class-validator";
+import { IsInt, IsOptional, IsString } from "class-validator";
import { ChildEntity, Column, OneToMany } from "typeorm";
import { ResponseRunnerOrganisation } from '../responses/ResponseRunnerOrganisation';
import { Address } from './Address';
@@ -27,6 +27,16 @@ export class RunnerOrganisation extends RunnerGroup {
@OneToMany(() => RunnerTeam, team => team.parentGroup, { nullable: true })
teams: RunnerTeam[];
+ /**
+ * The organisation's api key for self-service registration.
+ * The api key can be used for the /runners/register/:token endpoint.
+ * Is has to be base64 encoded if used via the api (to keep url-safety).
+ */
+ @Column({ nullable: true, unique: true })
+ @IsString()
+ @IsOptional()
+ key?: string;
+
/**
* Returns all runners associated with this organisation (directly or indirectly via teams).
*/
diff --git a/src/models/responses/ResponseRunnerOrganisation.ts b/src/models/responses/ResponseRunnerOrganisation.ts
index 69ccaf1..40c3c57 100644
--- a/src/models/responses/ResponseRunnerOrganisation.ts
+++ b/src/models/responses/ResponseRunnerOrganisation.ts
@@ -1,8 +1,13 @@
import {
IsArray,
+ IsBase64,
+
+ IsBoolean,
+
IsObject,
- IsOptional
+ IsOptional,
+ IsString
} from "class-validator";
import { Address } from '../entities/Address';
import { RunnerOrganisation } from '../entities/RunnerOrganisation';
@@ -27,6 +32,22 @@ export class ResponseRunnerOrganisation extends ResponseRunnerGroup {
@IsArray()
teams: RunnerTeam[];
+ /**
+ * The organisation's registration key.
+ * If registration is disabled this is null.
+ */
+ @IsString()
+ @IsOptional()
+ @IsBase64()
+ registrationKey?: string;
+
+ /**
+ * Is registration enabled for the organisation?
+ */
+ @IsOptional()
+ @IsBoolean()
+ registrationEnabled?: boolean = true;
+
/**
* Creates a ResponseRunnerOrganisation object from a runnerOrganisation.
* @param org The runnerOrganisation the response shall be build for.
@@ -35,5 +56,7 @@ export class ResponseRunnerOrganisation extends ResponseRunnerGroup {
super(org);
this.address = org.address;
this.teams = org.teams;
+ if (!org.key) { this.registrationEnabled = false; }
+ else { this.registrationKey = Buffer.from(org.key).toString('base64'); }
}
}
diff --git a/src/models/responses/ResponseRunnerTeam.ts b/src/models/responses/ResponseRunnerTeam.ts
index 0a15302..684005e 100644
--- a/src/models/responses/ResponseRunnerTeam.ts
+++ b/src/models/responses/ResponseRunnerTeam.ts
@@ -1,7 +1,7 @@
import { IsNotEmpty, IsObject } from "class-validator";
-import { RunnerOrganisation } from '../entities/RunnerOrganisation';
import { RunnerTeam } from '../entities/RunnerTeam';
import { ResponseRunnerGroup } from './ResponseRunnerGroup';
+import { ResponseRunnerOrganisation } from './ResponseRunnerOrganisation';
/**
* Defines the runnerTeam response.
@@ -13,7 +13,7 @@ export class ResponseRunnerTeam extends ResponseRunnerGroup {
*/
@IsObject()
@IsNotEmpty()
- parentGroup: RunnerOrganisation;
+ parentGroup: ResponseRunnerOrganisation;
/**
* Creates a ResponseRunnerTeam object from a runnerTeam.
@@ -21,6 +21,6 @@ export class ResponseRunnerTeam extends ResponseRunnerGroup {
*/
public constructor(team: RunnerTeam) {
super(team);
- this.parentGroup = team.parentGroup;
+ this.parentGroup = team.parentGroup.toResponse();
}
}
diff --git a/src/models/responses/ResponseSelfServiceRunner.ts b/src/models/responses/ResponseSelfServiceRunner.ts
index 6727c76..fa9a1f1 100644
--- a/src/models/responses/ResponseSelfServiceRunner.ts
+++ b/src/models/responses/ResponseSelfServiceRunner.ts
@@ -1,4 +1,4 @@
-import { IsInt, IsString } from "class-validator";
+import { IsInt, IsOptional, IsString } from "class-validator";
import { DistanceDonation } from '../entities/DistanceDonation';
import { Runner } from '../entities/Runner';
import { RunnerGroup } from '../entities/RunnerGroup';
@@ -36,6 +36,14 @@ export class ResponseSelfServiceRunner extends ResponseParticipant {
@IsString()
donations: ResponseSelfServiceDonation[]
+ /**
+ * The runner's self-service jwt for auth.
+ * Will only get delivered on registration/via email.
+ */
+ @IsString()
+ @IsOptional()
+ token: string;
+
/**
* Creates a ResponseRunner object from a runner.
* @param runner The user the response shall be build for.
diff --git a/src/tests/contacts/contact_add.spec.ts b/src/tests/contacts/contact_add.spec.ts
index 0cb5160..f17233f 100644
--- a/src/tests/contacts/contact_add.spec.ts
+++ b/src/tests/contacts/contact_add.spec.ts
@@ -123,7 +123,6 @@ describe('POST /api/contacts working (with group)', () => {
"parentGroup": added_org.id
}, axios_config);
delete res.data.contact;
- delete res.data.parentGroup;
added_team = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
diff --git a/src/tests/contacts/contact_update.spec.ts b/src/tests/contacts/contact_update.spec.ts
index e6d733d..89af1b3 100644
--- a/src/tests/contacts/contact_update.spec.ts
+++ b/src/tests/contacts/contact_update.spec.ts
@@ -74,7 +74,6 @@ describe('Update contact group after adding (should work)', () => {
"parentGroup": added_org.id
}, axios_config);
delete res.data.contact;
- delete res.data.parentGroup;
added_team = res.data;
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json")
@@ -112,6 +111,7 @@ describe('Update contact group after adding (should work)', () => {
"lastname": "last",
"groups": added_team.id
}, axios_config);
+ console.log(res.data)
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
expect(res.data).toEqual({
diff --git a/src/tests/runnerOrgs/org_add.spec.ts b/src/tests/runnerOrgs/org_add.spec.ts
index 9d939ef..4a376a8 100644
--- a/src/tests/runnerOrgs/org_add.spec.ts
+++ b/src/tests/runnerOrgs/org_add.spec.ts
@@ -63,6 +63,7 @@ describe('adding + getting from all orgs', () => {
"country": null,
"postalcode": null,
},
+ "registrationEnabled": false,
"teams": []
})
});
@@ -96,6 +97,7 @@ describe('adding + getting explicitly', () => {
"country": null,
"postalcode": null,
},
+ "registrationEnabled": false,
"teams": []
})
});
diff --git a/src/tests/runnerOrgs/org_delete.spec.ts b/src/tests/runnerOrgs/org_delete.spec.ts
index d28faa1..b16c52d 100644
--- a/src/tests/runnerOrgs/org_delete.spec.ts
+++ b/src/tests/runnerOrgs/org_delete.spec.ts
@@ -51,6 +51,7 @@ describe('adding + deletion (successfull)', () => {
"country": null,
"postalcode": null,
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -134,6 +135,7 @@ describe('adding + deletion with teams still existing (with force)', () => {
"country": null,
"postalcode": null,
},
+ "registrationEnabled": false,
});
});
it('check if org really was deleted', async () => {
diff --git a/src/tests/runnerOrgs/org_update.spec.ts b/src/tests/runnerOrgs/org_update.spec.ts
index 389dc84..254fd87 100644
--- a/src/tests/runnerOrgs/org_update.spec.ts
+++ b/src/tests/runnerOrgs/org_update.spec.ts
@@ -49,6 +49,7 @@ describe('adding + updating name', () => {
"country": null,
"postalcode": null,
},
+ "registrationEnabled": false,
"teams": []
})
});
@@ -116,6 +117,7 @@ describe('adding + updateing address valid)', () => {
"country": "Burkina Faso",
"postalcode": "90174"
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -145,6 +147,7 @@ describe('adding + updateing address valid)', () => {
"country": "Burkina Faso",
"postalcode": "90174"
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -174,6 +177,7 @@ describe('adding + updateing address valid)', () => {
"country": "Burkina Faso",
"postalcode": "90174"
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -203,6 +207,7 @@ describe('adding + updateing address valid)', () => {
"country": "Burkina Faso",
"postalcode": "90174"
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -232,6 +237,7 @@ describe('adding + updateing address valid)', () => {
"country": "Germany",
"postalcode": "90174"
},
+ "registrationEnabled": false,
"teams": []
});
});
@@ -261,14 +267,15 @@ describe('adding + updateing address valid)', () => {
"country": "Germany",
"postalcode": "91065"
},
+ "registrationEnabled": false,
"teams": []
});
});
- it('removing org\'s should return 200', async () => {
+ it('removing org\'s address should return 200', async () => {
const res = await axios.put(base + '/api/organisations/' + added_org_id, {
"id": added_org_id,
"name": "testlelele",
- "contact": null
+ "contact": null,
}, axios_config);
expect(res.status).toEqual(200);
expect(res.headers['content-type']).toContain("application/json");
@@ -283,6 +290,7 @@ describe('adding + updateing address valid)', () => {
"country": null,
"postalcode": null
},
+ "registrationEnabled": false,
"teams": []
});
});
diff --git a/src/tests/runnerTeams/team_update.spec.ts b/src/tests/runnerTeams/team_update.spec.ts
index 0257bfd..9f7007d 100644
--- a/src/tests/runnerTeams/team_update.spec.ts
+++ b/src/tests/runnerTeams/team_update.spec.ts
@@ -120,11 +120,13 @@ describe('add+update parent org (valid)', () => {
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.contact;
delete added_org2.teams;
- expect(updated_team.parentGroup).toEqual(added_org2)
+ delete added_org2.registrationEnabled;
+ delete res4.data.parentGroup.key;
+ delete res4.data.parentGroup.registrationEnabled;
+ expect(res4.data.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 92c80e7..81f80ac 100644
--- a/src/tests/runners/runner_update.spec.ts
+++ b/src/tests/runners/runner_update.spec.ts
@@ -17,11 +17,11 @@ beforeAll(async () => {
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);
+ delete res1.data.registrationEnabled;
added_org = res1.data
expect(res1.status).toEqual(200);
expect(res1.headers['content-type']).toContain("application/json")
@@ -43,11 +43,13 @@ describe('Update runner name after adding', () => {
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.contact;
delete added_org.teams;
runnercopy.group = added_org;
- expect(updated_runner).toEqual(runnercopy);
+ delete res3.data.group.key;
+ delete res3.data.group.registrationEnabled;
+ delete runnercopy.group.registrationEnabled;
+ expect(res3.data).toEqual(runnercopy);
});
});
// ---------------
@@ -86,9 +88,12 @@ describe('Update runner group after adding', () => {
});
it('valid group update should return 200', async () => {
added_runner.group = added_org_2.id;
+ delete added_org_2.registrationEnabled;
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")
+ delete res3.data.group.key;
+ delete res3.data.group.registrationEnabled;
expect(res3.data.group).toEqual(added_org_2);
});
});
diff --git a/src/tests/selfservice/selfservice_get.spec.ts b/src/tests/selfservice/selfservice_get.spec.ts
new file mode 100644
index 0000000..5074e57
--- /dev/null
+++ b/src/tests/selfservice/selfservice_get.spec.ts
@@ -0,0 +1,43 @@
+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/runners/me invalid should return fail', () => {
+ it('get with invalid jwt should return 401', async () => {
+ const res = await axios.get(base + '/api/runners/me/123.123', axios_config);
+ expect(res.status).toEqual(401);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
+// ---------------
+describe('register + get should return 200', () => {
+ let added_runner;
+ it('registering as citizen should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com"
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ added_runner = res.data;
+ });
+ it('get with valid jwt should return 200', async () => {
+ const res = await axios.get(base + '/api/runners/me/' + added_runner.token, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
\ No newline at end of file
diff --git a/src/tests/selfservice/selfservice_register.spec.ts b/src/tests/selfservice/selfservice_register.spec.ts
new file mode 100644
index 0000000..c1b9241
--- /dev/null
+++ b/src/tests/selfservice/selfservice_register.spec.ts
@@ -0,0 +1,248 @@
+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('register invalid citizen', () => {
+ it('registering as citizen without mail should return 406', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ }, axios_config);
+ expect(res.status).toEqual(406);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering as citizen with invalid mail should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user"
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering as citizen without fist name should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com"
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering as citizen without last name should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "email": "user@example.com"
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering as citizen with invalid mail should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "phone": "peter",
+ "email": "user@example.com"
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
+// ---------------
+describe('register citizen valid', () => {
+ 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);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering as citizen with all params should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com",
+ "phone": "+4909132123456",
+ "address": {
+ address1: "Teststreet 1",
+ address2: "Testapartement",
+ postalcode: "91074",
+ city: "Herzo",
+ country: "Germany"
+ }
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
+// ---------------
+describe('register invalid company', () => {
+ let added_org;
+ it('creating a new org with just a name and registration enabled should return 200', async () => {
+ const res = await axios.post(base + '/api/organisations', {
+ "name": "test123",
+ "registrationEnabled": true
+ }, axios_config);
+ added_org = res.data;
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json")
+ });
+ it('registering with bs token should return 404', async () => {
+ const res = await axios.post(base + '/api/runners/register/4040404', {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ }, axios_config);
+ expect(res.status).toEqual(404);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering without firstname should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "middlename": "string",
+ "lastname": "string",
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering without lastname should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "middlename": "string",
+ "firstname": "string",
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with bs mail should return 400', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "true"
+ }, axios_config);
+ expect(res.status).toEqual(400);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with invalid team should return 404', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "lastname": "string",
+ "team": 9999999999999999999999
+ }, axios_config);
+ expect(res.status).toEqual(404);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
+// ---------------
+describe('register valid company', () => {
+ let added_org;
+ let added_team;
+ it('creating a new org with just a name and registration enabled should return 200', async () => {
+ const res = await axios.post(base + '/api/organisations', {
+ "name": "test123",
+ "registrationEnabled": true
+ }, axios_config);
+ added_org = res.data;
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json")
+ });
+ it('creating a new team with a parent org should return 200', async () => {
+ const res = await axios.post(base + '/api/teams', {
+ "name": "test_team",
+ "parentGroup": added_org.id
+ }, axios_config);
+ added_team = res.data;
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json")
+ });
+ it('registering with minimal params should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "lastname": "string",
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with all params except team should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com",
+ "phone": "+4909132123456",
+ "address": {
+ address1: "Teststreet 1",
+ address2: "Testapartement",
+ postalcode: "91074",
+ city: "Herzo",
+ country: "Germany"
+ }
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with minimal params and team should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "lastname": "string",
+ "team": added_team.id
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with all params except team should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com",
+ "phone": "+4909132123456",
+ "address": {
+ address1: "Teststreet 1",
+ address2: "Testapartement",
+ postalcode: "91074",
+ city: "Herzo",
+ country: "Germany"
+ }
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+ it('registering with all params and team should return 200', async () => {
+ const res = await axios.post(base + '/api/runners/register/' + added_org.registrationKey, {
+ "firstname": "string",
+ "middlename": "string",
+ "lastname": "string",
+ "email": "user@example.com",
+ "phone": "+4909132123456",
+ "address": {
+ address1: "Teststreet 1",
+ address2: "Testapartement",
+ postalcode: "91074",
+ city: "Herzo",
+ country: "Germany"
+ },
+ "team": added_team.id
+ }, axios_config);
+ expect(res.status).toEqual(200);
+ expect(res.headers['content-type']).toContain("application/json");
+ });
+});
\ No newline at end of file