express-generator-typescript 2.2.9 → 2.3.1

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.
Files changed (42) hide show
  1. package/lib/express-generator-typescript.js +12 -12
  2. package/lib/project-files/.eslintrc.json +1 -0
  3. package/lib/project-files/env/development.env +1 -0
  4. package/lib/project-files/env/production.env +1 -0
  5. package/lib/project-files/env/test.env +1 -0
  6. package/lib/project-files/package.json +4 -4
  7. package/lib/project-files/spec/index.ts +32 -12
  8. package/lib/project-files/spec/support/login.ts +43 -0
  9. package/lib/project-files/spec/support/types.ts +1 -0
  10. package/lib/project-files/spec/tests/auth.spec.ts +75 -89
  11. package/lib/project-files/spec/tests/users.spec.ts +133 -212
  12. package/lib/project-files/src/constants/EnvVars.ts +28 -0
  13. package/lib/project-files/src/{declarations/major → constants}/HttpStatusCodes.ts +0 -0
  14. package/lib/project-files/src/{declarations/enums.ts → constants/misc.ts} +2 -6
  15. package/lib/project-files/src/index.ts +6 -4
  16. package/lib/project-files/src/models/User.ts +9 -2
  17. package/lib/project-files/src/{declarations → other}/classes.ts +1 -1
  18. package/lib/project-files/src/other/types.ts +4 -0
  19. package/lib/project-files/src/pre-start.ts +17 -15
  20. package/lib/project-files/src/repos/{mock-orm.ts → MockOrm.ts} +4 -4
  21. package/lib/project-files/src/repos/{user-repo.ts → UserRepo.ts} +4 -4
  22. package/lib/project-files/src/routes/AuthRoutes.ts +47 -0
  23. package/lib/project-files/src/routes/UserRoutes.ts +53 -0
  24. package/lib/project-files/src/routes/api.ts +27 -20
  25. package/lib/project-files/src/routes/constants/FullPaths.ts +40 -0
  26. package/lib/project-files/src/routes/constants/Paths.ts +28 -0
  27. package/lib/project-files/src/routes/{shared → middleware}/adminMw.ts +12 -22
  28. package/lib/project-files/src/{types → routes/types}/express/index.d.ts +0 -0
  29. package/lib/project-files/src/routes/types/express/misc.ts +16 -0
  30. package/lib/project-files/src/routes/{shared → types}/types.ts +1 -1
  31. package/lib/project-files/src/routes/user-routes.ts +2 -8
  32. package/lib/project-files/src/server.ts +28 -21
  33. package/lib/project-files/src/services/{auth-service.ts → AuthService.ts} +16 -14
  34. package/lib/project-files/src/services/{user-service.ts → UserService.ts} +12 -12
  35. package/lib/project-files/src/util/{jwt-util.ts → JwtUtil.ts} +8 -8
  36. package/lib/project-files/src/util/{pwd-util.ts → PwdUtil.ts} +3 -3
  37. package/lib/project-files/src/{declarations/functions.ts → util/misc.ts} +1 -0
  38. package/package.json +1 -1
  39. package/lib/project-files/spec/load-env.ts +0 -12
  40. package/lib/project-files/spec/support/login-agent.ts +0 -46
  41. package/lib/project-files/src/declarations/major/EnvVars.ts +0 -22
  42. 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
