Merge pull request #5728 from influxdata/5668/better_queries_table
feat(ui/admin/queries): Improve InfluxDB Admin | Queries pagepull/5731/head
commit
a6e3ed5d8a
|
@ -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/5713): 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
|
||||
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
|
|
|
@ -1,39 +1,69 @@
|
|||
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}) => (
|
||||
<div>
|
||||
<div className="panel panel-solid">
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style={{width: `${QUERIES_TABLE.colDatabase}px`}}>
|
||||
Database
|
||||
</th>
|
||||
<th>Query</th>
|
||||
<th style={{width: `${QUERIES_TABLE.colRunning}px`}}>Running</th>
|
||||
<th style={{width: `${QUERIES_TABLE.colKillQuery}px`}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{queries.map(q => (
|
||||
<QueryRow key={q.id} query={q} onKill={onKillQuery} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
const QueriesTable = ({queries, queriesSort, changeSort, onKillQuery}) => {
|
||||
let timeSortClass = 'col--sort-next-desc'
|
||||
let dbSortClass = 'col--sort-next-asc'
|
||||
let newTimeSort = '-time'
|
||||
let newDBSort = '+database'
|
||||
switch (queriesSort) {
|
||||
case '-time':
|
||||
timeSortClass = 'col--sort-desc'
|
||||
newTimeSort = '+time'
|
||||
break
|
||||
case '+time':
|
||||
timeSortClass = 'col--sort-asc'
|
||||
newTimeSort = '-time'
|
||||
break
|
||||
case '-database':
|
||||
dbSortClass = 'col--sort-desc'
|
||||
newDBSort = '+database'
|
||||
break
|
||||
case '+database':
|
||||
dbSortClass = 'col--sort-asc'
|
||||
newDBSort = '-database'
|
||||
break
|
||||
}
|
||||
return (
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
style={{width: `${QUERIES_TABLE.colDatabase}px`}}
|
||||
className={`col--sortable ${dbSortClass}`}
|
||||
onClick={() => changeSort(newDBSort)}
|
||||
>
|
||||
<div>Database</div>
|
||||
</th>
|
||||
<th>Query</th>
|
||||
<th
|
||||
style={{width: `${QUERIES_TABLE.colRunning}px`}}
|
||||
className={`col--sortable ${timeSortClass}`}
|
||||
onClick={() => changeSort(newTimeSort)}
|
||||
>
|
||||
Running
|
||||
</th>
|
||||
<th style={{width: `${QUERIES_TABLE.colKillQuery}px`}} />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{queries.map(q => (
|
||||
<QueryRow key={q.id} query={q} onKill={onKillQuery} />
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape} = PropTypes
|
||||
|
||||
QueriesTable.propTypes = {
|
||||
queries: arrayOf(shape()),
|
||||
queriesSort: string,
|
||||
changeSort: func,
|
||||
onConfirm: func,
|
||||
onKillQuery: func,
|
||||
}
|
||||
|
|
|
@ -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: '',
|
||||
|
|
|
@ -11,33 +11,69 @@ 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 {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,
|
||||
setQueryToKill as setQueryToKillAction,
|
||||
setQueriesSort as setQueriesSortAction,
|
||||
killQueryAsync,
|
||||
} from 'src/admin/actions/influxdb'
|
||||
|
||||
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} = this.props
|
||||
const {queries, queriesSort, changeSort} = this.props
|
||||
const {updateInterval, title} = this.state
|
||||
|
||||
return <QueriesTable queries={queries} onKillQuery={this.handleKillQuery} />
|
||||
return (
|
||||
<div className="panel panel-solid">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">{title}</h2>
|
||||
<div style={{float: 'right'}}>
|
||||
<AutoRefreshDropdown
|
||||
selected={updateInterval}
|
||||
onChoose={this.changeRefreshInterval}
|
||||
onManualRefresh={this.updateQueries}
|
||||
showManualRefresh={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<QueriesTable
|
||||
queries={queries}
|
||||
queriesSort={queriesSort}
|
||||
changeSort={changeSort}
|
||||
onKillQuery={this.handleKillQuery}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
updateQueries = () => {
|
||||
|
@ -45,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))
|
||||
|
||||
|
@ -72,18 +116,21 @@ class QueriesPage extends Component {
|
|||
})
|
||||
|
||||
const queries = uniqBy(flatten(allQueries), q => q.id)
|
||||
|
||||
// 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
|
||||
})
|
||||
|
||||
loadQueries(sortedQueries)
|
||||
loadQueries(queries)
|
||||
})
|
||||
})
|
||||
}
|
||||
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
|
||||
|
@ -100,21 +147,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),
|
||||
})
|
||||
|
|
|
@ -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 a.database.localeCompare(b.database)
|
||||
})
|
||||
},
|
||||
'-database'(queries) {
|
||||
queries.forEach(x => (x._pd = parseDuration(x.duration)))
|
||||
return queries.sort((a, b) => {
|
||||
return -a.database.localeCompare(b.database)
|
||||
})
|
||||
},
|
||||
}
|
||||
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,18 @@ const adminInfluxDB = (state = initialState, action) => {
|
|||
}
|
||||
|
||||
case 'INFLUXDB_LOAD_QUERIES': {
|
||||
return {...state, ...action.payload}
|
||||
return {
|
||||
...state,
|
||||
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': {
|
||||
|
|
|
@ -41,6 +41,39 @@ 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;
|
||||
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-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-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,
|
||||
table.table-highlight > tbody > tr.admin-table--edit-row,
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* 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<string, number> = {
|
||||
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<number>(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
|
||||
}
|
||||
|
||||
/**
|
||||
* CompareDurations implements sort comparator for InfluxDuration instances.
|
||||
*/
|
||||
export function compareDurations(a: InfluxDuration, b: InfluxDuration): number {
|
||||
let i = 0
|
||||
for (; i < 8; i++) {
|
||||
if (a[i] !== b[i]) {
|
||||
return a[i] - b[i]
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
|
@ -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: '16µs'},
|
||||
{id: 1, database: 'a', duration: '21µ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: '26µs'},
|
||||
{id: 1, database: 'x', duration: '11µs'},
|
||||
{id: 2, database: 'd', duration: '22µs'},
|
||||
]
|
||||
actual = reducer(actual, loadQueries(queries2))
|
||||
expect(actual.queries).toHaveLength(3)
|
||||
expect(actual.queries[0].id).toEqual(1)
|
||||
expect(actual.queries[1].id).toEqual(2)
|
||||
expect(actual.queries[2].id).toEqual(3)
|
||||
})
|
||||
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)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
import {
|
||||
parseDuration,
|
||||
InfluxDuration,
|
||||
compareDurations,
|
||||
} 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('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(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(compareDurations(x, x)).toEqual(0)
|
||||
expect(compareDurations(x, y)).toBeLessThan(0)
|
||||
expect(compareDurations(y, x)).toBeGreaterThan(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue