lesgo 0.7.1 → 0.7.5
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/package.json +2 -2
- package/src/middlewares/__tests__/errorHttpResponseMiddleware.spec.js +3 -3
- package/src/middlewares/__tests__/normalizeHttpRequestMiddleware.spec.js +39 -1
- package/src/middlewares/errorHttpResponseMiddleware.js +9 -7
- package/src/middlewares/normalizeHttpRequestMiddleware.js +16 -4
- package/src/services/ElasticsearchService.js +25 -76
- package/src/services/__tests__/LengthAwarePaginator.spec.js +44 -1
- package/src/services/pagination/Paginator.js +11 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lesgo",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Core framework for lesgo node.js serverless framework.",
|
|
5
5
|
"main": "./src/index.js",
|
|
6
6
|
"author": "Sufiyan Rahmat",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
"prettier": "^1.18.2"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@elastic/elasticsearch": "^7.
|
|
54
|
+
"@elastic/elasticsearch": "^7.16.0",
|
|
55
55
|
"@firebase/app": "^0.6.12",
|
|
56
56
|
"data-api-client": "^1.1.0",
|
|
57
57
|
"firebase-admin": "^9.3.0",
|
|
@@ -21,7 +21,7 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
|
|
|
21
21
|
expect(dataBody).toHaveProperty('status', 'error');
|
|
22
22
|
expect(dataBody).toHaveProperty('data', null);
|
|
23
23
|
expect(dataBody).toHaveProperty('error');
|
|
24
|
-
expect(dataBody).toHaveProperty('error.code', '
|
|
24
|
+
expect(dataBody).toHaveProperty('error.code', 'UNHANDLED_ERROR');
|
|
25
25
|
expect(dataBody).toHaveProperty(
|
|
26
26
|
'error.message',
|
|
27
27
|
'Error: Test validation error'
|
|
@@ -78,7 +78,7 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
|
|
|
78
78
|
|
|
79
79
|
expect(data.statusCode).toBe(500);
|
|
80
80
|
|
|
81
|
-
expect(dataBody).toHaveProperty('error.code', '
|
|
81
|
+
expect(dataBody).toHaveProperty('error.code', 'UNHANDLED_ERROR');
|
|
82
82
|
expect(dataBody).toHaveProperty('error.message', 'Test error message');
|
|
83
83
|
expect(dataBody).toHaveProperty('error.details', '');
|
|
84
84
|
});
|
|
@@ -126,7 +126,7 @@ describe('MiddlewareGroup: test errorHandler middleware', () => {
|
|
|
126
126
|
expect(dataBody).toHaveProperty('status', 'error');
|
|
127
127
|
expect(dataBody).toHaveProperty('data', null);
|
|
128
128
|
expect(dataBody).toHaveProperty('error');
|
|
129
|
-
expect(dataBody).toHaveProperty('error.code', '
|
|
129
|
+
expect(dataBody).toHaveProperty('error.code', 'UNHANDLED_ERROR');
|
|
130
130
|
expect(dataBody).toHaveProperty('error.message', '');
|
|
131
131
|
expect(dataBody).toHaveProperty('error.details', '');
|
|
132
132
|
});
|
|
@@ -102,7 +102,6 @@ describe('MiddlewareGroup: test normalizeHttpRequestBeforeHandler', () => {
|
|
|
102
102
|
};
|
|
103
103
|
|
|
104
104
|
normalizeHttpRequestBeforeHandler(handler, () => {});
|
|
105
|
-
expect(handler.event.requestContext.requestId).toBe('requestId');
|
|
106
105
|
});
|
|
107
106
|
|
|
108
107
|
it('should return auth.sub if Authorization exists', () => {
|
|
@@ -119,6 +118,44 @@ describe('MiddlewareGroup: test normalizeHttpRequestBeforeHandler', () => {
|
|
|
119
118
|
expect(handler.event.auth.sub).toBe('f2b5349d-f5e3-44f5-9c08-ae6b01e95434');
|
|
120
119
|
});
|
|
121
120
|
|
|
121
|
+
it.each`
|
|
122
|
+
version | tags
|
|
123
|
+
${'1.0'} | ${{ path: '/v1/path', httpMethod: 'GET' }}
|
|
124
|
+
${'2.0'} | ${{ path: '/v2/path', httpMethod: 'POST' }}
|
|
125
|
+
${'3.0'} | ${{ path: '/v2/path', httpMethod: 'POST' }}
|
|
126
|
+
`(
|
|
127
|
+
'should identify path and httpMethod based on version',
|
|
128
|
+
({ version, tags }) => {
|
|
129
|
+
const handler = {
|
|
130
|
+
event: {
|
|
131
|
+
version,
|
|
132
|
+
headers: {},
|
|
133
|
+
...tags,
|
|
134
|
+
requestContext: {
|
|
135
|
+
http: {
|
|
136
|
+
...tags,
|
|
137
|
+
method: tags.httpMethod,
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
};
|
|
142
|
+
normalizeHttpRequestBeforeHandler(handler, () => {});
|
|
143
|
+
expect(logger.meta.tags).toStrictEqual(tags);
|
|
144
|
+
}
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
it('should not set tags when using API Gateway v2 and requestContext is empty', () => {
|
|
148
|
+
const handler = {
|
|
149
|
+
event: {
|
|
150
|
+
version: '2.0',
|
|
151
|
+
headers: {},
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
normalizeHttpRequestBeforeHandler(handler, () => {});
|
|
156
|
+
expect(logger.meta.tags).toStrictEqual({});
|
|
157
|
+
});
|
|
158
|
+
|
|
122
159
|
it('should not set meta on debug', () => {
|
|
123
160
|
app.debug = true;
|
|
124
161
|
const handler = {
|
|
@@ -133,6 +170,7 @@ describe('MiddlewareGroup: test normalizeHttpRequestBeforeHandler', () => {
|
|
|
133
170
|
},
|
|
134
171
|
},
|
|
135
172
|
};
|
|
173
|
+
|
|
136
174
|
normalizeHttpRequestBeforeHandler(handler, () => {});
|
|
137
175
|
expect(logger.meta.auth).toBe(handler.event.auth);
|
|
138
176
|
expect(logger.meta.queryStringParameters).toStrictEqual(
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import logger from '../utils/logger';
|
|
2
2
|
import isEmpty from '../utils/isEmpty';
|
|
3
3
|
|
|
4
|
+
const FILE = 'Lesgo/middlewares/errorHttpResponseMiddleware';
|
|
5
|
+
|
|
4
6
|
export const errorHttpResponseHandler = async opts => {
|
|
5
7
|
const defaults = {
|
|
6
8
|
response: '',
|
|
@@ -29,7 +31,7 @@ export const errorHttpResponseHandler = async opts => {
|
|
|
29
31
|
status: 'error',
|
|
30
32
|
data: null,
|
|
31
33
|
error: {
|
|
32
|
-
code: options.error.code || '
|
|
34
|
+
code: options.error.code || 'UNHANDLED_ERROR',
|
|
33
35
|
message: options.error.name
|
|
34
36
|
? `${options.error.name}: ${options.error.message}`
|
|
35
37
|
: options.error.message || options.error,
|
|
@@ -40,18 +42,18 @@ export const errorHttpResponseHandler = async opts => {
|
|
|
40
42
|
|
|
41
43
|
const statusCode = options.error.statusCode || options.statusCode;
|
|
42
44
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
// this is likely an unhandled exception, log it
|
|
46
|
-
logger.error(options.error);
|
|
45
|
+
if (!isEmpty(options.error)) {
|
|
46
|
+
logger.log(statusCode === 500 ? 'error' : 'warn', options.error);
|
|
47
47
|
} else {
|
|
48
|
-
logger.warn
|
|
48
|
+
logger.log(statusCode === 500 ? 'error' : 'warn', jsonBody.error.message, {
|
|
49
|
+
error: jsonBody.error,
|
|
50
|
+
});
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
try {
|
|
52
54
|
if (!isEmpty(opts.db)) await opts.db.end();
|
|
53
55
|
} catch (err) {
|
|
54
|
-
|
|
56
|
+
logger.error(`${FILE}::Failed to end db connection`, err);
|
|
55
57
|
}
|
|
56
58
|
|
|
57
59
|
return {
|
|
@@ -53,14 +53,26 @@ export const normalizeHttpRequestBeforeHandler = (handler, next) => {
|
|
|
53
53
|
// eslint-disable-next-line no-param-reassign
|
|
54
54
|
handler.event.auth = auth;
|
|
55
55
|
|
|
56
|
+
const tags = {};
|
|
57
|
+
switch (handler.event.version) {
|
|
58
|
+
case '2.0': {
|
|
59
|
+
if (handler.event.requestContext && handler.event.requestContext.http) {
|
|
60
|
+
tags.path = handler.event.requestContext.http.path;
|
|
61
|
+
tags.httpMethod = handler.event.requestContext.http.method;
|
|
62
|
+
}
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
default:
|
|
66
|
+
tags.path = handler.event.path;
|
|
67
|
+
tags.httpMethod = handler.event.httpMethod;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
56
71
|
logger.addMeta({
|
|
57
72
|
requestId: handler.event.requestContext
|
|
58
73
|
? handler.event.requestContext.requestId
|
|
59
74
|
: null,
|
|
60
|
-
tags
|
|
61
|
-
path: handler.event.path,
|
|
62
|
-
httpMethod: handler.event.httpMethod,
|
|
63
|
-
},
|
|
75
|
+
tags,
|
|
64
76
|
});
|
|
65
77
|
|
|
66
78
|
if (app.debug) {
|
|
@@ -38,24 +38,18 @@ class ElasticsearchService {
|
|
|
38
38
|
return this.client;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
search(body) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.result = response;
|
|
55
|
-
|
|
56
|
-
resolve(response);
|
|
57
|
-
});
|
|
58
|
-
});
|
|
41
|
+
async search(body) {
|
|
42
|
+
const param = {
|
|
43
|
+
index: this.index,
|
|
44
|
+
type: this.type,
|
|
45
|
+
body,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const response = await this.client.search(param);
|
|
49
|
+
|
|
50
|
+
this.result = response;
|
|
51
|
+
|
|
52
|
+
return response;
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
/**
|
|
@@ -75,12 +69,7 @@ class ElasticsearchService {
|
|
|
75
69
|
body: settings,
|
|
76
70
|
};
|
|
77
71
|
|
|
78
|
-
return
|
|
79
|
-
this.client.indices.create(params, (err, response) => {
|
|
80
|
-
// eslint-disable-next-line no-unused-expressions
|
|
81
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
72
|
+
return this.client.indices.create(params);
|
|
84
73
|
}
|
|
85
74
|
|
|
86
75
|
deleteIndices(index, options = {}) {
|
|
@@ -89,32 +78,20 @@ class ElasticsearchService {
|
|
|
89
78
|
...options,
|
|
90
79
|
};
|
|
91
80
|
|
|
92
|
-
return
|
|
93
|
-
this.client.indices.delete(params, (err, response) => {
|
|
94
|
-
// eslint-disable-next-line no-unused-expressions
|
|
95
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
81
|
+
return this.client.indices.delete(params);
|
|
98
82
|
}
|
|
99
83
|
|
|
100
|
-
existIndices(index, options = {}) {
|
|
84
|
+
async existIndices(index, options = {}) {
|
|
101
85
|
const params = { index, ...options };
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response.body);
|
|
106
|
-
});
|
|
107
|
-
});
|
|
86
|
+
|
|
87
|
+
const response = await this.client.indices.exists(params);
|
|
88
|
+
return response.body;
|
|
108
89
|
}
|
|
109
90
|
|
|
110
91
|
putMapping(index, type, body) {
|
|
111
92
|
const params = { index, type, body: { properties: body } };
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
// eslint-disable-next-line no-unused-expressions
|
|
115
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
93
|
+
|
|
94
|
+
return this.client.indices.putMapping(params);
|
|
118
95
|
}
|
|
119
96
|
|
|
120
97
|
get(id) {
|
|
@@ -124,12 +101,7 @@ class ElasticsearchService {
|
|
|
124
101
|
id,
|
|
125
102
|
};
|
|
126
103
|
|
|
127
|
-
return
|
|
128
|
-
this.client.get(params, (err, response) => {
|
|
129
|
-
// eslint-disable-next-line no-unused-expressions
|
|
130
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
131
|
-
});
|
|
132
|
-
});
|
|
104
|
+
return this.client.get(params);
|
|
133
105
|
}
|
|
134
106
|
|
|
135
107
|
indexOrCreateById(body, refresh = false) {
|
|
@@ -141,24 +113,11 @@ class ElasticsearchService {
|
|
|
141
113
|
refresh,
|
|
142
114
|
};
|
|
143
115
|
|
|
144
|
-
return
|
|
145
|
-
this.client.index(params, (err, response) => {
|
|
146
|
-
// eslint-disable-next-line no-unused-expressions
|
|
147
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
148
|
-
});
|
|
149
|
-
});
|
|
116
|
+
return this.client.index(params);
|
|
150
117
|
}
|
|
151
118
|
|
|
152
119
|
bulkIndex(bodies) {
|
|
153
|
-
return
|
|
154
|
-
this.client.bulk(
|
|
155
|
-
{ body: this.constructBulkIndex(bodies) },
|
|
156
|
-
(err, response) => {
|
|
157
|
-
// eslint-disable-next-line no-unused-expressions
|
|
158
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
159
|
-
}
|
|
160
|
-
);
|
|
161
|
-
});
|
|
120
|
+
return this.client.bulk({ body: this.constructBulkIndex(bodies) });
|
|
162
121
|
}
|
|
163
122
|
|
|
164
123
|
create(id, body) {
|
|
@@ -169,12 +128,7 @@ class ElasticsearchService {
|
|
|
169
128
|
body,
|
|
170
129
|
};
|
|
171
130
|
|
|
172
|
-
return
|
|
173
|
-
this.client.index(params, (err, response) => {
|
|
174
|
-
// eslint-disable-next-line no-unused-expressions
|
|
175
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
176
|
-
});
|
|
177
|
-
});
|
|
131
|
+
return this.client.index(params);
|
|
178
132
|
}
|
|
179
133
|
|
|
180
134
|
updateById(id) {
|
|
@@ -184,12 +138,7 @@ class ElasticsearchService {
|
|
|
184
138
|
id,
|
|
185
139
|
};
|
|
186
140
|
|
|
187
|
-
return
|
|
188
|
-
this.client.get(params, (err, response) => {
|
|
189
|
-
// eslint-disable-next-line no-unused-expressions
|
|
190
|
-
err ? /* istanbul ignore next */ reject(err) : resolve(response);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
141
|
+
return this.client.get(params);
|
|
193
142
|
}
|
|
194
143
|
|
|
195
144
|
/**
|
|
@@ -32,6 +32,8 @@ describe('test LengthAwarePaginator instantiate', () => {
|
|
|
32
32
|
expect(await paginator.lastItem()).toMatchObject(mockDataLastItem);
|
|
33
33
|
expect(paginator.perPage()).toEqual(5);
|
|
34
34
|
expect(await paginator.total()).toEqual(30);
|
|
35
|
+
|
|
36
|
+
expect(db.select).toHaveBeenCalled();
|
|
35
37
|
});
|
|
36
38
|
it('should not throw exception when instantiating with current page', async () => {
|
|
37
39
|
const paginator = new LengthAwarePaginator(
|
|
@@ -51,6 +53,8 @@ describe('test LengthAwarePaginator instantiate', () => {
|
|
|
51
53
|
expect(await paginator.lastItem()).toMatchObject(mockDataLastItem);
|
|
52
54
|
expect(paginator.perPage()).toEqual(5);
|
|
53
55
|
expect(await paginator.total()).toEqual(30);
|
|
56
|
+
|
|
57
|
+
expect(db.select).toHaveBeenCalled();
|
|
54
58
|
});
|
|
55
59
|
it('should default perPage to 10 when instantiating without perPage', async () => {
|
|
56
60
|
const paginator = new LengthAwarePaginator(
|
|
@@ -66,6 +70,8 @@ describe('test LengthAwarePaginator instantiate', () => {
|
|
|
66
70
|
expect(paginator.currentPage()).toEqual(1);
|
|
67
71
|
expect(paginator.perPage()).toEqual(10);
|
|
68
72
|
expect(await paginator.total()).toEqual(30);
|
|
73
|
+
|
|
74
|
+
expect(db.select).toHaveBeenCalled();
|
|
69
75
|
});
|
|
70
76
|
it('should throw exception if total is not a number', async () => {
|
|
71
77
|
try {
|
|
@@ -116,6 +122,43 @@ describe('test LengthAwarePaginator instantiate', () => {
|
|
|
116
122
|
{ ...mockDataLastItem },
|
|
117
123
|
],
|
|
118
124
|
});
|
|
125
|
+
|
|
126
|
+
expect(db.select).toHaveBeenCalled();
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it('should simply return an empty paginator object if total is explicitly zero', async () => {
|
|
130
|
+
const paginator = new LengthAwarePaginator(
|
|
131
|
+
db,
|
|
132
|
+
'SELECT * FROM tests',
|
|
133
|
+
{},
|
|
134
|
+
{
|
|
135
|
+
perPage: 5,
|
|
136
|
+
currentPage: 1,
|
|
137
|
+
total: 0,
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
expect(await paginator.count()).toEqual(0);
|
|
142
|
+
expect(await paginator.previousPage()).toEqual(false);
|
|
143
|
+
expect(paginator.currentPage()).toEqual(1);
|
|
144
|
+
expect(await paginator.nextPage()).toEqual(false);
|
|
145
|
+
expect(await paginator.firstItem()).toBe(undefined);
|
|
146
|
+
expect(await paginator.lastItem()).toBe(undefined);
|
|
147
|
+
expect(paginator.perPage()).toEqual(5);
|
|
148
|
+
expect(await paginator.total()).toEqual(0);
|
|
149
|
+
|
|
150
|
+
expect(await paginator.toObject()).toMatchObject({
|
|
151
|
+
count: 0,
|
|
152
|
+
previous_page: false,
|
|
153
|
+
current_page: 1,
|
|
154
|
+
next_page: false,
|
|
155
|
+
per_page: 5,
|
|
156
|
+
last_page: 0,
|
|
157
|
+
total: 0,
|
|
158
|
+
items: [],
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(db.select).not.toHaveBeenCalled();
|
|
119
162
|
});
|
|
120
163
|
});
|
|
121
164
|
|
|
@@ -138,7 +181,7 @@ describe('test total() usage', () => {
|
|
|
138
181
|
});
|
|
139
182
|
|
|
140
183
|
describe('test lastPage() usage', () => {
|
|
141
|
-
it('should get the last page using supplied
|
|
184
|
+
it('should get the last page using supplied parameter as total data', async () => {
|
|
142
185
|
const paginator1 = new LengthAwarePaginator(
|
|
143
186
|
db,
|
|
144
187
|
'SELECT * FROM total_tests',
|
|
@@ -217,11 +217,17 @@ export default class Paginator {
|
|
|
217
217
|
}
|
|
218
218
|
|
|
219
219
|
async executeQuery() {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
)
|
|
220
|
+
const total = this.totalProp;
|
|
221
|
+
if (
|
|
222
|
+
(typeof total === 'number' && total > 0) ||
|
|
223
|
+
(typeof total !== 'number' && !total)
|
|
224
|
+
) {
|
|
225
|
+
this.response = await this.dbProp.select(
|
|
226
|
+
this.generatePaginationSqlSnippet(),
|
|
227
|
+
this.sqlParamsProp,
|
|
228
|
+
this.connection
|
|
229
|
+
);
|
|
230
|
+
}
|
|
225
231
|
|
|
226
232
|
this.hasNext = this.response.length > this.perPage();
|
|
227
233
|
if (this.hasNext) {
|