- const editJsonFile = require('edit-json-file');
9
- const childProcess = require('child_process');
10
- const ncp = require('ncp').ncp;
11
- const fs = require('fs');
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 prjFolder = './project-files';
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 command-line-args express-async-errors helmet jsonfile ' +
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 @types/command-line-args ' +
36
- '@typescript-eslint/eslint-plugin @typescript-eslint/parser eslint ' +
37
- '@types/fs-extra @types/bcrypt @types/jsonwebtoken eslint-plugin-node';
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, prjFolder);
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) => {
@@ -24,6 +24,7 @@
24
24
  "no-extra-boolean-cast": 0,
25
25
  "semi": 1,
26
26
  "indent": ["warn", 2],
27
+ "quotes": ["warn", "single"],
27
28
  "@typescript-eslint/no-misused-promises": 0,
28
29
  "@typescript-eslint/no-floating-promises": 0,
29
30
  "node/no-process-env": 1,
@@ -13,6 +13,7 @@ JET_LOGGER_FILEPATH=jet-logger.log
13
13
  JET_LOGGER_TIMESTAMP=TRUE
14
14
  JET_LOGGER_FORMAT=LINE
15
15
 
16
+
16
17
  ## Authentication ##
17
18
  COOKIE_DOMAIN=localhost
18
19
  COOKIE_PATH=/
@@ -13,6 +13,7 @@ JET_LOGGER_FILEPATH=jet-logger.log
13
13
  JET_LOGGER_TIMESTAMP=TRUE
14
14
  JET_LOGGER_FORMAT=LINE
15
15
 
16
+
16
17
  ## Authentication ##
17
18
  # SECURE_COOKIE 'false' here for demo-ing. But ideally should be true.
18
19
  COOKIE_DOMAIN=localhost
@@ -13,6 +13,7 @@ JET_LOGGER_FILEPATH=jet-logger.log
13
13
  JET_LOGGER_TIMESTAMP=TRUE
14
14
  JET_LOGGER_FORMAT=LINE
15
15
 
16
+
16
17
  ## Authentication ##
17
18
  COOKIE_DOMAIN=localhost
18
19
  COOKIE_PATH=/
@@ -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
- "jet-logger": "^1.2.3",
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 './load-env';
1
+ import dotenv from 'dotenv';
2
2
  import find from 'find';
3
3
  import Jasmine from 'jasmine';
4
- import commandLineArgs from 'command-line-args';
4
+ import { parse } from 'ts-command-line-args';
5
5
  import logger from 'jet-logger';
6
6
 
7
7
 
8
- // Setup command line options
9
- const options = commandLineArgs([
10
- {
11
- name: 'testFile',
12
- alias: 'f',
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 (options.testFile) {
34
- const testFile = options.testFile as string;
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.specFiles = [files[0]];
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
- import authRoutes from '@src/routes/auth-routes';
6
- import userRepo from '@src/repos/user-repo';
7
- import pwdUtil from '@src/util/pwd-util';
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 { errors as authServiceErrs } from '@src/services/auth-service';
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
- // Misc
17
- const { paths } = authRoutes,
18
- authPath = ('/api' + paths.basePath),
19
- loginPath = `${authPath}${paths.login}`,
20
- logoutPath = `${authPath}${paths.logout}`;
21
-
22
- // Test message
23
- const msgs = {
24
- goodLogin: 'should return a response with a status of ' +
25
- `"${HttpStatusCodes.OK}" and a cookie with a jwt if the login was ` +
26
- 'successful.',
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 loginCreds = {
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('auth-router', () => {
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:${loginPath}"`, () => {
65
-
66
- const callApi = (reqBody: TReqBody) => {
67
- return agent.post(loginPath).type('form').send(reqBody);
68
- };
69
-
70
- // Good login
71
- it(msgs.goodLogin, (done) => {
72
- const role = UserRoles.Standard;
73
- const pwdHash = pwdUtil.hashSync(loginCreds.password);
74
- const loginUser = User.new('john smith', loginCreds.email, role, pwdHash);
75
- spyOn(userRepo, 'getOne').and.returnValue(Promise.resolve(loginUser));
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(loginCreds)
78
- .end((err: Error, res: Response) => {
79
- !!err && logger.err(err);
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.cookieProps.key);
75
+ expect(cookie).toContain(EnvVars.CookieProps.Key);
83
76
  done();
84
77
  });
85
78
  });
86
79
 
87
80
  // Email not found error
88
- it(msgs.emailNotFound, (done) => {
89
- spyOn(userRepo, 'getOne').and.returnValue(Promise.resolve(null));
90
- callApi(loginCreds)
91
- .end((err: Error, res: Response) => {
92
- !!err && logger.err(err);
93
- expect(res.status).toBe(HttpStatusCodes.UNAUTHORIZED);
94
- const errMsg = authServiceErrs.emailNotFound(loginCreds.email);
95
- expect(res.body.error).toBe(errMsg);
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(msgs.pwdFailed, (done) => {
102
- const role = UserRoles.Standard;
103
- const pwdHash = pwdUtil.hashSync('bad password');
104
- const loginUser = User.new('john smith', loginCreds.email, role, pwdHash);
105
- spyOn(userRepo, 'getOne').and.returnValue(Promise.resolve(loginUser));
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(loginCreds)
108
- .end((err: Error, res: Response) => {
109
- !!err && logger.err(err);
110
- expect(res.status).toBe(HttpStatusCodes.UNAUTHORIZED);
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:${logoutPath}"`, () => {
116
+ // ** Test logout ** //
117
+ describe(`"GET:${Logout}"`, () => {
131
118
 
132
119
  // Successful logout
133
- it(msgs.goodLogout, (done) => {
134
- agent.get(logoutPath)
135
- .end((err: Error, res: Response) => {
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
  });