commit
4cb8a0632b
|
@ -78,7 +78,7 @@ export const getBuckets = () => async (
|
|||
orgs: {org},
|
||||
} = getState()
|
||||
|
||||
const buckets = (await client.buckets.getAll(org.id)) as Bucket[]
|
||||
const buckets = await client.buckets.getAll(org.id)
|
||||
|
||||
dispatch(setBuckets(RemoteDataState.Done, buckets))
|
||||
} catch (e) {
|
||||
|
|
|
@ -5,8 +5,8 @@ import _ from 'lodash'
|
|||
|
||||
// Components
|
||||
import {IndexList, ConfirmationButton, Context} from 'src/clockface'
|
||||
import CloudFeatureFlag from 'src/shared/components/CloudFeatureFlag'
|
||||
import EditableName from 'src/shared/components/EditableName'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
// Constants
|
||||
import {DEFAULT_BUCKET_NAME} from 'src/dashboards/constants'
|
||||
|
@ -77,13 +77,13 @@ class BucketRow extends PureComponent<Props & WithRouterProps> {
|
|||
description="Quickly load an existing line protocol file."
|
||||
action={this.handleAddLineProtocol}
|
||||
/>
|
||||
<CloudFeatureFlag>
|
||||
<CloudExclude>
|
||||
<Context.Item
|
||||
label="Scrape Metrics"
|
||||
description="Add a scrape target to pull data into your bucket."
|
||||
action={this.handleAddScraper}
|
||||
/>
|
||||
</CloudFeatureFlag>
|
||||
</CloudExclude>
|
||||
</Context.Menu>
|
||||
</Context>
|
||||
</IndexList.Cell>
|
||||
|
|
|
@ -5,7 +5,7 @@ import _ from 'lodash'
|
|||
// Components
|
||||
import Table from 'src/dashboards/components/dashboard_index/Table'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
import GetLabels from 'src/labels/components/GetLabels'
|
||||
import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
@ -43,7 +43,7 @@ export default class DashboardsIndexContents extends Component<Props> {
|
|||
} = this.props
|
||||
|
||||
return (
|
||||
<GetLabels>
|
||||
<GetResources resource={ResourceTypes.Labels}>
|
||||
<FilterList<Dashboard>
|
||||
list={dashboards}
|
||||
searchTerm={searchTerm}
|
||||
|
@ -64,7 +64,7 @@ export default class DashboardsIndexContents extends Component<Props> {
|
|||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</GetLabels>
|
||||
</GetResources>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
// TODO make recursive
|
||||
const exprStr = ({expr, val, type}) => {
|
||||
if (expr === 'reference') {
|
||||
if (val === 'time') {
|
||||
return val
|
||||
}
|
||||
return `"${val}"`
|
||||
} else if (expr === 'literal' || expr === '"string"') {
|
||||
if (type === 'regex') {
|
||||
return `${val}` // TODO add slashes `/${val}/`
|
||||
} else if (type === 'list') {
|
||||
throw new Error() // TODO list
|
||||
} else if (type === 'string') {
|
||||
return `'${val}'`
|
||||
} else {
|
||||
// types: boolean, number, integer, duration, time
|
||||
return val
|
||||
}
|
||||
} else if (expr === 'wildcard') {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
const recurse = root => {
|
||||
const {expr} = root
|
||||
|
||||
if (expr === 'binary') {
|
||||
const {op, lhs, rhs} = root
|
||||
return `${recurse(lhs)} ${op} ${recurse(rhs)}`
|
||||
} else if (expr === 'call') {
|
||||
const {name, args} = root
|
||||
if (!args) {
|
||||
return `${name}()`
|
||||
}
|
||||
return `${name}(${args.map(recurse).join(', ')})`
|
||||
}
|
||||
|
||||
return exprStr(root)
|
||||
}
|
||||
|
||||
export const toString = ast => {
|
||||
const {fields, sources, condition, groupBy, orderbys, limits} = ast
|
||||
|
||||
const strs = ['SELECT']
|
||||
|
||||
// SELECT
|
||||
const flds = []
|
||||
for (const field of fields) {
|
||||
const {column, alias} = field
|
||||
const result = recurse(column)
|
||||
flds.push(alias ? `${result} AS "${alias}"` : result)
|
||||
}
|
||||
strs.push(flds.join(', '))
|
||||
|
||||
// FROM
|
||||
if (sources.length) {
|
||||
strs.push('FROM')
|
||||
const srcs = []
|
||||
for (const source of sources) {
|
||||
// TODO subquery (type)
|
||||
const {database, retentionPolicy, name} = source
|
||||
srcs.push(`"${_.compact([database, retentionPolicy, name]).join('"."')}"`)
|
||||
}
|
||||
strs.push(srcs.join(', '))
|
||||
}
|
||||
|
||||
// WHERE
|
||||
if (condition) {
|
||||
strs.push('WHERE')
|
||||
const result = recurse(condition)
|
||||
strs.push(result)
|
||||
}
|
||||
|
||||
// GROUP BY
|
||||
if (groupBy) {
|
||||
strs.push('GROUP BY')
|
||||
|
||||
const dimensions = []
|
||||
const {time, tags, fill} = groupBy
|
||||
if (time) {
|
||||
const {interval, offset} = time
|
||||
// _.compact([interval, offset]).join(', ')
|
||||
dimensions.push(`time(${_.compact([interval, offset]).join(', ')})`)
|
||||
}
|
||||
|
||||
if (tags) {
|
||||
strs.push(dimensions.concat(tags).join(','))
|
||||
} else {
|
||||
strs.push(dimensions.join(','))
|
||||
}
|
||||
|
||||
if (fill) {
|
||||
strs.push(`fill(${fill})`)
|
||||
}
|
||||
}
|
||||
|
||||
// ORDER BY
|
||||
if (orderbys && orderbys.length) {
|
||||
strs.push('ORDER BY')
|
||||
strs.push(
|
||||
orderbys
|
||||
.map(({name, order}) => {
|
||||
return `${name} ${order === 'descending' ? 'DESC' : 'ASC'}`
|
||||
})
|
||||
.join(',')
|
||||
)
|
||||
}
|
||||
|
||||
// LIMIT
|
||||
if (limits) {
|
||||
const {limit, offset, slimit, soffset} = limits
|
||||
if (limit) {
|
||||
strs.push(`LIMIT ${limit}`)
|
||||
}
|
||||
if (offset) {
|
||||
strs.push(`OFFSET ${offset}`)
|
||||
}
|
||||
if (slimit) {
|
||||
strs.push(`SLIMIT ${slimit}`)
|
||||
}
|
||||
if (soffset) {
|
||||
strs.push(`SOFFSET ${soffset}`)
|
||||
}
|
||||
}
|
||||
|
||||
return strs.join(' ')
|
||||
}
|
|
@ -1,406 +0,0 @@
|
|||
import InfluxQL from 'src/influxql'
|
||||
|
||||
describe('influxql astToString', () => {
|
||||
it('simple query', () => {
|
||||
const ast = InfluxQL({
|
||||
fields: [
|
||||
{
|
||||
column: {
|
||||
expr: 'binary',
|
||||
op: '+',
|
||||
lhs: {
|
||||
expr: 'literal',
|
||||
val: '1',
|
||||
type: 'integer',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'reference',
|
||||
val: 'A',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'howdy',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected = `SELECT 1 + "A" FROM "howdy"`
|
||||
const actual = ast.toString()
|
||||
|
||||
// console.log(actual)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('simple query w/ multiple sources', () => {
|
||||
const ast = InfluxQL({
|
||||
fields: [
|
||||
{
|
||||
column: {
|
||||
expr: 'binary',
|
||||
op: '+',
|
||||
lhs: {
|
||||
expr: 'literal',
|
||||
val: '1',
|
||||
type: 'integer',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'reference',
|
||||
val: 'A',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'howdy',
|
||||
type: 'measurement',
|
||||
},
|
||||
{
|
||||
database: 'telegraf',
|
||||
retentionPolicy: 'autogen',
|
||||
name: 'doody',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected = `SELECT 1 + "A" FROM "howdy", "telegraf"."autogen"."doody"`
|
||||
const actual = ast.toString()
|
||||
|
||||
// console.log('actual ', actual)
|
||||
// console.log('expected', expected)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('query with AS', () => {
|
||||
const ast = InfluxQL({
|
||||
fields: [
|
||||
{
|
||||
alias: 'B',
|
||||
column: {
|
||||
expr: 'binary',
|
||||
op: '+',
|
||||
lhs: {
|
||||
expr: 'literal',
|
||||
val: '1',
|
||||
type: 'integer',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'reference',
|
||||
val: 'A',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'howdy',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected = `SELECT 1 + "A" AS "B" FROM "howdy"`
|
||||
const actual = ast.toString()
|
||||
|
||||
// console.log(actual)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('query with 2x func', () => {
|
||||
const ast = InfluxQL({
|
||||
fields: [
|
||||
{
|
||||
column: {
|
||||
expr: 'binary',
|
||||
op: '/',
|
||||
lhs: {
|
||||
expr: 'call',
|
||||
name: 'derivative',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'field1',
|
||||
},
|
||||
{
|
||||
expr: 'literal',
|
||||
val: '1h',
|
||||
type: 'duration',
|
||||
},
|
||||
],
|
||||
},
|
||||
rhs: {
|
||||
expr: 'call',
|
||||
name: 'derivative',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'field2',
|
||||
},
|
||||
{
|
||||
expr: 'literal',
|
||||
val: '1h',
|
||||
type: 'duration',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'myseries',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected = `SELECT derivative("field1", 1h) / derivative("field2", 1h) FROM "myseries"`
|
||||
const actual = ast.toString()
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('query with where and groupby', () => {
|
||||
const ast = InfluxQL({
|
||||
condition: {
|
||||
expr: 'binary',
|
||||
op: 'AND',
|
||||
lhs: {
|
||||
expr: 'binary',
|
||||
op: 'AND',
|
||||
lhs: {
|
||||
expr: 'binary',
|
||||
op: '=~',
|
||||
lhs: {
|
||||
expr: 'reference',
|
||||
val: 'cluster_id',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'literal',
|
||||
val: '/^23/',
|
||||
type: 'regex',
|
||||
},
|
||||
},
|
||||
rhs: {
|
||||
expr: 'binary',
|
||||
op: '=',
|
||||
lhs: {
|
||||
expr: 'reference',
|
||||
val: 'host',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'literal',
|
||||
val: 'prod-2ccccc04-us-east-1-data-3',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
rhs: {
|
||||
expr: 'binary',
|
||||
op: '\u003e',
|
||||
lhs: {
|
||||
expr: 'reference',
|
||||
val: 'time',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'binary',
|
||||
op: '-',
|
||||
lhs: {
|
||||
expr: 'call',
|
||||
name: 'now',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'literal',
|
||||
val: '15m',
|
||||
type: 'duration',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
alias: 'max_cpus',
|
||||
column: {
|
||||
expr: 'call',
|
||||
name: 'max',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'n_cpus',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
column: {
|
||||
expr: 'call',
|
||||
name: 'non_negative_derivative',
|
||||
args: [
|
||||
{
|
||||
expr: 'call',
|
||||
name: 'median',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'n_users',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
expr: 'literal',
|
||||
val: '5m',
|
||||
type: 'duration',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
groupBy: {
|
||||
time: {
|
||||
interval: '15m',
|
||||
offset: '10s',
|
||||
},
|
||||
tags: ['host', 'tag_x'],
|
||||
fill: '10',
|
||||
},
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'system',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected =
|
||||
'SELECT max("n_cpus") AS "max_cpus", non_negative_derivative(median("n_users"), 5m) FROM "system" WHERE "cluster_id" =~ /^23/ AND "host" = \'prod-2ccccc04-us-east-1-data-3\' AND time > now() - 15m GROUP BY time(15m, 10s),host,tag_x fill(10)'
|
||||
const actual = ast.toString()
|
||||
|
||||
// console.log('actual ', actual)
|
||||
// console.log('expected', expected)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
|
||||
it('query with orderby and limit', () => {
|
||||
const ast = InfluxQL({
|
||||
condition: {
|
||||
expr: 'binary',
|
||||
op: 'AND',
|
||||
lhs: {
|
||||
expr: 'binary',
|
||||
op: '=',
|
||||
lhs: {
|
||||
expr: 'reference',
|
||||
val: 'host',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'literal',
|
||||
val: 'hosta.influxdb.org',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
rhs: {
|
||||
expr: 'binary',
|
||||
op: '\u003e',
|
||||
lhs: {
|
||||
expr: 'reference',
|
||||
val: 'time',
|
||||
},
|
||||
rhs: {
|
||||
expr: 'literal',
|
||||
val: '2017-02-07T01:43:02.245407693Z',
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
column: {
|
||||
expr: 'call',
|
||||
name: 'mean',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'field1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
column: {
|
||||
expr: 'call',
|
||||
name: 'sum',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'field2',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
alias: 'field_x',
|
||||
column: {
|
||||
expr: 'call',
|
||||
name: 'count',
|
||||
args: [
|
||||
{
|
||||
expr: 'reference',
|
||||
val: 'field3',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
groupBy: {
|
||||
time: {
|
||||
interval: '10h',
|
||||
},
|
||||
},
|
||||
limits: {
|
||||
limit: 20,
|
||||
offset: 10,
|
||||
},
|
||||
orderbys: [
|
||||
{
|
||||
name: 'time',
|
||||
order: 'descending',
|
||||
},
|
||||
],
|
||||
sources: [
|
||||
{
|
||||
database: '',
|
||||
retentionPolicy: '',
|
||||
name: 'myseries',
|
||||
type: 'measurement',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const expected = `SELECT mean("field1"), sum("field2"), count("field3") AS "field_x" FROM "myseries" WHERE "host" = 'hosta.influxdb.org' AND time > '2017-02-07T01:43:02.245407693Z' GROUP BY time(10h) ORDER BY time DESC LIMIT 20 OFFSET 10`
|
||||
const actual = ast.toString()
|
||||
|
||||
// console.log('actual ', actual)
|
||||
// console.log('expected', expected)
|
||||
|
||||
expect(actual).toBe(expected)
|
||||
})
|
||||
})
|
|
@ -1,9 +0,0 @@
|
|||
import {toString} from './ast'
|
||||
|
||||
const InfluxQL = ast => {
|
||||
return {
|
||||
toString: () => toString(ast),
|
||||
}
|
||||
}
|
||||
|
||||
export default InfluxQL
|
|
@ -73,13 +73,11 @@ export const getLabels = () => async (
|
|||
) => {
|
||||
try {
|
||||
const {
|
||||
orgs: {
|
||||
org: {id},
|
||||
},
|
||||
orgs: {org},
|
||||
} = getState()
|
||||
dispatch(setLabels(RemoteDataState.Loading))
|
||||
|
||||
const labels = await client.labels.getAll(id)
|
||||
const labels = await client.labels.getAll(org.id)
|
||||
|
||||
dispatch(setLabels(RemoteDataState.Done, labels))
|
||||
} catch (e) {
|
||||
|
|
|
@ -1,57 +0,0 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
// Actions
|
||||
import {getLabels} from 'src/labels/actions'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
import {AppState} from 'src/types'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {TechnoSpinner} from '@influxdata/clockface'
|
||||
|
||||
interface StateProps {
|
||||
status: RemoteDataState
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
getLabels: typeof getLabels
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps
|
||||
|
||||
@ErrorHandling
|
||||
class GetLabels extends PureComponent<Props, StateProps> {
|
||||
public async componentDidMount() {
|
||||
await this.props.getLabels()
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {status, children} = this.props
|
||||
|
||||
if (status != RemoteDataState.Done) {
|
||||
return <TechnoSpinner />
|
||||
}
|
||||
|
||||
return children
|
||||
}
|
||||
}
|
||||
|
||||
const mstp = ({labels}: AppState): StateProps => {
|
||||
return {
|
||||
status: labels.status,
|
||||
}
|
||||
}
|
||||
|
||||
const mdtp = {
|
||||
getLabels: getLabels,
|
||||
}
|
||||
|
||||
export default connect<StateProps, DispatchProps, {}>(
|
||||
mstp,
|
||||
mdtp
|
||||
)(GetLabels)
|
|
@ -5,8 +5,8 @@ import {Link} from 'react-router'
|
|||
// Components
|
||||
import {NavMenu} from '@influxdata/clockface'
|
||||
import {Organization} from '@influxdata/influx'
|
||||
import CloudFeatureFlag from 'src/shared/components/CloudFeatureFlag'
|
||||
import SortingHat from 'src/shared/components/sorting_hat/SortingHat'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
interface Props {
|
||||
orgs: Organization[]
|
||||
|
@ -29,7 +29,7 @@ class AccountNavSubItem extends PureComponent<Props> {
|
|||
|
||||
return (
|
||||
<>
|
||||
<CloudFeatureFlag key="feature-flag">
|
||||
<CloudExclude key="feature-flag">
|
||||
{orgs.length > 1 && (
|
||||
<NavMenu.SubItem
|
||||
titleLink={className => (
|
||||
|
@ -50,7 +50,7 @@ class AccountNavSubItem extends PureComponent<Props> {
|
|||
)}
|
||||
active={false}
|
||||
/>
|
||||
</CloudFeatureFlag>
|
||||
</CloudExclude>
|
||||
|
||||
<NavMenu.SubItem
|
||||
titleLink={className => (
|
||||
|
|
|
@ -76,13 +76,12 @@ export const getScrapers = () => async (
|
|||
) => {
|
||||
try {
|
||||
const {
|
||||
orgs: {
|
||||
org: {id},
|
||||
},
|
||||
orgs: {org},
|
||||
} = getState()
|
||||
|
||||
dispatch(setScrapers(RemoteDataState.Loading))
|
||||
|
||||
const scrapers = await client.scrapers.getAll(id)
|
||||
const scrapers = await client.scrapers.getAll(org.id)
|
||||
|
||||
dispatch(setScrapers(RemoteDataState.Done, scrapers))
|
||||
} catch (e) {
|
||||
|
|
|
@ -7,7 +7,7 @@ import {Tabs} from 'src/clockface'
|
|||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import CloudFeatureFlag from 'src/shared/components/CloudFeatureFlag'
|
||||
import CloudExclude from 'src/shared/components/cloud/CloudExclude'
|
||||
|
||||
interface Props {
|
||||
tab: string
|
||||
|
@ -41,14 +41,14 @@ class SettingsNavigation extends PureComponent<Props> {
|
|||
url={`${route}/telegrafs`}
|
||||
active={'telegrafs' === tab}
|
||||
/>
|
||||
<CloudFeatureFlag>
|
||||
<CloudExclude>
|
||||
<Tabs.Tab
|
||||
title="Scrapers"
|
||||
id="scrapers"
|
||||
url={`${route}/scrapers`}
|
||||
active={'scrapers' === tab}
|
||||
/>
|
||||
</CloudFeatureFlag>
|
||||
</CloudExclude>
|
||||
<Tabs.Tab
|
||||
title="Variables"
|
||||
id="variables"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {MeState} from 'src/shared/reducers/v2/me'
|
||||
import {MeState} from 'src/shared/reducers/me'
|
||||
import {client} from 'src/utils/api'
|
||||
|
||||
export enum ActionTypes {
|
|
@ -1,23 +0,0 @@
|
|||
// Libraries
|
||||
import {PureComponent} from 'react'
|
||||
|
||||
// Constants
|
||||
import {CLOUD} from 'src/shared/constants'
|
||||
|
||||
interface Props {
|
||||
name?: string
|
||||
}
|
||||
|
||||
export default class extends PureComponent<Props> {
|
||||
public render() {
|
||||
if (this.isHidden) {
|
||||
return null
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
}
|
||||
|
||||
private get isHidden(): boolean {
|
||||
return CLOUD
|
||||
}
|
||||
}
|
|
@ -1,81 +0,0 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
// Components
|
||||
import {
|
||||
EmptyState,
|
||||
TechnoSpinner,
|
||||
SpinnerContainer,
|
||||
} from '@influxdata/clockface'
|
||||
|
||||
// APIs
|
||||
import {client} from 'src/utils/api'
|
||||
|
||||
// Types
|
||||
import {RemoteDataState} from 'src/types'
|
||||
import {Label} from 'src/types/labels'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface PassedInProps {
|
||||
children: (labels: Label[]) => JSX.Element
|
||||
}
|
||||
|
||||
interface RouterProps extends WithRouterProps {
|
||||
params: {
|
||||
orgID: string
|
||||
}
|
||||
}
|
||||
|
||||
type Props = PassedInProps & RouterProps
|
||||
|
||||
interface State {
|
||||
labels: Label[]
|
||||
loading: RemoteDataState
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class FetchLabels extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
labels: [],
|
||||
loading: RemoteDataState.NotStarted,
|
||||
}
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const {
|
||||
params: {orgID},
|
||||
} = this.props
|
||||
const labels = await client.labels.getAll(orgID)
|
||||
this.setState({
|
||||
loading: RemoteDataState.Done,
|
||||
labels: _.orderBy(labels, ['name']),
|
||||
})
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {loading} = this.state
|
||||
|
||||
if (loading === RemoteDataState.Error) {
|
||||
return (
|
||||
<EmptyState>
|
||||
<EmptyState.Text text="Could not load labels" />
|
||||
</EmptyState>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SpinnerContainer loading={loading} spinnerComponent={<TechnoSpinner />}>
|
||||
{this.props.children(this.state.labels)}
|
||||
</SpinnerContainer>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter<PassedInProps>(FetchLabels)
|
|
@ -1,86 +0,0 @@
|
|||
import {shallow} from 'enzyme'
|
||||
import React from 'react'
|
||||
import SubSections from 'src/shared/components/SubSections'
|
||||
import SubSectionsTab from 'src/shared/components/SubSectionsTab'
|
||||
|
||||
const Guava = () => {
|
||||
return <div />
|
||||
}
|
||||
|
||||
const Mango = () => {
|
||||
return <div />
|
||||
}
|
||||
|
||||
const Pineapple = () => {
|
||||
return <div />
|
||||
}
|
||||
|
||||
const guavaURL = 'guava'
|
||||
const mangoURL = 'mango'
|
||||
const pineappleURL = 'pineapple'
|
||||
|
||||
const defaultProps = {
|
||||
router: {
|
||||
push: () => {},
|
||||
replace: () => {},
|
||||
go: () => {},
|
||||
goBack: () => {},
|
||||
goForward: () => {},
|
||||
setRouteLeaveHook: () => {},
|
||||
isActive: () => {},
|
||||
},
|
||||
sourceID: 'fruitstand',
|
||||
parentUrl: 'fred-the-fruit-guy',
|
||||
activeSection: guavaURL,
|
||||
sections: [
|
||||
{
|
||||
url: guavaURL,
|
||||
name: 'Guava',
|
||||
component: <Guava />,
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
url: mangoURL,
|
||||
name: 'Mango',
|
||||
component: <Mango />,
|
||||
enabled: true,
|
||||
},
|
||||
{
|
||||
url: pineappleURL,
|
||||
name: 'Pineapple',
|
||||
component: <Pineapple />,
|
||||
enabled: false,
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const setup = (override?: {}) => {
|
||||
const props = {
|
||||
...defaultProps,
|
||||
...override,
|
||||
}
|
||||
|
||||
return shallow(<SubSections {...props} />)
|
||||
}
|
||||
|
||||
describe('SubSections', () => {
|
||||
describe('render', () => {
|
||||
it('renders the currently active tab', () => {
|
||||
const wrapper = setup()
|
||||
const content = wrapper.dive().find({'data-testid': 'subsectionContent'})
|
||||
|
||||
expect(content.find(Guava).exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('only renders enabled tabs', () => {
|
||||
const wrapper = setup()
|
||||
const nav = wrapper.dive().find({'data-testid': 'subsectionNav'})
|
||||
|
||||
const tabs = nav.find(SubSectionsTab)
|
||||
|
||||
tabs.forEach(tab => {
|
||||
expect(tab.exists()).toBe(tab.props().section.enabled)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -1,65 +0,0 @@
|
|||
import React, {Component, ReactNode} from 'react'
|
||||
import uuid from 'uuid'
|
||||
import {withRouter, InjectedRouter} from 'react-router'
|
||||
|
||||
import SubSectionsTab from 'src/shared/components/SubSectionsTab'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {PageSection} from 'src/types/shared'
|
||||
|
||||
interface Props {
|
||||
sections: PageSection[]
|
||||
activeSection: string
|
||||
sourceID: string
|
||||
router: InjectedRouter
|
||||
parentUrl: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class SubSections extends Component<Props> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {sections, activeSection} = this.props
|
||||
|
||||
return (
|
||||
<div className="row subsection">
|
||||
<div className="col-md-2 subsection--nav" data-testid="subsectionNav">
|
||||
<div className="subsection--tabs">
|
||||
{sections.map(
|
||||
section =>
|
||||
section.enabled && (
|
||||
<SubSectionsTab
|
||||
key={uuid.v4()}
|
||||
section={section}
|
||||
handleClick={this.handleTabClick(section.url)}
|
||||
activeSection={activeSection}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="col-md-10 subsection--content"
|
||||
data-testid="subsectionContent"
|
||||
>
|
||||
{this.activeSectionComponent}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get activeSectionComponent(): ReactNode {
|
||||
const {sections, activeSection} = this.props
|
||||
const {component} = sections.find(section => section.url === activeSection)
|
||||
return component
|
||||
}
|
||||
|
||||
public handleTabClick = url => () => {
|
||||
const {router, sourceID, parentUrl} = this.props
|
||||
router.push(`/sources/${sourceID}/${parentUrl}/${url}`)
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(SubSections)
|
|
@ -1,25 +0,0 @@
|
|||
import React, {SFC} from 'react'
|
||||
import {PageSection} from 'src/types/shared'
|
||||
|
||||
interface TabProps {
|
||||
handleClick: () => void
|
||||
section: PageSection
|
||||
activeSection: string
|
||||
}
|
||||
|
||||
const SubSectionsTab: SFC<TabProps> = ({
|
||||
handleClick,
|
||||
section,
|
||||
activeSection,
|
||||
}) => (
|
||||
<div
|
||||
className={`subsection--tab ${
|
||||
section.url === activeSection ? 'active' : ''
|
||||
}`}
|
||||
onClick={handleClick}
|
||||
>
|
||||
{section.name}
|
||||
</div>
|
||||
)
|
||||
|
||||
export default SubSectionsTab
|
|
@ -9,7 +9,7 @@ import {SpinnerContainer, TechnoSpinner} from '@influxdata/clockface'
|
|||
import {RemoteDataState} from 'src/types'
|
||||
|
||||
// Actions
|
||||
import {getMe} from 'src/shared/actions/v2/me'
|
||||
import {getMe} from 'src/shared/actions/me'
|
||||
|
||||
// Decorators
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Actions, ActionTypes} from 'src/shared/actions/v2/me'
|
||||
import {Actions, ActionTypes} from 'src/shared/actions/me'
|
||||
|
||||
export interface MeLinks {
|
||||
self: string
|
|
@ -10,7 +10,7 @@ import sharedReducers from 'src/shared/reducers'
|
|||
import persistStateEnhancer from './persistStateEnhancer'
|
||||
|
||||
// v2 reducers
|
||||
import meReducer from 'src/shared/reducers/v2/me'
|
||||
import meReducer from 'src/shared/reducers/me'
|
||||
import tasksReducer from 'src/tasks/reducers'
|
||||
import rangesReducer from 'src/dashboards/reducers/ranges'
|
||||
import {dashboardsReducer} from 'src/dashboards/reducers/dashboards'
|
||||
|
|
|
@ -10,7 +10,6 @@ import {Page} from 'src/pageLayout'
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import GetLabels from 'src/labels/components/GetLabels'
|
||||
import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||
|
||||
// Actions
|
||||
|
@ -106,7 +105,7 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
<Page.Contents fullWidth={false} scrollable={true}>
|
||||
<div className="col-xs-12">
|
||||
<GetResources resource={ResourceTypes.Tasks}>
|
||||
<GetLabels>
|
||||
<GetResources resource={ResourceTypes.Labels}>
|
||||
<FilterList<Task>
|
||||
list={this.filteredTasks}
|
||||
searchTerm={searchTerm}
|
||||
|
@ -133,7 +132,7 @@ class TasksPage extends PureComponent<Props, State> {
|
|||
)}
|
||||
</FilterList>
|
||||
{this.hiddenTaskAlert}
|
||||
</GetLabels>
|
||||
</GetResources>
|
||||
</GetResources>
|
||||
</div>
|
||||
</Page.Contents>
|
||||
|
|
|
@ -11,7 +11,7 @@ import CollectorList from 'src/telegrafs/components/CollectorList'
|
|||
import TelegrafExplainer from 'src/telegrafs/components/TelegrafExplainer'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
import NoBucketsWarning from 'src/buckets/components/NoBucketsWarning'
|
||||
import GetLabels from 'src/labels/components/GetLabels'
|
||||
import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||
|
||||
// Actions
|
||||
import {setBucketInfo} from 'src/dataLoaders/actions/steps'
|
||||
|
@ -102,7 +102,7 @@ class Collectors extends PureComponent<Props, State> {
|
|||
visible={this.hasNoBuckets}
|
||||
resourceName="Telegraf Configurations"
|
||||
/>
|
||||
<GetLabels>
|
||||
<GetResources resource={ResourceTypes.Labels}>
|
||||
<FilterList<Telegraf>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['plugins.0.config.bucket', 'labels[].name']}
|
||||
|
@ -119,7 +119,7 @@ class Collectors extends PureComponent<Props, State> {
|
|||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</GetLabels>
|
||||
</GetResources>
|
||||
</Grid.Column>
|
||||
<Grid.Column
|
||||
widthSM={Columns.Six}
|
||||
|
|
|
@ -9,7 +9,7 @@ import TemplatesHeader from 'src/templates/components/TemplatesHeader'
|
|||
import TemplatesList from 'src/templates/components/TemplatesList'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import SearchWidget from 'src/shared/components/search_widget/SearchWidget'
|
||||
import GetLabels from 'src/labels/components/GetLabels'
|
||||
import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||
|
||||
// Types
|
||||
import {TemplateSummary, AppState} from 'src/types'
|
||||
|
@ -50,7 +50,7 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
isFullPage={false}
|
||||
filterComponent={() => this.filterComponent}
|
||||
/>
|
||||
<GetLabels>
|
||||
<GetResources resource={ResourceTypes.Labels}>
|
||||
<FilterList<TemplateSummary>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['meta.name', 'labels[].name']}
|
||||
|
@ -65,7 +65,7 @@ class TemplatesPage extends PureComponent<Props, State> {
|
|||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</GetLabels>
|
||||
</GetResources>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import {TimeMachinesState} from 'src/timeMachine/reducers'
|
|||
import {AppState as AppPresentationState} from 'src/shared/reducers/app'
|
||||
import {TasksState} from 'src/tasks/reducers'
|
||||
import {RouterState} from 'react-router-redux'
|
||||
import {MeState} from 'src/shared/reducers/v2/me'
|
||||
import {MeState} from 'src/shared/reducers/me'
|
||||
import {NoteEditorState} from 'src/dashboards/reducers/notes'
|
||||
import {DataLoadingState} from 'src/dataLoaders/reducers'
|
||||
import {OnboardingState} from 'src/onboarding/reducers'
|
||||
|
|
|
@ -14,7 +14,7 @@ import TabbedPageHeader from 'src/shared/components/tabbed_page/TabbedPageHeader
|
|||
import VariableList from 'src/variables/components/VariableList'
|
||||
import FilterList from 'src/shared/components/Filter'
|
||||
import AddResourceDropdown from 'src/shared/components/AddResourceDropdown'
|
||||
import GetLabels from 'src/labels/components/GetLabels'
|
||||
import GetResources, {ResourceTypes} from 'src/shared/components/GetResources'
|
||||
|
||||
// Types
|
||||
import {OverlayState} from 'src/types'
|
||||
|
@ -65,7 +65,7 @@ class VariablesTab extends PureComponent<Props, State> {
|
|||
onSelectNew={this.handleOpenCreateOverlay}
|
||||
/>
|
||||
</TabbedPageHeader>
|
||||
<GetLabels>
|
||||
<GetResources resource={ResourceTypes.Labels}>
|
||||
<FilterList<Variable>
|
||||
searchTerm={searchTerm}
|
||||
searchKeys={['name', 'labels[].name']}
|
||||
|
@ -82,7 +82,7 @@ class VariablesTab extends PureComponent<Props, State> {
|
|||
/>
|
||||
)}
|
||||
</FilterList>
|
||||
</GetLabels>
|
||||
</GetResources>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue