diff --git a/.drone.yml b/.drone.yml index 204f6c3..db1b1e7 100644 --- a/.drone.yml +++ b/.drone.yml @@ -9,7 +9,6 @@ steps: commands: - git clone $DRONE_REMOTE_URL . - git checkout $DRONE_SOURCE_BRANCH - - mv .env.ci .env - name: run tests image: node:latest commands: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6385df1..ad3f914 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,55 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. +#### [v0.3.1](https://git.odit.services/lfk/backend/compare/v0.3.1...v0.3.1) + +- Merge pull request 'Email Basics feature/118-emails' (#128) from feature/118-emails into dev [`348e6cd`](https://git.odit.services/lfk/backend/commit/348e6cdec7411345953243edfb5322a17ad7614d) +- Added pw reset template provided by @philipp [`c116338`](https://git.odit.services/lfk/backend/commit/c116338cd74cf726362f8fa0ae5eea7ec9fabac4) +- Implemented automatic ci env generation [`536de2a`](https://git.odit.services/lfk/backend/commit/536de2a3199b1befed54b6fe520a2e3fcefe0d90) +- Implemented a basic mailer with reset link sending [`6379753`](https://git.odit.services/lfk/backend/commit/637975305f1adf9bf505507790638cf1e229cfb1) +- Table fix [`1f0c842`](https://git.odit.services/lfk/backend/commit/1f0c842d9e086456f1ae0f6908e474258a04beb4) +- Added documentation for the env vars [`13ccab5`](https://git.odit.services/lfk/backend/commit/13ccab5e289d0a629cefb7fe281a85a46058ae97) +- Password reset now enforces email [`979d36e`](https://git.odit.services/lfk/backend/commit/979d36ea9147dc575e9e989f6833388828285176) +- Translated the pw reset mail to english [`5cade25`](https://git.odit.services/lfk/backend/commit/5cade25eeb137eb5890b3fd450646acfbdff2e8b) +- 🧾New changelog file version [CI SKIP] [skip ci] [`e07f258`](https://git.odit.services/lfk/backend/commit/e07f258a315898d1183c311e7fcd8f65a415504c) +- Merge pull request 'Mail+Env documentation feature/123-mail_documentation' (#129) from feature/123-mail_documentation into dev [`61bbeb0`](https://git.odit.services/lfk/backend/commit/61bbeb0d8f3fd6bfafb65bd11eb4c076a27b4a53) +- Implementes mail sending on pw reset request [`e26744b`](https://git.odit.services/lfk/backend/commit/e26744b7925d32d65ef4cc3911651758cfc9274f) +- Added a txt variant of the pw-reset mail [`d3647e3`](https://git.odit.services/lfk/backend/commit/d3647e339990d989dbca4d91aa8c3fe5789dd24a) +- Changed order [`583a4bc`](https://git.odit.services/lfk/backend/commit/583a4bc0dd0de8026bb2eb6a9b0c31f59344e813) +- The auth tests now use mail to identify the user [`c43334b`](https://git.odit.services/lfk/backend/commit/c43334bf96901bfd5116301ff7cf4b2ae1dfcbd3) +- Added the first mail error [`c418603`](https://git.odit.services/lfk/backend/commit/c4186034233a296b5971fbef16e7ef6809fbac51) +- Now also sending txt mail body [`b92f633`](https://git.odit.services/lfk/backend/commit/b92f633d68604636cecc5e9fdd0d6990b9cb83fe) +- Removed tests working directly with the old pw-reset response [`d02e9de`](https://git.odit.services/lfk/backend/commit/d02e9dec5637aedefdf2ed3cd2c6d73216b6464b) +- Added the basics about mail templates to the readme [`b5018eb`](https://git.odit.services/lfk/backend/commit/b5018eb11492884db9f4ec969c767c3cce53f105) +- Cleaned up the replacements [`389e423`](https://git.odit.services/lfk/backend/commit/389e423850d68a5fe440b62413a6c662353ac9c6) +- Added mail env vars [`d7ea928`](https://git.odit.services/lfk/backend/commit/d7ea928714f94814695cbd2815c8730df58033f6) +- Added a barebones class for handleing mail stuff [`cf012c0`](https://git.odit.services/lfk/backend/commit/cf012c0b7efffb81b03497a04b0fdad0423c72f7) +- Added env vars [`470703c`](https://git.odit.services/lfk/backend/commit/470703c4de954da94726879becd57986b59e1f69) +- 🧾New changelog file version [CI SKIP] [skip ci] [`2071c4d`](https://git.odit.services/lfk/backend/commit/2071c4db33bbb9fd41ef650b409cac789732225f) +- Added a hint to ethereal.email [`53fcff7`](https://git.odit.services/lfk/backend/commit/53fcff77d00fc2b205ada0bcee7bdfe83d94a9f4) +- Fixed missing app_url protocol [`46af786`](https://git.odit.services/lfk/backend/commit/46af7865165bbfb97ed3e6cdfef15dfb72add611) +- Removed the duplicate env copy/create from ci tests [`08e6e59`](https://git.odit.services/lfk/backend/commit/08e6e5965544906f5033f2080166bddc37cc30c7) +- Removed bs console.log [`71c4caa`](https://git.odit.services/lfk/backend/commit/71c4caae8ba67e253d893409b3c5c3a39b08060a) +- Added nodemailer types [`78d2ac3`](https://git.odit.services/lfk/backend/commit/78d2ac3027f7109161ee36e9b3dda628a7039468) +- Added nodemailer dependecy [`908ac4f`](https://git.odit.services/lfk/backend/commit/908ac4f1ce9d78749268353c668e67b57eae6f94) +- Fixed wrong file location [`b4c117b`](https://git.odit.services/lfk/backend/commit/b4c117b7dc326d212598b6e720d0a422134e383d) +- Renamed the template [`fb77f4d`](https://git.odit.services/lfk/backend/commit/fb77f4d798550fdb6fe6b2c8a81198db0328f71e) +- Added a folder for the mail templates [`6b0155f`](https://git.odit.services/lfk/backend/commit/6b0155f01464f5ef73ab679b9e3219743e9db66b) +- Added a folder for the mail templates [`33890b5`](https://git.odit.services/lfk/backend/commit/33890b544b77272ab1c4797e91375d24568eae58) + #### [v0.3.1](https://git.odit.services/lfk/backend/compare/v0.3.0...v0.3.1) +> 27 January 2021 + +- Merge pull request 'Alpha Release 0.3.1' (#127) from dev into main [`20f960e`](https://git.odit.services/lfk/backend/commit/20f960ed6700fe58c0556895e6485d26c4a4f5e2) +- 🧾New changelog file version [CI SKIP] [skip ci] [`e6fe8fc`](https://git.odit.services/lfk/backend/commit/e6fe8fcd587751392970d3ee412559b4c1d60f21) +- Merge pull request 'new advanced endpoints feature/125-team_runner' (#126) from feature/125-team_runner into dev [`870fd47`](https://git.odit.services/lfk/backend/commit/870fd47c83389345d7b24a15df6a4e61e930d140) +- Added get runners by team test [`69417e9`](https://git.odit.services/lfk/backend/commit/69417e93c081422561db1e211b12f32e539010ce) - 🧾New changelog file version [CI SKIP] [skip ci] [`71898d5`](https://git.odit.services/lfk/backend/commit/71898d576c2620d2f2e2b4336e62f1d04a443201) +- Created the organizations/runners endpoint [`570c34b`](https://git.odit.services/lfk/backend/commit/570c34bed04e359f389a8f427486bf92891f1dcb) +- Created the runnerTeam/runners endpoint [`7be2971`](https://git.odit.services/lfk/backend/commit/7be2971a9e02bf0e784f7fe5cdd82afbbbf7f854) +- 🧾New changelog file version [CI SKIP] [skip ci] [`aedfcfc`](https://git.odit.services/lfk/backend/commit/aedfcfcc8359afd7dba4fa5e515e8e77fbb3fc6e) +- Added get runners by org test [`f71a22f`](https://git.odit.services/lfk/backend/commit/f71a22f4dd9bf14d39ced91908f6f6a5d8395e56) - 🚀Bumped version to v0.3.1 [`db08760`](https://git.odit.services/lfk/backend/commit/db0876015bf0599dabb21357f172735888c79aa8) #### [v0.3.0](https://git.odit.services/lfk/backend/compare/v0.2.1...v0.3.0) diff --git a/README.md b/README.md index 985a69f..8d538ea 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,41 @@ yarn test:watch yarn test:ci ``` +### Use your own mail templates +> You use your own mail templates by replacing the default ones we provided (either in-code or by mounting them into the /app/static/mail_templates folder). + +The mail templates always come in a .html and a .txt variant to provide compatability with legacy mail clients. +Currently the following templates exist: +* pw-reset.(html/txt) + ### Generate Docs ```bash yarn docs ``` +## ENV Vars +> You can provide them via .env file or docker env vars. +> You can use the `test:ci:generate_env` package script to generate a example env (uses [ethereal.email](https://ethereal.email) as the mailserver). + +| Name | Type | Default | Description +| - | - | - | - +| APP_PORT | Number | 4010 | The port the backend server listens on. Is optional. +| DB_TYPE | String | N/A | The type of the db u want to use. It has to be supported by typeorm. Possible: `sqlite`, `mysql`, `postgresql` +| DB_HOST | String | N/A | The db's host's ip-address/fqdn or file path for sqlite +| DB_PORT | String | N/A | The db's port +| DB_USER | String | N/A | The user for accessing the db +| DB_PASSWORD | String | N/A | The user's password for accessing the db +| DB_NAME | String | N/A | The db's name +| NODE_ENV | String | dev | The apps env - influences debug info. +| POSTALCODE_COUNTRYCODE | String/CountryCode | N/A | The countrycode used to validate address's postal codes +| PHONE_COUNTRYCODE | String/CountryCode | null (international) | The countrycode used to validate phone numers +| SEED_TEST_DATA | Boolean | False | If you want the app to seed some example data set this to true +| MAIL_SERVER | String | N/A | The smtp server's ip-address/fqdn +| MAIL_PORT | String | N/A | The smtp server's port +| MAIL_USER | String | N/A | The username for sending mails +| MAIL_PASSWORD | String | N/A | The user's password for sending mails +| MAIL_FROM | String | N/A | The from-address for sending mails + ## Recommended Editor [Visual Studio Code](https://code.visualstudio.com/) diff --git a/src/controllers/RunnerOrganizationController.ts b/src/controllers/RunnerOrganizationController.ts index b39e3cc..a74c103 100644 --- a/src/controllers/RunnerOrganizationController.ts +++ b/src/controllers/RunnerOrganizationController.ts @@ -4,8 +4,10 @@ import { getConnectionManager, Repository } from 'typeorm'; import { RunnerOrganizationHasRunnersError, RunnerOrganizationHasTeamsError, RunnerOrganizationIdsNotMatchingError, RunnerOrganizationNotFoundError } from '../errors/RunnerOrganizationErrors'; import { CreateRunnerOrganization } from '../models/actions/create/CreateRunnerOrganization'; import { UpdateRunnerOrganization } from '../models/actions/update/UpdateRunnerOrganization'; +import { Runner } from '../models/entities/Runner'; import { RunnerOrganization } from '../models/entities/RunnerOrganization'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; +import { ResponseRunner } from '../models/responses/ResponseRunner'; import { ResponseRunnerOrganization } from '../models/responses/ResponseRunnerOrganization'; import { RunnerController } from './RunnerController'; import { RunnerTeamController } from './RunnerTeamController'; @@ -48,6 +50,22 @@ export class RunnerOrganizationController { return new ResponseRunnerOrganization(runnerOrg); } + @Get('/:id/runners') + @Authorized(["RUNNER:GET", "SCAN:GET"]) + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerOrganizationNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Lists all runners from this org and it\'s teams (if you don\'t provide the ?onlyDirect=true param).
This includes the runner\'s group and distance ran.' }) + async getRunners(@Param('id') id: number, @QueryParam('onlyDirect') onlyDirect: boolean) { + let responseRunners: ResponseRunner[] = new Array(); + let runners: Runner[]; + if (!onlyDirect) { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track', 'teams', 'teams.runners', 'teams.runners.group', 'teams.runners.scans', 'teams.runners.scans.track'] })).allRunners; } + else { runners = (await this.runnerOrganizationRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track'] })).runners; } + runners.forEach(runner => { + responseRunners.push(new ResponseRunner(runner)); + }); + return responseRunners; + } + @Post() @Authorized("ORGANIZATION:CREATE") @ResponseSchema(ResponseRunnerOrganization) diff --git a/src/controllers/RunnerTeamController.ts b/src/controllers/RunnerTeamController.ts index e4952ac..420cfce 100644 --- a/src/controllers/RunnerTeamController.ts +++ b/src/controllers/RunnerTeamController.ts @@ -6,6 +6,7 @@ import { CreateRunnerTeam } from '../models/actions/create/CreateRunnerTeam'; import { UpdateRunnerTeam } from '../models/actions/update/UpdateRunnerTeam'; import { RunnerTeam } from '../models/entities/RunnerTeam'; import { ResponseEmpty } from '../models/responses/ResponseEmpty'; +import { ResponseRunner } from '../models/responses/ResponseRunner'; import { ResponseRunnerTeam } from '../models/responses/ResponseRunnerTeam'; import { RunnerController } from './RunnerController'; @@ -47,6 +48,20 @@ export class RunnerTeamController { return new ResponseRunnerTeam(runnerTeam); } + @Get('/:id/runners') + @Authorized(["RUNNER:GET", "SCAN:GET"]) + @ResponseSchema(ResponseRunner, { isArray: true }) + @ResponseSchema(RunnerTeamNotFoundError, { statusCode: 404 }) + @OpenAPI({ description: 'Lists all runners from this team.
This includes the runner\'s group and distance ran.' }) + async getRunners(@Param('id') id: number) { + let responseRunners: ResponseRunner[] = new Array(); + const runners = (await this.runnerTeamRepository.findOne({ id: id }, { relations: ['runners', 'runners.group', 'runners.scans', 'runners.scans.track'] })).runners; + runners.forEach(runner => { + responseRunners.push(new ResponseRunner(runner)); + }); + return responseRunners; + } + @Post() @Authorized("TEAM:CREATE") @ResponseSchema(ResponseRunnerTeam) diff --git a/src/tests/runners/runner_get.spec.ts b/src/tests/runners/runner_get.spec.ts index 7cc1a79..1b71abd 100644 --- a/src/tests/runners/runner_get.spec.ts +++ b/src/tests/runners/runner_get.spec.ts @@ -33,36 +33,106 @@ describe('GET /api/runners after adding', () => { let added_org_id; let added_runner; it('creating a new org with just a name should return 200', async () => { - const res1 = await axios.post(base + '/api/organizations', { + const res = await axios.post(base + '/api/organizations', { "name": "test123" }, axios_config); - let added_org = res1.data + let added_org = res.data added_org_id = added_org.id; - expect(res1.status).toEqual(200); - expect(res1.headers['content-type']).toContain("application/json") + expect(res.status).toEqual(200); + expect(res.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', { + const res = 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") + added_runner = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") }); it('explicit get should return 200', async () => { - const res3 = await axios.get(base + '/api/runners/' + added_runner.id, axios_config); - expect(res3.status).toEqual(200); - expect(res3.headers['content-type']).toContain("application/json") - let gotten_runner = res3.data + const res = await axios.get(base + '/api/runners/' + added_runner.id, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let gotten_runner = res.data expect(gotten_runner).toEqual(added_runner); }); it('get from all runners should return 200', async () => { - const res4 = await axios.get(base + '/api/runners/', axios_config); - expect(res4.status).toEqual(200); - expect(res4.headers['content-type']).toContain("application/json") - let gotten_runners = res4.data + const res = await axios.get(base + '/api/runners/', axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + let gotten_runners = res.data expect(gotten_runners).toContainEqual(added_runner); }); +}); +// --------------- +describe('GET /api/organizations/:id/runners after adding', () => { + let added_org_id; + let added_runner; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organizations', { + "name": "test123" + }, axios_config); + let added_org = res.data + added_org_id = added_org.id; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('creating a new runner with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_org_id + }, axios_config); + added_runner = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('check if scans was added via the orgs/runners endpoint.', async () => { + const res = await axios.get(base + '/api/organizations/' + added_org_id + "/runners", axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toContainEqual(added_runner); + }); +}); +// --------------- +describe('GET /api/teams/:id/runners after adding', () => { + let added_org_id; + let added_team; + let added_runner; + it('creating a new org with just a name should return 200', async () => { + const res = await axios.post(base + '/api/organizations', { + "name": "test123" + }, axios_config); + let added_org = res.data + added_org_id = added_org.id; + 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('creating a new runner with only needed params should return 200', async () => { + const res = await axios.post(base + '/api/runners', { + "firstname": "first", + "lastname": "last", + "group": added_team.id + }, axios_config); + added_runner = res.data; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json") + }); + it('check if scans was added via the orgs/runners endpoint.', async () => { + const res = await axios.get(base + '/api/teams/' + added_team.id + "/runners", axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toContainEqual(added_runner); + }); }); \ No newline at end of file