1 | const interceptor = require('./lib/interceptor');
|
2 | const Logger = require('./lib/logger');
|
3 | const utils = require('./lib/utils');
|
4 | const defaultResponseProcessor = require('./lib/default-response-processor');
|
5 | const LogStatusType = require('../utils/log-status-type');
|
6 | const _ = require('lodash');
|
7 |
|
8 | const logger = new Logger();
|
9 | const baseConfig = {
|
10 | serviceName: '',
|
11 | protectedFields: [
|
12 | 'password',
|
13 | 'currentPassword',
|
14 | 'newPassword',
|
15 | 'confirmationNewPassword',
|
16 | ],
|
17 | skip: [],
|
18 | responseProcessor: defaultResponseProcessor
|
19 | };
|
20 | let config;
|
21 |
|
22 | const getType = (req) => {
|
23 | const createMethods = [ 'POST' ];
|
24 | const updateMethods = [ 'PUT', 'PATCH' ];
|
25 | const deleteMethods = [ 'DELETE' ];
|
26 | const readMethods = [ 'GET' ];
|
27 | switch (true) {
|
28 | case createMethods.indexOf(req.method) >= 0:
|
29 | return 'Service Create';
|
30 | case updateMethods.indexOf(req.method) >= 0:
|
31 | return 'Service Update';
|
32 | case deleteMethods.indexOf(req.method) >= 0:
|
33 | return 'Service Delete';
|
34 | case readMethods.indexOf(req.method) >= 0:
|
35 | return 'Service Read';
|
36 | default:
|
37 | return 'Service Read';
|
38 | }
|
39 | };
|
40 |
|
41 | const getData = req => {
|
42 | return Object.assign({},
|
43 | req.query,
|
44 | req.params,
|
45 | req.body
|
46 | );
|
47 | };
|
48 |
|
49 |
|
50 | const mongoiseKeys = data => {
|
51 | if (_.isArray(data)) {
|
52 | return data;
|
53 | }
|
54 |
|
55 | return Object.keys(data).reduce((accumulator, key) => {
|
56 | const objKey = (key.indexOf('.') >= 0) ? _.kebabCase(key) : key
|
57 | accumulator[objKey] = data[key];
|
58 |
|
59 | return accumulator;
|
60 | }, {});
|
61 | };
|
62 |
|
63 | const clearProtectedData = data => {
|
64 | if (_.isArray(data)) {
|
65 | return data;
|
66 | }
|
67 |
|
68 | return _.omit(data, config.protectedFields);
|
69 | };
|
70 |
|
71 | const executeResponseProcessor = (data, req) => {
|
72 | const processedData = config.responseProcessor(data, req);
|
73 | if (typeof data !== 'object') {
|
74 | console.log('data processor return must be an object');
|
75 | throw new Error('data processor return must be an object');
|
76 | }
|
77 |
|
78 | return _.omit(processedData);
|
79 | };
|
80 |
|
81 | const normalizeData = _.flow(
|
82 | mongoiseKeys,
|
83 | clearProtectedData,
|
84 | );
|
85 |
|
86 | const shouldLog = req => {
|
87 | const allowedAction = req.method !== 'OPTIONS';
|
88 | const allowedPath = !config.skip.some(pathToSkip => new RegExp(pathToSkip).test(req.originalUrl));
|
89 | return allowedAction && allowedPath;
|
90 | }
|
91 |
|
92 | const logRequest = (req, res) => {
|
93 | if (!shouldLog(req)) {
|
94 | return false;
|
95 | }
|
96 |
|
97 | const type = getType(req);
|
98 | const data = getData(req);
|
99 | const result = 'trying';
|
100 | const reason = `${config.serviceName} - ${req.originalUrl}`;
|
101 | const system_id = req.system_id || _.get(req, 'user.system_id');
|
102 | const contractorId = req.contractor_id;
|
103 |
|
104 | if (!system_id) {
|
105 | console.warn('you must pass system_id to be able to log it', req.originalUrl);
|
106 | return false;
|
107 | }
|
108 |
|
109 | const promise = contractorId ?
|
110 | logger.log(type, reason, _.omit(normalizeData(data)), result, system_id, contractorId) :
|
111 | logger.broadcast(type, reason, _.omit(normalizeData(data)), result, system_id);
|
112 |
|
113 | return promise.catch(error => handleError(error, config));
|
114 | };
|
115 |
|
116 | const logResponse = (req, response, result) => {
|
117 | if (!shouldLog(req)) {
|
118 | return false;
|
119 | }
|
120 |
|
121 | const type = getType(req);
|
122 | const data = typeof response === 'object' ? response : { response };
|
123 | const reason = `${config.serviceName} - ${req.originalUrl}`;
|
124 | const system_id = req.system_id || _.get(req, 'user.system_id');
|
125 | const contractorId = req.contractor_id || _.get(data, 'contractor_id');
|
126 |
|
127 | if (!system_id) {
|
128 | console.warn('you must pass system_id to be able to log it', req.originalUrl);
|
129 | return false;
|
130 | }
|
131 |
|
132 |
|
133 | const promise = contractorId ?
|
134 | logger.log(type, reason, executeResponseProcessor(normalizeData(data), req), result, system_id, contractorId) :
|
135 | logger.broadcast(type, reason, executeResponseProcessor(normalizeData(data), req), result, system_id);
|
136 |
|
137 | return promise.catch(error => handleError(error, config));
|
138 | };
|
139 |
|
140 | const configureLogResponse = (req, res) => {
|
141 | interceptor.getResponseBody(res).then(responseBody => {
|
142 | const response = utils.parseResponse(responseBody);
|
143 | const responseStatus = _.get(response, 'statusLog[0].type') || LogStatusType.SUCCESS;
|
144 | const data = _.get(response, 'data') || _.get(response, 'statusLog[0]') || response;
|
145 |
|
146 | const status = responseStatus === LogStatusType.SUCCESS ? 'success' : 'error';
|
147 | logResponse(req, data, status);
|
148 | });
|
149 | };
|
150 |
|
151 | const configure = customConfig => (req, res, next) => {
|
152 | try {
|
153 | config = Object.assign({}, baseConfig, customConfig);
|
154 |
|
155 | logger.setRequestMetaData(interceptor.getRequestMetadata(req));
|
156 | logRequest(req, res);
|
157 | configureLogResponse(req, res);
|
158 | } catch (error) {
|
159 | handleError(error, config);
|
160 | }
|
161 |
|
162 | next();
|
163 | };
|
164 |
|
165 | const handleError = (error, config) => {
|
166 | const errorCode = _.get(error, 'code');
|
167 | console.error(`there was an error when logging ${config.serviceName}. code:`, errorCode);
|
168 | };
|
169 |
|
170 | module.exports = {
|
171 | configure,
|
172 | logger,
|
173 | };
|