express-generator-typescript 2.2.9 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/express-generator-typescript.js +12 -12
- package/lib/project-files/.eslintrc.json +1 -0
- package/lib/project-files/env/development.env +1 -0
- package/lib/project-files/env/production.env +1 -0
- package/lib/project-files/env/test.env +1 -0
- package/lib/project-files/package.json +4 -4
- package/lib/project-files/spec/index.ts +32 -12
- package/lib/project-files/spec/support/login.ts +43 -0
- package/lib/project-files/spec/support/types.ts +1 -0
- package/lib/project-files/spec/tests/auth.spec.ts +75 -89
- package/lib/project-files/spec/tests/users.spec.ts +133 -212
- package/lib/project-files/src/constants/EnvVars.ts +28 -0
- package/lib/project-files/src/{declarations/major → constants}/HttpStatusCodes.ts +0 -0
- package/lib/project-files/src/{declarations/enums.ts → constants/misc.ts} +2 -6
- package/lib/project-files/src/index.ts +6 -4
- package/lib/project-files/src/models/User.ts +9 -2
- package/lib/project-files/src/{declarations → other}/classes.ts +1 -1
- package/lib/project-files/src/other/types.ts +4 -0
- package/lib/project-files/src/pre-start.ts +17 -15
- package/lib/project-files/src/repos/{mock-orm.ts → MockOrm.ts} +4 -4
- package/lib/project-files/src/repos/{user-repo.ts → UserRepo.ts} +4 -4
- package/lib/project-files/src/routes/AuthRoutes.ts +47 -0
- package/lib/project-files/src/routes/UserRoutes.ts +53 -0
- package/lib/project-files/src/routes/api.ts +27 -20
- package/lib/project-files/src/routes/constants/FullPaths.ts +40 -0
- package/lib/project-files/src/routes/constants/Paths.ts +28 -0
- package/lib/project-files/src/routes/{shared → middleware}/adminMw.ts +12 -22
- package/lib/project-files/src/{types → routes/types}/express/index.d.ts +0 -0
- package/lib/project-files/src/routes/types/express/misc.ts +16 -0
- package/lib/project-files/src/routes/{shared → types}/types.ts +1 -1
- package/lib/project-files/src/routes/user-routes.ts +2 -8
- package/lib/project-files/src/server.ts +28 -21
- package/lib/project-files/src/services/{auth-service.ts → AuthService.ts} +16 -14
- package/lib/project-files/src/services/{user-service.ts → UserService.ts} +12 -12
- package/lib/project-files/src/util/{jwt-util.ts → JwtUtil.ts} +8 -8
- package/lib/project-files/src/util/{pwd-util.ts → PwdUtil.ts} +3 -3
- package/lib/project-files/src/{declarations/functions.ts → util/misc.ts} +1 -0
- package/package.json +1 -1
- package/lib/project-files/spec/load-env.ts +0 -12
- package/lib/project-files/spec/support/login-agent.ts +0 -46
- package/lib/project-files/src/declarations/major/EnvVars.ts +0 -22
- package/lib/project-files/src/routes/auth-routes.ts +0 -57
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
* created by Sean Maxwell, 5/31/2019
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
const path = require('path')
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
const path = require('path'),
|
|
8
|
+
editJsonFile = require('edit-json-file'),
|
|
9
|
+
childProcess = require('child_process'),
|
|
10
|
+
ncp = require('ncp').ncp,
|
|
11
|
+
fs = require('fs');
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
// **** Variables **** //
|
|
15
15
|
|
|
16
16
|
// Misc
|
|
17
|
-
const
|
|
17
|
+
const PROJECT_FOLDER_PATH = './project-files';
|
|
18
18
|
|
|
19
19
|
// ncp options
|
|
20
20
|
const ncpOpts = {
|
|
@@ -25,16 +25,16 @@ const ncpOpts = {
|
|
|
25
25
|
|
|
26
26
|
// Project-folder dependencies
|
|
27
27
|
const dependencies = 'express dotenv morgan cookie-parser jet-logger ' +
|
|
28
|
-
'module-alias
|
|
29
|
-
'bcrypt jsonwebtoken jet-validator';
|
|
28
|
+
'module-alias express-async-errors helmet jsonfile inserturlparams ' +
|
|
29
|
+
'bcrypt jsonwebtoken jet-validator ts-command-line-args';
|
|
30
30
|
|
|
31
31
|
// Project-folder development dependencies
|
|
32
32
|
const devDependencies = 'ts-node typescript nodemon find jasmine supertest ' +
|
|
33
33
|
'@types/node @types/express @types/jasmine @types/find @types/morgan ' +
|
|
34
34
|
'@types/cookie-parser @types/supertest fs-extra tsconfig-paths ' +
|
|
35
|
-
'@types/jsonfile @
|
|
36
|
-
'@typescript-eslint/
|
|
37
|
-
'@types/
|
|
35
|
+
'@types/jsonfile @typescript-eslint/eslint-plugin eslint ' +
|
|
36
|
+
'@typescript-eslint/parser @types/fs-extra @types/bcrypt ' +
|
|
37
|
+
'@types/jsonwebtoken eslint-plugin-node';
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
// **** Functions **** //
|
|
@@ -57,7 +57,7 @@ async function expressGenTs(destination, useYarn) {
|
|
|
57
57
|
* Copy project files
|
|
58
58
|
*/
|
|
59
59
|
function copyProjectFiles(destination) {
|
|
60
|
-
const source = path.join(__dirname,
|
|
60
|
+
const source = path.join(__dirname, PROJECT_FOLDER_PATH);
|
|
61
61
|
return new Promise((res, rej) => {
|
|
62
62
|
ncp.limit = 16;
|
|
63
63
|
return ncp(source, destination, ncpOpts, (err) => {
|
|
@@ -28,22 +28,22 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"bcrypt": "^5.0.1",
|
|
31
|
-
"command-line-args": "^5.2.1",
|
|
32
31
|
"cookie-parser": "^1.4.6",
|
|
33
32
|
"dotenv": "^16.0.2",
|
|
34
33
|
"express": "^4.18.1",
|
|
35
34
|
"express-async-errors": "^3.1.1",
|
|
36
35
|
"helmet": "^6.0.0",
|
|
37
|
-
"
|
|
36
|
+
"inserturlparams": "^1.0.1",
|
|
37
|
+
"jet-logger": "^1.3.1",
|
|
38
38
|
"jet-validator": "^1.0.3",
|
|
39
39
|
"jsonfile": "^6.1.0",
|
|
40
40
|
"jsonwebtoken": "^8.5.1",
|
|
41
41
|
"module-alias": "^2.2.2",
|
|
42
|
-
"morgan": "^1.10.0"
|
|
42
|
+
"morgan": "^1.10.0",
|
|
43
|
+
"ts-command-line-args": "^2.4.2"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"@types/bcrypt": "^5.0.0",
|
|
46
|
-
"@types/command-line-args": "^5.2.0",
|
|
47
47
|
"@types/cookie-parser": "^1.4.3",
|
|
48
48
|
"@types/express": "^4.17.13",
|
|
49
49
|
"@types/find": "^0.2.1",
|
|
@@ -1,18 +1,39 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
2
|
import find from 'find';
|
|
3
3
|
import Jasmine from 'jasmine';
|
|
4
|
-
import
|
|
4
|
+
import { parse } from 'ts-command-line-args';
|
|
5
5
|
import logger from 'jet-logger';
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
// **** Types **** //
|
|
9
|
+
|
|
10
|
+
interface IArgs {
|
|
11
|
+
testFile: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
// **** Setup **** //
|
|
16
|
+
|
|
17
|
+
// ** Init ** //
|
|
18
|
+
|
|
19
|
+
// NOTE: MUST BE FIRST!! Load env vars
|
|
20
|
+
const result2 = dotenv.config({
|
|
21
|
+
path: './env/test.env',
|
|
22
|
+
});
|
|
23
|
+
if (result2.error) {
|
|
24
|
+
throw result2.error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Setup command line options.
|
|
28
|
+
const args = parse<IArgs>({
|
|
29
|
+
testFile: {
|
|
13
30
|
type: String,
|
|
31
|
+
defaultValue: '',
|
|
14
32
|
},
|
|
15
|
-
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
// ** Start Jasmine ** //
|
|
16
37
|
|
|
17
38
|
// Init Jasmine
|
|
18
39
|
const jasmine = new Jasmine();
|
|
@@ -30,12 +51,11 @@ jasmine.loadConfig({
|
|
|
30
51
|
|
|
31
52
|
// Run all or a single unit-test
|
|
32
53
|
let execResp: Promise<jasmine.JasmineDoneInfo> | undefined;
|
|
33
|
-
if (
|
|
34
|
-
const testFile =
|
|
54
|
+
if (args.testFile) {
|
|
55
|
+
const testFile = args.testFile;
|
|
35
56
|
find.file(testFile + '.spec.ts', './spec', (files: string[]) => {
|
|
36
57
|
if (files.length === 1) {
|
|
37
|
-
jasmine.
|
|
38
|
-
jasmine.execute();
|
|
58
|
+
jasmine.execute([files[0]]);
|
|
39
59
|
} else {
|
|
40
60
|
logger.err('Test file not found!');
|
|
41
61
|
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { SuperTest, Test, Response } from 'supertest';
|
|
2
|
+
|
|
3
|
+
import User, { UserRoles } from '@src/models/User';
|
|
4
|
+
import UserRepo from '@src/repos/UserRepo';
|
|
5
|
+
import PwdUtil from '@src/util/PwdUtil';
|
|
6
|
+
import FullPaths from '@src/routes/constants/FullPaths';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
// **** Variables **** //
|
|
10
|
+
|
|
11
|
+
const LoginCreds = {
|
|
12
|
+
email: 'jsmith@gmail.com',
|
|
13
|
+
password: 'Password@1',
|
|
14
|
+
} as const;
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// **** Functions **** //
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Login a user.
|
|
21
|
+
*/
|
|
22
|
+
function login(beforeAgent: SuperTest<Test>, done: (arg: string) => void) {
|
|
23
|
+
// Setup dummy data
|
|
24
|
+
const role = UserRoles.Admin,
|
|
25
|
+
pwdHash = PwdUtil.hashSync(LoginCreds.password),
|
|
26
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
27
|
+
// Add spy
|
|
28
|
+
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
29
|
+
// Call Login API
|
|
30
|
+
beforeAgent
|
|
31
|
+
.post(FullPaths.Auth.Login)
|
|
32
|
+
.type('form')
|
|
33
|
+
.send(LoginCreds)
|
|
34
|
+
.end((_: Error, res: Response) => {
|
|
35
|
+
const cookie = res.headers['set-cookie'][0];
|
|
36
|
+
return done(cookie);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
// **** Export default **** //
|
|
42
|
+
|
|
43
|
+
export default login;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type TReqBody = string | object | undefined;
|
|
@@ -1,56 +1,43 @@
|
|
|
1
1
|
import supertest, { SuperTest, Test, Response } from 'supertest';
|
|
2
|
-
import logger from 'jet-logger';
|
|
3
2
|
|
|
4
3
|
import app from '@src/server';
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import EnvVars from '@src/declarations/major/EnvVars';
|
|
9
|
-
import HttpStatusCodes from '@src/declarations/major/HttpStatusCodes';
|
|
4
|
+
|
|
5
|
+
import UserRepo from '@src/repos/UserRepo';
|
|
6
|
+
import PwdUtil from '@src/util/PwdUtil';
|
|
10
7
|
import User, { UserRoles } from '@src/models/User';
|
|
11
|
-
import {
|
|
8
|
+
import { Errors } from '@src/services/AuthService';
|
|
9
|
+
|
|
10
|
+
import FullPaths from '@src/routes/constants/FullPaths';
|
|
11
|
+
import EnvVars from '@src/constants/EnvVars';
|
|
12
|
+
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
13
|
+
|
|
14
|
+
import { TReqBody } from 'spec/support/types';
|
|
12
15
|
|
|
13
16
|
|
|
14
17
|
// **** Variables **** //
|
|
15
18
|
|
|
16
|
-
//
|
|
17
|
-
const {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
//
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
emailNotFound: 'should return a response with a status of ' +
|
|
28
|
-
`"${HttpStatusCodes.UNAUTHORIZED}" and a json with an error message if ` +
|
|
29
|
-
'the email was not found.',
|
|
30
|
-
pwdFailed: 'should return a response with a status of ' +
|
|
31
|
-
`"${HttpStatusCodes.UNAUTHORIZED}" and a json with the error ` +
|
|
32
|
-
`"${authServiceErrs.unauth}" if the password failed.`,
|
|
33
|
-
fallbackErr: 'should return a response with a status of ' +
|
|
34
|
-
`"${HttpStatusCodes.BAD_REQUEST}" and a json with an error for all ` +
|
|
35
|
-
'other bad responses.',
|
|
36
|
-
goodLogout: `should return a response with a status of ${HttpStatusCodes.OK}`,
|
|
37
|
-
} as const;
|
|
19
|
+
// Paths
|
|
20
|
+
const {
|
|
21
|
+
Login,
|
|
22
|
+
Logout,
|
|
23
|
+
} = FullPaths.Auth;
|
|
24
|
+
|
|
25
|
+
// StatusCodes
|
|
26
|
+
const {
|
|
27
|
+
OK,
|
|
28
|
+
UNAUTHORIZED,
|
|
29
|
+
} = HttpStatusCodes;
|
|
38
30
|
|
|
39
31
|
// Login credentials
|
|
40
|
-
const
|
|
32
|
+
const LoginCreds = {
|
|
41
33
|
email: 'jsmith@gmail.com',
|
|
42
34
|
password: 'Password@1',
|
|
43
35
|
} as const;
|
|
44
36
|
|
|
45
37
|
|
|
46
|
-
// **** Types **** //
|
|
47
|
-
|
|
48
|
-
type TReqBody = string | object | undefined;
|
|
49
|
-
|
|
50
|
-
|
|
51
38
|
// **** Tests **** //
|
|
52
39
|
|
|
53
|
-
describe('
|
|
40
|
+
describe('AuthRouter', () => {
|
|
54
41
|
|
|
55
42
|
let agent: SuperTest<Test>;
|
|
56
43
|
|
|
@@ -60,80 +47,79 @@ describe('auth-router', () => {
|
|
|
60
47
|
done();
|
|
61
48
|
});
|
|
62
49
|
|
|
63
|
-
// Test login
|
|
64
|
-
describe(`"POST:${
|
|
65
|
-
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
50
|
+
// ** Test login ** //
|
|
51
|
+
describe(`"POST:${Login}"`, () => {
|
|
52
|
+
|
|
53
|
+
const EMAIL_NOT_FOUND_ERR = Errors.emailNotFound(LoginCreds.email);
|
|
54
|
+
|
|
55
|
+
const callApi = (reqBody: TReqBody) =>
|
|
56
|
+
agent
|
|
57
|
+
.post(Login)
|
|
58
|
+
.type('form')
|
|
59
|
+
.send(reqBody);
|
|
60
|
+
|
|
61
|
+
// Success
|
|
62
|
+
it(`should return a response with a status of "${OK}" and a cookie with ` +
|
|
63
|
+
'a jwt if the login was successful.', (done) => {
|
|
64
|
+
// Setup data
|
|
65
|
+
const role = UserRoles.Standard,
|
|
66
|
+
pwdHash = PwdUtil.hashSync(LoginCreds.password),
|
|
67
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
68
|
+
// Add spy
|
|
69
|
+
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
76
70
|
// Call API
|
|
77
|
-
callApi(
|
|
78
|
-
.end((
|
|
79
|
-
|
|
80
|
-
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
71
|
+
callApi(LoginCreds)
|
|
72
|
+
.end((_: Error, res: Response) => {
|
|
73
|
+
expect(res.status).toBe(OK);
|
|
81
74
|
const cookie = res.headers['set-cookie'][0];
|
|
82
|
-
expect(cookie).toContain(EnvVars.
|
|
75
|
+
expect(cookie).toContain(EnvVars.CookieProps.Key);
|
|
83
76
|
done();
|
|
84
77
|
});
|
|
85
78
|
});
|
|
86
79
|
|
|
87
80
|
// Email not found error
|
|
88
|
-
it(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
it(`should return a response with a status of "${UNAUTHORIZED}" and a ` +
|
|
82
|
+
`json with an error message of "${EMAIL_NOT_FOUND_ERR}" if the email ` +
|
|
83
|
+
'was not found.', (done) => {
|
|
84
|
+
// Spy
|
|
85
|
+
spyOn(UserRepo, 'getOne').and.resolveTo(null);
|
|
86
|
+
// Call
|
|
87
|
+
callApi(LoginCreds)
|
|
88
|
+
.end((_: Error, res: Response) => {
|
|
89
|
+
expect(res.status).toBe(UNAUTHORIZED);
|
|
90
|
+
expect(res.body.error).toBe(EMAIL_NOT_FOUND_ERR);
|
|
96
91
|
done();
|
|
97
92
|
});
|
|
98
93
|
});
|
|
99
94
|
|
|
100
95
|
// Password failed
|
|
101
|
-
it(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
96
|
+
it(`should return a response with a status of "${UNAUTHORIZED}" and a ` +
|
|
97
|
+
`json with the error "${Errors.Unauth}" if the password failed.`,
|
|
98
|
+
(done) => {
|
|
99
|
+
// Setup data
|
|
100
|
+
const role = UserRoles.Standard,
|
|
101
|
+
pwdHash = PwdUtil.hashSync('bad password'),
|
|
102
|
+
loginUser = User.new('john smith', LoginCreds.email, role, pwdHash);
|
|
103
|
+
// Add spy
|
|
104
|
+
spyOn(UserRepo, 'getOne').and.resolveTo(loginUser);
|
|
106
105
|
// Call API
|
|
107
|
-
callApi(
|
|
108
|
-
.end((
|
|
109
|
-
|
|
110
|
-
expect(res.
|
|
111
|
-
expect(res.body.error).toBe(authServiceErrs.unauth);
|
|
106
|
+
callApi(LoginCreds)
|
|
107
|
+
.end((_: Error, res: Response) => {
|
|
108
|
+
expect(res.status).toBe(UNAUTHORIZED);
|
|
109
|
+
expect(res.body.error).toBe(Errors.Unauth);
|
|
112
110
|
done();
|
|
113
111
|
});
|
|
114
112
|
});
|
|
115
113
|
|
|
116
|
-
// Fallback error
|
|
117
|
-
it(msgs.fallbackErr, (done) => {
|
|
118
|
-
spyOn(userRepo, 'getOne').and.throwError('Database query failed.');
|
|
119
|
-
callApi(loginCreds)
|
|
120
|
-
.end((err: Error, res: Response) => {
|
|
121
|
-
!!err && logger.err(err);
|
|
122
|
-
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
123
|
-
expect(res.body.error).toBeTruthy();
|
|
124
|
-
done();
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
114
|
});
|
|
128
115
|
|
|
129
|
-
// Test logout
|
|
130
|
-
describe(`"GET:${
|
|
116
|
+
// ** Test logout ** //
|
|
117
|
+
describe(`"GET:${Logout}"`, () => {
|
|
131
118
|
|
|
132
119
|
// Successful logout
|
|
133
|
-
it(
|
|
134
|
-
agent.get(
|
|
135
|
-
.end((
|
|
136
|
-
!!err && logger.err(err);
|
|
120
|
+
it(`should return a response with a status of ${OK}`, (done) => {
|
|
121
|
+
agent.get(Logout)
|
|
122
|
+
.end((_: Error, res: Response) => {
|
|
137
123
|
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
138
124
|
done();
|
|
139
125
|
});
|