Move tags, fields, and measurements under category labels
parent
093a5af033
commit
3ef3dfab11
|
@ -1,16 +1,14 @@
|
||||||
import React, {PureComponent, ChangeEvent, MouseEvent} from 'react'
|
import React, {PureComponent} from 'react'
|
||||||
import classnames from 'classnames'
|
import classnames from 'classnames'
|
||||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||||
|
|
||||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
|
||||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
|
||||||
import TagList from 'src/flux/components/TagList'
|
|
||||||
import {
|
import {
|
||||||
notifyCopyToClipboardSuccess,
|
notifyCopyToClipboardSuccess,
|
||||||
notifyCopyToClipboardFailed,
|
notifyCopyToClipboardFailed,
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {Source, NotificationAction} from 'src/types'
|
import {Source, NotificationAction} from 'src/types'
|
||||||
|
import SchemaItemCategories from 'src/flux/components/SchemaItemCategories'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
db: string
|
db: string
|
||||||
|
@ -20,7 +18,6 @@ interface Props {
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isOpen: boolean
|
isOpen: boolean
|
||||||
tags: string[]
|
|
||||||
searchTerm: string
|
searchTerm: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,23 +26,10 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
||||||
super(props)
|
super(props)
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: false,
|
isOpen: false,
|
||||||
tags: [],
|
|
||||||
searchTerm: '',
|
searchTerm: '',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidMount() {
|
|
||||||
const {db, source} = this.props
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetchTagKeys(source, db, [])
|
|
||||||
const tags = parseValuesColumn(response)
|
|
||||||
this.setState({tags})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {db} = this.props
|
const {db} = this.props
|
||||||
|
|
||||||
|
@ -64,15 +48,20 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
{this.filterAndTagList}
|
{this.categories}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get tags(): string[] {
|
private get categories(): JSX.Element {
|
||||||
const {tags, searchTerm} = this.state
|
const {db, source, notify} = this.props
|
||||||
const term = searchTerm.toLocaleLowerCase()
|
const {isOpen} = this.state
|
||||||
return tags.filter(t => t.toLocaleLowerCase().includes(term))
|
|
||||||
|
return (
|
||||||
|
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
||||||
|
<SchemaItemCategories db={db} source={source} notify={notify} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get className(): string {
|
private get className(): string {
|
||||||
|
@ -81,35 +70,6 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private get filterAndTagList(): JSX.Element {
|
|
||||||
const {db, source, notify} = this.props
|
|
||||||
const {isOpen, searchTerm} = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
|
||||||
<div className="flux-schema--filter">
|
|
||||||
<input
|
|
||||||
className="form-control input-xs"
|
|
||||||
placeholder={`Filter within ${db}`}
|
|
||||||
type="text"
|
|
||||||
spellCheck={false}
|
|
||||||
autoComplete="off"
|
|
||||||
value={searchTerm}
|
|
||||||
onClick={this.handleInputClick}
|
|
||||||
onChange={this.onSearch}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<TagList
|
|
||||||
db={db}
|
|
||||||
source={source}
|
|
||||||
tags={this.tags}
|
|
||||||
filter={[]}
|
|
||||||
notify={notify}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClickCopy = e => {
|
private handleClickCopy = e => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
@ -126,19 +86,9 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({
|
|
||||||
searchTerm: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick = () => {
|
private handleClick = () => {
|
||||||
this.setState({isOpen: !this.state.isOpen})
|
this.setState({isOpen: !this.state.isOpen})
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleInputClick = (e: MouseEvent<HTMLInputElement>) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default DatabaseListItem
|
export default DatabaseListItem
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent, ChangeEvent, MouseEvent} from 'react'
|
||||||
|
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||||
|
|
||||||
|
// APIS
|
||||||
|
import {fields as fetchFields} from 'src/shared/apis/flux/metaQueries'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||||
|
import {
|
||||||
|
notifyCopyToClipboardSuccess,
|
||||||
|
notifyCopyToClipboardFailed,
|
||||||
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction, RemoteDataState} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
db: string
|
||||||
|
source: Source
|
||||||
|
tag?: {key: string; value: string}
|
||||||
|
measurement?: string
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
fields: string[]
|
||||||
|
searchTerm: string
|
||||||
|
loading: RemoteDataState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class FieldList extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fields: [],
|
||||||
|
searchTerm: '',
|
||||||
|
loading: RemoteDataState.NotStarted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
this.setState({loading: RemoteDataState.Loading})
|
||||||
|
try {
|
||||||
|
const fields = await this.fetchFields()
|
||||||
|
this.setState({fields, loading: RemoteDataState.Done})
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({loading: RemoteDataState.Error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {tag} = this.props
|
||||||
|
const {searchTerm} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flux-schema--filter">
|
||||||
|
<input
|
||||||
|
className="form-control input-xs"
|
||||||
|
placeholder={`Filter within ${(tag && tag.value) || 'Fields'}`}
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete="off"
|
||||||
|
value={searchTerm}
|
||||||
|
onClick={this.handleInputClick}
|
||||||
|
onChange={this.onSearch}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{this.fields}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get fields(): JSX.Element | JSX.Element[] {
|
||||||
|
const {searchTerm, loading} = this.state
|
||||||
|
|
||||||
|
if (loading === RemoteDataState.Loading) {
|
||||||
|
return <LoaderSkeleton />
|
||||||
|
}
|
||||||
|
|
||||||
|
const term = searchTerm.toLocaleLowerCase()
|
||||||
|
const fields = this.state.fields.filter(f =>
|
||||||
|
f.toLocaleLowerCase().includes(term)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (fields.length) {
|
||||||
|
return fields.map(field => (
|
||||||
|
<div
|
||||||
|
className="flux-schema-tree flux-schema--child"
|
||||||
|
key={field}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
>
|
||||||
|
<div className="flux-schema--item">
|
||||||
|
<div className="flex-schema-item-group">
|
||||||
|
{field}
|
||||||
|
<span className="flux-schema--type">Field</span>
|
||||||
|
</div>
|
||||||
|
<CopyToClipboard text={field} onCopy={this.handleCopyAttempt}>
|
||||||
|
<div className="flux-schema-copy" onClick={this.handleClick}>
|
||||||
|
<span className="icon duplicate" title="copy to clipboard" />
|
||||||
|
Copy
|
||||||
|
</div>
|
||||||
|
</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flux-schema-tree flux-schema--child">
|
||||||
|
<div className="flux-schema--item no-hover" onClick={this.handleClick}>
|
||||||
|
<div className="no-results">No more fields.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchFields(): Promise<string[]> {
|
||||||
|
const {source, db} = this.props
|
||||||
|
|
||||||
|
const filter = this.filters
|
||||||
|
const limit = 50
|
||||||
|
|
||||||
|
const response = await fetchFields(source, db, filter, limit)
|
||||||
|
const fields = parseValuesColumn(response)
|
||||||
|
return fields
|
||||||
|
}
|
||||||
|
|
||||||
|
private get filters(): Array<{value: string; key: string}> {
|
||||||
|
const {tag, measurement} = this.props
|
||||||
|
const filters = []
|
||||||
|
if (tag) {
|
||||||
|
filters.push(tag)
|
||||||
|
}
|
||||||
|
if (measurement) {
|
||||||
|
filters.push({key: '_measurement', value: measurement})
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = (e): void => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCopyAttempt = (
|
||||||
|
copiedText: string,
|
||||||
|
isSuccessful: boolean
|
||||||
|
): void => {
|
||||||
|
const {notify} = this.props
|
||||||
|
if (isSuccessful) {
|
||||||
|
notify(notifyCopyToClipboardSuccess(copiedText))
|
||||||
|
} else {
|
||||||
|
notify(notifyCopyToClipboardFailed(copiedText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({
|
||||||
|
searchTerm: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleInputClick = (e: MouseEvent<HTMLInputElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FieldList
|
|
@ -0,0 +1,125 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent, MouseEvent, ChangeEvent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import MeasurementListItem from 'src/flux/components/MeasurementListItem'
|
||||||
|
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||||
|
|
||||||
|
// apis
|
||||||
|
import {measurements as fetchMeasurements} from 'src/shared/apis/flux/metaQueries'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction, RemoteDataState} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
db: string
|
||||||
|
source: Source
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
searchTerm: string
|
||||||
|
measurements: string[]
|
||||||
|
loading: RemoteDataState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class TagValueList extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
measurements: [],
|
||||||
|
searchTerm: '',
|
||||||
|
loading: RemoteDataState.NotStarted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
this.setState({loading: RemoteDataState.Loading})
|
||||||
|
try {
|
||||||
|
const measurements = await this.fetchMeasurements()
|
||||||
|
this.setState({measurements, loading: RemoteDataState.Done})
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({loading: RemoteDataState.Error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {searchTerm} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flux-schema--filter">
|
||||||
|
<input
|
||||||
|
className="form-control input-xs"
|
||||||
|
placeholder="Filter within Measurements"
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete="off"
|
||||||
|
value={searchTerm}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
onChange={this.onSearch}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{this.measurements}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get measurements(): JSX.Element | JSX.Element[] {
|
||||||
|
const {source, db, notify} = this.props
|
||||||
|
const {searchTerm, loading} = this.state
|
||||||
|
|
||||||
|
if (loading === RemoteDataState.Loading) {
|
||||||
|
return <LoaderSkeleton />
|
||||||
|
}
|
||||||
|
const term = searchTerm.toLocaleLowerCase()
|
||||||
|
const measurements = this.state.measurements.filter(m =>
|
||||||
|
m.toLocaleLowerCase().includes(term)
|
||||||
|
)
|
||||||
|
if (measurements.length) {
|
||||||
|
return measurements.map(measurement => (
|
||||||
|
<MeasurementListItem
|
||||||
|
source={source}
|
||||||
|
db={db}
|
||||||
|
searchTerm={searchTerm}
|
||||||
|
measurement={measurement}
|
||||||
|
key={measurement}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flux-schema-tree flux-schema--child">
|
||||||
|
<div className="flux-schema--item no-hover" onClick={this.handleClick}>
|
||||||
|
<div className="no-results">No more measurements.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchMeasurements(): Promise<string[]> {
|
||||||
|
const {source, db} = this.props
|
||||||
|
|
||||||
|
const response = await fetchMeasurements(source, db)
|
||||||
|
const measurements = parseValuesColumn(response)
|
||||||
|
return measurements
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({
|
||||||
|
searchTerm: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = (e: MouseEvent<HTMLInputElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagValueList
|
|
@ -0,0 +1,113 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
|
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import TagKeyList from 'src/flux/components/TagKeyList'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {
|
||||||
|
notifyCopyToClipboardSuccess,
|
||||||
|
notifyCopyToClipboardFailed,
|
||||||
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {OpenState} from 'src/flux/constants/explorer'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
db: string
|
||||||
|
source: Source
|
||||||
|
searchTerm: string
|
||||||
|
measurement: string
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
opened: OpenState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class MeasurementListItem extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
opened: OpenState.UNOPENED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {db, source, measurement, notify} = this.props
|
||||||
|
const {opened} = this.state
|
||||||
|
const isOpen = opened === OpenState.OPENED
|
||||||
|
const isUnopen = opened === OpenState.UNOPENED
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flux-schema-tree flux-schema--child ${
|
||||||
|
isOpen ? 'expanded' : ''
|
||||||
|
}`}
|
||||||
|
key={measurement}
|
||||||
|
onClick={this.handleItemClick}
|
||||||
|
>
|
||||||
|
<div className="flux-schema--item">
|
||||||
|
<div className="flex-schema-item-group">
|
||||||
|
<div className="flux-schema--expander" />
|
||||||
|
{measurement}
|
||||||
|
<span className="flux-schema--type">Measurement</span>
|
||||||
|
</div>
|
||||||
|
<CopyToClipboard text={measurement} onCopy={this.handleCopyAttempt}>
|
||||||
|
<div className="flux-schema-copy" onClick={this.handleClickCopy}>
|
||||||
|
<span className="icon duplicate" title="copy to clipboard" />
|
||||||
|
Copy
|
||||||
|
</div>
|
||||||
|
</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
{!isUnopen && (
|
||||||
|
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
||||||
|
<TagKeyList
|
||||||
|
db={db}
|
||||||
|
source={source}
|
||||||
|
notify={notify}
|
||||||
|
measurement={measurement}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClickCopy = (e): void => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCopyAttempt = (
|
||||||
|
copiedText: string,
|
||||||
|
isSuccessful: boolean
|
||||||
|
): void => {
|
||||||
|
const {notify} = this.props
|
||||||
|
if (isSuccessful) {
|
||||||
|
notify(notifyCopyToClipboardSuccess(copiedText))
|
||||||
|
} else {
|
||||||
|
notify(notifyCopyToClipboardFailed(copiedText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleItemClick = (e): void => {
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const opened = this.state.opened
|
||||||
|
|
||||||
|
if (opened === OpenState.OPENED) {
|
||||||
|
this.setState({opened: OpenState.ClOSED})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({opened: OpenState.OPENED})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MeasurementListItem
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import SchemaItemCategory, {
|
||||||
|
CategoryType,
|
||||||
|
} from 'src/flux/components/SchemaItemCategory'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {Source, NotificationAction} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
source: Source
|
||||||
|
db: string
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class SchemaItemCategories extends PureComponent<Props> {
|
||||||
|
public render() {
|
||||||
|
const {source, db, notify} = this.props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<SchemaItemCategory
|
||||||
|
source={source}
|
||||||
|
db={db}
|
||||||
|
type={CategoryType.Measurements}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
<SchemaItemCategory
|
||||||
|
source={source}
|
||||||
|
db={db}
|
||||||
|
type={CategoryType.Tags}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
<SchemaItemCategory
|
||||||
|
source={source}
|
||||||
|
db={db}
|
||||||
|
type={CategoryType.Fields}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SchemaItemCategories
|
|
@ -0,0 +1,106 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import MeasurementList from 'src/flux/components/MeasurementList'
|
||||||
|
import FieldList from 'src/flux/components/FieldList'
|
||||||
|
import TagKeyList from 'src/flux/components/TagKeyList'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {OpenState} from 'src/flux/constants/explorer'
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import {Source, NotificationAction} from 'src/types'
|
||||||
|
|
||||||
|
export enum CategoryType {
|
||||||
|
Measurements = 'measurements',
|
||||||
|
Fields = 'fields',
|
||||||
|
Tags = 'tags',
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
source: Source
|
||||||
|
notify: NotificationAction
|
||||||
|
db: string
|
||||||
|
type: CategoryType
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
opened: OpenState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class SchemaItemCategory extends PureComponent<Props, State> {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
opened: OpenState.UNOPENED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {opened} = this.state
|
||||||
|
const isOpen = opened === OpenState.OPENED
|
||||||
|
const isUnopened = opened === OpenState.UNOPENED
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flux-schema-tree flux-schema--child ${
|
||||||
|
isOpen ? 'expanded' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<div className="flux-schema--item" onClick={this.handleClick}>
|
||||||
|
<div className="flex-schema-item-group">
|
||||||
|
<div className="flux-schema--expander" />
|
||||||
|
{this.categoryName}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!isUnopened && (
|
||||||
|
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
||||||
|
{this.itemList}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get categoryName(): string {
|
||||||
|
switch (this.props.type) {
|
||||||
|
case CategoryType.Measurements:
|
||||||
|
return 'MEASURMENTS'
|
||||||
|
case CategoryType.Fields:
|
||||||
|
return 'FIELDS'
|
||||||
|
case CategoryType.Tags:
|
||||||
|
return 'TAGS'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private get itemList(): JSX.Element {
|
||||||
|
const {type, db, source, notify} = this.props
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case CategoryType.Measurements:
|
||||||
|
return <MeasurementList db={db} source={source} notify={notify} />
|
||||||
|
|
||||||
|
case CategoryType.Fields:
|
||||||
|
return <FieldList db={db} source={source} notify={notify} />
|
||||||
|
case CategoryType.Tags:
|
||||||
|
return <TagKeyList db={db} source={source} notify={notify} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = e => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const opened = this.state.opened
|
||||||
|
|
||||||
|
if (opened === OpenState.OPENED) {
|
||||||
|
this.setState({opened: OpenState.ClOSED})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({opened: OpenState.OPENED})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SchemaItemCategory
|
|
@ -0,0 +1,134 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent, ChangeEvent, MouseEvent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import TagKeyListItem from 'src/flux/components/TagKeyListItem'
|
||||||
|
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||||
|
|
||||||
|
// apis
|
||||||
|
import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction, RemoteDataState} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
db: string
|
||||||
|
measurement?: string
|
||||||
|
source: Source
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
tagKeys: string[]
|
||||||
|
searchTerm: string
|
||||||
|
loading: RemoteDataState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class TagKeyList extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
tagKeys: [],
|
||||||
|
searchTerm: '',
|
||||||
|
loading: RemoteDataState.NotStarted,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
this.setState({loading: RemoteDataState.Loading})
|
||||||
|
try {
|
||||||
|
const tagKeys = await this.fetchTagKeys()
|
||||||
|
this.setState({tagKeys, loading: RemoteDataState.Done})
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({loading: RemoteDataState.Error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {measurement} = this.props
|
||||||
|
const {searchTerm} = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="flux-schema--filter">
|
||||||
|
<input
|
||||||
|
className="form-control input-xs"
|
||||||
|
placeholder={`Filter within ${measurement || 'Tags'}`}
|
||||||
|
type="text"
|
||||||
|
spellCheck={false}
|
||||||
|
autoComplete="off"
|
||||||
|
value={searchTerm}
|
||||||
|
onClick={this.handleClick}
|
||||||
|
onChange={this.onSearch}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{this.tagKeys}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get tagKeys(): JSX.Element | JSX.Element[] {
|
||||||
|
const {db, source, notify, measurement} = this.props
|
||||||
|
const {searchTerm, loading} = this.state
|
||||||
|
|
||||||
|
if (loading === RemoteDataState.Loading) {
|
||||||
|
return <LoaderSkeleton />
|
||||||
|
}
|
||||||
|
|
||||||
|
const excludedTagKeys = ['_measurement', '_field']
|
||||||
|
const term = searchTerm.toLocaleLowerCase()
|
||||||
|
const tagKeys = this.state.tagKeys.filter(
|
||||||
|
tk =>
|
||||||
|
!excludedTagKeys.includes(tk) && tk.toLocaleLowerCase().includes(term)
|
||||||
|
)
|
||||||
|
if (tagKeys.length) {
|
||||||
|
return tagKeys.map(tagKey => (
|
||||||
|
<TagKeyListItem
|
||||||
|
db={db}
|
||||||
|
source={source}
|
||||||
|
searchTerm={searchTerm}
|
||||||
|
tagKey={tagKey}
|
||||||
|
measurement={measurement}
|
||||||
|
key={tagKey}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flux-schema-tree flux-schema--child">
|
||||||
|
<div className="flux-schema--item no-hover" onClick={this.handleClick}>
|
||||||
|
<div className="no-results">No more tag keys.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchTagKeys(): Promise<string[]> {
|
||||||
|
const {source, db, measurement} = this.props
|
||||||
|
const filter = measurement
|
||||||
|
? [{key: '_measurement', value: measurement}]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const response = await fetchTagKeys(source, db, filter)
|
||||||
|
const tagKeys = parseValuesColumn(response)
|
||||||
|
return tagKeys
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({
|
||||||
|
searchTerm: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = (e: MouseEvent<HTMLInputElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagKeyList
|
|
@ -0,0 +1,115 @@
|
||||||
|
// Libraries
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
|
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||||
|
|
||||||
|
// Components
|
||||||
|
import TagValueList from 'src/flux/components/TagValueList'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import {
|
||||||
|
notifyCopyToClipboardSuccess,
|
||||||
|
notifyCopyToClipboardFailed,
|
||||||
|
} from 'src/shared/copy/notifications'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// Constants
|
||||||
|
import {OpenState} from 'src/flux/constants/explorer'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction} from 'src/types'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
db: string
|
||||||
|
source: Source
|
||||||
|
searchTerm: string
|
||||||
|
tagKey: string
|
||||||
|
measurement?: string
|
||||||
|
notify: NotificationAction
|
||||||
|
}
|
||||||
|
|
||||||
|
interface State {
|
||||||
|
opened: OpenState
|
||||||
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class TagKeyListItem extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
opened: OpenState.UNOPENED,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {db, source, tagKey, notify, measurement} = this.props
|
||||||
|
const {opened} = this.state
|
||||||
|
const isOpen = opened === OpenState.OPENED
|
||||||
|
const isUnopen = opened === OpenState.UNOPENED
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flux-schema-tree flux-schema--child ${
|
||||||
|
isOpen ? 'expanded' : ''
|
||||||
|
}`}
|
||||||
|
key={tagKey}
|
||||||
|
onClick={this.handleItemClick}
|
||||||
|
>
|
||||||
|
<div className="flux-schema--item">
|
||||||
|
<div className="flex-schema-item-group">
|
||||||
|
<div className="flux-schema--expander" />
|
||||||
|
{tagKey}
|
||||||
|
<span className="flux-schema--type">Tag Key</span>
|
||||||
|
</div>
|
||||||
|
<CopyToClipboard text={tagKey} onCopy={this.handleCopyAttempt}>
|
||||||
|
<div className="flux-schema-copy" onClick={this.handleClickCopy}>
|
||||||
|
<span className="icon duplicate" title="copy to clipboard" />
|
||||||
|
Copy
|
||||||
|
</div>
|
||||||
|
</CopyToClipboard>
|
||||||
|
</div>
|
||||||
|
{!isUnopen && (
|
||||||
|
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
||||||
|
<TagValueList
|
||||||
|
db={db}
|
||||||
|
source={source}
|
||||||
|
tagKey={tagKey}
|
||||||
|
measurement={measurement}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClickCopy = (e): void => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleCopyAttempt = (
|
||||||
|
copiedText: string,
|
||||||
|
isSuccessful: boolean
|
||||||
|
): void => {
|
||||||
|
const {notify} = this.props
|
||||||
|
if (isSuccessful) {
|
||||||
|
notify(notifyCopyToClipboardSuccess(copiedText))
|
||||||
|
} else {
|
||||||
|
notify(notifyCopyToClipboardFailed(copiedText))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleItemClick = (e): void => {
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
const opened = this.state.opened
|
||||||
|
|
||||||
|
if (opened === OpenState.OPENED) {
|
||||||
|
this.setState({opened: OpenState.ClOSED})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({opened: OpenState.OPENED})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TagKeyListItem
|
|
@ -1,48 +0,0 @@
|
||||||
import React, {PureComponent, MouseEvent} from 'react'
|
|
||||||
|
|
||||||
import TagListItem from 'src/flux/components/TagListItem'
|
|
||||||
|
|
||||||
import {SchemaFilter, Source, NotificationAction} from 'src/types'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
db: string
|
|
||||||
source: Source
|
|
||||||
tags: string[]
|
|
||||||
filter: SchemaFilter[]
|
|
||||||
notify: NotificationAction
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TagList extends PureComponent<Props> {
|
|
||||||
public render() {
|
|
||||||
const {db, source, tags, filter, notify} = this.props
|
|
||||||
|
|
||||||
if (tags.length) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{tags.map(t => (
|
|
||||||
<TagListItem
|
|
||||||
db={db}
|
|
||||||
key={t}
|
|
||||||
tagKey={t}
|
|
||||||
source={source}
|
|
||||||
filter={filter}
|
|
||||||
notify={notify}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flux-schema-tree flux-schema--child">
|
|
||||||
<div className="flux-schema--item no-hover" onClick={this.handleClick}>
|
|
||||||
<div className="no-results">No more tag keys.</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick(e: MouseEvent<HTMLDivElement>) {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,327 +0,0 @@
|
||||||
import React, {
|
|
||||||
PureComponent,
|
|
||||||
CSSProperties,
|
|
||||||
ChangeEvent,
|
|
||||||
MouseEvent,
|
|
||||||
} from 'react'
|
|
||||||
|
|
||||||
import _ from 'lodash'
|
|
||||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
|
||||||
|
|
||||||
import {Source, SchemaFilter, RemoteDataState} from 'src/types'
|
|
||||||
import {tagValues as fetchTagValues} from 'src/shared/apis/flux/metaQueries'
|
|
||||||
import {explorer} from 'src/flux/constants'
|
|
||||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
|
||||||
import TagValueList from 'src/flux/components/TagValueList'
|
|
||||||
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
|
||||||
import LoadingSpinner from 'src/flux/components/LoadingSpinner'
|
|
||||||
import {
|
|
||||||
notifyCopyToClipboardSuccess,
|
|
||||||
notifyCopyToClipboardFailed,
|
|
||||||
} from 'src/shared/copy/notifications'
|
|
||||||
|
|
||||||
import {NotificationAction} from 'src/types'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
tagKey: string
|
|
||||||
db: string
|
|
||||||
source: Source
|
|
||||||
filter: SchemaFilter[]
|
|
||||||
notify: NotificationAction
|
|
||||||
}
|
|
||||||
|
|
||||||
interface State {
|
|
||||||
isOpen: boolean
|
|
||||||
loadingAll: RemoteDataState
|
|
||||||
loadingSearch: RemoteDataState
|
|
||||||
loadingMore: RemoteDataState
|
|
||||||
tagValues: string[]
|
|
||||||
searchTerm: string
|
|
||||||
limit: number
|
|
||||||
count: number | null
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class TagListItem extends PureComponent<Props, State> {
|
|
||||||
private debouncedOnSearch: () => void
|
|
||||||
|
|
||||||
constructor(props) {
|
|
||||||
super(props)
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
isOpen: false,
|
|
||||||
loadingAll: RemoteDataState.NotStarted,
|
|
||||||
loadingSearch: RemoteDataState.NotStarted,
|
|
||||||
loadingMore: RemoteDataState.NotStarted,
|
|
||||||
tagValues: [],
|
|
||||||
count: null,
|
|
||||||
searchTerm: '',
|
|
||||||
limit: explorer.TAG_VALUES_LIMIT,
|
|
||||||
}
|
|
||||||
|
|
||||||
this.debouncedOnSearch = _.debounce(() => {
|
|
||||||
this.searchTagValues()
|
|
||||||
this.getCount()
|
|
||||||
}, 250)
|
|
||||||
}
|
|
||||||
|
|
||||||
public render() {
|
|
||||||
const {tagKey, db, source, filter, notify} = this.props
|
|
||||||
const {
|
|
||||||
tagValues,
|
|
||||||
searchTerm,
|
|
||||||
loadingMore,
|
|
||||||
count,
|
|
||||||
limit,
|
|
||||||
isOpen,
|
|
||||||
} = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={this.className}>
|
|
||||||
<div className="flux-schema--item" onClick={this.handleClick}>
|
|
||||||
<div className="flex-schema-item-group">
|
|
||||||
<div className="flux-schema--expander" />
|
|
||||||
{tagKey}
|
|
||||||
<span className="flux-schema--type">Tag Key</span>
|
|
||||||
</div>
|
|
||||||
<CopyToClipboard text={tagKey} onCopy={this.handleCopyAttempt}>
|
|
||||||
<div className="flux-schema-copy" onClick={this.handleClickCopy}>
|
|
||||||
<span className="icon duplicate" title="copy to clipboard" />
|
|
||||||
Copy
|
|
||||||
</div>
|
|
||||||
</CopyToClipboard>
|
|
||||||
</div>
|
|
||||||
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
|
||||||
<div className="flux-schema--header" onClick={this.handleInputClick}>
|
|
||||||
<div className="flux-schema--filter">
|
|
||||||
<input
|
|
||||||
className="form-control input-xs"
|
|
||||||
placeholder={`Filter within ${tagKey}`}
|
|
||||||
type="text"
|
|
||||||
spellCheck={false}
|
|
||||||
autoComplete="off"
|
|
||||||
value={searchTerm}
|
|
||||||
onChange={this.onSearch}
|
|
||||||
/>
|
|
||||||
{this.isSearching && <LoadingSpinner style={this.spinnerStyle} />}
|
|
||||||
</div>
|
|
||||||
{this.count}
|
|
||||||
</div>
|
|
||||||
{this.isLoading && <LoaderSkeleton />}
|
|
||||||
{!this.isLoading && (
|
|
||||||
<TagValueList
|
|
||||||
db={db}
|
|
||||||
notify={notify}
|
|
||||||
source={source}
|
|
||||||
values={tagValues}
|
|
||||||
tagKey={tagKey}
|
|
||||||
filter={filter}
|
|
||||||
onLoadMoreValues={this.handleLoadMoreValues}
|
|
||||||
isLoadingMoreValues={loadingMore === RemoteDataState.Loading}
|
|
||||||
shouldShowMoreValues={limit < count}
|
|
||||||
loadMoreCount={this.loadMoreCount}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private get count(): JSX.Element {
|
|
||||||
const {count} = this.state
|
|
||||||
|
|
||||||
if (!count) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let pluralizer = 's'
|
|
||||||
|
|
||||||
if (count === 1) {
|
|
||||||
pluralizer = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flux-schema--count">{`${count} Tag Value${pluralizer}`}</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private get spinnerStyle(): CSSProperties {
|
|
||||||
return {
|
|
||||||
position: 'absolute',
|
|
||||||
right: '18px',
|
|
||||||
top: '11px',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isSearching(): boolean {
|
|
||||||
return this.state.loadingSearch === RemoteDataState.Loading
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isLoading(): boolean {
|
|
||||||
return this.state.loadingAll === RemoteDataState.Loading
|
|
||||||
}
|
|
||||||
|
|
||||||
private onSearch = (e: ChangeEvent<HTMLInputElement>): void => {
|
|
||||||
const searchTerm = e.target.value
|
|
||||||
|
|
||||||
this.setState({searchTerm, loadingSearch: RemoteDataState.Loading}, () =>
|
|
||||||
this.debouncedOnSearch()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleInputClick = (e: MouseEvent<HTMLDivElement>): void => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
private searchTagValues = async () => {
|
|
||||||
try {
|
|
||||||
const tagValues = await this.getTagValues()
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tagValues,
|
|
||||||
loadingSearch: RemoteDataState.Done,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
this.setState({loadingSearch: RemoteDataState.Error})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getAllTagValues = async () => {
|
|
||||||
this.setState({loadingAll: RemoteDataState.Loading})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tagValues = await this.getTagValues()
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tagValues,
|
|
||||||
loadingAll: RemoteDataState.Done,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
this.setState({loadingAll: RemoteDataState.Error})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMoreTagValues = async () => {
|
|
||||||
this.setState({loadingMore: RemoteDataState.Loading})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const tagValues = await this.getTagValues()
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
tagValues,
|
|
||||||
loadingMore: RemoteDataState.Done,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
this.setState({loadingMore: RemoteDataState.Error})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTagValues = async () => {
|
|
||||||
const {db, source, tagKey, filter} = this.props
|
|
||||||
const {searchTerm, limit} = this.state
|
|
||||||
const response = await fetchTagValues({
|
|
||||||
source,
|
|
||||||
bucket: db,
|
|
||||||
filter,
|
|
||||||
tagKey,
|
|
||||||
limit,
|
|
||||||
searchTerm,
|
|
||||||
})
|
|
||||||
const tagValues = parseValuesColumn(response)
|
|
||||||
return tagValues
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
|
|
||||||
if (this.isFetchable) {
|
|
||||||
this.getCount()
|
|
||||||
this.getAllTagValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({isOpen: !this.state.isOpen})
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleLoadMoreValues = (): void => {
|
|
||||||
const {limit} = this.state
|
|
||||||
|
|
||||||
this.setState(
|
|
||||||
{limit: limit + explorer.TAG_VALUES_LIMIT},
|
|
||||||
this.getMoreTagValues
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClickCopy = e => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleCopyAttempt = (
|
|
||||||
copiedText: string,
|
|
||||||
isSuccessful: boolean
|
|
||||||
): void => {
|
|
||||||
const {notify} = this.props
|
|
||||||
if (isSuccessful) {
|
|
||||||
notify(notifyCopyToClipboardSuccess(copiedText))
|
|
||||||
} else {
|
|
||||||
notify(notifyCopyToClipboardFailed(copiedText))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getCount() {
|
|
||||||
const {source, db, filter, tagKey} = this.props
|
|
||||||
const {limit, searchTerm} = this.state
|
|
||||||
try {
|
|
||||||
const response = await fetchTagValues({
|
|
||||||
source,
|
|
||||||
bucket: db,
|
|
||||||
filter,
|
|
||||||
tagKey,
|
|
||||||
limit,
|
|
||||||
searchTerm,
|
|
||||||
count: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
const parsed = parseValuesColumn(response)
|
|
||||||
|
|
||||||
if (parsed.length !== 1) {
|
|
||||||
// We expect to never reach this state; instead, the Flux server should
|
|
||||||
// return a non-200 status code is handled earlier (after fetching).
|
|
||||||
// This return guards against some unexpected behavior---the Flux server
|
|
||||||
// returning a 200 status code but ALSO having an error in the CSV
|
|
||||||
// response body
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = Number(parsed[0])
|
|
||||||
|
|
||||||
this.setState({count})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get loadMoreCount(): number {
|
|
||||||
const {count, limit} = this.state
|
|
||||||
|
|
||||||
return Math.min(Math.abs(count - limit), explorer.TAG_VALUES_LIMIT)
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isFetchable(): boolean {
|
|
||||||
const {isOpen, loadingAll} = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isOpen &&
|
|
||||||
(loadingAll === RemoteDataState.NotStarted ||
|
|
||||||
loadingAll === RemoteDataState.Error)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private get className(): string {
|
|
||||||
const {isOpen} = this.state
|
|
||||||
const openClass = isOpen ? 'expanded' : ''
|
|
||||||
|
|
||||||
return `flux-schema-tree flux-schema--child ${openClass}`
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,76 +1,142 @@
|
||||||
import React, {PureComponent, MouseEvent} from 'react'
|
// Libraries
|
||||||
|
import React, {PureComponent, ChangeEvent, MouseEvent} from 'react'
|
||||||
|
|
||||||
|
// Components
|
||||||
import TagValueListItem from 'src/flux/components/TagValueListItem'
|
import TagValueListItem from 'src/flux/components/TagValueListItem'
|
||||||
import LoadingSpinner from 'src/flux/components/LoadingSpinner'
|
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||||
|
|
||||||
import {Source, SchemaFilter, NotificationAction} from 'src/types'
|
// apis
|
||||||
|
import {tagValues as fetchTagValues} from 'src/shared/apis/flux/metaQueries'
|
||||||
|
|
||||||
|
// Utils
|
||||||
|
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||||
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
|
// types
|
||||||
|
import {Source, NotificationAction, RemoteDataState} from 'src/types'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
db: string
|
db: string
|
||||||
tagKey: string
|
|
||||||
values: string[]
|
|
||||||
source: Source
|
source: Source
|
||||||
loadMoreCount: number
|
tagKey: string
|
||||||
filter: SchemaFilter[]
|
|
||||||
notify: NotificationAction
|
notify: NotificationAction
|
||||||
isLoadingMoreValues: boolean
|
measurement: string
|
||||||
onLoadMoreValues: () => void
|
|
||||||
shouldShowMoreValues: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class TagValueList extends PureComponent<Props> {
|
interface State {
|
||||||
public render() {
|
tagValues: string[]
|
||||||
const {
|
searchTerm: string
|
||||||
db,
|
loading: RemoteDataState
|
||||||
notify,
|
}
|
||||||
source,
|
|
||||||
values,
|
|
||||||
tagKey,
|
|
||||||
filter,
|
|
||||||
shouldShowMoreValues,
|
|
||||||
} = this.props
|
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
|
class TagValueList extends PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
|
super(props)
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
tagValues: [],
|
||||||
|
searchTerm: '',
|
||||||
|
loading: RemoteDataState.Loading,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async componentDidMount() {
|
||||||
|
this.setState({loading: RemoteDataState.Loading})
|
||||||
|
try {
|
||||||
|
const tagValues = await this.fetchTagValues()
|
||||||
|
this.setState({tagValues, loading: RemoteDataState.Done})
|
||||||
|
} catch (error) {
|
||||||
|
this.setState({loading: RemoteDataState.Error})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const {tagKey} = this.props
|
||||||
|
const {searchTerm} = this.state
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{values.map((v, i) => (
|
<div className="flux-schema--filter">
|
||||||
<TagValueListItem
|
<input
|
||||||
key={i}
|
className="form-control input-xs"
|
||||||
db={db}
|
placeholder={`Filter within ${tagKey}`}
|
||||||
value={v}
|
type="text"
|
||||||
tagKey={tagKey}
|
spellCheck={false}
|
||||||
source={source}
|
autoComplete="off"
|
||||||
filter={filter}
|
value={searchTerm}
|
||||||
notify={notify}
|
onClick={this.handleClick}
|
||||||
|
onChange={this.onSearch}
|
||||||
/>
|
/>
|
||||||
))}
|
</div>
|
||||||
{shouldShowMoreValues && (
|
{this.tagValues}
|
||||||
<div className="flux-schema-tree flux-schema--child">
|
|
||||||
<div className="flux-schema--item no-hover">
|
|
||||||
<button
|
|
||||||
className="btn btn-xs btn-default increase-values-limit"
|
|
||||||
onClick={this.handleClick}
|
|
||||||
>
|
|
||||||
{this.buttonValue}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
private get tagValues(): JSX.Element | JSX.Element[] {
|
||||||
e.stopPropagation()
|
const {source, db, tagKey, measurement, notify} = this.props
|
||||||
this.props.onLoadMoreValues()
|
const {searchTerm, loading} = this.state
|
||||||
}
|
|
||||||
|
|
||||||
private get buttonValue(): string | JSX.Element {
|
if (loading === RemoteDataState.Loading) {
|
||||||
const {isLoadingMoreValues, loadMoreCount, tagKey} = this.props
|
return <LoaderSkeleton />
|
||||||
|
|
||||||
if (isLoadingMoreValues) {
|
|
||||||
return <LoadingSpinner />
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return `Load next ${loadMoreCount} values for ${tagKey}`
|
const term = searchTerm.toLocaleLowerCase()
|
||||||
|
const tagValues = this.state.tagValues.filter(
|
||||||
|
tv => tv !== '' && tv.toLocaleLowerCase().includes(term)
|
||||||
|
)
|
||||||
|
|
||||||
|
if (tagValues.length) {
|
||||||
|
return tagValues.map(tagValue => (
|
||||||
|
<TagValueListItem
|
||||||
|
source={source}
|
||||||
|
db={db}
|
||||||
|
searchTerm={searchTerm}
|
||||||
|
tagValue={tagValue}
|
||||||
|
tagKey={tagKey}
|
||||||
|
measurement={measurement}
|
||||||
|
key={tagValue}
|
||||||
|
notify={notify}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className="flux-schema-tree flux-schema--child">
|
||||||
|
<div className="flux-schema--item no-hover" onClick={this.handleClick}>
|
||||||
|
<div className="no-results">No more tag values.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fetchTagValues(): Promise<string[]> {
|
||||||
|
const {source, db, tagKey, measurement} = this.props
|
||||||
|
const limit = 50
|
||||||
|
|
||||||
|
const filter = measurement
|
||||||
|
? [{key: '_measurement', value: measurement}]
|
||||||
|
: []
|
||||||
|
|
||||||
|
const response = await fetchTagValues({
|
||||||
|
source,
|
||||||
|
bucket: db,
|
||||||
|
tagKey,
|
||||||
|
filter,
|
||||||
|
limit,
|
||||||
|
})
|
||||||
|
const tagValues = parseValuesColumn(response)
|
||||||
|
return tagValues
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({
|
||||||
|
searchTerm: e.target.value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleClick = (e: MouseEvent<HTMLInputElement>) => {
|
||||||
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default TagValueList
|
||||||
|
|
|
@ -1,154 +1,104 @@
|
||||||
import React, {PureComponent, MouseEvent, ChangeEvent} from 'react'
|
// Libraries
|
||||||
|
import React, {PureComponent} from 'react'
|
||||||
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
import {CopyToClipboard} from 'react-copy-to-clipboard'
|
||||||
|
|
||||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
// Components
|
||||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
import FieldList from 'src/flux/components/FieldList'
|
||||||
import TagList from 'src/flux/components/TagList'
|
|
||||||
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
|
||||||
|
|
||||||
|
// Utils
|
||||||
import {
|
import {
|
||||||
notifyCopyToClipboardSuccess,
|
notifyCopyToClipboardSuccess,
|
||||||
notifyCopyToClipboardFailed,
|
notifyCopyToClipboardFailed,
|
||||||
} from 'src/shared/copy/notifications'
|
} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
import {
|
// Constants
|
||||||
Source,
|
import {OpenState} from 'src/flux/constants/explorer'
|
||||||
SchemaFilter,
|
|
||||||
RemoteDataState,
|
// types
|
||||||
NotificationAction,
|
import {Source, NotificationAction} from 'src/types'
|
||||||
} from 'src/types'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
db: string
|
db: string
|
||||||
source: Source
|
source: Source
|
||||||
|
searchTerm: string
|
||||||
|
tagValue: string
|
||||||
tagKey: string
|
tagKey: string
|
||||||
value: string
|
|
||||||
filter: SchemaFilter[]
|
|
||||||
notify: NotificationAction
|
notify: NotificationAction
|
||||||
|
measurement: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
isOpen: boolean
|
opened: OpenState
|
||||||
tags: string[]
|
|
||||||
loading: RemoteDataState
|
|
||||||
searchTerm: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ErrorHandling
|
||||||
class TagValueListItem extends PureComponent<Props, State> {
|
class TagValueListItem extends PureComponent<Props, State> {
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
isOpen: false,
|
opened: OpenState.UNOPENED,
|
||||||
tags: [],
|
|
||||||
loading: RemoteDataState.NotStarted,
|
|
||||||
searchTerm: '',
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {db, source, value, notify} = this.props
|
const {db, source, tagValue, tagKey, notify, measurement} = this.props
|
||||||
const {searchTerm, isOpen} = this.state
|
const {opened} = this.state
|
||||||
|
const isOpen = opened === OpenState.OPENED
|
||||||
|
const isUnopen = opened === OpenState.UNOPENED
|
||||||
|
|
||||||
|
const tag = {key: tagKey, value: tagValue}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={this.className} onClick={this.handleClick}>
|
<div
|
||||||
|
className={`flux-schema-tree flux-schema--child ${
|
||||||
|
isOpen ? 'expanded' : ''
|
||||||
|
}`}
|
||||||
|
key={tagValue}
|
||||||
|
onClick={this.handleItemClick}
|
||||||
|
>
|
||||||
<div className="flux-schema--item">
|
<div className="flux-schema--item">
|
||||||
<div className="flex-schema-item-group">
|
<div className="flex-schema-item-group">
|
||||||
<div className="flux-schema--expander" />
|
<div className="flux-schema--expander" />
|
||||||
{value}
|
{tagValue}
|
||||||
<span className="flux-schema--type">Tag Value</span>
|
<span className="flux-schema--type">Tag Value</span>
|
||||||
</div>
|
</div>
|
||||||
<CopyToClipboard text={value} onCopy={this.handleCopyAttempt}>
|
<CopyToClipboard text={tagValue} onCopy={this.handleCopyAttempt}>
|
||||||
<div className="flux-schema-copy" onClick={this.handleClickCopy}>
|
<div className="flux-schema-copy" onClick={this.handleClickCopy}>
|
||||||
<span className="icon duplicate" title="copy to clipboard" />
|
<span className="icon duplicate" title="copy to clipboard" />
|
||||||
Copy
|
Copy
|
||||||
</div>
|
</div>
|
||||||
</CopyToClipboard>
|
</CopyToClipboard>
|
||||||
</div>
|
</div>
|
||||||
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
{!isUnopen && (
|
||||||
{this.isLoading && <LoaderSkeleton />}
|
<div className={`flux-schema--children ${isOpen ? '' : 'hidden'}`}>
|
||||||
{!this.isLoading && (
|
<FieldList
|
||||||
<>
|
db={db}
|
||||||
{!!this.tags.length && (
|
source={source}
|
||||||
<div className="flux-schema--filter">
|
tag={tag}
|
||||||
<input
|
notify={notify}
|
||||||
className="form-control input-xs"
|
measurement={measurement}
|
||||||
placeholder={`Filter within ${value}`}
|
/>
|
||||||
type="text"
|
</div>
|
||||||
spellCheck={false}
|
)}
|
||||||
autoComplete="off"
|
|
||||||
value={searchTerm}
|
|
||||||
onClick={this.handleInputClick}
|
|
||||||
onChange={this.onSearch}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
<TagList
|
|
||||||
db={db}
|
|
||||||
notify={notify}
|
|
||||||
source={source}
|
|
||||||
tags={this.tags}
|
|
||||||
filter={this.filter}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get isLoading(): boolean {
|
private handleItemClick = (e): void => {
|
||||||
return this.state.loading === RemoteDataState.Loading
|
|
||||||
}
|
|
||||||
|
|
||||||
private get filter(): SchemaFilter[] {
|
|
||||||
const {filter, tagKey, value} = this.props
|
|
||||||
|
|
||||||
return [...filter, {key: tagKey, value}]
|
|
||||||
}
|
|
||||||
|
|
||||||
private get tags(): string[] {
|
|
||||||
const {tags, searchTerm} = this.state
|
|
||||||
const term = searchTerm.toLocaleLowerCase()
|
|
||||||
return tags.filter(t => t.toLocaleLowerCase().includes(term))
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getTags() {
|
|
||||||
const {db, source} = this.props
|
|
||||||
|
|
||||||
this.setState({loading: RemoteDataState.Loading})
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetchTagKeys(source, db, this.filter)
|
|
||||||
const tags = parseValuesColumn(response)
|
|
||||||
this.setState({tags, loading: RemoteDataState.Done})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private get className(): string {
|
|
||||||
const {isOpen} = this.state
|
|
||||||
const openClass = isOpen ? 'expanded' : ''
|
|
||||||
|
|
||||||
return `flux-schema-tree flux-schema--child ${openClass}`
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleInputClick = (e: MouseEvent<HTMLInputElement>) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
|
||||||
if (this.isFetchable) {
|
const opened = this.state.opened
|
||||||
this.getTags()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({isOpen: !this.state.isOpen})
|
if (opened === OpenState.OPENED) {
|
||||||
|
this.setState({opened: OpenState.ClOSED})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.setState({opened: OpenState.OPENED})
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClickCopy = e => {
|
private handleClickCopy = (e): void => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,22 +113,6 @@ class TagValueListItem extends PureComponent<Props, State> {
|
||||||
notify(notifyCopyToClipboardFailed(copiedText))
|
notify(notifyCopyToClipboardFailed(copiedText))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSearch = (e: ChangeEvent<HTMLInputElement>) => {
|
|
||||||
this.setState({
|
|
||||||
searchTerm: e.target.value,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private get isFetchable(): boolean {
|
|
||||||
const {isOpen, loading} = this.state
|
|
||||||
|
|
||||||
return (
|
|
||||||
!isOpen &&
|
|
||||||
(loading === RemoteDataState.NotStarted ||
|
|
||||||
loading === RemoteDataState.Error)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TagValueListItem
|
export default TagValueListItem
|
||||||
|
|
|
@ -1 +1,7 @@
|
||||||
export const TAG_VALUES_LIMIT = 10
|
export const TAG_VALUES_LIMIT = 10
|
||||||
|
|
||||||
|
export enum OpenState {
|
||||||
|
UNOPENED = 'unopened',
|
||||||
|
OPENED = 'opened',
|
||||||
|
ClOSED = 'closed',
|
||||||
|
}
|
||||||
|
|
|
@ -12,12 +12,27 @@ export const measurements = async (
|
||||||
|> range(start:-24h)
|
|> range(start:-24h)
|
||||||
|> group(by:["_measurement"])
|
|> group(by:["_measurement"])
|
||||||
|> distinct(column:"_measurement")
|
|> distinct(column:"_measurement")
|
||||||
|> group()s
|
|> group()
|
||||||
`
|
`
|
||||||
|
|
||||||
return proxy(source, script)
|
return proxy(source, script)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const fields = async (
|
||||||
|
source: Source,
|
||||||
|
bucket: string,
|
||||||
|
filter: SchemaFilter[],
|
||||||
|
limit: number
|
||||||
|
): Promise<any> => {
|
||||||
|
return await tagValues({
|
||||||
|
bucket,
|
||||||
|
source,
|
||||||
|
tagKey: '_field',
|
||||||
|
limit,
|
||||||
|
filter,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export const tagKeys = async (
|
export const tagKeys = async (
|
||||||
source: Source,
|
source: Source,
|
||||||
bucket: string,
|
bucket: string,
|
||||||
|
|
Loading…
Reference in New Issue