From 0a54b5dba0c2dc08433a70e7a2c865ecc8507918 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 19:15:08 +0200 Subject: [PATCH 01/15] feat(ui): implement InfluxDB duration parser and comparator --- ui/src/utils/influxDuration.ts | 62 ++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 ui/src/utils/influxDuration.ts diff --git a/ui/src/utils/influxDuration.ts b/ui/src/utils/influxDuration.ts new file mode 100644 index 000000000..1609467bf --- /dev/null +++ b/ui/src/utils/influxDuration.ts @@ -0,0 +1,62 @@ +/** + * InfluxDuration represents components of InfluxDB duration. + * See https://docs.influxdata.com/influxdb/v1.8/query_language/spec/#durations + */ +export type InfluxDuration = [ + w: number, + d: number, + h: number, + m: number, + s: number, + ms: number, + us: number, + ns: number +] + +const durationPartIndex: Record = { + w: 0, + d: 1, + h: 2, + m: 3, + s: 4, + ms: 5, + u: 6, + µs: 6, + ns: 7, +} + +/** + * ParseDuration parses string into a InfluxDuration, unknown duration parts are simply ignored. + * @param duration duration literal per https://docs.influxdata.com/influxdb/v1.8/query_language/spec/#durations + * @returns InfluxDuration + */ +export function parseDuration(duration: string): InfluxDuration { + const retVal = new Array(8).fill(0) as InfluxDuration + const regExp = /([0-9]+)([^0-9]+)/g + let matched: string[] + while ((matched = regExp.exec(duration)) !== null) { + const index = durationPartIndex[matched[2]] + if (index === undefined) { + // ignore unknown part + continue + } + retVal[index] = parseInt(matched[1], 10) + } + return retVal +} + +/** + * DurationComparator implements sort comparator for InfluxDuration instances. + */ +export function durationComparator( + a: InfluxDuration, + b: InfluxDuration +): number { + let i = 0 + for (; i < 8; i++) { + if (a[i] !== b[i]) { + return a[i] - b[i] + } + } + return 0 +} From b48558eb5ce11da55a655b9fb8695f22eaadc283 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 19:15:17 +0200 Subject: [PATCH 02/15] feat(ui): test InfluxDB duration parser and comparator --- ui/test/utils/influxDuration.test.ts | 81 ++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 ui/test/utils/influxDuration.test.ts diff --git a/ui/test/utils/influxDuration.test.ts b/ui/test/utils/influxDuration.test.ts new file mode 100644 index 000000000..deedd5b82 --- /dev/null +++ b/ui/test/utils/influxDuration.test.ts @@ -0,0 +1,81 @@ +import { + parseDuration, + InfluxDuration, + durationComparator, +} from 'src/utils/influxDuration' +describe('InfluxDuration', () => { + describe('parseDuration', () => { + ;[ + { + str: '', + duration: [0, 0, 0, 0, 0, 0, 0, 0] as InfluxDuration, + }, + { + str: '1w', + duration: [1, 0, 0, 0, 0, 0, 0, 0] as InfluxDuration, + }, + { + str: '1d', + duration: [0, 1, 0, 0, 0, 0, 0, 0] as InfluxDuration, + }, + { + str: '1h', + duration: [0, 0, 1, 0, 0, 0, 0, 0] as InfluxDuration, + }, + { + str: '1m', + duration: [0, 0, 0, 1, 0, 0, 0, 0] as InfluxDuration, + }, + { + str: '1s', + duration: [0, 0, 0, 0, 1, 0, 0, 0] as InfluxDuration, + }, + { + str: '1ms', + duration: [0, 0, 0, 0, 0, 1, 0, 0] as InfluxDuration, + }, + { + str: '1µs', + duration: [0, 0, 0, 0, 0, 0, 1, 0] as InfluxDuration, + }, + { + str: '1u', + duration: [0, 0, 0, 0, 0, 0, 1, 0] as InfluxDuration, + }, + { + str: '1ns', + duration: [0, 0, 0, 0, 0, 0, 0, 1] as InfluxDuration, + }, + { + str: '11w22d33h44m55s66ms77u88ns', + duration: [11, 22, 33, 44, 55, 66, 77, 88] as InfluxDuration, + }, + ].forEach(({str, duration}) => { + it(`parses '${str}'`, () => { + const retVal = parseDuration(str) + expect(retVal).toEqual(duration) + }) + }) + }) + describe('durationComparator', () => { + const units = ['w', 'd', 'h', 'm', 's', 'ms', 'u', 'ns'] + it('compares simple durations', () => { + units.forEach(unit => { + const x = parseDuration(`1${unit}`) + const y = parseDuration(`2${unit}`) + expect(durationComparator(x, x)).toEqual(0) + expect(durationComparator(x, y)).toBeLessThan(0) + expect(durationComparator(y, x)).toBeGreaterThan(0) + }) + }) + it('compares 3-unit durations', () => { + units.slice(0, 5).forEach(unit => { + const x = parseDuration(`1${unit}2u3ns`) + const y = parseDuration(`1${unit}2u14ns`) + expect(durationComparator(x, x)).toEqual(0) + expect(durationComparator(x, y)).toBeLessThan(0) + expect(durationComparator(y, x)).toBeGreaterThan(0) + }) + }) + }) +}) From 0eb2da3f481892e6c448d10862f5ce12b061f7e0 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 19:18:24 +0200 Subject: [PATCH 03/15] chore(ui): rename durationComparator to compareDurations --- ui/src/utils/influxDuration.ts | 7 ++----- ui/test/utils/influxDuration.test.ts | 16 ++++++++-------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/ui/src/utils/influxDuration.ts b/ui/src/utils/influxDuration.ts index 1609467bf..c9ff380af 100644 --- a/ui/src/utils/influxDuration.ts +++ b/ui/src/utils/influxDuration.ts @@ -46,12 +46,9 @@ export function parseDuration(duration: string): InfluxDuration { } /** - * DurationComparator implements sort comparator for InfluxDuration instances. + * CompareDurations implements sort comparator for InfluxDuration instances. */ -export function durationComparator( - a: InfluxDuration, - b: InfluxDuration -): number { +export function compareDurations(a: InfluxDuration, b: InfluxDuration): number { let i = 0 for (; i < 8; i++) { if (a[i] !== b[i]) { diff --git a/ui/test/utils/influxDuration.test.ts b/ui/test/utils/influxDuration.test.ts index deedd5b82..b08dd5df4 100644 --- a/ui/test/utils/influxDuration.test.ts +++ b/ui/test/utils/influxDuration.test.ts @@ -1,7 +1,7 @@ import { parseDuration, InfluxDuration, - durationComparator, + compareDurations, } from 'src/utils/influxDuration' describe('InfluxDuration', () => { describe('parseDuration', () => { @@ -57,24 +57,24 @@ describe('InfluxDuration', () => { }) }) }) - describe('durationComparator', () => { + describe('compareDurations', () => { const units = ['w', 'd', 'h', 'm', 's', 'ms', 'u', 'ns'] it('compares simple durations', () => { units.forEach(unit => { const x = parseDuration(`1${unit}`) const y = parseDuration(`2${unit}`) - expect(durationComparator(x, x)).toEqual(0) - expect(durationComparator(x, y)).toBeLessThan(0) - expect(durationComparator(y, x)).toBeGreaterThan(0) + expect(compareDurations(x, x)).toEqual(0) + expect(compareDurations(x, y)).toBeLessThan(0) + expect(compareDurations(y, x)).toBeGreaterThan(0) }) }) it('compares 3-unit durations', () => { units.slice(0, 5).forEach(unit => { const x = parseDuration(`1${unit}2u3ns`) const y = parseDuration(`1${unit}2u14ns`) - expect(durationComparator(x, x)).toEqual(0) - expect(durationComparator(x, y)).toBeLessThan(0) - expect(durationComparator(y, x)).toBeGreaterThan(0) + expect(compareDurations(x, x)).toEqual(0) + expect(compareDurations(x, y)).toBeLessThan(0) + expect(compareDurations(y, x)).toBeGreaterThan(0) }) }) }) From 2fcbad893928e16670fb0810f72d10d01ef312f7 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 19:38:42 +0200 Subject: [PATCH 04/15] feat(ui/admin/queries): sort queries by time --- ui/src/admin/containers/QueriesPage.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js index cf007c110..6c2dd9726 100644 --- a/ui/src/admin/containers/QueriesPage.js +++ b/ui/src/admin/containers/QueriesPage.js @@ -11,7 +11,7 @@ import {showDatabases, showQueries} from 'shared/apis/metaQuery' import QueriesTable from 'src/admin/components/QueriesTable' import showDatabasesParser from 'shared/parsing/showDatabases' import showQueriesParser from 'shared/parsing/showQueries' -import {TIMES} from 'src/admin/constants' +import {parseDuration, compareDurations} from 'src/utils/influxDuration' import {notifyQueriesError} from 'shared/copy/notifications' import {ErrorHandling} from 'src/shared/decorators/errors' @@ -72,12 +72,12 @@ class QueriesPage extends Component { }) const queries = uniqBy(flatten(allQueries), q => q.id) + // parse durations so that they can be compared + queries.forEach(x => (x._pd = parseDuration(x.duration))) // sorting queries by magnitude, so generally longer queries will appear atop the list const sortedQueries = queries.sort((a, b) => { - const aTime = TIMES.find(t => a.duration.match(t.test)) - const bTime = TIMES.find(t => b.duration.match(t.test)) - return +aTime.magnitude <= +bTime.magnitude + return -compareDurations(a._pd, b._pd) }) loadQueries(sortedQueries) From b0a31ba4e608d97399a418419103f5431783f36d Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 19:39:34 +0200 Subject: [PATCH 05/15] chore(ui): remove old duration comparators --- ui/src/admin/constants/index.js | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/ui/src/admin/constants/index.js b/ui/src/admin/constants/index.js index b55fe2dab..029ee96d6 100644 --- a/ui/src/admin/constants/index.js +++ b/ui/src/admin/constants/index.js @@ -1,13 +1,3 @@ -export const TIMES = [ - {test: /ns/, magnitude: 0}, - {test: /µs/, magnitude: 1}, - {test: /u/, magnitude: 1}, - {test: /^\d*ms/, magnitude: 2}, - {test: /^\d*s/, magnitude: 3}, - {test: /^\d*m\d*s/, magnitude: 4}, - {test: /^\d*h\d*m\d*s/, magnitude: 5}, -] - export const NEW_DEFAULT_USER = { name: '', password: '', From 64524f8001705416ab8c96c15076ca639656f5ee Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 20:40:57 +0200 Subject: [PATCH 06/15] feat(ui/admin/queries): move queries sorting functionality to reducer --- ui/src/admin/containers/QueriesPage.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js index 6c2dd9726..23929b09f 100644 --- a/ui/src/admin/containers/QueriesPage.js +++ b/ui/src/admin/containers/QueriesPage.js @@ -11,7 +11,6 @@ import {showDatabases, showQueries} from 'shared/apis/metaQuery' import QueriesTable from 'src/admin/components/QueriesTable' import showDatabasesParser from 'shared/parsing/showDatabases' import showQueriesParser from 'shared/parsing/showQueries' -import {parseDuration, compareDurations} from 'src/utils/influxDuration' import {notifyQueriesError} from 'shared/copy/notifications' import {ErrorHandling} from 'src/shared/decorators/errors' @@ -72,15 +71,7 @@ class QueriesPage extends Component { }) const queries = uniqBy(flatten(allQueries), q => q.id) - // parse durations so that they can be compared - queries.forEach(x => (x._pd = parseDuration(x.duration))) - - // sorting queries by magnitude, so generally longer queries will appear atop the list - const sortedQueries = queries.sort((a, b) => { - return -compareDurations(a._pd, b._pd) - }) - - loadQueries(sortedQueries) + loadQueries(queries) }) }) } From e64139eaa671fd8f3bc64de521b1dea07e36ee58 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 20:42:07 +0200 Subject: [PATCH 07/15] feat(ui/admin/queries): add queries sort spec to redux state, apply sorting on load --- ui/src/admin/reducers/influxdb.js | 38 ++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/ui/src/admin/reducers/influxdb.js b/ui/src/admin/reducers/influxdb.js index 8d21aa9ba..92d875fed 100644 --- a/ui/src/admin/reducers/influxdb.js +++ b/ui/src/admin/reducers/influxdb.js @@ -6,12 +6,45 @@ import { NEW_EMPTY_RP, } from 'src/admin/constants' import uuid from 'uuid' +import {parseDuration, compareDurations} from 'src/utils/influxDuration' + +const querySorters = { + '+time'(queries) { + queries.forEach(x => (x._pd = parseDuration(x.duration))) + return queries.sort((a, b) => { + return compareDurations(a._pd, b._pd) + }) + }, + '-time'(queries) { + queries.forEach(x => (x._pd = parseDuration(x.duration))) + return queries.sort((a, b) => { + return -compareDurations(a._pd, b._pd) + }) + }, + '+database'(queries) { + queries.forEach(x => (x._pd = parseDuration(x.duration))) + return queries.sort((a, b) => { + return compareDurations(a._pd, b._pd) + }) + }, + '-database'(queries) { + queries.forEach(x => (x._pd = parseDuration(x.duration))) + return queries.sort((a, b) => { + return -compareDurations(a._pd, b._pd) + }) + }, +} +const identity = x => x +function sortQueries(queries, queriesSort) { + return (querySorters[queriesSort] || identity)(queries) +} const initialState = { users: [], roles: [], permissions: [], queries: [], + queriesSort: '-time', queryIDToKill: null, databases: [], } @@ -274,7 +307,10 @@ const adminInfluxDB = (state = initialState, action) => { } case 'INFLUXDB_LOAD_QUERIES': { - return {...state, ...action.payload} + return { + ...state, + queries: sortQueries(action.payload.queries, state.queriesSort), + } } case 'INFLUXDB_FILTER_USERS': { From 08e53896bd42a79c8ee88470b7ed1c5abbd67814 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 21:09:24 +0200 Subject: [PATCH 08/15] feat(ui/admin/queries): manage queries sort with redux --- ui/src/admin/actions/influxdb.js | 8 +++++++- ui/src/admin/reducers/influxdb.js | 8 ++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/ui/src/admin/actions/influxdb.js b/ui/src/admin/actions/influxdb.js index 3e96dede4..25fdbc40d 100644 --- a/ui/src/admin/actions/influxdb.js +++ b/ui/src/admin/actions/influxdb.js @@ -177,7 +177,13 @@ export const setQueryToKill = queryIDToKill => ({ export const loadQueries = queries => ({ type: 'INFLUXDB_LOAD_QUERIES', payload: { - queries, + queries: [...queries], + }, +}) +export const setQueriesSort = queriesSort => ({ + type: 'INFLUXDB_SET_QUERIES_SORT', + payload: { + queriesSort, }, }) diff --git a/ui/src/admin/reducers/influxdb.js b/ui/src/admin/reducers/influxdb.js index 92d875fed..8214052d8 100644 --- a/ui/src/admin/reducers/influxdb.js +++ b/ui/src/admin/reducers/influxdb.js @@ -312,6 +312,14 @@ const adminInfluxDB = (state = initialState, action) => { queries: sortQueries(action.payload.queries, state.queriesSort), } } + case 'INFLUXDB_SET_QUERIES_SORT': { + const queriesSort = action.payload.queriesSort + return { + ...state, + queriesSort, + queries: sortQueries(state.queries, queriesSort), + } + } case 'INFLUXDB_FILTER_USERS': { const {text} = action.payload From 59a960931712f3a60c58b3a3511a3700e018356d Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Thu, 15 Apr 2021 21:10:08 +0200 Subject: [PATCH 09/15] feat(ui/admin/queries): add tests for queries reducers --- ui/test/admin/reducers/influxdb.test.js | 70 +++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/ui/test/admin/reducers/influxdb.test.js b/ui/test/admin/reducers/influxdb.test.js index 8ddd14ea0..52c2d86bd 100644 --- a/ui/test/admin/reducers/influxdb.test.js +++ b/ui/test/admin/reducers/influxdb.test.js @@ -21,6 +21,8 @@ import { filterUsers, addDatabaseDeleteCode, removeDatabaseDeleteCode, + loadQueries, + setQueriesSort, } from 'src/admin/actions/influxdb' import { @@ -375,4 +377,72 @@ describe('Admin.InfluxDB.Reducers', () => { expect(actual.permissions).toEqual(expected.permissions) }) + + describe('Queries', () => { + it('it sorts queries by time descending OOTB', () => { + const queries = [ + {id: 1, database: 'a', duration: '11µs'}, + {id: 2, database: 'b', duration: '36µs'}, + ] + const actual = reducer(undefined, loadQueries(queries)) + expect(actual.queriesSort).toEqual('-time') + expect(actual.queries).toHaveLength(2) + expect(actual.queries[0].id).toEqual(2) + expect(actual.queries[1].id).toEqual(1) + }) + it('it sorts queries by database', () => { + const queries = [ + {id: 2, database: 'b', duration: '36µs'}, + {id: 1, database: 'a', duration: '11µs'}, + ] + let actual = reducer(undefined, loadQueries(queries)) + actual = reducer(actual, setQueriesSort('+database')) + expect(actual.queriesSort).toEqual('+database') + expect(actual.queries).toHaveLength(2) + expect(actual.queries[0].id).toEqual(1) + expect(actual.queries[1].id).toEqual(2) + actual = reducer(actual, setQueriesSort('-database')) + expect(actual.queriesSort).toEqual('-database') + expect(actual.queries).toHaveLength(2) + expect(actual.queries[0].id).toEqual(2) + expect(actual.queries[1].id).toEqual(1) + const queries2 = [ + {id: 3, database: 'c', duration: '36µs'}, + {id: 1, database: 'x', duration: '11µs'}, + {id: 2, database: 'd', duration: '12µs'}, + ] + actual = reducer(actual, loadQueries(queries2)) + expect(actual.queries).toHaveLength(3) + expect(actual.queries[0].id).toEqual(3) + expect(actual.queries[1].id).toEqual(2) + expect(actual.queries[2].id).toEqual(1) + }) + it('it sorts queries by time', () => { + const queries = [ + {id: 2, database: 'b', duration: '36µs'}, + {id: 1, database: 'a', duration: '11µs'}, + ] + let actual = reducer(undefined, loadQueries(queries)) + actual = reducer(actual, setQueriesSort('+time')) + expect(actual.queriesSort).toEqual('+time') + expect(actual.queries).toHaveLength(2) + expect(actual.queries[0].id).toEqual(1) + expect(actual.queries[1].id).toEqual(2) + actual = reducer(actual, setQueriesSort('-time')) + expect(actual.queriesSort).toEqual('-time') + expect(actual.queries).toHaveLength(2) + expect(actual.queries[0].id).toEqual(2) + expect(actual.queries[1].id).toEqual(1) + const queries2 = [ + {id: 3, database: 'c', duration: '36µs'}, + {id: 1, database: 'x', duration: '11µs'}, + {id: 2, database: 'd', duration: '12µs'}, + ] + actual = reducer(actual, loadQueries(queries2)) + expect(actual.queries).toHaveLength(3) + expect(actual.queries[0].id).toEqual(3) + expect(actual.queries[1].id).toEqual(2) + expect(actual.queries[2].id).toEqual(1) + }) + }) }) From dc6db6aafc9a9e8c69b945e905bc02dde09a22cd Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Fri, 16 Apr 2021 07:09:19 +0200 Subject: [PATCH 10/15] fix(ui/admin/queries): fix database sorting reducers --- ui/src/admin/reducers/influxdb.js | 4 ++-- ui/test/admin/reducers/influxdb.test.js | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ui/src/admin/reducers/influxdb.js b/ui/src/admin/reducers/influxdb.js index 8214052d8..86c670508 100644 --- a/ui/src/admin/reducers/influxdb.js +++ b/ui/src/admin/reducers/influxdb.js @@ -24,13 +24,13 @@ const querySorters = { '+database'(queries) { queries.forEach(x => (x._pd = parseDuration(x.duration))) return queries.sort((a, b) => { - return compareDurations(a._pd, b._pd) + return a.database.localeCompare(b.database) }) }, '-database'(queries) { queries.forEach(x => (x._pd = parseDuration(x.duration))) return queries.sort((a, b) => { - return -compareDurations(a._pd, b._pd) + return -a.database.localeCompare(b.database) }) }, } diff --git a/ui/test/admin/reducers/influxdb.test.js b/ui/test/admin/reducers/influxdb.test.js index 52c2d86bd..8a0bf82a3 100644 --- a/ui/test/admin/reducers/influxdb.test.js +++ b/ui/test/admin/reducers/influxdb.test.js @@ -392,8 +392,8 @@ describe('Admin.InfluxDB.Reducers', () => { }) it('it sorts queries by database', () => { const queries = [ - {id: 2, database: 'b', duration: '36µs'}, - {id: 1, database: 'a', duration: '11µs'}, + {id: 2, database: 'b', duration: '16µs'}, + {id: 1, database: 'a', duration: '21µs'}, ] let actual = reducer(undefined, loadQueries(queries)) actual = reducer(actual, setQueriesSort('+database')) @@ -407,15 +407,15 @@ describe('Admin.InfluxDB.Reducers', () => { expect(actual.queries[0].id).toEqual(2) expect(actual.queries[1].id).toEqual(1) const queries2 = [ - {id: 3, database: 'c', duration: '36µs'}, + {id: 3, database: 'c', duration: '26µs'}, {id: 1, database: 'x', duration: '11µs'}, - {id: 2, database: 'd', duration: '12µs'}, + {id: 2, database: 'd', duration: '22µs'}, ] actual = reducer(actual, loadQueries(queries2)) expect(actual.queries).toHaveLength(3) - expect(actual.queries[0].id).toEqual(3) + expect(actual.queries[0].id).toEqual(1) expect(actual.queries[1].id).toEqual(2) - expect(actual.queries[2].id).toEqual(1) + expect(actual.queries[2].id).toEqual(3) }) it('it sorts queries by time', () => { const queries = [ From 115e316490fd3023b465a6a74522151ec0f5cb1e Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Fri, 16 Apr 2021 08:02:18 +0200 Subject: [PATCH 11/15] feat(ui/admin/queries): add UI logic that changes sorting --- ui/src/admin/components/QueriesTable.js | 82 +++++++++++++++++-------- ui/src/admin/containers/QueriesPage.js | 20 +++++- 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index 637a8741c..6b3852df2 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -1,39 +1,73 @@ import React from 'react' -import PropTypes from 'prop-types' +import PropTypes, {string} from 'prop-types' import QueryRow from 'src/admin/components/QueryRow' import {QUERIES_TABLE} from 'src/admin/constants/tableSizing' -const QueriesTable = ({queries, onKillQuery}) => ( -
-
-
- - - - - - - - - - {queries.map(q => ( - - ))} - -
- Database - QueryRunning -
+const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => { + let currentTimeSort = '' + let currentDBSort = '' + let newTimeSort = '-time' + let newDBSort = '+database' + switch (queriesSort) { + case '-time': + currentTimeSort = 'desc' + newTimeSort = '+time' + break + case '+time': + currentTimeSort = 'asc' + newTimeSort = '-time' + break + case '-database': + currentDBSort = 'desc' + newDBSort = '+database' + break + case '+database': + currentDBSort = 'asc' + newDBSort = '-database' + break + } + return ( +
+
+
+ + + + + + + + + + {queries.map(q => ( + + ))} + +
changeSort(newDBSort)} + > + Database {currentDBSort} + Query changeSort(newTimeSort)} + > + Running {currentTimeSort} + +
+
-
-) + ) +} const {arrayOf, func, shape} = PropTypes QueriesTable.propTypes = { queries: arrayOf(shape()), + queriesSort: string, + changeSort: func, onConfirm: func, onKillQuery: func, } diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js index 23929b09f..eb00c859d 100644 --- a/ui/src/admin/containers/QueriesPage.js +++ b/ui/src/admin/containers/QueriesPage.js @@ -17,6 +17,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors' import { loadQueries as loadQueriesAction, setQueryToKill as setQueryToKillAction, + setQueriesSort as setQueriesSortAction, killQueryAsync, } from 'src/admin/actions/influxdb' @@ -34,9 +35,16 @@ class QueriesPage extends Component { } render() { - const {queries} = this.props + const {queries, queriesSort, changeSort} = this.props - return + return ( + + ) } updateQueries = () => { @@ -91,21 +99,27 @@ QueriesPage.propTypes = { }), }), queries: arrayOf(shape()), + queriesSort: string, loadQueries: func, queryIDToKill: string, setQueryToKill: func, + changeSort: func, killQuery: func, notify: func.isRequired, } -const mapStateToProps = ({adminInfluxDB: {queries, queryIDToKill}}) => ({ +const mapStateToProps = ({ + adminInfluxDB: {queries, queriesSort, queryIDToKill}, +}) => ({ queries, + queriesSort, queryIDToKill, }) const mapDispatchToProps = dispatch => ({ loadQueries: bindActionCreators(loadQueriesAction, dispatch), setQueryToKill: bindActionCreators(setQueryToKillAction, dispatch), + changeSort: bindActionCreators(setQueriesSortAction, dispatch), killQuery: bindActionCreators(killQueryAsync, dispatch), notify: bindActionCreators(notifyAction, dispatch), }) From 5a165b555e08a8fdbe54079b28c733087931474c Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Fri, 16 Apr 2021 11:02:37 +0200 Subject: [PATCH 12/15] feat(ui/admin/queries): signalize current sort order in table header --- ui/src/admin/components/QueriesTable.js | 18 +++++++++-------- ui/src/style/pages/admin.scss | 27 +++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index 6b3852df2..f8895afae 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -5,25 +5,25 @@ import QueryRow from 'src/admin/components/QueryRow' import {QUERIES_TABLE} from 'src/admin/constants/tableSizing' const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => { - let currentTimeSort = '' - let currentDBSort = '' + let timeSortClass = '' + let dbSortClass = '' let newTimeSort = '-time' let newDBSort = '+database' switch (queriesSort) { case '-time': - currentTimeSort = 'desc' + timeSortClass = 'col--sort-desc' newTimeSort = '+time' break case '+time': - currentTimeSort = 'asc' + timeSortClass = 'col--sort-asc' newTimeSort = '-time' break case '-database': - currentDBSort = 'desc' + dbSortClass = 'col--sort-desc' newDBSort = '+database' break case '+database': - currentDBSort = 'asc' + dbSortClass = 'col--sort-asc' newDBSort = '-database' break } @@ -36,16 +36,18 @@ const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => { changeSort(newDBSort)} > - Database {currentDBSort} +
Database
Query changeSort(newTimeSort)} > - Running {currentTimeSort} + Running diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index 1ad355ecd..9631cd186 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -41,6 +41,33 @@ table > thead > tr > th.admin-table--left-offset { padding-left: 15px; } +.admin-table > thead > tr > th.col--sort-asc, +.admin-table > thead > tr > th.col--sort-desc { + color: #22adf6; +} +.admin-table > thead > tr > th.col--sortable { + position: absolute; + &:after { + font-family: 'icomoon'; + content: '\e902'; + font-size: 13px; + position: absolute; + top: 50%; + right: 6px; + opacity: 0; + transition: opacity 0.25s ease, color 0.25s ease, transform 0.25s ease; + } +} + +.admin-table > thead > tr > th.col--sortable.col--sort-asc:after { + transform: translateY(-50%) rotate(180deg); + opacity: 1; +} +.admin-table > thead > tr > th.col--sortable.col--sort-desc:after { + transform: translateY(-50%) rotate(0deg); + opacity: 1; +} + table > tbody > tr.admin-table--edit-row, table > tbody > tr.admin-table--edit-row:hover, table.table-highlight > tbody > tr.admin-table--edit-row, From e04f7c1307854dc2916e45389bf5b4b2187017d8 Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Fri, 16 Apr 2021 11:21:44 +0200 Subject: [PATCH 13/15] feat(ui/admin/queries): visualize possible sorting on hover --- ui/src/admin/components/QueriesTable.js | 4 ++-- ui/src/style/pages/admin.scss | 10 ++++++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index f8895afae..4c29a54b3 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -5,8 +5,8 @@ import QueryRow from 'src/admin/components/QueryRow' import {QUERIES_TABLE} from 'src/admin/constants/tableSizing' const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => { - let timeSortClass = '' - let dbSortClass = '' + let timeSortClass = 'col--sort-next-desc' + let dbSortClass = 'col--sort-next-asc' let newTimeSort = '-time' let newDBSort = '+database' switch (queriesSort) { diff --git a/ui/src/style/pages/admin.scss b/ui/src/style/pages/admin.scss index 9631cd186..d5d352749 100644 --- a/ui/src/style/pages/admin.scss +++ b/ui/src/style/pages/admin.scss @@ -55,18 +55,24 @@ table > thead > tr > th.admin-table--left-offset { top: 50%; right: 6px; opacity: 0; + transform: translateY(-50%); transition: opacity 0.25s ease, color 0.25s ease, transform 0.25s ease; } } -.admin-table > thead > tr > th.col--sortable.col--sort-asc:after { +.admin-table > thead > tr > th.col--sortable.col--sort-asc:after, +.admin-table > thead > tr > th.col--sortable.col--sort-next-asc:hover:after { transform: translateY(-50%) rotate(180deg); opacity: 1; } -.admin-table > thead > tr > th.col--sortable.col--sort-desc:after { +.admin-table > thead > tr > th.col--sortable.col--sort-desc:after, +.admin-table > thead > tr > th.col--sortable.col--sort-next-desc:hover:after { transform: translateY(-50%) rotate(0deg); opacity: 1; } +.admin-table > thead > tr > th.col--sortable.col--sort-next-asc:after { + transform: translateY(-50%) rotate(180deg); +} table > tbody > tr.admin-table--edit-row, table > tbody > tr.admin-table--edit-row:hover, From dfc14dc9ea3749a6a907aca3387e6b675e39fd8c Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Fri, 16 Apr 2021 13:18:01 +0200 Subject: [PATCH 14/15] feat(ui/admin/queries): let use choose queries refresh interval, add page header --- ui/src/admin/components/QueriesTable.js | 60 ++++++++++------------ ui/src/admin/containers/QueriesPage.js | 66 +++++++++++++++++++++---- 2 files changed, 84 insertions(+), 42 deletions(-) diff --git a/ui/src/admin/components/QueriesTable.js b/ui/src/admin/components/QueriesTable.js index 4c29a54b3..6a00f6119 100644 --- a/ui/src/admin/components/QueriesTable.js +++ b/ui/src/admin/components/QueriesTable.js @@ -28,39 +28,33 @@ const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => { break } return ( -
-
-
- - - - - - - - - - {queries.map(q => ( - - ))} - -
changeSort(newDBSort)} - > -
Database
-
Query changeSort(newTimeSort)} - > - Running - -
-
-
-
+ + + + + + + + + + {queries.map(q => ( + + ))} + +
changeSort(newDBSort)} + > +
Database
+
Query changeSort(newTimeSort)} + > + Running + +
) } diff --git a/ui/src/admin/containers/QueriesPage.js b/ui/src/admin/containers/QueriesPage.js index eb00c859d..22c07c048 100644 --- a/ui/src/admin/containers/QueriesPage.js +++ b/ui/src/admin/containers/QueriesPage.js @@ -13,6 +13,7 @@ import showDatabasesParser from 'shared/parsing/showDatabases' import showQueriesParser from 'shared/parsing/showQueries' import {notifyQueriesError} from 'shared/copy/notifications' import {ErrorHandling} from 'src/shared/decorators/errors' +import AutoRefreshDropdown from 'src/shared/components/dropdown_auto_refresh/AutoRefreshDropdown' import { loadQueries as loadQueriesAction, @@ -24,26 +25,54 @@ import { import {notify as notifyAction} from 'shared/actions/notifications' class QueriesPage extends Component { + constructor(props) { + super(props) + this.state = { + updateInterval: 5000, + } + } componentDidMount() { this.updateQueries() - const updateInterval = 5000 - this.intervalID = setInterval(this.updateQueries, updateInterval) + if (this.state.updateInterval > 0) { + this.intervalID = setInterval( + this.updateQueries, + this.state.updateInterval + ) + } } componentWillUnmount() { - clearInterval(this.intervalID) + if (this.intervalID) { + clearInterval(this.intervalID) + } } render() { const {queries, queriesSort, changeSort} = this.props + const {updateInterval, title} = this.state return ( - +
+
+

{title}

+
+ +
+
+
+ +
+
) } @@ -52,9 +81,17 @@ class QueriesPage extends Component { showDatabases(source.links.proxy).then(resp => { const {databases, errors} = showDatabasesParser(resp.data) if (errors.length) { + this.setState(state => ({...state, title: ''})) errors.forEach(message => notify(notifyQueriesError(message))) return } + this.setState(state => ({ + ...state, + title: + databases.length === 1 + ? '1 Database' + : `${databases.length} Databases`, + })) const fetches = databases.map(db => showQueries(source.links.proxy, db)) @@ -83,6 +120,17 @@ class QueriesPage extends Component { }) }) } + changeRefreshInterval = ({milliseconds: updateInterval}) => { + this.setState(state => ({...state, updateInterval})) + if (this.intervalID) { + clearInterval(this.intervalID) + this.intervalID = undefined + } + if (updateInterval > 0) { + this.updateQueries() + this.intervalID = setInterval(this.updateQueries, updateInterval) + } + } handleKillQuery = query => { const {source, killQuery} = this.props From b0c41242d088612cbae0608bbc05aa06b606b8ea Mon Sep 17 00:00:00 2001 From: Pavel Zavora Date: Mon, 19 Apr 2021 05:21:07 +0200 Subject: [PATCH 15/15] ciore: update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 590f65119..2beb7f209 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ 1. [#5712](https://github.com/influxdata/chronograf/pull/5712): Allow to change write precission. 1. [#5710](https://github.com/influxdata/chronograf/pull/5710): Add PKCE to OAuth integrations. 1. [#5713](https://github.com/influxdata/chronograf/pull/5710): Support GitHub Enterprise in the existing GitHub OAuth integration. +1. [#5728](https://github.com/influxdata/chronograf/pull/5728): Improve InfluxDB Admin | Queries page. ### Bug Fixes