@adaptivestone/framework 3.0.23 → 3.1.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.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,13 @@
1
- ### 3.1.0 // NEXT
1
+ ### 3.1.1
2
+
3
+ [UPDATE] updated deps
4
+ [FIX] fix cache error handling.
5
+
6
+ ### 3.1.0
2
7
 
3
8
  [NEW] new comand to generate open API documentation (wip)
4
9
  [NEW] coverage report
10
+ [UPDATE] updated deps
5
11
 
6
12
  ### 3.0.23
7
13
 
@@ -1,17 +1,51 @@
1
+ const fs = require('fs').promises;
2
+
1
3
  const AbstractCommand = require('../modules/AbstractCommand');
2
4
 
5
+ /**
6
+ * Command for generate documentation json file openApi
7
+ */
3
8
  class GetOpenApiJson extends AbstractCommand {
4
9
  async run() {
5
10
  const { myDomain } = this.app.getConfig('http');
11
+ let jsonFile = process.env.npm_package_json;
12
+ if (!jsonFile) {
13
+ jsonFile = `${process.env.PWD}/package.json`;
14
+ }
15
+
16
+ try {
17
+ // eslint-disable-next-line import/no-dynamic-require, global-require
18
+ jsonFile = require(jsonFile);
19
+ } catch (e) {
20
+ this.logger.error(
21
+ 'No npm package detected. Please start this command via NPM as it depends on package.json',
22
+ );
23
+ }
24
+
25
+ if (!jsonFile) {
26
+ jsonFile = {
27
+ name: 'UNDETECTD PROJECT',
28
+ description: 'UNDETECTD PROJECT DECCRIPTION',
29
+ version: '0.0.0-undetrcted',
30
+ author: {
31
+ email: 'none@example.com',
32
+ },
33
+ };
34
+ }
35
+
36
+ if (!jsonFile.author) {
37
+ jsonFile.author = 'none@example.com';
38
+ }
39
+
6
40
  const openApi = {
7
41
  openapi: '3.0.0',
8
42
  info: {
9
- title: 'Some title',
10
- description: 'This is a simple API',
43
+ title: jsonFile.name,
44
+ description: jsonFile.description,
11
45
  contact: {
12
- email: 'you@your-company.com',
46
+ email: jsonFile.author.email,
13
47
  },
14
- version: '1.0.0',
48
+ version: jsonFile.version,
15
49
  },
16
50
  servers: [
17
51
  {
@@ -215,7 +249,12 @@ class GetOpenApiJson extends AbstractCommand {
215
249
  }
216
250
 
217
251
  const result = JSON.stringify(openApi);
218
- console.log(result);
252
+
253
+ if (this.args.output) {
254
+ await fs.writeFile(this.args.output, result);
255
+ this.logger.info(`Output to: ${this.args.output}`);
256
+ }
257
+
219
258
  return result;
220
259
  }
221
260
  }
@@ -31,6 +31,26 @@ describe('auth', () => {
31
31
  expect(status).toBe(201);
32
32
  });
33
33
 
34
+ it('can not create user with the same nickname', async () => {
35
+ expect.assertions(1);
36
+ await request(global.server.app.httpServer.express)
37
+ .post('/auth/register')
38
+ .send({
39
+ email: userEmail,
40
+ password: userPassword,
41
+ nickName: 'test',
42
+ });
43
+
44
+ const { status } = await request(global.server.app.httpServer.express)
45
+ .post('/auth/register')
46
+ .send({
47
+ email: userEmail2,
48
+ password: '123',
49
+ nickName: 'test',
50
+ });
51
+ expect(status).toBe(400);
52
+ });
53
+
34
54
  it('can NOT create SAME user', async () => {
35
55
  expect.assertions(1);
36
56
  const { status } = await request(global.server.app.httpServer.express)
@@ -90,6 +110,136 @@ describe('auth', () => {
90
110
  });
91
111
 
92
112
  describe('isAuthWithVefificationFlow auth option', () => {
113
+ it('can verify user', async () => {
114
+ expect.assertions(2);
115
+ const user = await global.server.app.getModel('User').create({
116
+ email: 'Test@gmail.com',
117
+ password: 'userPassword',
118
+ name: {
119
+ nick: 'nickname',
120
+ },
121
+ });
122
+
123
+ user.verificationTokens.push({
124
+ token: 'testToken',
125
+ });
126
+
127
+ await user.save();
128
+
129
+ const { status } = await request(
130
+ global.server.app.httpServer.express,
131
+ ).post(`/auth/verify?verification_token=testToken`);
132
+
133
+ const { isVerified } = await global.server.app.getModel('User').findOne({
134
+ email: 'Test@gmail.com',
135
+ });
136
+
137
+ expect(status).toBe(200);
138
+ expect(isVerified).toBeTruthy();
139
+ });
140
+
141
+ it('can not verify user with wrong token', async () => {
142
+ expect.assertions(2);
143
+ const user = await global.server.app.getModel('User').create({
144
+ email: 'Test423@gmail.com',
145
+ password: 'userPassword',
146
+ name: {
147
+ nick: 'nicknameee',
148
+ },
149
+ });
150
+
151
+ user.verificationTokens.push({
152
+ token: 'testToken',
153
+ });
154
+
155
+ await user.save();
156
+
157
+ const { status } = await request(
158
+ global.server.app.httpServer.express,
159
+ ).post(`/auth/verify?verification_token=testToken123wrong`);
160
+
161
+ const { isVerified } = await global.server.app.getModel('User').findOne({
162
+ email: 'Test423@gmail.com',
163
+ });
164
+
165
+ expect(status).toBe(400);
166
+ expect(isVerified).toBeFalsy();
167
+ });
168
+
169
+ it('can NOT send recovery to not exist email', async () => {
170
+ expect.assertions(1);
171
+ const { status } = await request(global.server.app.httpServer.express)
172
+ .post('/auth/send-recovery-email')
173
+ .send({
174
+ email: 'notExists@gmail.com',
175
+ });
176
+ expect(status).toBe(400);
177
+ });
178
+
179
+ it('can send recovery to exist email', async () => {
180
+ expect.assertions(1);
181
+ const { status } = await request(global.server.app.httpServer.express)
182
+ .post('/auth/send-recovery-email')
183
+ .send({
184
+ email: userEmail,
185
+ });
186
+ expect(status).toBe(200);
187
+ });
188
+
189
+ it('can recover password', async () => {
190
+ expect.assertions(1);
191
+
192
+ const user = await global.server.app.getModel('User').create({
193
+ email: 'Test1@gmail.com',
194
+ password: 'userPassword',
195
+ name: {
196
+ nick: 'nickname1',
197
+ },
198
+ });
199
+
200
+ user.passwordRecoveryTokens.push({
201
+ token: 'superPassword',
202
+ });
203
+
204
+ await user.save();
205
+
206
+ const { status } = await request(global.server.app.httpServer.express)
207
+ .post('/auth/recover-password')
208
+ .send({
209
+ password: 'newPass',
210
+ passwordRecoveryToken: 'superPassword',
211
+ });
212
+
213
+ expect(status).toBe(200);
214
+ });
215
+
216
+ it('can not recover password with wrong token', async () => {
217
+ expect.assertions(1);
218
+
219
+ const user = await global.server.app.getModel('User').create({
220
+ email: 'Test2@gmail.com',
221
+ password: 'userPassword',
222
+ name: {
223
+ nick: 'nickname2',
224
+ },
225
+ });
226
+
227
+ user.passwordRecoveryTokens.push({
228
+ token: 'superPassword',
229
+ });
230
+
231
+ await user.save();
232
+
233
+ const { status } = await request(global.server.app.httpServer.express)
234
+ .post('/auth/recover-password')
235
+ .send({
236
+ password: 'newPass',
237
+ passwordRecoveryToken: '13123',
238
+ });
239
+
240
+ expect(status).toBe(400);
241
+ });
242
+
93
243
  it('can login with normal creds and NOT verifyed email is option isAuthWithVefificationFlow is set', async () => {
94
244
  expect.assertions(4);
95
245
 
@@ -129,6 +279,28 @@ describe('auth', () => {
129
279
  });
130
280
  });
131
281
 
282
+ it('can user send verification', async () => {
283
+ expect.assertions(1);
284
+
285
+ const { status } = await request(global.server.app.httpServer.express)
286
+ .post('/auth/send-verification')
287
+ .send({
288
+ email: userEmail2,
289
+ });
290
+ expect(status).toBe(200);
291
+ });
292
+
293
+ it('can not user send verification to wrong email', async () => {
294
+ expect.assertions(1);
295
+
296
+ const { status } = await request(global.server.app.httpServer.express)
297
+ .post('/auth/send-verification')
298
+ .send({
299
+ email: 'wrong@gmail.com',
300
+ });
301
+ expect(status).toBe(400);
302
+ });
303
+
132
304
  describe('rate limiter', () => {
133
305
  it('we should receive 429 on rate limit exceeded', async () => {
134
306
  expect.assertions(1);
@@ -0,0 +1,17 @@
1
+ const SomeController = require('../controllers/test/SomeController');
2
+ const AbstractController = require('./AbstractController');
3
+
4
+ describe('abstract controller methods', () => {
5
+ it('can get routes', async () => {
6
+ expect.assertions(2);
7
+
8
+ const controller = new AbstractController(global.server.app);
9
+ const childController = new SomeController(global.server.app);
10
+
11
+ const { routes } = controller;
12
+ const { routes: childRoutes } = childController;
13
+
14
+ expect(routes).toStrictEqual({});
15
+ expect(childRoutes).toBeDefined();
16
+ });
17
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adaptivestone/framework",
3
- "version": "3.0.23",
3
+ "version": "3.1.1",
4
4
  "description": "Adaptive stone node js framework",
5
5
  "main": "src/index.js",
6
6
  "engines": {
@@ -35,21 +35,33 @@ class Cache extends Base {
35
35
  const key = `${this.redisNamespace}-${keyValue}`;
36
36
  // 5 mins default
37
37
  let resolve = null;
38
+ let reject = null;
38
39
  if (this.promiseMapping.has(key)) {
39
40
  return this.promiseMapping.get(key);
40
41
  }
41
42
 
42
43
  this.promiseMapping.set(
43
44
  key,
44
- new Promise((res) => {
45
+ new Promise((res, rej) => {
45
46
  resolve = res;
47
+ reject = rej;
46
48
  }),
47
49
  );
48
50
 
49
51
  let result = await this.redisClient.get(key);
50
52
  if (!result) {
51
53
  this.logger.verbose(`getSetValueFromCache not found for key ${key}`);
52
- result = await onNotFound();
54
+ try {
55
+ result = await onNotFound();
56
+ } catch (e) {
57
+ this.logger.error(
58
+ `Cache onNotFound for key '${key}' error: ${e.message}`,
59
+ );
60
+ this.promiseMapping.delete(key);
61
+ reject(e);
62
+ return Promise.reject(e);
63
+ }
64
+
53
65
  this.redisClient.setEx(key, storeTime, JSON.stringify(result));
54
66
  } else {
55
67
  this.logger.verbose(
@@ -0,0 +1,42 @@
1
+ const RateLimiter = require('./RateLimiter');
2
+
3
+ describe('middlewares methods', () => {
4
+ it('can create redis rateLimiter', async () => {
5
+ expect.assertions(1);
6
+
7
+ const redisRateLimiter = new RateLimiter(global.server.app, {
8
+ driver: 'redis',
9
+ });
10
+
11
+ expect(redisRateLimiter.limiter).toBeDefined();
12
+ });
13
+
14
+ it('can not create rateLimiter with unknown driver', async () => {
15
+ expect.assertions(1);
16
+
17
+ const rateLimiter = new RateLimiter(global.server.app, {
18
+ driver: 'unknown',
19
+ });
20
+
21
+ expect(rateLimiter.limiter).toBeNull();
22
+ });
23
+
24
+ it('generateConsumeKey works correctly', async () => {
25
+ expect.assertions(1);
26
+
27
+ const redisRateLimiter = new RateLimiter(global.server.app, {
28
+ driver: 'redis',
29
+ });
30
+
31
+ const res = await redisRateLimiter.gerenateConsumeKey({
32
+ ip: '192.168.0.0',
33
+ appInfo: {
34
+ user: {
35
+ id: 'someId',
36
+ },
37
+ },
38
+ });
39
+
40
+ expect(res).toBe('192.168.0.0__someId');
41
+ });
42
+ });