Skip to content

Commit

Permalink
Feat: Log levels in Agent Log are now multiple selectable (#831)
Browse files Browse the repository at this point in the history
  • Loading branch information
crazyplayy authored Sep 19, 2023
1 parent c11d2cb commit 66b1ecb
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 42 deletions.
10 changes: 5 additions & 5 deletions web/api/maestro_api/controllers/agent_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
jsonify,
)
from maestro_api.libs.datetime import strptime
from maestro_api.libs.utils import str_to_list


class AgentLogController:
Expand Down Expand Up @@ -34,7 +35,7 @@ def all(self, data, user):
"""
date_from = strptime(data.get("date_from"))
agent_ids = data.get("agent_ids", None)
level = data.get("level", None)
log_levels = data.get("log_levels", None)
sort = data.get("sort", "-created_at")

filter_query = Q(created_at__gte=date_from)
Expand All @@ -48,10 +49,9 @@ def all(self, data, user):
agent_id__in=agent_ids_filter,
)

if level is not None:
filter_query = filter_query & Q(
level=level,
)
if log_levels is not None:
filter_query = filter_query & Q(level__in=str_to_list(log_levels))

agent_logs = AgentLog.objects.filter(filter_query).order_by(sort)

return jsonify_list_of_docs(agent_logs)
Expand Down
10 changes: 9 additions & 1 deletion web/api/maestro_api/validation_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,15 @@
]
},
"sort": {"type": "string"},
"level": {"type": "string", "enum": AgentLogLevel.list()},
"log_levels": {
"anyOf": [
{
"type": "array",
"items": {"type": "string", "enum": AgentLogLevel.list()},
},
{"type": "string", "minLength": 4, "maxLength": 12},
]
},
},
"required": ["date_from"],
"additionalProperties": False,
Expand Down
10 changes: 9 additions & 1 deletion web/api/tests/routes/test_agent_log.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
dict(
agent_id="6076d69ba216ff15b6e95ea2",
log_message="",
level="DEBUG",
created_at=datetime(2019, 1, 1),
),
dict(
Expand Down Expand Up @@ -85,9 +86,16 @@
[],
),
(
"?date_from=2019-01-01 00:00:00&level=ERROR",
"?date_from=2019-01-01 00:00:00&log_levels=ERROR",
["6076d69ba216ff15b6e95ea8"],
),
(
"?date_from=2019-01-01 00:00:00&log_levels=ERROR&log_levels=DEBUG",
[
"6076d69ba216ff15b6e95ea8",
"6076d69ba216ff15b6e95ea2",
],
),
],
)
def test_agent_log_list(client, db_data, input_params, extected_ids):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,58 @@
import { Select } from "antd";
import PropTypes from "prop-types";
import { Button, Select } from "antd";
import React, { useState } from "react";

import { agentLogLevel } from "../../../../lib/api/models";

const AgentLogLevelsSelect = ({ onChange = () => {}, mode = "single" }) => (
<Select
mode={mode}
allowClear
placeholder="Select Log level"
onChange={onChange}
style={{ width: "100%" }}
defaultValue={agentLogLevel.DEBUG}
>
{Object.values(agentLogLevel).map((logLevel) => (
<Select.Option key={logLevel}>{logLevel}</Select.Option>
))}
</Select>
);
const AgentLogLevelsSelect = ({ onChange = () => {} }) => {
const [selectedLevels, setSelectedLevels] = useState([]);

AgentLogLevelsSelect.propTypes = {
mode: PropTypes.oneOf(["single", "multiple"]).isRequired
const handleSelectAll = () => {
const allLevels = Object.values(agentLogLevel);
setSelectedLevels(allLevels);
onChange(allLevels);
};

const handleUnselectAll = () => {
setSelectedLevels([]);
onChange([]);
};

const handleChange = (selected) => {
setSelectedLevels(selected);
onChange(selected);
};

return (
<div style={{ display: "flex", alignItems: "center" }}>
<Select
mode={"multiple"}
allowClear
placeholder="Select Log level"
onChange={handleChange}
style={{ width: "100%" }}
defaultValue={agentLogLevel.DEBUG}
value={selectedLevels}
>
{Object.values(agentLogLevel).map((logLevel) => (
<Select.Option key={logLevel}>{logLevel}</Select.Option>
))}
</Select>
<Button
type="primary"
onClick={handleSelectAll}
style={{ marginLeft: "10px" }}
>
Select All
</Button>
<Button
type="danger"
onClick={handleUnselectAll}
style={{ marginLeft: "10px" }}
>
Unselect All
</Button>
</div>
);
};

export default AgentLogLevelsSelect;
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/form/select/AgentLogLevelsSelect should render AgentLogLevelsSelect component with mode=multiple 1`] = `"<div><div class=\\"ant-select ant-select-multiple ant-select-allow-clear ant-select-show-search\\" style=\\"width: 100%;\\"><div class=\\"ant-select-selector\\"><div class=\\"ant-select-selection-overflow\\"><div class=\\"ant-select-selection-overflow-item\\" style=\\"opacity: 1;\\"><span class=\\"ant-select-selection-item\\" title=\\"DEBUG\\"><span class=\\"ant-select-selection-item-content\\">DEBUG</span><span class=\\"ant-select-selection-item-remove\\" style=\\"user-select: none;\\" unselectable=\\"on\\" aria-hidden=\\"true\\"><span role=\\"img\\" aria-label=\\"close\\" class=\\"anticon anticon-close\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" data-icon=\\"close\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M563.8 512l262.5-312.9c4.4-5.2.7-13.1-6.1-13.1h-79.8c-4.7 0-9.2 2.1-12.3 5.7L511.6 449.8 295.1 191.7c-3-3.6-7.5-5.7-12.3-5.7H203c-6.8 0-10.5 7.9-6.1 13.1L459.4 512 196.9 824.9A7.95 7.95 0 00203 838h79.8c4.7 0 9.2-2.1 12.3-5.7l216.5-258.1 216.5 258.1c3 3.6 7.5 5.7 12.3 5.7h79.8c6.8 0 10.5-7.9 6.1-13.1L563.8 512z\\"></path></svg></span></span></span></div><div class=\\"ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix\\" style=\\"opacity: 1;\\"><div class=\\"ant-select-selection-search\\" style=\\"width: 0px;\\"><input type=\\"search\\" autocomplete=\\"off\\" class=\\"ant-select-selection-search-input\\" role=\\"combobox\\" aria-haspopup=\\"listbox\\" aria-owns=\\"rc_select_TEST_OR_SSR_list\\" aria-autocomplete=\\"list\\" aria-controls=\\"rc_select_TEST_OR_SSR_list\\" aria-activedescendant=\\"rc_select_TEST_OR_SSR_list_0\\" readonly=\\"\\" unselectable=\\"on\\" style=\\"opacity: 0;\\" value=\\"\\" id=\\"rc_select_TEST_OR_SSR\\"><span class=\\"ant-select-selection-search-mirror\\" aria-hidden=\\"true\\">&nbsp;</span></div></div></div></div><span class=\\"ant-select-clear\\" style=\\"user-select: none;\\" unselectable=\\"on\\" aria-hidden=\\"true\\"><span role=\\"img\\" aria-label=\\"close-circle\\" class=\\"anticon anticon-close-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" data-icon=\\"close-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z\\"></path></svg></span></span></div></div>"`;
exports[`components/form/select/AgentLogLevelsSelect should render AgentLogLevelsSelect component with mode=multiple 1`] = `"<div><div style=\\"display: flex; align-items: center;\\"><div class=\\"ant-select ant-select-multiple ant-select-allow-clear ant-select-show-search\\" style=\\"width: 100%;\\"><div class=\\"ant-select-selector\\"><div class=\\"ant-select-selection-overflow\\"><div class=\\"ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix\\" style=\\"opacity: 1;\\"><div class=\\"ant-select-selection-search\\" style=\\"width: 0px;\\"><input type=\\"search\\" autocomplete=\\"off\\" class=\\"ant-select-selection-search-input\\" role=\\"combobox\\" aria-haspopup=\\"listbox\\" aria-owns=\\"rc_select_TEST_OR_SSR_list\\" aria-autocomplete=\\"list\\" aria-controls=\\"rc_select_TEST_OR_SSR_list\\" aria-activedescendant=\\"rc_select_TEST_OR_SSR_list_0\\" readonly=\\"\\" unselectable=\\"on\\" style=\\"opacity: 0;\\" value=\\"\\" id=\\"rc_select_TEST_OR_SSR\\"><span class=\\"ant-select-selection-search-mirror\\" aria-hidden=\\"true\\">&nbsp;</span></div></div></div><span class=\\"ant-select-selection-placeholder\\">Select Log level</span></div></div><button style=\\"margin-left: 10px;\\" type=\\"button\\" class=\\"ant-btn ant-btn-primary\\"><span>Select All</span></button><button style=\\"margin-left: 10px;\\" type=\\"button\\" class=\\"ant-btn ant-btn-danger\\"><span>Unselect All</span></button></div></div>"`;

exports[`components/form/select/AgentLogLevelsSelect should render AgentLogLevelsSelect component with mode=single 1`] = `"<div><div class=\\"ant-select ant-select-single ant-select-allow-clear ant-select-show-arrow\\" style=\\"width: 100%;\\"><div class=\\"ant-select-selector\\"><span class=\\"ant-select-selection-search\\"><input type=\\"search\\" autocomplete=\\"off\\" class=\\"ant-select-selection-search-input\\" role=\\"combobox\\" aria-haspopup=\\"listbox\\" aria-owns=\\"rc_select_TEST_OR_SSR_list\\" aria-autocomplete=\\"list\\" aria-controls=\\"rc_select_TEST_OR_SSR_list\\" aria-activedescendant=\\"rc_select_TEST_OR_SSR_list_0\\" readonly=\\"\\" unselectable=\\"on\\" style=\\"opacity: 0;\\" value=\\"\\" id=\\"rc_select_TEST_OR_SSR\\"></span><span class=\\"ant-select-selection-item\\" title=\\"DEBUG\\">DEBUG</span></div><span class=\\"ant-select-arrow\\" style=\\"user-select: none;\\" unselectable=\\"on\\" aria-hidden=\\"true\\"><span role=\\"img\\" aria-label=\\"down\\" class=\\"anticon anticon-down ant-select-suffix\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" data-icon=\\"down\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M884 256h-75c-5.1 0-9.9 2.5-12.9 6.6L512 654.2 227.9 262.6c-3-4.1-7.8-6.6-12.9-6.6h-75c-6.5 0-10.3 7.4-6.5 12.7l352.6 486.1c12.8 17.6 39 17.6 51.7 0l352.6-486.1c3.9-5.3.1-12.7-6.4-12.7z\\"></path></svg></span></span><span class=\\"ant-select-clear\\" style=\\"user-select: none;\\" unselectable=\\"on\\" aria-hidden=\\"true\\"><span role=\\"img\\" aria-label=\\"close-circle\\" class=\\"anticon anticon-close-circle\\"><svg viewBox=\\"64 64 896 896\\" focusable=\\"false\\" data-icon=\\"close-circle\\" width=\\"1em\\" height=\\"1em\\" fill=\\"currentColor\\" aria-hidden=\\"true\\"><path d=\\"M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z\\"></path></svg></span></span></div></div>"`;
exports[`components/form/select/AgentLogLevelsSelect should render AgentLogLevelsSelect component with mode=single 1`] = `"<div><div style=\\"display: flex; align-items: center;\\"><div class=\\"ant-select ant-select-multiple ant-select-allow-clear ant-select-show-search\\" style=\\"width: 100%;\\"><div class=\\"ant-select-selector\\"><div class=\\"ant-select-selection-overflow\\"><div class=\\"ant-select-selection-overflow-item ant-select-selection-overflow-item-suffix\\" style=\\"opacity: 1;\\"><div class=\\"ant-select-selection-search\\" style=\\"width: 0px;\\"><input type=\\"search\\" autocomplete=\\"off\\" class=\\"ant-select-selection-search-input\\" role=\\"combobox\\" aria-haspopup=\\"listbox\\" aria-owns=\\"rc_select_TEST_OR_SSR_list\\" aria-autocomplete=\\"list\\" aria-controls=\\"rc_select_TEST_OR_SSR_list\\" aria-activedescendant=\\"rc_select_TEST_OR_SSR_list_0\\" readonly=\\"\\" unselectable=\\"on\\" style=\\"opacity: 0;\\" value=\\"\\" id=\\"rc_select_TEST_OR_SSR\\"><span class=\\"ant-select-selection-search-mirror\\" aria-hidden=\\"true\\">&nbsp;</span></div></div></div><span class=\\"ant-select-selection-placeholder\\">Select Log level</span></div></div><button style=\\"margin-left: 10px;\\" type=\\"button\\" class=\\"ant-btn ant-btn-primary\\"><span>Select All</span></button><button style=\\"margin-left: 10px;\\" type=\\"button\\" class=\\"ant-btn ant-btn-danger\\"><span>Unselect All</span></button></div></div>"`;
40 changes: 34 additions & 6 deletions web/frontend/src/lib/api/__tests__/agentLog.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,15 @@ describe("libs/api/endpoints/agentLog", () => {

axiosMock
.onGet("/api/agent_logs", {
params: { date_from: toUtcString(dateFrom), level, agent_ids: [] }
params: {
date_from: toUtcString(dateFrom),
log_levels: [],
agent_ids: []
}
})
.reply(200, [apiResponse]);

const customDataRes = await fetchAgentLogs({ dateFrom, level });
const customDataRes = await fetchAgentLogs({ dateFrom });

expect(customDataRes).toStrictEqual([expectedData]);
});
Expand All @@ -55,13 +59,13 @@ describe("libs/api/endpoints/agentLog", () => {
.onGet("/api/agent_logs", {
params: {
date_from: toUtcString(dateFrom),
level,
log_levels: [],
agent_ids: agentIds
}
})
.reply(200, [apiResponse]);

const customDataRes = await fetchAgentLogs({ dateFrom, level, agentIds });
const customDataRes = await fetchAgentLogs({ dateFrom, agentIds });

expect(customDataRes).toStrictEqual([expectedData]);
});
Expand All @@ -71,12 +75,36 @@ describe("libs/api/endpoints/agentLog", () => {

axiosMock
.onGet("/api/agent_logs", {
params: { date_from: toUtcString(dateFrom), level, agent_ids: [] }
params: {
date_from: toUtcString(dateFrom),
log_levels: [],
agent_ids: []
}
})
.reply(200, []);

const customDataRes = await fetchAgentLogs({ dateFrom, level });
const customDataRes = await fetchAgentLogs({ dateFrom });

expect(customDataRes).toStrictEqual([]);
});

test("should return list of agent logs with agentIds filter and level filter", async () => {
const dateFrom = moment("2018-05-24T13:48:04.313000");
const agentIds = ["6077280d0c739b2adf6e5684"];
const levels = ["INFO"];

axiosMock
.onGet("/api/agent_logs", {
params: {
date_from: toUtcString(dateFrom),
log_levels: levels,
agent_ids: agentIds
}
})
.reply(200, [apiResponse]);

const customDataRes = await fetchAgentLogs({ dateFrom, levels, agentIds });

expect(customDataRes).toStrictEqual([expectedData]);
});
});
8 changes: 6 additions & 2 deletions web/frontend/src/lib/api/endpoints/agentLog.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ const agentLogObjectMapper = (agentLog) => ({
* @param {Array} agentIds
* @returns
*/
export const fetchAgentLogs = async ({ dateFrom, level, agentIds = [] }) => {
export const fetchAgentLogs = async ({
dateFrom,
levels = [],
agentIds = []
}) => {
try {
const res = await maestroClient.get("/api/agent_logs", {
params: {
date_from: toUtcString(dateFrom),
level,
log_levels: levels,
agent_ids: agentIds
}
});
Expand Down
13 changes: 6 additions & 7 deletions web/frontend/src/pages/AgentSingleLogs.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ const AgentSingleLogs = () => {
const [isLoading, setIsLoading] = useState(true);

const defaultDate = moment().startOf("day");
const [selectedLogLevel, setSelectedLogLevel] = useState(
const [selectedLogLevels, setSelectedLogLevels] = useState([
agentLogLevelModel.DEBUG
);
]);
const [selectedDateFrom, setSelectedDateFrom] = useState(defaultDate);

const reloadAgentLogs = async (params, agentData) => {
Expand Down Expand Up @@ -51,13 +51,13 @@ const AgentSingleLogs = () => {
reloadAgentLogs(
{
dateFrom: selectedDateFrom,
level: selectedLogLevel,
levels: selectedLogLevels,
agentIds: [agent.id]
},
agent
);
}
}, [selectedDateFrom, selectedLogLevel, agent]);
}, [selectedDateFrom, selectedLogLevels, agent]);

return (
<Row gutter={[32, 32]} justify="start" align="middle">
Expand All @@ -78,12 +78,11 @@ const AgentSingleLogs = () => {
</Space>
<Space size="small" align="left" direction="vertical">
Level
<div style={{ width: "200px" }}>
<div style={{ width: "500px" }}>
<AgentLogLevelsSelect
onChange={(changedLogLevel) =>
setSelectedLogLevel(changedLogLevel)
setSelectedLogLevels(changedLogLevel)
}
mode={"single"}
/>
</div>
</Space>
Expand Down

0 comments on commit 66b1ecb

Please sign in to comment.