Skip to content

Commit

Permalink
feat: Add functionality to filter by creation date
Browse files Browse the repository at this point in the history
  • Loading branch information
MauritsioRK committed Jul 16, 2020
1 parent 7267d8d commit 274174d
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 15 deletions.
5 changes: 5 additions & 0 deletions lib/database/seeders/20200508094502-logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ module.exports = {
origin: 'human',
user_id: 1,
text: 'Power interruption due to unplugged wire.',
created_at: new Date('2000-01-02 12:00:00 PM'),
},
{
title: 'Second entry',
Expand All @@ -28,6 +29,7 @@ module.exports = {
text: 'Detected particle ABC123',
parent_log_id: 1,
root_log_id: 1,
created_at: new Date(),
},
{
title: 'Third entry',
Expand All @@ -37,6 +39,7 @@ module.exports = {
text: 'Cake at the particle accelerator!',
parent_log_id: 1,
root_log_id: 1,
created_at: new Date(),
},
{
title: 'Fourth entry',
Expand All @@ -46,13 +49,15 @@ module.exports = {
text: 'The cake is a lie!',
parent_log_id: 2,
root_log_id: 1,
created_at: new Date(),
},
{
title: 'Fifth entry',
subtype: 'run',
origin: 'process',
user_id: 1,
text: 'Run #1 stopped',
created_at: new Date(),
},
]),

Expand Down
9 changes: 9 additions & 0 deletions lib/domain/dtos/GetAllLogsDto.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ const customJoi = Joi.extend((joi) => ({
coerce: (value) => ({ value: value.split ? value.split(',') : value }),
}));

const CreatedFilterDto = Joi.object({
from: Joi.date().max(new Date().setHours(0, 0, 0, 0)),
to: Joi.date().max(new Date().setHours(23, 59, 59, 999)).when('from', {
is: Joi.exist(),
then: Joi.date().min(Joi.ref('from')),
}),
});

const TagFilterDto = Joi.object({
values: customJoi.stringArray().items(EntityIdDto).single().required(),
operation: Joi.string().valid('and', 'or').required(),
Expand All @@ -31,6 +39,7 @@ const FilterDto = Joi.object({
.valid('human', 'process'),
parentLog: EntityIdDto,
rootLog: EntityIdDto,
created: CreatedFilterDto,
tag: TagFilterDto,
});

Expand Down
33 changes: 30 additions & 3 deletions lib/public/components/Filters/created.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,37 @@

import { h } from '/js/src/index.js';

let today = new Date();
today.setMinutes(today.getMinutes() - today.getTimezoneOffset());
[today] = today.toISOString().split('T');

/**
* TODO
* @return {vnode} TODO
* Returns the creation date filter components
* @param {Object} model The global model object
* @return {vnode} Two date selection boxes to control the minimum and maximum creation dates for the log filters
*/
const createdFilter = () => h('', 'TODO');
const createdFilter = (model) => {
const createdFrom = model.logs.getCreatedFrom();
const createdTo = model.logs.getCreatedTo();
return h('', [
h('.f6', 'From:'),
h('input.mv1', {
type: 'date',
id: 'createdFilterFrom',
max: createdTo || today,
value: createdFrom,
onchange: (e) => model.logs.setCreatedFilter('createdFrom', e.target.value, e.target.validity.valid),
}, ''),
h('.f6', 'To:'),
h('input.mv1', {
type: 'date',
id: 'createdFilterTo',
min: createdFrom,
max: today,
value: createdTo,
onchange: (e) => model.logs.setCreatedFilter('createdTo', e.target.value, e.target.validity.valid),
}, ''),
]);
};

export default createdFilter;
4 changes: 2 additions & 2 deletions lib/public/views/Logs/ActiveColumns/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import titleFilter from '../../../components/Filters/title.js';
import authorFilter from '../../../components/Filters/author.js';
import creationTimeFilter from '../../../components/Filters/created.js';
import createdFilter from '../../../components/Filters/created.js';
import tagsFilter from '../../../components/Filters/tags.js';

/**
Expand Down Expand Up @@ -46,7 +46,7 @@ const activeColumns = (model) => ({
visible: true,
size: 'cell-l',
format: (date) => new Date(date).toLocaleString(),
filter: creationTimeFilter(),
filter: createdFilter(model),
},
tags: {
name: 'Tags',
Expand Down
36 changes: 35 additions & 1 deletion lib/public/views/Logs/Logs.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ export default class Overview extends Observable {
const params = {
'page[offset]': offset,
'page[limit]': this.logsPerPage,
...this.createdFrom && { 'filter[created][from]': new Date(`${this.createdFrom}T00:00:00.000`).getTime() },
...this.createdTo && { 'filter[created][to]': new Date(`${this.createdTo}T23:59:59.999`).getTime() },
...this.tagFilterValues.length > 0 && {
'filter[tag][values]': this.tagFilterValues.join(),
'filter[tag][operation]': this.tagFilterOperation.toLowerCase(),
Expand Down Expand Up @@ -262,6 +264,36 @@ export default class Overview extends Observable {
return this.expandedFilters.includes(targetKey);
}

/**
* Returns the current minimum creation datetime
* @returns {Integer} The current minimum creation datetime
*/
getCreatedFrom() {
return this.createdFrom;
}

/**
* Returns the current maximum creation datetime
* @returns {Integer} The current maximum creation datetime
*/
getCreatedTo() {
return this.createdTo;
}

/**
* Set a datetime for the creation datetime filter
* @param {String} key The filter value to apply the datetime to
* @param {Object} date The datetime to be applied to the creation datetime filter
* @param {Boolean} valid Whether the inserted date passes validity check
* @returns {undefined}
*/
setCreatedFilter(key, date, valid) {
if (valid) {
this[key] = date;
this.fetchAllLogs();
}
}

/**
* Add a tag to the filter
* @param {string} tag The tag to be added to the filter criteria
Expand Down Expand Up @@ -359,8 +391,10 @@ export default class Overview extends Observable {
this.collapsedColumns = [];

this.expandedFilters = [];
this.tagFilterValues = [];
this.createdFrom = null;
this.createdTo = null;
this.tagFilterOperation = 'AND';
this.tagFilterValues = [];
this.moreTags = false;

this.amountDropdownVisible = false;
Expand Down
9 changes: 8 additions & 1 deletion lib/usecases/log/GetAllLogsUseCase.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,14 @@ class GetAllLogsUseCase {
const { filter, page = {}, sort = { id: 'desc' } } = query;

if (filter) {
const { origin, parentLog, rootLog } = filter;
const { created, origin, parentLog, rootLog } = filter;

if (created) {
const from = created.from !== undefined ? created.from : 0;
const to = created.to !== undefined ? created.to : new Date().getTime();
queryBuilder.where('createdAt').between(from, to);
}

if (origin) {
queryBuilder.where('origin').is(origin);
}
Expand Down
21 changes: 19 additions & 2 deletions spec/openapi-source.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -735,18 +735,35 @@ components:
description: Size of a file.
type: integer
minimum: 0
FilterLogsCreatedOptions:
description: Specifies the creation time related filter requirements for a request.
type: object
properties:
from:
description: The earliest allowed unix timestamp for the fetched data.
type: integer
format: int64
example: 1577833200000
to:
description: The latest allowed unix timestamp for the fetched data.
type: integer
format: int64
example: 1578437999999
additionalProperties: false
FilterLogsOptions:
description: Specifies the log related filter requirements for a request.
type: object
properties:
created:
$ref: '#/components/schemas/FilterLogsCreatedOptions'
tag:
$ref: '#/components/schemas/FilterLogsTagOptions'
origin:
$ref: '#/components/schemas/LogOrigin'
parentLog:
$ref: '#/components/schemas/EntityId'
rootLog:
$ref: '#/components/schemas/EntityId'
tag:
$ref: '#/components/schemas/FilterLogsTagOptions'
additionalProperties: false
FilterLogsTagOptions:
description: Specifies the tag related filter requirements for a request.
Expand Down
19 changes: 18 additions & 1 deletion spec/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Generated on Tue, 30 Jun 2020 12:15:50 GMT
# Generated on Thu, 16 Jul 2020 18:29:30 GMT

openapi: 3.0.0
info:
Expand Down Expand Up @@ -789,10 +789,27 @@ components:
description: Size of a file.
type: integer
minimum: 0
FilterLogsCreatedOptions:
description: Specifies the creation time related filter requirements for a request.
type: object
properties:
from:
description: The earliest allowed unix timestamp for the fetched data.
type: integer
format: int64
example: 1577833200000
to:
description: The latest allowed unix timestamp for the fetched data.
type: integer
format: int64
example: 1578437999999
additionalProperties: false
FilterLogsOptions:
description: Specifies the log related filter requirements for a request.
type: object
properties:
created:
$ref: '#/components/schemas/FilterLogsCreatedOptions'
origin:
$ref: '#/components/schemas/LogOrigin'
parentLog:
Expand Down
86 changes: 86 additions & 0 deletions test/e2e/logs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,92 @@ module.exports = () => {
});
});

it('should support filtering by creation time', (done) => {
request(server)
.get('/api/logs?filter[created][from]=946684800000')
.expect(200)
.end((err, res) => {
if (err) {
done(err);
return;
}

// Response must satisfy the OpenAPI specification
expect(res).to.satisfyApiSpec;

expect(res.body.data).to.be.an('array');
expect(res.body.data.length).to.be.greaterThan(1);

done();
});
});

it('should support filtering by creation time', (done) => {
request(server)
.get('/api/logs?filter[created][from]=946771200000&filter[created][to]=1577833200000')
.expect(200)
.end((err, res) => {
if (err) {
done(err);
return;
}

// Response must satisfy the OpenAPI specification
expect(res).to.satisfyApiSpec;

expect(res.body.data).to.be.an('array');
expect(res.body.data.length).to.equal(1);

done();
});
});

it('should return 400 if filtering in the future', (done) => {
request(server)
.get('/api/logs?filter[created][to]=4102531200000')
.expect(400)
.end((err, res) => {
if (err) {
done(err);
return;
}

// Response must satisfy the OpenAPI specification
expect(res).to.satisfyApiSpec;

let today = new Date();
today.setMinutes(today.getMinutes() - today.getTimezoneOffset());
[today] = today.toISOString().split('T');

const { errors } = res.body;
expect(errors[0].detail).to
.equal(`"query.filter.created.to" must be less than or equal to "${today}T23:59:59.999Z"`);

done();
});
});

it('should return 400 if minimum is larger than maximum', (done) => {
request(server)
.get('/api/logs?filter[created][from]=946771200000&filter[created][to]=946684800000')
.expect(400)
.end((err, res) => {
if (err) {
done(err);
return;
}

// Response must satisfy the OpenAPI specification
expect(res).to.satisfyApiSpec;

const { errors } = res.body;
expect(errors[0].detail).to
.equal('"query.filter.created.to" must be larger than or equal to "ref:from"');

done();
});
});

it('should support filtering by tag', (done) => {
request(server)
.get('/api/logs?filter[tag][values]=1&filter[tag][operation]=and')
Expand Down
Loading

0 comments on commit 274174d

Please sign in to comment.