diff --git a/CHANGELOG.md b/CHANGELOG.md index ce402a4..208f0ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,12 +2,37 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. +#### [v0.4.6](https://git.odit.services/lfk/backend/compare/v0.4.5...v0.4.6) + +- Merge pull request 'Fixed wrong body acceptance type' (#150) from bugfix/146-usergroup_update into dev [`d870b2f`](https://git.odit.services/lfk/backend/commit/d870b2fd01b11b1732fcbb6feecaf6a6155fa702) +- Added tests for the new org selfservice endpoints [`28ef139`](https://git.odit.services/lfk/backend/commit/28ef139a70e0c063982b2eb9167b7abe41db1621) +- Added selfservice org response model [`ba3b5ee`](https://git.odit.services/lfk/backend/commit/ba3b5eeefc45f9bd94aef24f9f509f6835f5ea7c) +- Added selfservice team response model [`ba396e0`](https://git.odit.services/lfk/backend/commit/ba396e0eba15647b3004437a5a9949c7a69e828d) +- 📖New license file version [CI SKIP] [skip ci] [`bce8811`](https://git.odit.services/lfk/backend/commit/bce8811925e7f77c64fc507d55335ac45b0e5572) +- 📖New license file version [CI SKIP] [skip ci] [`b1fced7`](https://git.odit.services/lfk/backend/commit/b1fced77640b6c26438331474f368f2b0708b672) +- Added selfservice org info endpoint [`656f63d`](https://git.odit.services/lfk/backend/commit/656f63dfd5fdbe13554fc98440e416be7e56d909) +- 🧾New changelog file version [CI SKIP] [skip ci] [`c0cafb4`](https://git.odit.services/lfk/backend/commit/c0cafb4d510116773fed12592cad1efc2ef09f38) +- 🧾New changelog file version [CI SKIP] [skip ci] [`09fe47b`](https://git.odit.services/lfk/backend/commit/09fe47b9aaac47b65d4e910ef89d558c47fd7364) +- Fixed wrong body acceptance type [`aaec09d`](https://git.odit.services/lfk/backend/commit/aaec09d2ab08a76e9d367fdfefc01cea5588f1b9) +- Pinned package version to avoid dependency conflicts 📌 [`39ebfbf`](https://git.odit.services/lfk/backend/commit/39ebfbf0b633ecc479a33fdf851cd6550616bfee) +- 🧾New changelog file version [CI SKIP] [skip ci] [`3736b29`](https://git.odit.services/lfk/backend/commit/3736b29e5435abb05de03e5d99d9adb438cd7d7e) +- 🧾New changelog file version [CI SKIP] [skip ci] [`305fa00`](https://git.odit.services/lfk/backend/commit/305fa0078d44b39b0391e84ba67b048285cf77b9) +- 🧾New changelog file version [CI SKIP] [skip ci] [`3afc207`](https://git.odit.services/lfk/backend/commit/3afc207903c9cf1e62e6f4a62601b4213f608192) +- Quick bugfix [`5d6c8c9`](https://git.odit.services/lfk/backend/commit/5d6c8c957acd098a20e674ce5529f60cbc9f4151) +- 🚀Bumped version to v0.4.6 [`b4acd15`](https://git.odit.services/lfk/backend/commit/b4acd157fc075154a60946c1ee8876ee5f5dfbee) +- Merge pull request 'New org selfservice endpoint feature/146-more_selfservice_endpoints' (#147) from feature/146-more_selfservice_endpoints into dev [`45d61b4`](https://git.odit.services/lfk/backend/commit/45d61b487e8e6fdd8e00c184a08c9d6e34a1b6bf) +- Added new response types [`3c11d88`](https://git.odit.services/lfk/backend/commit/3c11d88557a2612bf4320ff669323bc048634e94) + #### [v0.4.5](https://git.odit.services/lfk/backend/compare/v0.4.4...v0.4.5) +> 9 February 2021 + +- Merge pull request 'Alpha release 0.4.5' (#145) from dev into main [`a46d142`](https://git.odit.services/lfk/backend/commit/a46d14278b9a084ca54f8f90e5e70b04739c2dd7) - 🚀Bumped version to v0.4.5 [`cc869f6`](https://git.odit.services/lfk/backend/commit/cc869f69add1f1a175ff94510d52888f81bccb69) -- Implemented /groups/permissions endpoint [`0c9867d`](https://git.odit.services/lfk/backend/commit/0c9867d70616615c8f3c72bbec37a4441e4868ef) +- 🧾New changelog file version [CI SKIP] [skip ci] [`680ae8e`](https://git.odit.services/lfk/backend/commit/680ae8ebbb39d103085fe1fe8781d71b3c3ed055) - 🧾New changelog file version [CI SKIP] [skip ci] [`b9aac71`](https://git.odit.services/lfk/backend/commit/b9aac7167681ff0945e538dd177abd6f97771bf2) - Merge pull request 'usergroups/permissions endpoint feature/143-usergroup_permissions_endpoint' (#144) from feature/143-usergroup_permissions_endpoint into dev [`a30a342`](https://git.odit.services/lfk/backend/commit/a30a342e00ba944f8014044bba28141c0657a17f) +- Implemented /groups/permissions endpoint [`0c9867d`](https://git.odit.services/lfk/backend/commit/0c9867d70616615c8f3c72bbec37a4441e4868ef) - Now all /usergroups endpoints return ResponseUserGroup [`bdcfce8`](https://git.odit.services/lfk/backend/commit/bdcfce88cbe069f9ba1925fcaac06367a109d2b7) - The ResponseUserGroup now returns their permisssions as a string array [`416f2a1`](https://git.odit.services/lfk/backend/commit/416f2a1366c570998011d022ebd7f5f44276b2c9) - The ResponseUserGroup now returns their permisssions as a string array [`5e353db`](https://git.odit.services/lfk/backend/commit/5e353db2061c30b4d10965c47f0dcbecb7f59fc5) diff --git a/package.json b/package.json index bac45ce..b2c115d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@odit/lfk-backend", - "version": "0.4.5", + "version": "0.4.6", "main": "src/app.ts", "repository": "https://git.odit.services/lfk/backend", "author": { @@ -40,7 +40,7 @@ "nodemailer": "^6.4.17", "pg": "^8.5.1", "reflect-metadata": "^0.1.13", - "routing-controllers": "^0.9.0-alpha.6", + "routing-controllers": "0.9.0-alpha.6", "routing-controllers-openapi": "^2.2.0", "sqlite3": "5.0.0", "typeorm": "^0.2.30", @@ -104,4 +104,4 @@ "docs/*" ] } -} +} \ No newline at end of file diff --git a/src/controllers/RunnerSelfServiceController.ts b/src/controllers/RunnerSelfServiceController.ts index eb35f48..324f9c1 100644 --- a/src/controllers/RunnerSelfServiceController.ts +++ b/src/controllers/RunnerSelfServiceController.ts @@ -12,10 +12,11 @@ import { CreateSelfServiceRunner } from '../models/actions/create/CreateSelfServ import { Runner } from '../models/entities/Runner'; import { RunnerGroup } from '../models/entities/RunnerGroup'; import { RunnerOrganization } from '../models/entities/RunnerOrganization'; +import { ResponseSelfServiceOrganisation } from '../models/responses/ResponseSelfServiceOrganisation'; import { ResponseSelfServiceRunner } from '../models/responses/ResponseSelfServiceRunner'; -@JsonController('/runners') +@JsonController() export class RunnerSelfServiceController { private runnerRepository: Repository; private orgRepository: Repository; @@ -28,7 +29,7 @@ export class RunnerSelfServiceController { this.orgRepository = getConnectionManager().get().getRepository(RunnerOrganization); } - @Get('/me/:jwt') + @Get('/runners/me/:jwt') @ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(RunnerNotFoundError, { statusCode: 404 }) @OnUndefined(RunnerNotFoundError) @@ -37,7 +38,7 @@ export class RunnerSelfServiceController { return (new ResponseSelfServiceRunner(await this.getRunner(token))); } - @Post('/register') + @Post('/runners/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.' }) @@ -50,7 +51,7 @@ export class RunnerSelfServiceController { return response; } - @Post('/register/:token') + @Post('/runners/register/:token') @ResponseSchema(ResponseSelfServiceRunner) @ResponseSchema(RunnerOrganizationNotFoundError, { 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 /organizations endpoint.' }) @@ -65,6 +66,17 @@ export class RunnerSelfServiceController { return response; } + @Get('/organizations/selfservice/:token') + @ResponseSchema(ResponseSelfServiceOrganisation, { isArray: false }) + @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Get the basic info and teams for a org.' }) + async getSelfserviceOrg(@Param('token') token: string) { + const orgid = (await this.getOrgansisation(token)).id; + const org = await this.orgRepository.findOne({ id: orgid }, { relations: ['teams'] }) + + return new ResponseSelfServiceOrganisation(org); + } + /** * Get's a runner by a provided jwt token. * @param token The runner jwt provided by the runner to identitfy themselves. diff --git a/src/controllers/UserGroupController.ts b/src/controllers/UserGroupController.ts index c68baff..71d0b3a 100644 --- a/src/controllers/UserGroupController.ts +++ b/src/controllers/UserGroupController.ts @@ -1,7 +1,6 @@ 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 { EntityFromBody } from 'typeorm-routing-controllers-extensions'; import { UserGroupIdsNotMatchingError, UserGroupNotFoundError } from '../errors/UserGroupErrors'; import { CreateUserGroup } from '../models/actions/create/CreateUserGroup'; import { UpdateUserGroup } from '../models/actions/update/UpdateUserGroup'; @@ -82,7 +81,7 @@ export class UserGroupController { @ResponseSchema(UserGroupNotFoundError, { statusCode: 404 }) @ResponseSchema(UserGroupIdsNotMatchingError, { statusCode: 406 }) @OpenAPI({ description: "Update the group whose id you provided.
To change the permissions granted to the group please use /api/permissions instead.
Please remember that ids can't be changed." }) - async put(@Param('id') id: number, @EntityFromBody() updateGroup: UpdateUserGroup) { + async put(@Param('id') id: number, @Body({ validate: true }) updateGroup: UpdateUserGroup) { let oldGroup = await this.userGroupsRepository.findOne({ id: id }); if (!oldGroup) { @@ -94,7 +93,7 @@ export class UserGroupController { } await this.userGroupsRepository.save(await updateGroup.update(oldGroup)); - return (await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions', 'groups'] })).toResponse(); + return (await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] })).toResponse(); } @Delete('/:id') @@ -104,7 +103,7 @@ export class UserGroupController { @OnUndefined(204) @OpenAPI({ description: 'Delete the group whose id you provided.
If there are any permissions directly granted to the group they will get deleted as well.
Users associated with this group won\'t get deleted - just deassociated.
If no group with this id exists it will just return 204(no content).' }) async remove(@Param("id") id: number, @QueryParam("force") force: boolean) { - let group = await this.userGroupsRepository.findOne({ id: id }, { relations: ["permissions"] }); + let group = await this.userGroupsRepository.findOne({ id: id }); if (!group) { return null; } const responseGroup = await this.userGroupsRepository.findOne({ id: id }, { relations: ['permissions'] }); diff --git a/src/models/enums/ResponseObjectType.ts b/src/models/enums/ResponseObjectType.ts index e0622be..a25ebd4 100644 --- a/src/models/enums/ResponseObjectType.ts +++ b/src/models/enums/ResponseObjectType.ts @@ -21,6 +21,8 @@ export enum ResponseObjectType { SCANSTATION = 'SCANSTATION', SELFSERVICEDONATION = 'SELFSERVICEDONATION', SELFSERVICERUNNER = 'SELFSERVICRUNNER', + SELFSERVICETEAM = 'SELFSERVICETEAM', + SELFSERVICEORGANIZATION = 'SELFSERVICEORGANIZATION', STATS = 'STATS', STATSCLIENT = 'STATSCLIENT', STATSORGANIZATION = 'STATSORGANIZATION', diff --git a/src/models/responses/ResponseSelfServiceOrganisation.ts b/src/models/responses/ResponseSelfServiceOrganisation.ts new file mode 100644 index 0000000..aa8ae8b --- /dev/null +++ b/src/models/responses/ResponseSelfServiceOrganisation.ts @@ -0,0 +1,38 @@ +import { IsArray, IsNotEmpty, IsString } from 'class-validator'; +import { RunnerOrganization } from '../entities/RunnerOrganization'; +import { ResponseObjectType } from '../enums/ResponseObjectType'; +import { IResponse } from './IResponse'; +import { ResponseSelfServiceTeam } from './ResponseSelfServiceTeam'; + +/** + * Defines the runner selfservice organization response. + * Why? B/C runner's are not allowed to view all information available to admin users. +*/ +export class ResponseSelfServiceOrganisation implements IResponse { + /** + * The responseType. + * This contains the type of class/entity this response contains. + */ + responseType: ResponseObjectType = ResponseObjectType.SELFSERVICEORGANIZATION; + + /** + * The org's name. + */ + @IsNotEmpty() + @IsString() + name: string; + + /** + * The org's teams (just containing name and id). + */ + @IsArray() + teams: ResponseSelfServiceTeam[]; + + public constructor(org: RunnerOrganization) { + this.name = org.name; + this.teams = new Array(); + for (let team of org.teams) { + this.teams.push(new ResponseSelfServiceTeam(team)); + } + } +} \ No newline at end of file diff --git a/src/models/responses/ResponseSelfServiceTeam.ts b/src/models/responses/ResponseSelfServiceTeam.ts new file mode 100644 index 0000000..3e43343 --- /dev/null +++ b/src/models/responses/ResponseSelfServiceTeam.ts @@ -0,0 +1,36 @@ +import { IsInt, IsNotEmpty, IsPositive, IsString } from 'class-validator'; +import { RunnerTeam } from '../entities/RunnerTeam'; +import { ResponseObjectType } from '../enums/ResponseObjectType'; +import { IResponse } from './IResponse'; + +/** + * Defines the runner selfservice team response. + * Why? B/C runner's are not allowed to view all information available to admin users. +*/ +export class ResponseSelfServiceTeam implements IResponse { + /** + * The responseType. + * This contains the type of class/entity this response contains. + */ + responseType: ResponseObjectType = ResponseObjectType.SELFSERVICETEAM; + + /** + * The team's name. + */ + @IsNotEmpty() + @IsString() + name: string; + + /** + * The team's id. + * Will be used to insert runners it into that team. + */ + @IsInt() + @IsPositive() + id: number; + + public constructor(team: RunnerTeam) { + this.name = team.name; + this.id = team.id; + } +} \ No newline at end of file diff --git a/src/tests/selfservice/selfservice_org.ts b/src/tests/selfservice/selfservice_org.ts new file mode 100644 index 0000000..f269285 --- /dev/null +++ b/src/tests/selfservice/selfservice_org.ts @@ -0,0 +1,54 @@ +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 invalid org', () => { + it('getting random org via selfservice should return 4040', async () => { + const res = await axios.get(base + '/api/organizations/selfservice/asfdasfasdfsdafsadfsadfasdfasdfsdf', axios_config); + expect(res.status).toEqual(404); + }); +}); + +// --------------- +describe('get valid org w/teams', () => { + 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/organizations', { + "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('getting org via selfservice should return 200', async () => { + const res = await axios.get(base + '/api/organizations/selfservice/' + added_org.registrationKey, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data.name).toEqual(added_org.name); + expect(res.data.teams[0]).toEqual({ name: added_team.name, id: added_team.id }); + }); +}); \ No newline at end of file