Skip to content

Commit

Permalink
v1.161.0
Browse files Browse the repository at this point in the history
  • Loading branch information
varovaro committed Apr 9, 2024
2 parents 5360c1f + a43e3c5 commit dffb90e
Show file tree
Hide file tree
Showing 89 changed files with 3,057 additions and 2,171 deletions.
25 changes: 23 additions & 2 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,20 @@ module.exports = {
'react/self-closing-comp': ['warn'],
'react/no-multi-comp': ['warn'],
'react/jsx-closing-bracket-location': ['warn'],
'react/jsx-boolean-value': ['warn'],
'react/jsx-boolean-value': [
'warn',
'never',
{
always: [
'date-rangepicker',
'datepicker',
'inline-datepicker',
'datepicker-autohide',
'datepicker-buttons',
'datepicker-autoselect-today',
],
},
],
'react/jsx-indent': ['off'],
'react/jsx-indent-props': ['warn'],
'react/no-array-index-key': ['warn'],
Expand Down Expand Up @@ -184,7 +197,15 @@ module.exports = {
'react/no-unknown-property': [
'error',
{
ignore: ['no-translate'],
ignore: [
'no-translate',
'date-rangepicker',
'datepicker',
'inline-datepicker',
'datepicker-autohide',
'datepicker-buttons',
'datepicker-autoselect-today',
],
},
],
//jsx-a11y
Expand Down
1 change: 1 addition & 0 deletions .storybook/preview-body.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<div id="tw-container" class='tw-content tw-datepicker'></div>
51 changes: 45 additions & 6 deletions app/api/activitylog/activitylog.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
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 = {};

Expand Down Expand Up @@ -63,16 +74,43 @@ const timeQuery = ({ time = {}, before = null }) => {
return result;
};

const getLimit = query => {
const getPagination = query => {
const { page } = query;
const limit = parseInt(query.limit || 15, 10);
return { limit, sort: { time: -1 } };
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));

Expand All @@ -85,10 +123,10 @@ export default {
query.username !== 'anonymous' ? query.username : { $in: [null, query.username] };
}

const limitQuery = getLimit(query);
const optionsQuery = getOptions(query);

const totalRows = await model.count(mongoQuery);
const dbResults = await model.get(mongoQuery, null, limitQuery);
const dbResults = await model.get(mongoQuery, null, optionsQuery);

const semanticResults = await dbResults.reduce(async (prev, logEntry) => {
const results = await prev;
Expand All @@ -98,8 +136,9 @@ export default {

return {
rows: semanticResults,
remainingRows: totalRows - dbResults.length,
limit: limitQuery.limit,
remainingRows: Math.max(0, totalRows - dbResults.length - (optionsQuery.skip || 0)),
limit: optionsQuery.limit,
page: query.page,
};
},
};
5 changes: 5 additions & 0 deletions app/api/activitylog/activitylogModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,9 @@ const pagesSchema = new mongoose.Schema({
expireAt: Date,
});

pagesSchema.index({ method: 1 });
pagesSchema.index({ time: 1 });
pagesSchema.index({ url: 1 });
pagesSchema.index({ username: 1 }, { collation: { locale: 'en', strength: 2 } });

export default instanceModel('activitylog', pagesSchema);
29 changes: 3 additions & 26 deletions app/api/activitylog/routes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { objectIdSchema } from 'shared/types/commonSchemas';
import { ActivityLogGetRequestSchema } from 'shared/types/activityLogApiSchemas';

import { parseQuery, validation } from '../utils';
import needsAuthorization from '../auth/authMiddleware';
import activitylog from './activitylog';
Expand All @@ -8,31 +9,7 @@ export default app => {
'/api/activitylog',
needsAuthorization(['admin']),
parseQuery,
validation.validateRequest({
type: 'object',
properties: {
query: {
additionalProperties: false,
type: 'object',
properties: {
user: objectIdSchema,
username: { type: 'string' },
find: { type: 'string' },
time: {
type: 'object',
properties: {
from: { type: 'number' },
to: { type: 'number' },
},
},
before: { type: 'number' },
limit: { type: 'number' },
method: { type: 'array', items: { type: 'string' } },
search: { type: 'string' },
},
},
},
}),
validation.validateRequest(ActivityLogGetRequestSchema),
(req, res, next) =>
activitylog
.get(req.query)
Expand Down
157 changes: 148 additions & 9 deletions app/api/activitylog/specs/activitylog.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ import activitylog from '../activitylog';
import * as activityLogParser from '../activitylogParser';
import fixtures from './fixtures';

const assessResults = (results, expected) => {
expect(results.rows.length).toBe(expected.size);
expect(results.remainingRows).toBe(expected.remainingRows);
expect(results.limit).toBe(expected.limit);
expect(results.rows.map(r => r.time)).toEqual(expected.times);
expect(results.query).toBe(expected.query);
};

describe('activitylog', () => {
beforeEach(async () => {
await testingEnvironment.setUp(fixtures);
Expand Down Expand Up @@ -108,19 +116,150 @@ describe('activitylog', () => {
});

describe('Load More functionality', () => {
const assessResults = (results, expected) => {
expect(results.rows.length).toBe(expected.size);
expect(results.remainingRows).toBe(expected.remainingRows);
expect(results.limit).toBe(expected.limit);
expect(results.rows[0].time).toBe(expected.initialTime);
};

it('should allow to load more via "before" param', async () => {
const initialResults = await activitylog.get({ limit: 2 });
assessResults(initialResults, { size: 2, remainingRows: 3, limit: 2, initialTime: 8000 });
assessResults(initialResults, { size: 2, remainingRows: 3, limit: 2, times: [8000, 6000] });

const nextResults = await activitylog.get({ before: 6000, limit: 2 });
assessResults(nextResults, { size: 2, remainingRows: 1, limit: 2, initialTime: 5000 });
assessResults(nextResults, { size: 2, remainingRows: 1, limit: 2, times: [5000, 2000] });
});
});

describe('pagination via the "page" and "limit" keywords', () => {
it('should paginate the results', async () => {
let results = await activitylog.get({ limit: 2 });
assessResults(results, {
size: 2,
remainingRows: 3,
limit: 2,
page: undefined,
times: [8000, 6000],
});

results = await activitylog.get({ page: 1, limit: 2 });
assessResults(results, {
size: 2,
remainingRows: 3,
limit: 2,
page: 1,
times: [8000, 6000],
});

results = await activitylog.get({ page: 2, limit: 2 });
assessResults(results, {
size: 2,
remainingRows: 1,
limit: 2,
page: 2,
times: [5000, 2000],
});

results = await activitylog.get({ page: 3, limit: 2 });
assessResults(results, { size: 1, remainingRows: 0, limit: 2, page: 3, times: [1000] });
});

it('should return an empty array if the page is out of range', async () => {
const results = await activitylog.get({ page: 4, limit: 2 });
assessResults(results, { size: 0, remainingRows: 0, limit: 2, page: 4, times: [] });
});

it('should still filter the results', async () => {
let results = await activitylog.get({ page: 1, limit: 2, method: ['PUT'] });
assessResults(results, {
size: 2,
remainingRows: 1,
limit: 2,
page: 1,
times: [8000, 6000],
});

results = await activitylog.get({ page: 2, limit: 2, method: ['PUT'] });
assessResults(results, { size: 1, remainingRows: 0, limit: 2, page: 2, times: [1000] });
});
});

describe('sorting through the "sort" keyword', () => {
it('without the "sort" keyword, it should sort by time, descending as default', async () => {
const results = await activitylog.get();
expect(results.rows.map(r => r.time)).toEqual([8000, 6000, 5000, 2000, 1000]);
});

it('should sort by method', async () => {
let results = await activitylog.get({ sort: { prop: 'method', asc: 1 } });
expect(results.rows.map(r => r.time)).toEqual([2000, 5000, 8000, 6000, 1000]);

results = await activitylog.get({ sort: { prop: 'method', asc: 0 } });
expect(results.rows.map(r => r.time)).toEqual([8000, 6000, 1000, 5000, 2000]);
});

it('should sort by user', async () => {
let results = await activitylog.get({ sort: { prop: 'username', asc: 1 } });
expect(results.rows.map(r => r.time)).toEqual([8000, 6000, 5000, 2000, 1000]);

results = await activitylog.get({ sort: { prop: 'username', asc: 0 } });
expect(results.rows.map(r => r.time)).toEqual([1000, 5000, 2000, 8000, 6000]);
});

it('should sort by url', async () => {
let results = await activitylog.get({ sort: { prop: 'url', asc: 1 } });
expect(results.rows.map(r => r.time)).toEqual([8000, 5000, 2000, 6000, 1000]);

results = await activitylog.get({ sort: { prop: 'url', asc: 0 } });
expect(results.rows.map(r => r.time)).toEqual([1000, 6000, 8000, 5000, 2000]);
});

it('should sort by time', async () => {
let results = await activitylog.get({ sort: { prop: 'time', asc: 1 } });
expect(results.rows.map(r => r.time)).toEqual([1000, 2000, 5000, 6000, 8000]);

results = await activitylog.get({ sort: { prop: 'time', asc: 0 } });
expect(results.rows.map(r => r.time)).toEqual([8000, 6000, 5000, 2000, 1000]);
});

it('should respect filters', async () => {
let results = await activitylog.get({
sort: { prop: 'username', asc: 1 },
method: ['PUT'],
});
expect(results.rows.map(r => r.time)).toEqual([8000, 6000, 1000]);

results = await activitylog.get({
sort: { prop: 'username', asc: 0 },
method: ['PUT'],
});
expect(results.rows.map(r => r.time)).toEqual([1000, 8000, 6000]);
});

it('should respect pagination', async () => {
let results = await activitylog.get({
sort: { prop: 'username', asc: 1 },
page: 1,
limit: 2,
method: ['PUT'],
});
assessResults(results, {
size: 2,
remainingRows: 1,
limit: 2,
page: 1,
times: [8000, 6000],
});

results = await activitylog.get({
sort: { prop: 'username', asc: 1 },
page: 2,
limit: 2,
method: ['PUT'],
});
assessResults(results, { size: 1, remainingRows: 0, limit: 2, page: 2, times: [1000] });

results = await activitylog.get({
sort: { prop: 'username', asc: 1 },
page: 3,
limit: 2,
method: ['PUT'],
});
assessResults(results, { size: 0, remainingRows: 0, limit: 2, page: 3, times: [] });
});
});
});
Expand Down
Loading

0 comments on commit dffb90e

Please sign in to comment.