-
Notifications
You must be signed in to change notification settings - Fork 79
/
activitylog.js
144 lines (115 loc) · 3.45 KB
/
activitylog.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
import { sortingParams } from 'shared/types/activityLogApiSchemas';
import model from './activitylogModel';
import { getSemanticData } from './activitylogParser';
const sortingParamsAsSet = new Set(sortingParams);
const isValidSortingParam = param => sortingParamsAsSet.has(param);
const validateSortingParam = param => {
if (!isValidSortingParam(param)) {
throw new Error(`Invalid sorting parameter: ${param}`);
}
};
const prepareRegexpQueries = query => {
const result = {};
if (query.url) {
result.url = new RegExp(query.url);
}
if (query.query) {
result.query = new RegExp(query.query);
}
if (query.body) {
result.body = new RegExp(query.body);
}
if (query.params) {
result.params = new RegExp(query.params);
}
return result;
};
const prepareQuery = query => {
if (!query.find) {
return prepareRegexpQueries(query);
}
const term = new RegExp(query.find);
return {
$or: [{ method: term }, { url: term }, { query: term }, { body: term }, { params: term }],
};
};
const prepareToFromRanges = sanitizedTime => {
const time = {};
if (sanitizedTime.from) {
time.$gte = parseInt(sanitizedTime.from, 10) * 1000;
}
if (sanitizedTime.to) {
time.$lte = parseInt(sanitizedTime.to, 10) * 1000;
}
return time;
};
const timeQuery = ({ time = {}, before = null }) => {
const sanitizedTime = Object.keys(time).reduce(
(memo, k) => (time[k] !== null ? Object.assign(memo, { [k]: time[k] }) : memo),
{}
);
if (before === null && !Object.keys(sanitizedTime).length) {
return {};
}
const result = { time: prepareToFromRanges(sanitizedTime) };
if (before !== null) {
result.time.$lt = parseInt(before, 10);
}
return result;
};
const getPagination = query => {
const { page } = query;
const limit = parseInt(query.limit || 15, 10);
const paginationOptions = { limit };
if (page) {
paginationOptions.skip = (page - 1) * limit;
}
return paginationOptions;
};
const getSort = query => {
const { sort } = query;
const prop = sort?.prop || 'time';
const asc = !!sort?.asc || false;
validateSortingParam(prop);
const sortOptions = { sort: {}, collation: { locale: 'en', strength: 2 } };
sortOptions.sort[prop] = asc ? 1 : -1;
if (prop !== 'time') {
sortOptions.sort.time = -1;
}
return sortOptions;
};
const getOptions = query => ({
...getPagination(query),
...getSort(query),
});
export default {
save(entry) {
return model.save(entry);
},
sortingParams,
isValidSortingParam,
async get(query = {}) {
const mongoQuery = Object.assign(prepareQuery(query), timeQuery(query));
if (query.method && query.method.length) {
mongoQuery.method = { $in: query.method };
}
if (query.username) {
mongoQuery.username =
query.username !== 'anonymous' ? query.username : { $in: [null, query.username] };
}
const optionsQuery = getOptions(query);
const totalRows = await model.count(mongoQuery);
const dbResults = await model.get(mongoQuery, null, optionsQuery);
const semanticResults = await dbResults.reduce(async (prev, logEntry) => {
const results = await prev;
const semantic = await getSemanticData(logEntry);
return results.concat([{ ...logEntry, semantic }]);
}, Promise.resolve([]));
return {
rows: semanticResults,
remainingRows: Math.max(0, totalRows - dbResults.length - (optionsQuery.skip || 0)),
limit: optionsQuery.limit,
page: query.page,
};
},
};