express-generator-typescript 2.4.6 → 2.4.8
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 +1 -1
- package/lib/project-files/package.json +4 -3
- package/lib/project-files/spec/support/apiCb.ts +76 -0
- package/lib/project-files/spec/tests/users.spec.ts +102 -127
- package/lib/project-files/spec/types/misc.ts +18 -0
- package/lib/project-files/src/models/User.ts +13 -27
- package/lib/project-files/src/public/scripts/http.js +3 -1
- package/lib/project-files/src/public/scripts/users.js +31 -65
- package/lib/project-files/src/public/stylesheets/users.css +4 -6
- package/lib/project-files/src/repos/UserRepo.ts +6 -1
- package/lib/project-files/src/repos/database.json +22 -1
- package/lib/project-files/src/routes/types/express/misc.ts +1 -5
- package/lib/project-files/src/routes/types/types.ts +0 -8
- package/lib/project-files/src/views/users.html +100 -5
- package/lib/project-files-auth/package.json +4 -3
- package/lib/project-files-auth/spec/support/apiCb.ts +76 -0
- package/lib/project-files-auth/spec/support/login.ts +6 -8
- package/lib/project-files-auth/spec/tests/auth.spec.ts +52 -59
- package/lib/project-files-auth/spec/tests/users.spec.ts +101 -133
- package/lib/project-files-auth/spec/types/misc.ts +18 -0
- package/lib/project-files-auth/src/models/User.ts +17 -12
- package/lib/project-files-auth/src/public/scripts/http.js +3 -1
- package/lib/project-files-auth/src/public/scripts/login.js +1 -3
- package/lib/project-files-auth/src/public/scripts/users.js +31 -63
- package/lib/project-files-auth/src/public/stylesheets/users.css +4 -11
- package/lib/project-files-auth/src/repos/UserRepo.ts +6 -1
- package/lib/project-files-auth/src/repos/database.json +1 -1
- package/lib/project-files-auth/src/services/UserService.ts +2 -8
- package/lib/project-files-auth/src/views/login.html +26 -3
- package/lib/project-files-auth/src/views/users.html +106 -8
- package/package.json +1 -1
- package/lib/project-files/spec/support/types.ts +0 -1
- package/lib/project-files-auth/spec/support/types.ts +0 -1
|
@@ -20,7 +20,7 @@ const PROJECT_FOLDER_PATH = './project-files';
|
|
|
20
20
|
// Project-folder dependencies
|
|
21
21
|
const DEPENDENCIES = 'express dotenv morgan cookie-parser jet-logger ' +
|
|
22
22
|
'module-alias express-async-errors helmet jsonfile inserturlparams ' +
|
|
23
|
-
'jet-validator ts-command-line-args jet-paths',
|
|
23
|
+
'jet-validator ts-command-line-args jet-paths moment',
|
|
24
24
|
DEPENDENCIES_AUTH = ' bcrypt jsonwebtoken',
|
|
25
25
|
DEV_DEPENDENCIES = 'ts-node typescript nodemon find jasmine supertest ' +
|
|
26
26
|
'@types/node @types/express @types/jasmine @types/find @types/morgan ' +
|
|
@@ -35,11 +35,12 @@
|
|
|
35
35
|
"helmet": "^6.0.0",
|
|
36
36
|
"inserturlparams": "^1.0.1",
|
|
37
37
|
"jet-logger": "^1.3.1",
|
|
38
|
-
"jet-validator": "^1.0.3",
|
|
39
38
|
"jet-paths": "^1.0.5",
|
|
39
|
+
"jet-validator": "^1.0.3",
|
|
40
40
|
"jsonfile": "^6.1.0",
|
|
41
41
|
"jsonwebtoken": "^8.5.1",
|
|
42
42
|
"module-alias": "^2.2.2",
|
|
43
|
+
"moment": "^2.30.1",
|
|
43
44
|
"morgan": "^1.10.0",
|
|
44
45
|
"ts-command-line-args": "^2.4.2"
|
|
45
46
|
},
|
|
@@ -54,7 +55,7 @@
|
|
|
54
55
|
"@types/jsonwebtoken": "^8.5.9",
|
|
55
56
|
"@types/morgan": "^1.9.3",
|
|
56
57
|
"@types/node": "^18.7.14",
|
|
57
|
-
"@types/supertest": "^
|
|
58
|
+
"@types/supertest": "^6.0.2",
|
|
58
59
|
"@typescript-eslint/eslint-plugin": "^5.36.1",
|
|
59
60
|
"@typescript-eslint/parser": "^5.36.1",
|
|
60
61
|
"eslint": "^8.23.0",
|
|
@@ -63,7 +64,7 @@
|
|
|
63
64
|
"fs-extra": "^10.1.0",
|
|
64
65
|
"jasmine": "^4.3.0",
|
|
65
66
|
"nodemon": "^2.0.19",
|
|
66
|
-
"supertest": "^6.
|
|
67
|
+
"supertest": "^6.3.4",
|
|
67
68
|
"ts-node": "^10.9.1",
|
|
68
69
|
"tsconfig-paths": "^4.1.0",
|
|
69
70
|
"typescript": "^4.8.2"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { CallbackHandler } from 'supertest';
|
|
2
|
+
import moment from 'moment';
|
|
3
|
+
import logger from 'jet-logger';
|
|
4
|
+
|
|
5
|
+
import { TApiCb, TRes } from 'spec/types/misc';
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* API callback function.
|
|
10
|
+
*/
|
|
11
|
+
function apiCb(
|
|
12
|
+
cb: TApiCb,
|
|
13
|
+
dateParam = 'created',
|
|
14
|
+
printErr?: boolean,
|
|
15
|
+
): CallbackHandler {
|
|
16
|
+
return (err: Error, res: TRes) => {
|
|
17
|
+
if (printErr) {
|
|
18
|
+
logger.err(err);
|
|
19
|
+
}
|
|
20
|
+
_strToDate(res.body, dateParam);
|
|
21
|
+
return cb(res);
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* When date objects get sent through supertest they are converted to
|
|
27
|
+
* iso-strings. This will cause "toEqual()"" tests to fail, so we need to
|
|
28
|
+
* convert them back to Date objects.
|
|
29
|
+
*/
|
|
30
|
+
function _strToDate(
|
|
31
|
+
param: unknown,
|
|
32
|
+
prop: string,
|
|
33
|
+
): void {
|
|
34
|
+
return _iterate(param, prop);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Interate object recursively and convert string-dates to "Date" objects.
|
|
39
|
+
*/
|
|
40
|
+
function _iterate(param: unknown, prop: string): void {
|
|
41
|
+
if (!param || typeof param !== 'object') {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const paramF = param as Record<string, unknown>;
|
|
45
|
+
// For Arrays
|
|
46
|
+
if (Array.isArray(paramF)) {
|
|
47
|
+
for (const item of paramF) {
|
|
48
|
+
_iterate(item, prop);
|
|
49
|
+
}
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
// Check valid string or Date object. If undefined just skip
|
|
53
|
+
const val = paramF[prop];
|
|
54
|
+
if (
|
|
55
|
+
(typeof val !== 'undefined') &&
|
|
56
|
+
!((typeof val === 'string') || (val instanceof Date)) &&
|
|
57
|
+
!moment(val as string | Date).isValid()
|
|
58
|
+
) {
|
|
59
|
+
throw new Error('Property must be a valid date-string or Date() object');
|
|
60
|
+
}
|
|
61
|
+
// Convert and iterate
|
|
62
|
+
if (typeof val !== 'undefined') {
|
|
63
|
+
paramF[prop] = new Date(val as string | Date);
|
|
64
|
+
}
|
|
65
|
+
for (const key in paramF) {
|
|
66
|
+
const oval = paramF[key];
|
|
67
|
+
if (typeof oval === 'object' && key !== prop) {
|
|
68
|
+
_iterate(oval, prop);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
// **** Export default **** //
|
|
75
|
+
|
|
76
|
+
export default apiCb;
|
|
@@ -1,211 +1,186 @@
|
|
|
1
|
-
import supertest, {
|
|
1
|
+
import supertest, { Test } from 'supertest';
|
|
2
|
+
import TestAgent from 'supertest/lib/agent';
|
|
2
3
|
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
3
4
|
import insertUrlParams from 'inserturlparams';
|
|
4
5
|
|
|
5
6
|
import app from '@src/server';
|
|
6
7
|
|
|
7
8
|
import UserRepo from '@src/repos/UserRepo';
|
|
8
|
-
import User from '@src/models/User';
|
|
9
|
+
import User, { IUser } from '@src/models/User';
|
|
9
10
|
import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
10
11
|
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
11
12
|
|
|
12
13
|
import Paths from 'spec/support/Paths';
|
|
13
|
-
import
|
|
14
|
+
import apiCb from 'spec/support/apiCb';
|
|
15
|
+
import { TApiCb } from 'spec/types/misc';
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
// **** Variables **** //
|
|
17
|
-
|
|
18
|
-
// StatusCodes
|
|
19
|
-
const {
|
|
20
|
-
OK,
|
|
21
|
-
CREATED,
|
|
22
|
-
NOT_FOUND,
|
|
23
|
-
BAD_REQUEST,
|
|
24
|
-
} = HttpStatusCodes;
|
|
25
|
-
|
|
26
18
|
// Dummy users for GET req
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const DummyUserData = {
|
|
35
|
-
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
36
|
-
} as const;
|
|
19
|
+
const getDummyUsers = () => {
|
|
20
|
+
return [
|
|
21
|
+
User.new('Sean Maxwell', 'sean.maxwell@gmail.com'),
|
|
22
|
+
User.new('John Smith', 'john.smith@gmail.com'),
|
|
23
|
+
User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
24
|
+
];
|
|
25
|
+
};
|
|
37
26
|
|
|
38
27
|
|
|
39
|
-
//
|
|
40
|
-
|
|
28
|
+
// Tests
|
|
41
29
|
describe('UserRouter', () => {
|
|
42
30
|
|
|
43
|
-
let agent:
|
|
31
|
+
let agent: TestAgent<Test>;
|
|
44
32
|
|
|
45
33
|
// Run before all tests
|
|
46
|
-
beforeAll(
|
|
34
|
+
beforeAll(done => {
|
|
47
35
|
agent = supertest.agent(app);
|
|
48
36
|
done();
|
|
49
37
|
});
|
|
50
38
|
|
|
51
|
-
//
|
|
39
|
+
// Get all users
|
|
52
40
|
describe(`"GET:${Paths.Users.Get}"`, () => {
|
|
53
41
|
|
|
54
|
-
|
|
42
|
+
// Setup API
|
|
43
|
+
const api = (cb: TApiCb) =>
|
|
44
|
+
agent
|
|
45
|
+
.get(Paths.Users.Get)
|
|
46
|
+
.end(apiCb(cb));
|
|
55
47
|
|
|
56
48
|
// Success
|
|
57
49
|
it('should return a JSON object with all the users and a status code ' +
|
|
58
|
-
`of "${OK}" if the request was successful.`, (done) => {
|
|
50
|
+
`of "${HttpStatusCodes.OK}" if the request was successful.`, (done) => {
|
|
59
51
|
// Add spy
|
|
60
|
-
|
|
52
|
+
const data = getDummyUsers();
|
|
53
|
+
spyOn(UserRepo, 'getAll').and.resolveTo(data);
|
|
61
54
|
// Call API
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
expect(user).toEqual(DummyGetAllUsers[i]);
|
|
68
|
-
}
|
|
69
|
-
done();
|
|
70
|
-
});
|
|
55
|
+
api(res => {
|
|
56
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
57
|
+
expect(res.body).toEqual({ users: data });
|
|
58
|
+
done();
|
|
59
|
+
});
|
|
71
60
|
});
|
|
72
61
|
});
|
|
73
62
|
|
|
74
63
|
// Test add user
|
|
75
64
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
76
65
|
|
|
77
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
66
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
67
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
78
68
|
|
|
79
|
-
|
|
69
|
+
// Setup API
|
|
70
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
80
71
|
agent
|
|
81
72
|
.post(Paths.Users.Add)
|
|
82
|
-
.
|
|
73
|
+
.send({ user })
|
|
74
|
+
.end(apiCb(cb));
|
|
83
75
|
|
|
84
76
|
// Test add user success
|
|
85
|
-
it(`should return a status code of "${CREATED}" if the
|
|
86
|
-
'successful.', (done) => {
|
|
77
|
+
it(`should return a status code of "${HttpStatusCodes.CREATED}" if the ` +
|
|
78
|
+
'request was successful.', (done) => {
|
|
87
79
|
// Spy
|
|
88
80
|
spyOn(UserRepo, 'add').and.resolveTo();
|
|
89
81
|
// Call api
|
|
90
|
-
callApi(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
done();
|
|
95
|
-
});
|
|
82
|
+
callApi(DUMMY_USER, res => {
|
|
83
|
+
expect(res.status).toBe(HttpStatusCodes.CREATED);
|
|
84
|
+
done();
|
|
85
|
+
});
|
|
96
86
|
});
|
|
97
87
|
|
|
98
88
|
// Missing param
|
|
99
|
-
it(
|
|
100
|
-
`
|
|
89
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
90
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
101
91
|
'param was missing.', (done) => {
|
|
102
92
|
// Call api
|
|
103
|
-
callApi({
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
});
|
|
93
|
+
callApi(null, res => {
|
|
94
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
95
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
96
|
+
done();
|
|
97
|
+
});
|
|
109
98
|
});
|
|
110
99
|
});
|
|
111
100
|
|
|
112
|
-
//
|
|
101
|
+
// Update users
|
|
113
102
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
114
103
|
|
|
115
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
104
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
105
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
116
106
|
|
|
117
|
-
|
|
107
|
+
// Setup API
|
|
108
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
118
109
|
agent
|
|
119
110
|
.put(Paths.Users.Update)
|
|
120
|
-
.
|
|
111
|
+
.send({ user })
|
|
112
|
+
.end(apiCb(cb));
|
|
121
113
|
|
|
122
114
|
// Success
|
|
123
|
-
it(`should return a status code of "${OK}" if the
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
expect(res.body.error).toBeUndefined();
|
|
133
|
-
done();
|
|
134
|
-
});
|
|
115
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
116
|
+
'request was successful.', (done) => {
|
|
117
|
+
// Setup spies
|
|
118
|
+
spyOn(UserRepo, 'update').and.resolveTo();
|
|
119
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
120
|
+
// Call api
|
|
121
|
+
callApi(DUMMY_USER, res => {
|
|
122
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
123
|
+
done();
|
|
135
124
|
});
|
|
125
|
+
});
|
|
136
126
|
|
|
137
127
|
// Param missing
|
|
138
|
-
it(
|
|
139
|
-
`
|
|
128
|
+
it(`should return a JSON object with an error message of "${ERROR_MSG}" ` +
|
|
129
|
+
`and a status code of "${HttpStatusCodes.BAD_REQUEST}" if the user ` +
|
|
140
130
|
'param was missing.', (done) => {
|
|
141
131
|
// Call api
|
|
142
|
-
callApi({
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
});
|
|
132
|
+
callApi(null, res => {
|
|
133
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
134
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
135
|
+
done();
|
|
136
|
+
});
|
|
148
137
|
});
|
|
149
138
|
|
|
150
139
|
// User not found
|
|
151
140
|
it('should return a JSON object with the error message of ' +
|
|
152
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
153
|
-
|
|
141
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
142
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, (done) => {
|
|
154
143
|
// Call api
|
|
155
|
-
callApi(
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
});
|
|
144
|
+
callApi(DUMMY_USER, res => {
|
|
145
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
146
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
147
|
+
done();
|
|
148
|
+
});
|
|
161
149
|
});
|
|
162
150
|
});
|
|
163
151
|
|
|
164
|
-
//
|
|
152
|
+
// Delete User
|
|
165
153
|
describe(`"DELETE:${Paths.Users.Delete}"`, () => {
|
|
166
154
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const callApi = (id: number) =>
|
|
155
|
+
// Call API
|
|
156
|
+
const callApi = (id: number, cb: TApiCb) =>
|
|
170
157
|
agent
|
|
171
|
-
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
158
|
+
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
159
|
+
.end(apiCb(cb));
|
|
172
160
|
|
|
173
161
|
// Success
|
|
174
|
-
it(`should return a status code of "${OK}" if the
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
expect(res.body.error).toBeUndefined();
|
|
184
|
-
done();
|
|
185
|
-
});
|
|
162
|
+
it(`should return a status code of "${HttpStatusCodes.OK}" if the ` +
|
|
163
|
+
'request was successful.', (done) => {
|
|
164
|
+
// Setup spies
|
|
165
|
+
spyOn(UserRepo, 'delete').and.resolveTo();
|
|
166
|
+
spyOn(UserRepo, 'persists').and.resolveTo(true);
|
|
167
|
+
// Call api
|
|
168
|
+
callApi(5, res => {
|
|
169
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
170
|
+
done();
|
|
186
171
|
});
|
|
172
|
+
});
|
|
187
173
|
|
|
188
174
|
// User not found
|
|
189
175
|
it('should return a JSON object with the error message of ' +
|
|
190
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
// Invalid param
|
|
201
|
-
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
|
|
202
|
-
`message of "${VALIDATOR_ERR}" if the id was not a valid number`, (done) => {
|
|
203
|
-
callApi('horse' as unknown as number)
|
|
204
|
-
.end((_: Error, res: Response) => {
|
|
205
|
-
expect(res.status).toBe(BAD_REQUEST);
|
|
206
|
-
expect(res.body.error).toBe(VALIDATOR_ERR);
|
|
207
|
-
done();
|
|
208
|
-
});
|
|
176
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
177
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, done => {
|
|
178
|
+
// Setup spies
|
|
179
|
+
callApi(-1, res => {
|
|
180
|
+
expect(res.status).toBe(HttpStatusCodes.NOT_FOUND);
|
|
181
|
+
expect(res.body.error).toBe(USER_NOT_FOUND_ERR);
|
|
182
|
+
done();
|
|
183
|
+
});
|
|
209
184
|
});
|
|
210
185
|
});
|
|
211
186
|
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Response } from 'supertest';
|
|
2
|
+
|
|
3
|
+
import { IUser } from '@src/models/User';
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
// Misc
|
|
7
|
+
export type TReqBody = Record<string, unknown>;
|
|
8
|
+
export type TRes = Omit<Response, 'body'> & { body: TBody };
|
|
9
|
+
export type TApiCb = (res: TRes) => void;
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
// Response Body: Add things to the body here over time to prevent
|
|
13
|
+
// typescript errors.
|
|
14
|
+
type TBody = {
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
user?: IUser;
|
|
17
|
+
users?: IUser[];
|
|
18
|
+
}
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import moment from 'moment';
|
|
2
2
|
|
|
3
|
-
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an ' +
|
|
4
|
-
'object with the appropriate user keys.';
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
// **** Variables **** //
|
|
5
|
+
|
|
6
|
+
const INVALID_CONSTRUCTOR_PARAM = 'nameOrObj arg must a string or an object ' +
|
|
7
|
+
'with the appropriate user keys.';
|
|
10
8
|
|
|
11
9
|
|
|
12
10
|
// **** Types **** //
|
|
@@ -15,15 +13,7 @@ export interface IUser {
|
|
|
15
13
|
id: number;
|
|
16
14
|
name: string;
|
|
17
15
|
email: string;
|
|
18
|
-
|
|
19
|
-
role?: UserRoles;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface ISessionUser {
|
|
23
|
-
id: number;
|
|
24
|
-
email: string;
|
|
25
|
-
name: string;
|
|
26
|
-
role: IUser['role'];
|
|
16
|
+
created: Date;
|
|
27
17
|
}
|
|
28
18
|
|
|
29
19
|
|
|
@@ -35,16 +25,14 @@ export interface ISessionUser {
|
|
|
35
25
|
function new_(
|
|
36
26
|
name?: string,
|
|
37
27
|
email?: string,
|
|
38
|
-
|
|
39
|
-
pwdHash?: string,
|
|
28
|
+
created?: Date,
|
|
40
29
|
id?: number, // id last cause usually set by db
|
|
41
30
|
): IUser {
|
|
42
31
|
return {
|
|
43
32
|
id: (id ?? -1),
|
|
44
33
|
name: (name ?? ''),
|
|
45
34
|
email: (email ?? ''),
|
|
46
|
-
|
|
47
|
-
pwdHash: (pwdHash ?? ''),
|
|
35
|
+
created: (created ? new Date(created) : new Date()),
|
|
48
36
|
};
|
|
49
37
|
}
|
|
50
38
|
|
|
@@ -52,13 +40,11 @@ function new_(
|
|
|
52
40
|
* Get user instance from object.
|
|
53
41
|
*/
|
|
54
42
|
function from(param: object): IUser {
|
|
55
|
-
// Check is user
|
|
56
43
|
if (!isUser(param)) {
|
|
57
44
|
throw new Error(INVALID_CONSTRUCTOR_PARAM);
|
|
58
45
|
}
|
|
59
|
-
// Get user instance
|
|
60
46
|
const p = param as IUser;
|
|
61
|
-
return new_(p.name, p.email, p.
|
|
47
|
+
return new_(p.name, p.email, p.created, p.id);
|
|
62
48
|
}
|
|
63
49
|
|
|
64
50
|
/**
|
|
@@ -68,10 +54,10 @@ function isUser(arg: unknown): boolean {
|
|
|
68
54
|
return (
|
|
69
55
|
!!arg &&
|
|
70
56
|
typeof arg === 'object' &&
|
|
71
|
-
'id' in arg &&
|
|
72
|
-
'email' in arg &&
|
|
73
|
-
'name' in arg &&
|
|
74
|
-
'
|
|
57
|
+
'id' in arg && typeof arg.id === 'number' &&
|
|
58
|
+
'email' in arg && typeof arg.email === 'string' &&
|
|
59
|
+
'name' in arg && typeof arg.name === 'string' &&
|
|
60
|
+
'created' in arg && moment(arg.created as string | Date).isValid()
|
|
75
61
|
);
|
|
76
62
|
}
|
|
77
63
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var Http = (() => {
|
|
2
|
+
|
|
2
3
|
// Setup request for json
|
|
3
4
|
var getOptions = (verb, data) => {
|
|
4
5
|
var options = {
|
|
@@ -9,11 +10,12 @@ var Http = (() => {
|
|
|
9
10
|
'Content-Type': 'application/json',
|
|
10
11
|
},
|
|
11
12
|
};
|
|
12
|
-
if (data) {
|
|
13
|
+
if (!!data) {
|
|
13
14
|
options.body = JSON.stringify(data);
|
|
14
15
|
}
|
|
15
16
|
return options;
|
|
16
17
|
};
|
|
18
|
+
|
|
17
19
|
// Set Http methods
|
|
18
20
|
return {
|
|
19
21
|
get: (path) => fetch(path, getOptions('GET')),
|