express-generator-typescript 2.4.7 → 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 +2 -1
- package/lib/project-files/spec/support/apiCb.ts +76 -0
- package/lib/project-files/spec/tests/users.spec.ts +98 -135
- 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 +2 -1
- package/lib/project-files-auth/spec/support/apiCb.ts +76 -0
- package/lib/project-files-auth/spec/support/login.ts +3 -6
- package/lib/project-files-auth/spec/tests/auth.spec.ts +50 -67
- package/lib/project-files-auth/spec/tests/users.spec.ts +98 -142
- 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
|
},
|
|
@@ -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,4 +1,4 @@
|
|
|
1
|
-
import supertest, { Test
|
|
1
|
+
import supertest, { Test } from 'supertest';
|
|
2
2
|
import TestAgent from 'supertest/lib/agent';
|
|
3
3
|
import { defaultErrMsg as ValidatorErr } from 'jet-validator';
|
|
4
4
|
import insertUrlParams from 'inserturlparams';
|
|
@@ -11,213 +11,176 @@ import HttpStatusCodes from '@src/constants/HttpStatusCodes';
|
|
|
11
11
|
import { USER_NOT_FOUND_ERR } from '@src/services/UserService';
|
|
12
12
|
|
|
13
13
|
import Paths from 'spec/support/Paths';
|
|
14
|
-
import
|
|
14
|
+
import apiCb from 'spec/support/apiCb';
|
|
15
|
+
import { TApiCb } from 'spec/types/misc';
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
// **** Variables **** //
|
|
18
|
-
|
|
19
|
-
// StatusCodes
|
|
20
|
-
const {
|
|
21
|
-
OK,
|
|
22
|
-
CREATED,
|
|
23
|
-
NOT_FOUND,
|
|
24
|
-
BAD_REQUEST,
|
|
25
|
-
} = HttpStatusCodes;
|
|
26
|
-
|
|
27
18
|
// Dummy users for GET req
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Dummy update user
|
|
35
|
-
const DummyUserData = {
|
|
36
|
-
user: User.new('Gordan Freeman', 'gordan.freeman@gmail.com'),
|
|
37
|
-
} as const;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
// **** Types **** //
|
|
41
|
-
|
|
42
|
-
type TRes = Omit<Response, 'body'> & {
|
|
43
|
-
body: {
|
|
44
|
-
users: IUser[];
|
|
45
|
-
error: string;
|
|
46
|
-
}
|
|
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
|
+
];
|
|
47
25
|
};
|
|
48
26
|
|
|
49
27
|
|
|
50
|
-
//
|
|
51
|
-
|
|
28
|
+
// Tests
|
|
52
29
|
describe('UserRouter', () => {
|
|
53
30
|
|
|
54
31
|
let agent: TestAgent<Test>;
|
|
55
32
|
|
|
56
33
|
// Run before all tests
|
|
57
|
-
beforeAll(
|
|
34
|
+
beforeAll(done => {
|
|
58
35
|
agent = supertest.agent(app);
|
|
59
36
|
done();
|
|
60
37
|
});
|
|
61
38
|
|
|
62
|
-
//
|
|
39
|
+
// Get all users
|
|
63
40
|
describe(`"GET:${Paths.Users.Get}"`, () => {
|
|
64
41
|
|
|
65
|
-
|
|
42
|
+
// Setup API
|
|
43
|
+
const api = (cb: TApiCb) =>
|
|
44
|
+
agent
|
|
45
|
+
.get(Paths.Users.Get)
|
|
46
|
+
.end(apiCb(cb));
|
|
66
47
|
|
|
67
48
|
// Success
|
|
68
49
|
it('should return a JSON object with all the users and a status code ' +
|
|
69
|
-
`of "${OK}" if the request was successful.`, (done) => {
|
|
50
|
+
`of "${HttpStatusCodes.OK}" if the request was successful.`, (done) => {
|
|
70
51
|
// Add spy
|
|
71
|
-
|
|
52
|
+
const data = getDummyUsers();
|
|
53
|
+
spyOn(UserRepo, 'getAll').and.resolveTo(data);
|
|
72
54
|
// Call API
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
expect(user).toEqual(DummyGetAllUsers[i]);
|
|
79
|
-
}
|
|
80
|
-
done();
|
|
81
|
-
});
|
|
55
|
+
api(res => {
|
|
56
|
+
expect(res.status).toBe(HttpStatusCodes.OK);
|
|
57
|
+
expect(res.body).toEqual({ users: data });
|
|
58
|
+
done();
|
|
59
|
+
});
|
|
82
60
|
});
|
|
83
61
|
});
|
|
84
62
|
|
|
85
63
|
// Test add user
|
|
86
64
|
describe(`"POST:${Paths.Users.Add}"`, () => {
|
|
87
65
|
|
|
88
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
66
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
67
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
89
68
|
|
|
90
|
-
|
|
69
|
+
// Setup API
|
|
70
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
91
71
|
agent
|
|
92
72
|
.post(Paths.Users.Add)
|
|
93
|
-
.
|
|
73
|
+
.send({ user })
|
|
74
|
+
.end(apiCb(cb));
|
|
94
75
|
|
|
95
76
|
// Test add user success
|
|
96
|
-
it(`should return a status code of "${CREATED}" if the
|
|
97
|
-
'successful.', (done) => {
|
|
77
|
+
it(`should return a status code of "${HttpStatusCodes.CREATED}" if the ` +
|
|
78
|
+
'request was successful.', (done) => {
|
|
98
79
|
// Spy
|
|
99
80
|
spyOn(UserRepo, 'add').and.resolveTo();
|
|
100
81
|
// Call api
|
|
101
|
-
callApi(
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
done();
|
|
106
|
-
});
|
|
82
|
+
callApi(DUMMY_USER, res => {
|
|
83
|
+
expect(res.status).toBe(HttpStatusCodes.CREATED);
|
|
84
|
+
done();
|
|
85
|
+
});
|
|
107
86
|
});
|
|
108
87
|
|
|
109
88
|
// Missing param
|
|
110
|
-
it(
|
|
111
|
-
`
|
|
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 ` +
|
|
112
91
|
'param was missing.', (done) => {
|
|
113
92
|
// Call api
|
|
114
|
-
callApi({
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
});
|
|
93
|
+
callApi(null, res => {
|
|
94
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
95
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
96
|
+
done();
|
|
97
|
+
});
|
|
120
98
|
});
|
|
121
99
|
});
|
|
122
100
|
|
|
123
|
-
//
|
|
101
|
+
// Update users
|
|
124
102
|
describe(`"PUT:${Paths.Users.Update}"`, () => {
|
|
125
103
|
|
|
126
|
-
const ERROR_MSG = `${ValidatorErr}"user"
|
|
104
|
+
const ERROR_MSG = `${ValidatorErr}"user".`,
|
|
105
|
+
DUMMY_USER = getDummyUsers()[0];
|
|
127
106
|
|
|
128
|
-
|
|
107
|
+
// Setup API
|
|
108
|
+
const callApi = (user: IUser | null, cb: TApiCb) =>
|
|
129
109
|
agent
|
|
130
110
|
.put(Paths.Users.Update)
|
|
131
|
-
.
|
|
111
|
+
.send({ user })
|
|
112
|
+
.end(apiCb(cb));
|
|
132
113
|
|
|
133
114
|
// Success
|
|
134
|
-
it(`should return a status code of "${OK}" if the
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
expect(res.body.error).toBeUndefined();
|
|
144
|
-
done();
|
|
145
|
-
});
|
|
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();
|
|
146
124
|
});
|
|
125
|
+
});
|
|
147
126
|
|
|
148
127
|
// Param missing
|
|
149
|
-
it(
|
|
150
|
-
`
|
|
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 ` +
|
|
151
130
|
'param was missing.', (done) => {
|
|
152
131
|
// Call api
|
|
153
|
-
callApi({
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
});
|
|
132
|
+
callApi(null, res => {
|
|
133
|
+
expect(res.status).toBe(HttpStatusCodes.BAD_REQUEST);
|
|
134
|
+
expect(res.body.error).toBe(ERROR_MSG);
|
|
135
|
+
done();
|
|
136
|
+
});
|
|
159
137
|
});
|
|
160
138
|
|
|
161
139
|
// User not found
|
|
162
140
|
it('should return a JSON object with the error message of ' +
|
|
163
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
164
|
-
|
|
141
|
+
`"${USER_NOT_FOUND_ERR}" and a status code of ` +
|
|
142
|
+
`"${HttpStatusCodes.NOT_FOUND}" if the id was not found.`, (done) => {
|
|
165
143
|
// Call api
|
|
166
|
-
callApi(
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
});
|
|
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
|
+
});
|
|
172
149
|
});
|
|
173
150
|
});
|
|
174
151
|
|
|
175
|
-
//
|
|
152
|
+
// Delete User
|
|
176
153
|
describe(`"DELETE:${Paths.Users.Delete}"`, () => {
|
|
177
154
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const callApi = (id: number) =>
|
|
155
|
+
// Call API
|
|
156
|
+
const callApi = (id: number, cb: TApiCb) =>
|
|
181
157
|
agent
|
|
182
|
-
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
158
|
+
.delete(insertUrlParams(Paths.Users.Delete, { id }))
|
|
159
|
+
.end(apiCb(cb));
|
|
183
160
|
|
|
184
161
|
// Success
|
|
185
|
-
it(`should return a status code of "${OK}" if the
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
expect(res.body.error).toBeUndefined();
|
|
195
|
-
done();
|
|
196
|
-
});
|
|
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();
|
|
197
171
|
});
|
|
172
|
+
});
|
|
198
173
|
|
|
199
174
|
// User not found
|
|
200
175
|
it('should return a JSON object with the error message of ' +
|
|
201
|
-
`"${USER_NOT_FOUND_ERR}" and a status code of
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
// Invalid param
|
|
212
|
-
it(`should return a status code of "${BAD_REQUEST}" and return an error ` +
|
|
213
|
-
`message of "${VALIDATOR_ERR}" if the id was not a valid number`,
|
|
214
|
-
(done) => {
|
|
215
|
-
callApi('horse' as unknown as number)
|
|
216
|
-
.end((_: Error, res: TRes) => {
|
|
217
|
-
expect(res.status).toBe(BAD_REQUEST);
|
|
218
|
-
expect(res.body.error).toBe(VALIDATOR_ERR);
|
|
219
|
-
done();
|
|
220
|
-
});
|
|
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
|
+
});
|
|
221
184
|
});
|
|
222
185
|
});
|
|
223
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')),
|